Georg Brandl | 1a3284e | 2007-12-02 09:40:06 +0000 | [diff] [blame] | 1 | import builtins |
Terry Jan Reedy | bfbaa6b | 2016-08-31 00:50:55 -0400 | [diff] [blame] | 2 | import keyword |
| 3 | import re |
| 4 | import time |
| 5 | |
Terry Jan Reedy | 6fa5bdc | 2016-05-28 13:22:31 -0400 | [diff] [blame] | 6 | from idlelib.config import idleConf |
Terry Jan Reedy | bfbaa6b | 2016-08-31 00:50:55 -0400 | [diff] [blame] | 7 | from idlelib.delegator import Delegator |
David Scherer | 7aced17 | 2000-08-15 01:13:23 +0000 | [diff] [blame] | 8 | |
Kurt B. Kaiser | 0bc3d98 | 2004-03-15 04:26:37 +0000 | [diff] [blame] | 9 | DEBUG = False |
David Scherer | 7aced17 | 2000-08-15 01:13:23 +0000 | [diff] [blame] | 10 | |
Thomas Wouters | 0e3f591 | 2006-08-11 14:57:12 +0000 | [diff] [blame] | 11 | def any(name, alternates): |
| 12 | "Return a named group pattern matching list of alternates." |
| 13 | return "(?P<%s>" % name + "|".join(alternates) + ")" |
David Scherer | 7aced17 | 2000-08-15 01:13:23 +0000 | [diff] [blame] | 14 | |
| 15 | def make_pat(): |
| 16 | kw = r"\b" + any("KEYWORD", keyword.kwlist) + r"\b" |
Georg Brandl | 1a3284e | 2007-12-02 09:40:06 +0000 | [diff] [blame] | 17 | builtinlist = [str(name) for name in dir(builtins) |
Terry Jan Reedy | dc224f8 | 2012-01-16 03:20:27 -0500 | [diff] [blame] | 18 | if not name.startswith('_') and \ |
| 19 | name not in keyword.kwlist] |
Thomas Wouters | 0e3f591 | 2006-08-11 14:57:12 +0000 | [diff] [blame] | 20 | builtin = r"([^.'\"\\#]\b|^)" + any("BUILTIN", builtinlist) + r"\b" |
David Scherer | 7aced17 | 2000-08-15 01:13:23 +0000 | [diff] [blame] | 21 | comment = any("COMMENT", [r"#[^\n]*"]) |
Terry Jan Reedy | da58533 | 2018-04-02 01:47:46 -0400 | [diff] [blame] | 22 | stringprefix = r"(?i:r|u|f|fr|rf|b|br|rb)?" |
Ned Deily | 5e92a1e | 2012-05-29 22:55:43 -0700 | [diff] [blame] | 23 | sqstring = stringprefix + r"'[^'\\\n]*(\\.[^'\\\n]*)*'?" |
| 24 | dqstring = stringprefix + r'"[^"\\\n]*(\\.[^"\\\n]*)*"?' |
| 25 | sq3string = stringprefix + r"'''[^'\\]*((\\.|'(?!''))[^'\\]*)*(''')?" |
| 26 | dq3string = stringprefix + r'"""[^"\\]*((\\.|"(?!""))[^"\\]*)*(""")?' |
David Scherer | 7aced17 | 2000-08-15 01:13:23 +0000 | [diff] [blame] | 27 | string = any("STRING", [sq3string, dq3string, sqstring, dqstring]) |
Kurt B. Kaiser | 0bc3d98 | 2004-03-15 04:26:37 +0000 | [diff] [blame] | 28 | return kw + "|" + builtin + "|" + comment + "|" + string +\ |
| 29 | "|" + any("SYNC", [r"\n"]) |
David Scherer | 7aced17 | 2000-08-15 01:13:23 +0000 | [diff] [blame] | 30 | |
| 31 | prog = re.compile(make_pat(), re.S) |
| 32 | idprog = re.compile(r"\s+(\w+)", re.S) |
| 33 | |
Tal Einat | c87d9f4 | 2018-09-23 15:23:15 +0300 | [diff] [blame] | 34 | def color_config(text): |
| 35 | """Set color options of Text widget. |
Terry Jan Reedy | 2bac3b7 | 2016-05-29 01:40:22 -0400 | [diff] [blame] | 36 | |
Tal Einat | c87d9f4 | 2018-09-23 15:23:15 +0300 | [diff] [blame] | 37 | If ColorDelegator is used, this should be called first. |
| 38 | """ |
| 39 | # Called from htest, TextFrame, Editor, and Turtledemo. |
Terry Jan Reedy | 2bac3b7 | 2016-05-29 01:40:22 -0400 | [diff] [blame] | 40 | # Not automatic because ColorDelegator does not know 'text'. |
| 41 | theme = idleConf.CurrentTheme() |
| 42 | normal_colors = idleConf.GetHighlight(theme, 'normal') |
| 43 | cursor_color = idleConf.GetHighlight(theme, 'cursor', fgBg='fg') |
| 44 | select_colors = idleConf.GetHighlight(theme, 'hilite') |
| 45 | text.config( |
| 46 | foreground=normal_colors['foreground'], |
| 47 | background=normal_colors['background'], |
| 48 | insertbackground=cursor_color, |
| 49 | selectforeground=select_colors['foreground'], |
| 50 | selectbackground=select_colors['background'], |
Terry Jan Reedy | 1080d13 | 2016-06-09 21:09:15 -0400 | [diff] [blame] | 51 | inactiveselectbackground=select_colors['background'], # new in 8.5 |
| 52 | ) |
Terry Jan Reedy | 2bac3b7 | 2016-05-29 01:40:22 -0400 | [diff] [blame] | 53 | |
Tal Einat | c87d9f4 | 2018-09-23 15:23:15 +0300 | [diff] [blame] | 54 | |
David Scherer | 7aced17 | 2000-08-15 01:13:23 +0000 | [diff] [blame] | 55 | class ColorDelegator(Delegator): |
Cheryl Sabella | ee0f927 | 2019-02-19 00:11:18 -0500 | [diff] [blame] | 56 | """Delegator for syntax highlighting (text coloring). |
| 57 | |
Cheryl Sabella | ed1deb0 | 2019-02-27 08:21:16 -0500 | [diff] [blame] | 58 | Instance variables: |
| 59 | delegate: Delegator below this one in the stack, meaning the |
| 60 | one this one delegates to. |
| 61 | |
| 62 | Used to track state: |
| 63 | after_id: Identifier for scheduled after event, which is a |
| 64 | timer for colorizing the text. |
Cheryl Sabella | ee0f927 | 2019-02-19 00:11:18 -0500 | [diff] [blame] | 65 | allow_colorizing: Boolean toggle for applying colorizing. |
| 66 | colorizing: Boolean flag when colorizing is in process. |
| 67 | stop_colorizing: Boolean flag to end an active colorizing |
| 68 | process. |
Cheryl Sabella | ee0f927 | 2019-02-19 00:11:18 -0500 | [diff] [blame] | 69 | """ |
David Scherer | 7aced17 | 2000-08-15 01:13:23 +0000 | [diff] [blame] | 70 | |
| 71 | def __init__(self): |
| 72 | Delegator.__init__(self) |
Cheryl Sabella | ed1deb0 | 2019-02-27 08:21:16 -0500 | [diff] [blame] | 73 | self.init_state() |
David Scherer | 7aced17 | 2000-08-15 01:13:23 +0000 | [diff] [blame] | 74 | self.prog = prog |
| 75 | self.idprog = idprog |
Steven M. Gava | b77d343 | 2002-03-02 07:16:21 +0000 | [diff] [blame] | 76 | self.LoadTagDefs() |
David Scherer | 7aced17 | 2000-08-15 01:13:23 +0000 | [diff] [blame] | 77 | |
Cheryl Sabella | ed1deb0 | 2019-02-27 08:21:16 -0500 | [diff] [blame] | 78 | def init_state(self): |
| 79 | "Initialize variables that track colorizing state." |
| 80 | self.after_id = None |
| 81 | self.allow_colorizing = True |
| 82 | self.stop_colorizing = False |
| 83 | self.colorizing = False |
| 84 | |
David Scherer | 7aced17 | 2000-08-15 01:13:23 +0000 | [diff] [blame] | 85 | def setdelegate(self, delegate): |
Cheryl Sabella | ee0f927 | 2019-02-19 00:11:18 -0500 | [diff] [blame] | 86 | """Set the delegate for this instance. |
| 87 | |
| 88 | A delegate is an instance of a Delegator class and each |
| 89 | delegate points to the next delegator in the stack. This |
| 90 | allows multiple delegators to be chained together for a |
| 91 | widget. The bottom delegate for a colorizer is a Text |
| 92 | widget. |
| 93 | |
| 94 | If there is a delegate, also start the colorizing process. |
| 95 | """ |
David Scherer | 7aced17 | 2000-08-15 01:13:23 +0000 | [diff] [blame] | 96 | if self.delegate is not None: |
| 97 | self.unbind("<<toggle-auto-coloring>>") |
| 98 | Delegator.setdelegate(self, delegate) |
| 99 | if delegate is not None: |
| 100 | self.config_colors() |
| 101 | self.bind("<<toggle-auto-coloring>>", self.toggle_colorize_event) |
| 102 | self.notify_range("1.0", "end") |
Roger Serwy | 7733be8 | 2013-04-07 12:41:16 -0500 | [diff] [blame] | 103 | else: |
Cheryl Sabella | ee0f927 | 2019-02-19 00:11:18 -0500 | [diff] [blame] | 104 | # No delegate - stop any colorizing. |
Roger Serwy | 7733be8 | 2013-04-07 12:41:16 -0500 | [diff] [blame] | 105 | self.stop_colorizing = True |
| 106 | self.allow_colorizing = False |
David Scherer | 7aced17 | 2000-08-15 01:13:23 +0000 | [diff] [blame] | 107 | |
| 108 | def config_colors(self): |
Cheryl Sabella | ee0f927 | 2019-02-19 00:11:18 -0500 | [diff] [blame] | 109 | "Configure text widget tags with colors from tagdefs." |
David Scherer | 7aced17 | 2000-08-15 01:13:23 +0000 | [diff] [blame] | 110 | for tag, cnf in self.tagdefs.items(): |
Cheryl Sabella | ee0f927 | 2019-02-19 00:11:18 -0500 | [diff] [blame] | 111 | self.tag_configure(tag, **cnf) |
David Scherer | 7aced17 | 2000-08-15 01:13:23 +0000 | [diff] [blame] | 112 | self.tag_raise('sel') |
Kurt B. Kaiser | 6655e4b | 2002-12-31 16:03:23 +0000 | [diff] [blame] | 113 | |
Steven M. Gava | b77d343 | 2002-03-02 07:16:21 +0000 | [diff] [blame] | 114 | def LoadTagDefs(self): |
Cheryl Sabella | ee0f927 | 2019-02-19 00:11:18 -0500 | [diff] [blame] | 115 | "Create dictionary of tag names to text colors." |
Terry Jan Reedy | d0c0f00 | 2015-11-12 15:02:57 -0500 | [diff] [blame] | 116 | theme = idleConf.CurrentTheme() |
Steven M. Gava | b77d343 | 2002-03-02 07:16:21 +0000 | [diff] [blame] | 117 | self.tagdefs = { |
| 118 | "COMMENT": idleConf.GetHighlight(theme, "comment"), |
| 119 | "KEYWORD": idleConf.GetHighlight(theme, "keyword"), |
Kurt B. Kaiser | 73360a3 | 2004-03-08 18:15:31 +0000 | [diff] [blame] | 120 | "BUILTIN": idleConf.GetHighlight(theme, "builtin"), |
Steven M. Gava | b77d343 | 2002-03-02 07:16:21 +0000 | [diff] [blame] | 121 | "STRING": idleConf.GetHighlight(theme, "string"), |
| 122 | "DEFINITION": idleConf.GetHighlight(theme, "definition"), |
| 123 | "SYNC": {'background':None,'foreground':None}, |
| 124 | "TODO": {'background':None,'foreground':None}, |
Kurt B. Kaiser | 92b5ca3 | 2002-12-17 21:16:12 +0000 | [diff] [blame] | 125 | "ERROR": idleConf.GetHighlight(theme, "error"), |
Steven M. Gava | b77d343 | 2002-03-02 07:16:21 +0000 | [diff] [blame] | 126 | # The following is used by ReplaceDialog: |
| 127 | "hit": idleConf.GetHighlight(theme, "hit"), |
| 128 | } |
Kurt B. Kaiser | 6655e4b | 2002-12-31 16:03:23 +0000 | [diff] [blame] | 129 | |
Guido van Rossum | be19ed7 | 2007-02-09 05:37:30 +0000 | [diff] [blame] | 130 | if DEBUG: print('tagdefs',self.tagdefs) |
David Scherer | 7aced17 | 2000-08-15 01:13:23 +0000 | [diff] [blame] | 131 | |
| 132 | def insert(self, index, chars, tags=None): |
Cheryl Sabella | ee0f927 | 2019-02-19 00:11:18 -0500 | [diff] [blame] | 133 | "Insert chars into widget at index and mark for colorizing." |
David Scherer | 7aced17 | 2000-08-15 01:13:23 +0000 | [diff] [blame] | 134 | index = self.index(index) |
| 135 | self.delegate.insert(index, chars, tags) |
| 136 | self.notify_range(index, index + "+%dc" % len(chars)) |
| 137 | |
| 138 | def delete(self, index1, index2=None): |
Cheryl Sabella | ee0f927 | 2019-02-19 00:11:18 -0500 | [diff] [blame] | 139 | "Delete chars between indexes and mark for colorizing." |
David Scherer | 7aced17 | 2000-08-15 01:13:23 +0000 | [diff] [blame] | 140 | index1 = self.index(index1) |
| 141 | self.delegate.delete(index1, index2) |
| 142 | self.notify_range(index1) |
| 143 | |
David Scherer | 7aced17 | 2000-08-15 01:13:23 +0000 | [diff] [blame] | 144 | def notify_range(self, index1, index2=None): |
Cheryl Sabella | ee0f927 | 2019-02-19 00:11:18 -0500 | [diff] [blame] | 145 | "Mark text changes for processing and restart colorizing, if active." |
David Scherer | 7aced17 | 2000-08-15 01:13:23 +0000 | [diff] [blame] | 146 | self.tag_add("TODO", index1, index2) |
| 147 | if self.after_id: |
Guido van Rossum | be19ed7 | 2007-02-09 05:37:30 +0000 | [diff] [blame] | 148 | if DEBUG: print("colorizing already scheduled") |
David Scherer | 7aced17 | 2000-08-15 01:13:23 +0000 | [diff] [blame] | 149 | return |
| 150 | if self.colorizing: |
Kurt B. Kaiser | 0bc3d98 | 2004-03-15 04:26:37 +0000 | [diff] [blame] | 151 | self.stop_colorizing = True |
Guido van Rossum | be19ed7 | 2007-02-09 05:37:30 +0000 | [diff] [blame] | 152 | if DEBUG: print("stop colorizing") |
David Scherer | 7aced17 | 2000-08-15 01:13:23 +0000 | [diff] [blame] | 153 | if self.allow_colorizing: |
Guido van Rossum | be19ed7 | 2007-02-09 05:37:30 +0000 | [diff] [blame] | 154 | if DEBUG: print("schedule colorizing") |
David Scherer | 7aced17 | 2000-08-15 01:13:23 +0000 | [diff] [blame] | 155 | self.after_id = self.after(1, self.recolorize) |
Cheryl Sabella | ee0f927 | 2019-02-19 00:11:18 -0500 | [diff] [blame] | 156 | return |
David Scherer | 7aced17 | 2000-08-15 01:13:23 +0000 | [diff] [blame] | 157 | |
Cheryl Sabella | b9f0354 | 2019-03-01 05:19:40 -0500 | [diff] [blame^] | 158 | def close(self): |
David Scherer | 7aced17 | 2000-08-15 01:13:23 +0000 | [diff] [blame] | 159 | if self.after_id: |
| 160 | after_id = self.after_id |
| 161 | self.after_id = None |
Guido van Rossum | be19ed7 | 2007-02-09 05:37:30 +0000 | [diff] [blame] | 162 | if DEBUG: print("cancel scheduled recolorizer") |
David Scherer | 7aced17 | 2000-08-15 01:13:23 +0000 | [diff] [blame] | 163 | self.after_cancel(after_id) |
Kurt B. Kaiser | 0bc3d98 | 2004-03-15 04:26:37 +0000 | [diff] [blame] | 164 | self.allow_colorizing = False |
| 165 | self.stop_colorizing = True |
David Scherer | 7aced17 | 2000-08-15 01:13:23 +0000 | [diff] [blame] | 166 | |
Cheryl Sabella | ee0f927 | 2019-02-19 00:11:18 -0500 | [diff] [blame] | 167 | def toggle_colorize_event(self, event=None): |
| 168 | """Toggle colorizing on and off. |
| 169 | |
| 170 | When toggling off, if colorizing is scheduled or is in |
| 171 | process, it will be cancelled and/or stopped. |
| 172 | |
| 173 | When toggling on, colorizing will be scheduled. |
| 174 | """ |
David Scherer | 7aced17 | 2000-08-15 01:13:23 +0000 | [diff] [blame] | 175 | if self.after_id: |
| 176 | after_id = self.after_id |
| 177 | self.after_id = None |
Guido van Rossum | be19ed7 | 2007-02-09 05:37:30 +0000 | [diff] [blame] | 178 | if DEBUG: print("cancel scheduled recolorizer") |
David Scherer | 7aced17 | 2000-08-15 01:13:23 +0000 | [diff] [blame] | 179 | self.after_cancel(after_id) |
| 180 | if self.allow_colorizing and self.colorizing: |
Guido van Rossum | be19ed7 | 2007-02-09 05:37:30 +0000 | [diff] [blame] | 181 | if DEBUG: print("stop colorizing") |
Kurt B. Kaiser | 0bc3d98 | 2004-03-15 04:26:37 +0000 | [diff] [blame] | 182 | self.stop_colorizing = True |
David Scherer | 7aced17 | 2000-08-15 01:13:23 +0000 | [diff] [blame] | 183 | self.allow_colorizing = not self.allow_colorizing |
| 184 | if self.allow_colorizing and not self.colorizing: |
| 185 | self.after_id = self.after(1, self.recolorize) |
Kurt B. Kaiser | 6df4bf2 | 2001-07-13 00:04:24 +0000 | [diff] [blame] | 186 | if DEBUG: |
Guido van Rossum | be19ed7 | 2007-02-09 05:37:30 +0000 | [diff] [blame] | 187 | print("auto colorizing turned",\ |
| 188 | self.allow_colorizing and "on" or "off") |
David Scherer | 7aced17 | 2000-08-15 01:13:23 +0000 | [diff] [blame] | 189 | return "break" |
| 190 | |
| 191 | def recolorize(self): |
Cheryl Sabella | ee0f927 | 2019-02-19 00:11:18 -0500 | [diff] [blame] | 192 | """Timer event (every 1ms) to colorize text. |
| 193 | |
| 194 | Colorizing is only attempted when the text widget exists, |
| 195 | when colorizing is toggled on, and when the colorizing |
| 196 | process is not already running. |
| 197 | |
| 198 | After colorizing is complete, some cleanup is done to |
Cheryl Sabella | b9f0354 | 2019-03-01 05:19:40 -0500 | [diff] [blame^] | 199 | make sure that all the text has been colorized. |
Cheryl Sabella | ee0f927 | 2019-02-19 00:11:18 -0500 | [diff] [blame] | 200 | """ |
David Scherer | 7aced17 | 2000-08-15 01:13:23 +0000 | [diff] [blame] | 201 | self.after_id = None |
| 202 | if not self.delegate: |
Guido van Rossum | be19ed7 | 2007-02-09 05:37:30 +0000 | [diff] [blame] | 203 | if DEBUG: print("no delegate") |
David Scherer | 7aced17 | 2000-08-15 01:13:23 +0000 | [diff] [blame] | 204 | return |
| 205 | if not self.allow_colorizing: |
Guido van Rossum | be19ed7 | 2007-02-09 05:37:30 +0000 | [diff] [blame] | 206 | if DEBUG: print("auto colorizing is off") |
David Scherer | 7aced17 | 2000-08-15 01:13:23 +0000 | [diff] [blame] | 207 | return |
| 208 | if self.colorizing: |
Guido van Rossum | be19ed7 | 2007-02-09 05:37:30 +0000 | [diff] [blame] | 209 | if DEBUG: print("already colorizing") |
David Scherer | 7aced17 | 2000-08-15 01:13:23 +0000 | [diff] [blame] | 210 | return |
| 211 | try: |
Kurt B. Kaiser | 0bc3d98 | 2004-03-15 04:26:37 +0000 | [diff] [blame] | 212 | self.stop_colorizing = False |
| 213 | self.colorizing = True |
Guido van Rossum | be19ed7 | 2007-02-09 05:37:30 +0000 | [diff] [blame] | 214 | if DEBUG: print("colorizing...") |
Victor Stinner | fe98e2f | 2012-04-29 03:01:20 +0200 | [diff] [blame] | 215 | t0 = time.perf_counter() |
David Scherer | 7aced17 | 2000-08-15 01:13:23 +0000 | [diff] [blame] | 216 | self.recolorize_main() |
Victor Stinner | fe98e2f | 2012-04-29 03:01:20 +0200 | [diff] [blame] | 217 | t1 = time.perf_counter() |
Guido van Rossum | be19ed7 | 2007-02-09 05:37:30 +0000 | [diff] [blame] | 218 | if DEBUG: print("%.3f seconds" % (t1-t0)) |
David Scherer | 7aced17 | 2000-08-15 01:13:23 +0000 | [diff] [blame] | 219 | finally: |
Kurt B. Kaiser | 0bc3d98 | 2004-03-15 04:26:37 +0000 | [diff] [blame] | 220 | self.colorizing = False |
David Scherer | 7aced17 | 2000-08-15 01:13:23 +0000 | [diff] [blame] | 221 | if self.allow_colorizing and self.tag_nextrange("TODO", "1.0"): |
Guido van Rossum | be19ed7 | 2007-02-09 05:37:30 +0000 | [diff] [blame] | 222 | if DEBUG: print("reschedule colorizing") |
David Scherer | 7aced17 | 2000-08-15 01:13:23 +0000 | [diff] [blame] | 223 | self.after_id = self.after(1, self.recolorize) |
David Scherer | 7aced17 | 2000-08-15 01:13:23 +0000 | [diff] [blame] | 224 | |
| 225 | def recolorize_main(self): |
Cheryl Sabella | ee0f927 | 2019-02-19 00:11:18 -0500 | [diff] [blame] | 226 | "Evaluate text and apply colorizing tags." |
David Scherer | 7aced17 | 2000-08-15 01:13:23 +0000 | [diff] [blame] | 227 | next = "1.0" |
Kurt B. Kaiser | 0bc3d98 | 2004-03-15 04:26:37 +0000 | [diff] [blame] | 228 | while True: |
David Scherer | 7aced17 | 2000-08-15 01:13:23 +0000 | [diff] [blame] | 229 | item = self.tag_nextrange("TODO", next) |
| 230 | if not item: |
| 231 | break |
| 232 | head, tail = item |
| 233 | self.tag_remove("SYNC", head, tail) |
| 234 | item = self.tag_prevrange("SYNC", head) |
| 235 | if item: |
| 236 | head = item[1] |
| 237 | else: |
| 238 | head = "1.0" |
| 239 | |
| 240 | chars = "" |
| 241 | next = head |
| 242 | lines_to_get = 1 |
Kurt B. Kaiser | 0bc3d98 | 2004-03-15 04:26:37 +0000 | [diff] [blame] | 243 | ok = False |
David Scherer | 7aced17 | 2000-08-15 01:13:23 +0000 | [diff] [blame] | 244 | while not ok: |
| 245 | mark = next |
| 246 | next = self.index(mark + "+%d lines linestart" % |
| 247 | lines_to_get) |
| 248 | lines_to_get = min(lines_to_get * 2, 100) |
| 249 | ok = "SYNC" in self.tag_names(next + "-1c") |
| 250 | line = self.get(mark, next) |
Walter Dörwald | 70a6b49 | 2004-02-12 17:35:32 +0000 | [diff] [blame] | 251 | ##print head, "get", mark, next, "->", repr(line) |
David Scherer | 7aced17 | 2000-08-15 01:13:23 +0000 | [diff] [blame] | 252 | if not line: |
| 253 | return |
Kurt B. Kaiser | e071277 | 2007-08-23 05:25:55 +0000 | [diff] [blame] | 254 | for tag in self.tagdefs: |
David Scherer | 7aced17 | 2000-08-15 01:13:23 +0000 | [diff] [blame] | 255 | self.tag_remove(tag, mark, next) |
| 256 | chars = chars + line |
| 257 | m = self.prog.search(chars) |
| 258 | while m: |
| 259 | for key, value in m.groupdict().items(): |
| 260 | if value: |
| 261 | a, b = m.span(key) |
| 262 | self.tag_add(key, |
| 263 | head + "+%dc" % a, |
| 264 | head + "+%dc" % b) |
| 265 | if value in ("def", "class"): |
| 266 | m1 = self.idprog.match(chars, b) |
| 267 | if m1: |
| 268 | a, b = m1.span(1) |
| 269 | self.tag_add("DEFINITION", |
| 270 | head + "+%dc" % a, |
| 271 | head + "+%dc" % b) |
| 272 | m = self.prog.search(chars, m.end()) |
| 273 | if "SYNC" in self.tag_names(next + "-1c"): |
| 274 | head = next |
| 275 | chars = "" |
| 276 | else: |
Kurt B. Kaiser | 0bc3d98 | 2004-03-15 04:26:37 +0000 | [diff] [blame] | 277 | ok = False |
David Scherer | 7aced17 | 2000-08-15 01:13:23 +0000 | [diff] [blame] | 278 | if not ok: |
| 279 | # We're in an inconsistent state, and the call to |
| 280 | # update may tell us to stop. It may also change |
| 281 | # the correct value for "next" (since this is a |
| 282 | # line.col string, not a true mark). So leave a |
| 283 | # crumb telling the next invocation to resume here |
| 284 | # in case update tells us to leave. |
| 285 | self.tag_add("TODO", next) |
| 286 | self.update() |
| 287 | if self.stop_colorizing: |
Guido van Rossum | be19ed7 | 2007-02-09 05:37:30 +0000 | [diff] [blame] | 288 | if DEBUG: print("colorizing stopped") |
David Scherer | 7aced17 | 2000-08-15 01:13:23 +0000 | [diff] [blame] | 289 | return |
| 290 | |
Kurt B. Kaiser | df506ea | 2005-06-12 04:33:30 +0000 | [diff] [blame] | 291 | def removecolors(self): |
Cheryl Sabella | ee0f927 | 2019-02-19 00:11:18 -0500 | [diff] [blame] | 292 | "Remove all colorizing tags." |
Kurt B. Kaiser | e071277 | 2007-08-23 05:25:55 +0000 | [diff] [blame] | 293 | for tag in self.tagdefs: |
Kurt B. Kaiser | df506ea | 2005-06-12 04:33:30 +0000 | [diff] [blame] | 294 | self.tag_remove(tag, "1.0", "end") |
David Scherer | 7aced17 | 2000-08-15 01:13:23 +0000 | [diff] [blame] | 295 | |
Terry Jan Reedy | 2bac3b7 | 2016-05-29 01:40:22 -0400 | [diff] [blame] | 296 | |
Terry Jan Reedy | cd56736 | 2014-10-17 01:31:35 -0400 | [diff] [blame] | 297 | def _color_delegator(parent): # htest # |
| 298 | from tkinter import Toplevel, Text |
Terry Jan Reedy | 6fa5bdc | 2016-05-28 13:22:31 -0400 | [diff] [blame] | 299 | from idlelib.percolator import Percolator |
Terry Jan Reedy | cd56736 | 2014-10-17 01:31:35 -0400 | [diff] [blame] | 300 | |
| 301 | top = Toplevel(parent) |
| 302 | top.title("Test ColorDelegator") |
Terry Jan Reedy | a748032 | 2016-07-10 17:28:10 -0400 | [diff] [blame] | 303 | x, y = map(int, parent.geometry().split('+')[1:]) |
Terry Jan Reedy | 0e10243 | 2017-01-01 21:21:39 -0500 | [diff] [blame] | 304 | top.geometry("700x250+%d+%d" % (x + 20, y + 175)) |
Terry Jan Reedy | 55966f3 | 2018-04-02 11:18:02 -0400 | [diff] [blame] | 305 | source = ( |
| 306 | "if True: int ('1') # keyword, builtin, string, comment\n" |
| 307 | "elif False: print(0)\n" |
| 308 | "else: float(None)\n" |
| 309 | "if iF + If + IF: 'keyword matching must respect case'\n" |
| 310 | "if'': x or'' # valid string-keyword no-space combinations\n" |
Terry Jan Reedy | 389a48e | 2018-05-15 14:20:38 -0400 | [diff] [blame] | 311 | "async def f(): await g()\n" |
Terry Jan Reedy | da58533 | 2018-04-02 01:47:46 -0400 | [diff] [blame] | 312 | "# All valid prefixes for unicode and byte strings should be colored.\n" |
Terry Jan Reedy | 246cbf2 | 2016-12-27 00:05:26 -0500 | [diff] [blame] | 313 | "'x', '''x''', \"x\", \"\"\"x\"\"\"\n" |
Terry Jan Reedy | da58533 | 2018-04-02 01:47:46 -0400 | [diff] [blame] | 314 | "r'x', u'x', R'x', U'x', f'x', F'x'\n" |
Terry Jan Reedy | 246cbf2 | 2016-12-27 00:05:26 -0500 | [diff] [blame] | 315 | "fr'x', Fr'x', fR'x', FR'x', rf'x', rF'x', Rf'x', RF'x'\n" |
Cheryl Sabella | ee0f927 | 2019-02-19 00:11:18 -0500 | [diff] [blame] | 316 | "b'x',B'x', br'x',Br'x',bR'x',BR'x', rb'x', rB'x',Rb'x',RB'x'\n" |
Terry Jan Reedy | da58533 | 2018-04-02 01:47:46 -0400 | [diff] [blame] | 317 | "# Invalid combinations of legal characters should be half colored.\n" |
Terry Jan Reedy | 389a48e | 2018-05-15 14:20:38 -0400 | [diff] [blame] | 318 | "ur'x', ru'x', uf'x', fu'x', UR'x', ufr'x', rfu'x', xf'x', fx'x'\n" |
Terry Jan Reedy | da58533 | 2018-04-02 01:47:46 -0400 | [diff] [blame] | 319 | ) |
Terry Jan Reedy | cd56736 | 2014-10-17 01:31:35 -0400 | [diff] [blame] | 320 | text = Text(top, background="white") |
David Scherer | 7aced17 | 2000-08-15 01:13:23 +0000 | [diff] [blame] | 321 | text.pack(expand=1, fill="both") |
Terry Jan Reedy | cd56736 | 2014-10-17 01:31:35 -0400 | [diff] [blame] | 322 | text.insert("insert", source) |
| 323 | text.focus_set() |
| 324 | |
Terry Jan Reedy | 2bac3b7 | 2016-05-29 01:40:22 -0400 | [diff] [blame] | 325 | color_config(text) |
David Scherer | 7aced17 | 2000-08-15 01:13:23 +0000 | [diff] [blame] | 326 | p = Percolator(text) |
| 327 | d = ColorDelegator() |
| 328 | p.insertfilter(d) |
David Scherer | 7aced17 | 2000-08-15 01:13:23 +0000 | [diff] [blame] | 329 | |
Tal Einat | c87d9f4 | 2018-09-23 15:23:15 +0300 | [diff] [blame] | 330 | |
David Scherer | 7aced17 | 2000-08-15 01:13:23 +0000 | [diff] [blame] | 331 | if __name__ == "__main__": |
Terry Jan Reedy | ee5ef30 | 2018-06-15 18:20:55 -0400 | [diff] [blame] | 332 | from unittest import main |
| 333 | main('idlelib.idle_test.test_colorizer', verbosity=2, exit=False) |
Terry Jan Reedy | a748032 | 2016-07-10 17:28:10 -0400 | [diff] [blame] | 334 | |
Terry Jan Reedy | 1b392ff | 2014-05-24 18:48:18 -0400 | [diff] [blame] | 335 | from idlelib.idle_test.htest import run |
| 336 | run(_color_delegator) |