88
99DEBUG = False
1010
11+ # Adding a tag to a line takes time proportional to the number of tags
12+ # already in the line, so only the beginning of a line is colorized;
13+ # the rest is usually not visible anyway (gh-103089).
14+ MAX_COLORIZED_LINE = 2000
15+
1116
1217def any (name , alternates ):
1318 "Return a named group pattern matching list of alternates."
@@ -42,6 +47,11 @@ def make_pat():
4247 ]) +
4348 r"))"
4449 )
50+ type_softkw = (
51+ r"^[ \t]*" + # at beginning of line + possible indentation
52+ r"(?P<TYPE_SOFTKW>type)" +
53+ r"(?=[ \t]+(?!(?:" + "|" .join (keyword .kwlist ) + r")\b)[^\W\d])"
54+ )
4555 lazy_softkw = ( # lazy new in 3.15 (+ 2 lines below).
4656 r"^[ \t]*" + # at beginning of line + possible indentation
4757 r"(?P<LAZY_SOFTKW>lazy)" +
@@ -59,7 +69,7 @@ def make_pat():
5969 dq3string = stringprefix + r'"""[^"\\]*((\\.|"(?!""))[^"\\]*)*(""")?'
6070 string = any ("STRING" , [sq3string , dq3string , sqstring , dqstring ])
6171 prog = re .compile ("|" .join ([
62- builtin , comment , string , kw ,
72+ type_softkw , builtin , comment , string , kw ,
6373 match_softkw , case_default ,
6474 case_softkw_and_pattern , lazy_softkw ,
6575 any ("SYNC" , [r"\n" ]),
@@ -75,6 +85,7 @@ def make_pat():
7585 "CASE_SOFTKW" : "KEYWORD" ,
7686 "CASE_DEFAULT_UNDERSCORE" : "KEYWORD" ,
7787 "CASE_SOFTKW2" : "KEYWORD" ,
88+ "TYPE_SOFTKW" : "KEYWORD" ,
7889 "LAZY_SOFTKW" : "KEYWORD" ,
7990}
8091
@@ -344,16 +355,39 @@ def _add_tags_in_section(self, chars, head):
344355 `chars` is a string with the text to parse and to which
345356 highlighting is to be applied.
346357
347- `head` is the index in the text widget where the text is found.
358+ `head` is the index in the text widget where the text is found.
348359 """
349- for m in self .prog .finditer (chars ):
360+ # Positions are relative to the start of the current line, so that
361+ # Tk does not resolve them through the previous lines.
362+ line = int (head .split ('.' )[0 ])
363+ line_start = 0 # Offset of the current line in chars.
364+ tags = []
365+ pos = 0
366+ while True :
367+ m = self .prog .search (chars , pos )
368+ if m is None :
369+ break
350370 for name , matched_text in matched_named_groups (m ):
351371 a , b = m .span (name )
352- self . _add_tag ( a , b , head , name )
372+ tags . append (( a - line_start , b - line_start , head , name ) )
353373 if matched_text in ("def" , "class" ):
354374 if m1 := self .idprog .match (chars , b ):
355375 a , b = m1 .span (1 )
356- self ._add_tag (a , b , head , "DEFINITION" )
376+ tags .append ((a - line_start , b - line_start ,
377+ head , "DEFINITION" ))
378+ pos = m .end ()
379+ if '\n ' in m [0 ]:
380+ line += m [0 ].count ('\n ' )
381+ line_start = m .start () + m [0 ].rindex ('\n ' ) + 1
382+ head = f"{ line } .0"
383+ elif pos - line_start >= MAX_COLORIZED_LINE :
384+ # The rest of a long line is not colorized.
385+ pos = chars .find ('\n ' , pos )
386+ if pos < 0 :
387+ break
388+ # Adding a tag is faster if there are no tags after it.
389+ for args in reversed (tags ):
390+ self ._add_tag (* args )
357391
358392 def removecolors (self ):
359393 "Remove all colorizing tags."
0 commit comments