def __init__(self, master=None, **kwargs): """ Constructor. Get default settings from user's IDLE configuration. """ currentTheme = idleConf.CurrentTheme() textcf = { 'padx': 5, 'wrap': 'word', 'undo': 'True', 'foreground': idleConf.GetHighlight( currentTheme, 'normal', fgBg='fg'), 'background': idleConf.GetHighlight( currentTheme, 'normal', fgBg='bg'), 'highlightcolor': idleConf.GetHighlight( currentTheme, 'hilite', fgBg='fg'), 'highlightbackground': idleConf.GetHighlight( currentTheme, 'hilite', fgBg='bg'), 'insertbackground': idleConf.GetHighlight( currentTheme, 'cursor', fgBg='fg'), 'width': idleConf.GetOption('main', 'EditorWindow', 'width'), 'height': idleConf.GetOption('main', 'EditorWindow', 'height'), } fontWeight = 'normal' if idleConf.GetOption('main', 'EditorWindow', 'font-bold', type='bool'): fontWeight = 'bold' textcf['font'] = (idleConf.GetOption('main', 'EditorWindow', 'font'), idleConf.GetOption('main', 'EditorWindow', 'font-size'), fontWeight) # override defaults with any user-specified settings textcf.update(kwargs) ScrolledText.__init__(self, master, **textcf)
def __init__(self, master=None, **kwargs): # get default settings from user's IDLE configuration currentTheme = idleConf.CurrentTheme() textcf = dict(padx=5, wrap='word', undo='True', foreground=idleConf.GetHighlight(currentTheme, 'normal', fgBg='fg'), background=idleConf.GetHighlight(currentTheme, 'normal', fgBg='bg'), highlightcolor=idleConf.GetHighlight(currentTheme, 'hilite', fgBg='fg'), highlightbackground=idleConf.GetHighlight(currentTheme, 'hilite', fgBg='bg'), insertbackground=idleConf.GetHighlight(currentTheme, 'cursor', fgBg='fg'), width=idleConf.GetOption('main', 'EditorWindow', 'width'), height=idleConf.GetOption('main', 'EditorWindow', 'height')) fontWeight = 'normal' if idleConf.GetOption('main', 'EditorWindow', 'font-bold', type='bool'): fontWeight = 'bold' textcf['font'] = (idleConf.GetOption('main', 'EditorWindow', 'font'), idleConf.GetOption('main', 'EditorWindow', 'font-size'), fontWeight) # override defaults with any user-specified settings textcf.update(kwargs) ScrolledText.__init__(self, master, **textcf)
def drawtext(self): textx = self.x+20-1 texty = self.y-1 labeltext = self.item.GetLabelText() if labeltext: id = self.canvas.create_text(textx, texty, anchor="nw", text=labeltext) self.canvas.tag_bind(id, "<1>", self.select) self.canvas.tag_bind(id, "<Double-1>", self.flip) x0, y0, x1, y1 = self.canvas.bbox(id) textx = max(x1, 200) + 10 text = self.item.GetText() or "<no text>" try: self.entry except AttributeError: pass else: self.edit_finish() try: label = self.label except AttributeError: # padding carefully selected (on Windows) to match Entry widget: self.label = Label(self.canvas, text=text, bd=0, padx=2, pady=2) theme = idleConf.GetOption('main','Theme','name') if self.selected: self.label.configure(idleConf.GetHighlight(theme, 'hilite')) else: self.label.configure(idleConf.GetHighlight(theme, 'normal')) id = self.canvas.create_window(textx, texty, anchor="nw", window=self.label) self.label.bind("<1>", self.select_or_edit) self.label.bind("<Double-1>", self.flip) self.text_id = id
def LoadTagDefs(self): ColorDelegator.LoadTagDefs(self) theme = idleConf.GetOption('main', 'Theme', 'name') self.tagdefs.update({'stdin': {'background': None,'foreground': None},'stdout': idleConf.GetHighlight(theme, 'stdout'), 'stderr': idleConf.GetHighlight(theme, 'stderr'), 'console': idleConf.GetHighlight(theme, 'console') }) return
def ResetColorizer(self): """Update the colour theme""" self._rmcolorizer() self._addcolorizer() theme = idleConf.GetOption('main', 'Theme', 'name') normal_colors = idleConf.GetHighlight(theme, 'normal') cursor_color = idleConf.GetHighlight(theme, 'cursor', fgBg='fg') select_colors = idleConf.GetHighlight(theme, 'hilite') self.text.config(foreground=normal_colors['foreground'], background=normal_colors['background'], insertbackground=cursor_color, selectforeground=select_colors['foreground'], selectbackground=select_colors['background'])
def LoadTagDefs(self): ColorDelegator.LoadTagDefs(self) theme = idleConf.GetOption('main','Theme','name') self.tagdefs.update({ "stdin": {'background':None,'foreground':None}, "stdout": idleConf.GetHighlight(theme, "stdout"), "stderr": idleConf.GetHighlight(theme, "stderr"), "console": idleConf.GetHighlight(theme, "console"), })
def show_find_marks(self): # Get the highlight colors for 'hit' # Do this here (and not in __init__) for color config changes to take # effect immediately currentTheme = idleConf.CurrentTheme() mark_fg = idleConf.GetHighlight(currentTheme, 'hit', fgBg='fg') mark_bg = idleConf.GetHighlight(currentTheme, 'hit', fgBg='bg') self.text.tag_configure("findmark", foreground=mark_fg, background=mark_bg)
def init(self, flist): self.flist = flist # reset pyclbr pyclbr._modules.clear() # create top # NOTE: later we will be passed in container, rather than creating it self.top = top = flist.new_container(own_window=True) self.top.add_component(self) top.top.bind("<Escape>", self.close) if self._htest: # place dialog below parent if running htest top.top.geometry( "+%d+%d" % (flist.root.winfo_rootx(), flist.root.winfo_rooty() + 200)) top.top.focus_set() # create scrolled canvas theme = idleConf.CurrentTheme() background = idleConf.GetHighlight(theme, 'normal')['background'] sc = ScrolledCanvas(top.w, bg=background, highlightthickness=0, takefocus=1) sc.frame.pack(expand=1, fill="both") item = self.rootnode() self.node = node = TreeNode(sc.canvas, None, item) node.update() node.expand()
def init(self, flist): self.flist = flist # reset pyclbr pyclbr._modules.clear() # create top self.top = top = ListedToplevel(flist.root) top.protocol("WM_DELETE_WINDOW", self.close) top.bind("<Escape>", self.close) if self._htest: # place dialog below parent if running htest top.geometry( "+%d+%d" % (flist.root.winfo_rootx(), flist.root.winfo_rooty() + 200)) self.settitle() top.focus_set() # create scrolled canvas theme = idleConf.GetOption('main', 'Theme', 'name') background = idleConf.GetHighlight(theme, 'normal')['background'] sc = ScrolledCanvas(top, bg=background, highlightthickness=0, takefocus=1) sc.frame.pack(expand=1, fill="both") item = self.rootnode() self.node = node = TreeNode(sc.canvas, None, item) node.update() node.expand()
def PaintThemeSample(self): if self.themeIsBuiltin.get(): theme = self.builtinTheme.get() else: theme = self.customTheme.get() for elementTitle in self.themeElements.keys(): element = self.themeElements[elementTitle][0] colours = idleConf.GetHighlight(theme, element) if element == 'cursor': colours['background'] = idleConf.GetHighlight(theme, 'normal', fgBg='bg') if theme in self.changedItems['highlight'].keys(): themeDict = self.changedItems['highlight'][theme] if element + '-foreground' in themeDict: colours['foreground'] = themeDict[element + '-foreground'] if element + '-background' in themeDict: colours['background'] = themeDict[element + '-background'] self.textHighlightSample.tag_config(element, **colours) self.SetColourSample()
def color_config(text): # Called from htest, Editor, and Turtle Demo. '''Set color opitons of Text widget. Should be called whenever ColorDelegator is called. ''' # Not automatic because ColorDelegator does not know 'text'. theme = idleConf.CurrentTheme() normal_colors = idleConf.GetHighlight(theme, 'normal') cursor_color = idleConf.GetHighlight(theme, 'cursor', fgBg='fg') select_colors = idleConf.GetHighlight(theme, 'hilite') text.config( foreground=normal_colors['foreground'], background=normal_colors['background'], insertbackground=cursor_color, selectforeground=select_colors['foreground'], selectbackground=select_colors['background'], ) if TkVersion >= 8.5: text.config(inactiveselectbackground=select_colors['background'])
def set_subcode_colors(self): # This is a HACK to allow for sane coloring with other # color themes. Patches are welcome! from . import SubCode theme = idleConf.GetOption('main', 'Theme', 'name') normal_colors = idleConf.GetHighlight(theme, 'normal') background = normal_colors['background'] def rgb_h2d(c): # Take a '#RRGGBB' and convert to integer tuple R = c[1:3] G = c[3:5] B = c[5:7] return tuple([int(x, 16) for x in (R, G, B)]) def rgb_d2h(c): # (R, B, G) -> '#RRGGBB' c = [min((255, int(x))) for x in c] c = [max((0, int(x))) for x in c] return '#%02X%02X%02X' % tuple(c) def colorhack(rgb, target): # apply some DC offset and "gamma" correction R, G, B = map(float, rgb) mR, mG, mB = target m = lambda x, y: (x + y[0])**y[1] if x < 128 else (x - y[0])**( 1 / y[1]) R = m(R, mR) G = m(G, mG) B = m(B, mB) return (R, G, B) def average(a, b): return [(x[0] + x[1]) / 2.0 for x in zip(a, b)] BACK = rgb_h2d(background) a = (10, 1.03) SubCode.SUBCODE_BACKGROUND = rgb_d2h(colorhack(BACK, (a, a, a))) a = (10, 1.019) b = (10, .98) c = colorhack(BACK, (a, b, a)) HL = rgb_d2h(c) SubCode.SUBCODE_HIGHLIGHT = HL d = average(BACK, c) SubCode.SUBCODE_HIGHLIGHT_OUT = rgb_d2h(d) self.tag_config('ACTIVE', background=SUBCODE_HIGHLIGHT)
def remote_stack_viewer(self): from idlelib import RemoteObjectBrowser oid = self.rpcclt.remotequeue("exec", "stackviewer", ("flist",), {}) if oid is None: self.tkconsole.root.bell() return item = RemoteObjectBrowser.StubObjectTreeItem(self.rpcclt, oid) from idlelib.TreeWidget import ScrolledCanvas, TreeNode top = Toplevel(self.tkconsole.root) theme = idleConf.GetOption('main','Theme','name') background = idleConf.GetHighlight(theme, 'normal')['background'] sc = ScrolledCanvas(top, bg=background, highlightthickness=0) sc.frame.pack(expand=1, fill="both") node = TreeNode(sc.canvas, None, item) node.expand()
def LoadTagDefs(self): theme = idleConf.GetOption('main','Theme','name') self.tagdefs = { "COMMENT": idleConf.GetHighlight(theme, "comment"), "KEYWORD": idleConf.GetHighlight(theme, "keyword"), "BUILTIN": idleConf.GetHighlight(theme, "builtin"), "STRING": idleConf.GetHighlight(theme, "string"), "DEFINITION": idleConf.GetHighlight(theme, "definition"), "SYNC": {'background':None,'foreground':None}, "TODO": {'background':None,'foreground':None}, "BREAK": idleConf.GetHighlight(theme, "break"), "ERROR": idleConf.GetHighlight(theme, "error"), # The following is used by ReplaceDialog: "hit": idleConf.GetHighlight(theme, "hit"), } if DEBUG: print('tagdefs',self.tagdefs)
def nearest(self): """ Enter ClassDefBrowser """ # scroll textln to the nearest keyword found in text text = self.text textln = self.textln text_insert, text_col = sp(text.index(INSERT)) text_end, col = sp(text.index(END)) text_end -= 1 text_top = (text.yview()[0] * text_end) text_bot = (text.yview()[1] * text_end) if text_top <= text_insert <= text_bot: pass else: text_insert = round((text_bot + text_top) / 2.0) for i in reversed(self.taglines): if i[0] <= text_insert: text_insert = i[0] break n = 0 for n, i in enumerate(self.taglines): if i[0] > text_insert: target_line = n break else: target_line = n + 1 textln.tag_add("NEAREST", '%i.0' % target_line, '%i.0' % (target_line + 1)) theme = idleConf.GetOption('main', 'Theme', 'name') hilite = idleConf.GetHighlight(theme, "hilite") textln.tag_configure("NEAREST", **hilite) textln.tag_raise('NEAREST') # place cursor at beginning of line text tline = min(target_line - 1, len(self.taglines) - 1) if self.taglines: origline, txt = self.taglines[tline] text_col = txt.find(txt.strip()) textln.mark_set(INSERT, '%i.%i' % (target_line, text_col)) offset = text_insert - round(text_top) - 1 textln.yview(target_line - offset) textln.focus_set()
def init(self, flist): self.flist = flist pyclbr._modules.clear() self.top = top = ListedToplevel(flist.root) top.protocol('WM_DELETE_WINDOW', self.close) top.bind('<Escape>', self.close) self.settitle() top.focus_set() theme = idleConf.GetOption('main', 'Theme', 'name') background = idleConf.GetHighlight(theme, 'normal')['background'] sc = ScrolledCanvas(top, bg=background, highlightthickness=0, takefocus=1) sc.frame.pack(expand=1, fill='both') item = self.rootnode() self.node = node = TreeNode(sc.canvas, None, item) node.update() node.expand() return
def LoadTagDefs(self): theme = idleConf.GetOption('main', 'Theme', 'name') self.tagdefs = { 'COMMENT': idleConf.GetHighlight(theme, 'comment'), 'KEYWORD': idleConf.GetHighlight(theme, 'keyword'), 'BUILTIN': idleConf.GetHighlight(theme, 'builtin'), 'STRING': idleConf.GetHighlight(theme, 'string'), 'DEFINITION': idleConf.GetHighlight(theme, 'definition'), 'SYNC': { 'background': None, 'foreground': None }, 'TODO': { 'background': None, 'foreground': None }, 'BREAK': idleConf.GetHighlight(theme, 'break'), 'ERROR': idleConf.GetHighlight(theme, 'error'), 'hit': idleConf.GetHighlight(theme, 'hit') } if DEBUG: print 'tagdefs', self.tagdefs return
def LoadTagDefs(self): theme = idleConf.CurrentTheme() font = idleConf.GetOption('main', 'EditorWindow', 'font') self.tagdefs = { "COMMENT": idleConf.GetHighlight(theme, "comment"), "KEYWORD": idleConf.GetHighlight(theme, "keyword"), "BUILTIN": idleConf.GetHighlight(theme, "builtin"), "STRING": idleConf.GetHighlight(theme, "string"), "DEFINITION": idleConf.GetHighlight(theme, "definition"), "SYNC": { 'background': None, 'foreground': None }, "TODO": { 'background': None, 'foreground': None }, "ERROR": idleConf.GetHighlight(theme, "error"), # The following is used by ReplaceDialog: "hit": idleConf.GetHighlight(theme, "hit"), } if DEBUG: print 'tagdefs', self.tagdefs try: lang = getattr(__import__('languages' + self.ext), self.ext[1:]) if hasattr(lang, 'append_tags'): for key, value in lang.append_tags.items(): self.tagdefs[key] = value except (ImportError, AttributeError): pass def _style(string): list_ = [] for c in string: if c == 'b': list_.append('bold') elif c == 'i': list_.append('italic') elif c == 'u': list_.append('underline') elif c == 's': list_.append('overstrike') return (font, 10, ' '.join(list_)) try: alttag = getattr(__import__('languages.' + theme), theme) if hasattr(alttag, 'append_tags'): for key, value in alttag.append_tags.items(): if 'font' in value: if isinstance(value['font'], str): value['font'] = _style(value['font']) else: value['font'] = (font, 10, '') self.tagdefs[key] = value for xtag, atag in ALTERNATE_TAGS.items(): if xtag not in self.tagdefs: if atag is None: self.tagdefs[xtag] = { 'foreground': None, 'background': None } else: self.tagdefs[xtag] = dict(self.tagdefs[atag]) if hasattr(alttag, 'read_twice'): self.read_twice = alttag.read_twice except (ImportError, AttributeError) as e: pass
class ParenMatch: """Highlight matching parentheses There are three supported style of paren matching, based loosely on the Emacs options. The style is select based on the HILITE_STYLE attribute; it can be changed used the set_style method. The supported styles are: default -- When a right paren is typed, highlight the matching left paren for 1/2 sec. expression -- When a right paren is typed, highlight the entire expression from the left paren to the right paren. TODO: - extend IDLE with configuration dialog to change options - implement rest of Emacs highlight styles (see below) - print mismatch warning in IDLE status window Note: In Emacs, there are several styles of highlight where the matching paren is highlighted whenever the cursor is immediately to the right of a right paren. I don't know how to do that in Tk, so I haven't bothered. """ menudefs = [('edit', [ ("Show surrounding parens", "<<flash-paren>>"), ])] STYLE = idleConf.GetOption('extensions', 'ParenMatch', 'style', default='expression') FLASH_DELAY = idleConf.GetOption('extensions', 'ParenMatch', 'flash-delay', type='int', default=500) HILITE_CONFIG = idleConf.GetHighlight(idleConf.CurrentTheme(), 'hilite') BELL = idleConf.GetOption('extensions', 'ParenMatch', 'bell', type='bool', default=1) RESTORE_VIRTUAL_EVENT_NAME = "<<parenmatch-check-restore>>" # We want the restore event be called before the usual return and # backspace events. RESTORE_SEQUENCES = ("<KeyPress>", "<ButtonPress>", "<Key-Return>", "<Key-BackSpace>") def __init__(self, editwin): self.editwin = editwin self.text = editwin.text # Bind the check-restore event to the function restore_event, # so that we can then use activate_restore (which calls event_add) # and deactivate_restore (which calls event_delete). editwin.text.bind(self.RESTORE_VIRTUAL_EVENT_NAME, self.restore_event) self.counter = 0 self.is_restore_active = 0 self.set_style(self.STYLE) def activate_restore(self): if not self.is_restore_active: for seq in self.RESTORE_SEQUENCES: self.text.event_add(self.RESTORE_VIRTUAL_EVENT_NAME, seq) self.is_restore_active = True def deactivate_restore(self): if self.is_restore_active: for seq in self.RESTORE_SEQUENCES: self.text.event_delete(self.RESTORE_VIRTUAL_EVENT_NAME, seq) self.is_restore_active = False def set_style(self, style): self.STYLE = style if style == "default": self.create_tag = self.create_tag_default self.set_timeout = self.set_timeout_last elif style == "expression": self.create_tag = self.create_tag_expression self.set_timeout = self.set_timeout_none def flash_paren_event(self, event): indices = HyperParser(self.editwin, "insert").get_surrounding_brackets() if indices is None: self.warn_mismatched() return self.activate_restore() self.create_tag(indices) self.set_timeout_last() def paren_closed_event(self, event): # If it was a shortcut and not really a closing paren, quit. closer = self.text.get("insert-1c") if closer not in _openers: return hp = HyperParser(self.editwin, "insert-1c") if not hp.is_in_code(): return indices = hp.get_surrounding_brackets(_openers[closer], True) if indices is None: self.warn_mismatched() return self.activate_restore() self.create_tag(indices) self.set_timeout() def restore_event(self, event=None): self.text.tag_delete("paren") self.deactivate_restore() self.counter += 1 # disable the last timer, if there is one. def handle_restore_timer(self, timer_count): if timer_count == self.counter: self.restore_event() def warn_mismatched(self): if self.BELL: self.text.bell() # any one of the create_tag_XXX methods can be used depending on # the style def create_tag_default(self, indices): """Highlight the single paren that matches""" self.text.tag_add("paren", indices[0]) self.text.tag_config("paren", self.HILITE_CONFIG) def create_tag_expression(self, indices): """Highlight the entire expression""" if self.text.get(indices[1]) in (')', ']', '}'): rightindex = indices[1] + "+1c" else: rightindex = indices[1] self.text.tag_add("paren", indices[0], rightindex) self.text.tag_config("paren", self.HILITE_CONFIG) # any one of the set_timeout_XXX methods can be used depending on # the style def set_timeout_none(self): """Highlight will remain until user input turns it off or the insert has moved""" # After CHECK_DELAY, call a function which disables the "paren" tag # if the event is for the most recent timer and the insert has changed, # or schedules another call for itself. self.counter += 1 def callme(callme, self=self, c=self.counter, index=self.text.index("insert")): if index != self.text.index("insert"): self.handle_restore_timer(c) else: self.editwin.text_frame.after(CHECK_DELAY, callme, callme) self.editwin.text_frame.after(CHECK_DELAY, callme, callme) def set_timeout_last(self): """The last highlight created will be removed after .5 sec""" # associate a counter with an event; only disable the "paren" # tag if the event is for the most recent timer. self.counter += 1 self.editwin.text_frame.after(self.FLASH_DELAY, lambda self=self, c=self.counter: \ self.handle_restore_timer(c))
class ParenMatch: menudefs = [('edit', [('Show surrounding parens', '<<flash-paren>>')])] STYLE = idleConf.GetOption('extensions', 'ParenMatch', 'style', default='expression') FLASH_DELAY = idleConf.GetOption('extensions', 'ParenMatch', 'flash-delay', type='int', default=500) HILITE_CONFIG = idleConf.GetHighlight(idleConf.CurrentTheme(), 'hilite') BELL = idleConf.GetOption('extensions', 'ParenMatch', 'bell', type='bool', default=1) RESTORE_VIRTUAL_EVENT_NAME = '<<parenmatch-check-restore>>' RESTORE_SEQUENCES = ('<KeyPress>', '<ButtonPress>', '<Key-Return>', '<Key-BackSpace>') def __init__(self, editwin): self.editwin = editwin self.text = editwin.text editwin.text.bind(self.RESTORE_VIRTUAL_EVENT_NAME, self.restore_event) self.counter = 0 self.is_restore_active = 0 self.set_style(self.STYLE) def activate_restore(self): if not self.is_restore_active: for seq in self.RESTORE_SEQUENCES: self.text.event_add(self.RESTORE_VIRTUAL_EVENT_NAME, seq) self.is_restore_active = True def deactivate_restore(self): if self.is_restore_active: for seq in self.RESTORE_SEQUENCES: self.text.event_delete(self.RESTORE_VIRTUAL_EVENT_NAME, seq) self.is_restore_active = False def set_style(self, style): self.STYLE = style if style == 'default': self.create_tag = self.create_tag_default self.set_timeout = self.set_timeout_last elif style == 'expression': self.create_tag = self.create_tag_expression self.set_timeout = self.set_timeout_none def flash_paren_event(self, event): indices = HyperParser(self.editwin, 'insert').get_surrounding_brackets() if indices is None: self.warn_mismatched() return else: self.activate_restore() self.create_tag(indices) self.set_timeout_last() return def paren_closed_event(self, event): closer = self.text.get('insert-1c') if closer not in _openers: return else: hp = HyperParser(self.editwin, 'insert-1c') if not hp.is_in_code(): return indices = hp.get_surrounding_brackets(_openers[closer], True) if indices is None: self.warn_mismatched() return self.activate_restore() self.create_tag(indices) self.set_timeout() return def restore_event(self, event=None): self.text.tag_delete('paren') self.deactivate_restore() self.counter += 1 def handle_restore_timer(self, timer_count): if timer_count == self.counter: self.restore_event() def warn_mismatched(self): if self.BELL: self.text.bell() def create_tag_default(self, indices): self.text.tag_add('paren', indices[0]) self.text.tag_config('paren', self.HILITE_CONFIG) def create_tag_expression(self, indices): if self.text.get(indices[1]) in (')', ']', '}'): rightindex = indices[1] + '+1c' else: rightindex = indices[1] self.text.tag_add('paren', indices[0], rightindex) self.text.tag_config('paren', self.HILITE_CONFIG) def set_timeout_none(self): self.counter += 1 def callme(callme, self=self, c=self.counter, index=self.text.index('insert')): if index != self.text.index('insert'): self.handle_restore_timer(c) else: self.editwin.text_frame.after(CHECK_DELAY, callme, callme) self.editwin.text_frame.after(CHECK_DELAY, callme, callme) def set_timeout_last(self): self.counter += 1 self.editwin.text_frame.after( self.FLASH_DELAY, lambda self=self, c=self.counter: self.handle_restore_timer(c))
def show(self, tag_filter=None): if self.hidden == False: self.hide() return self.hidden = False # add a text widget, left of code text widget self.text_frame = text_frame = Frame(self.editwin.text_frame) self.vbar = vbar = Scrollbar(text_frame, name='vbar') vbar.pack(side=RIGHT, fill=Y) theme = idleConf.GetOption('main', 'Theme', 'name') normal_colors = idleConf.GetHighlight(theme, 'normal') text_options = { 'padx': 5, 'wrap': 'none', 'cursor': 'arrow', 'wrap': 'none', 'foreground': normal_colors['foreground'], 'background': normal_colors['background'], } self.textln = textln = Text(text_frame, **text_options) textln.pack(side='left', fill=BOTH, expand=YES) vbar['command'] = textln.yview textln['yscrollcommand'] = vbar.set # adjust font textln.config( font=(idleConf.GetOption('main', 'EditorWindow', 'font'), idleConf.GetOption('main', 'EditorWindow', 'font-size'))) textln.bind("<ButtonRelease>", self.focus_in_event) textln.bind("<Return>", self.enter_callback) textln.bind("<Escape>", self.escape_callback) textln.bind('<FocusOut>', self.focus_out, '+') # pass through keybindings for classdefbrowser keydefs = idleConf.GetExtensionBindings('CodeBrowser') for event, keylist in list(keydefs.items()): for k in keylist: def passthru(event, evName=event, text=self.text): text.event_generate(evName) try: textln.bind(k, passthru) except TclError as err: print(err) pass # start the line numbers self.per = per = Percolator(textln) self.color = ColorDelegator() self.per.insertfilter(self.color) self.line_delegator = LineDelegator() per.insertfilter(self.line_delegator) textln._insert = self.line_delegator.delegate.insert textln._delete = self.line_delegator.delegate.delete self.update_display(tag_filter) self.textfont = "" self.font_timer_event() self.nearest() text_frame.place(x=0, rely=1, relheight=1, relwidth=1, anchor=SW) text_frame.lift()
class ParenMatch: """Highlight matching parentheses There are three supported style of paren matching, based loosely on the Emacs options. The style is select based on the HILITE_STYLE attribute; it can be changed used the set_style method. The supported styles are: default -- When a right paren is typed, highlight the matching left paren for 1/2 sec. expression -- When a right paren is typed, highlight the entire expression from the left paren to the right paren. TODO: - extend IDLE with configuration dialog to change options - implement rest of Emacs highlight styles (see below) - print mismatch warning in IDLE status window Note: In Emacs, there are several styles of highlight where the matching paren is highlighted whenever the cursor is immediately to the right of a right paren. I don't know how to do that in Tk, so I haven't bothered. """ menudefs = [('edit', [('Show surrounding parens', '<<flash-paren>>')])] STYLE = idleConf.GetOption('extensions', 'ParenMatch', 'style', default='expression') FLASH_DELAY = idleConf.GetOption('extensions', 'ParenMatch', 'flash-delay', type='int', default=500) HILITE_CONFIG = idleConf.GetHighlight(idleConf.CurrentTheme(), 'hilite') BELL = idleConf.GetOption('extensions', 'ParenMatch', 'bell', type='bool', default=1) RESTORE_VIRTUAL_EVENT_NAME = '<<parenmatch-check-restore>>' RESTORE_SEQUENCES = ('<KeyPress>', '<ButtonPress>', '<Key-Return>', '<Key-BackSpace>') def __init__(self, editwin): self.editwin = editwin self.text = editwin.text editwin.text.bind(self.RESTORE_VIRTUAL_EVENT_NAME, self.restore_event) self.counter = 0 self.is_restore_active = 0 self.set_style(self.STYLE) def activate_restore(self): if not self.is_restore_active: for seq in self.RESTORE_SEQUENCES: self.text.event_add(self.RESTORE_VIRTUAL_EVENT_NAME, seq) self.is_restore_active = True def deactivate_restore(self): if self.is_restore_active: for seq in self.RESTORE_SEQUENCES: self.text.event_delete(self.RESTORE_VIRTUAL_EVENT_NAME, seq) self.is_restore_active = False def set_style(self, style): self.STYLE = style if style == 'default': self.create_tag = self.create_tag_default self.set_timeout = self.set_timeout_last elif style == 'expression': self.create_tag = self.create_tag_expression self.set_timeout = self.set_timeout_none def flash_paren_event(self, event): indices = HyperParser(self.editwin, 'insert').get_surrounding_brackets() if indices is None: self.warn_mismatched() return else: self.activate_restore() self.create_tag(indices) self.set_timeout_last() return def paren_closed_event(self, event): closer = self.text.get('insert-1c') if closer not in _openers: return else: hp = HyperParser(self.editwin, 'insert-1c') if not hp.is_in_code(): return indices = hp.get_surrounding_brackets(_openers[closer], True) if indices is None: self.warn_mismatched() return self.activate_restore() self.create_tag(indices) self.set_timeout() return def restore_event(self, event=None): self.text.tag_delete('paren') self.deactivate_restore() self.counter += 1 def handle_restore_timer(self, timer_count): if timer_count == self.counter: self.restore_event() def warn_mismatched(self): if self.BELL: self.text.bell() def create_tag_default(self, indices): """Highlight the single paren that matches""" self.text.tag_add('paren', indices[0]) self.text.tag_config('paren', self.HILITE_CONFIG) def create_tag_expression(self, indices): """Highlight the entire expression""" if self.text.get(indices[1]) in (')', ']', '}'): rightindex = indices[1] + '+1c' else: rightindex = indices[1] self.text.tag_add('paren', indices[0], rightindex) self.text.tag_config('paren', self.HILITE_CONFIG) def set_timeout_none(self): """Highlight will remain until user input turns it off or the insert has moved""" self.counter += 1 def callme(callme, self=self, c=self.counter, index=self.text.index('insert')): if index != self.text.index('insert'): self.handle_restore_timer(c) else: self.editwin.text_frame.after(CHECK_DELAY, callme, callme) self.editwin.text_frame.after(CHECK_DELAY, callme, callme) def set_timeout_last(self): """The last highlight created will be removed after .5 sec""" self.counter += 1 self.editwin.text_frame.after( self.FLASH_DELAY, lambda self=self, c=self.counter: self.handle_restore_timer(c))