From 5c57fafbcc72dbeb0ca3a6bc0a3d745b666db60b Mon Sep 17 00:00:00 2001 From: wohlganger Date: Thu, 29 Jun 2017 15:11:01 -0500 Subject: [PATCH 01/64] parenmatch highlighting options adds fg color, bg color, font, and underline options for parens highlighting --- Lib/idlelib/config-extensions.def | 4 ++++ Lib/idlelib/configdialog.py | 4 +++- Lib/idlelib/parenmatch.py | 18 ++++++++++++++---- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/Lib/idlelib/config-extensions.def b/Lib/idlelib/config-extensions.def index a24b8c9316ba07..f8abd38d9aa49d 100644 --- a/Lib/idlelib/config-extensions.def +++ b/Lib/idlelib/config-extensions.def @@ -73,6 +73,10 @@ format-paragraph= [ParenMatch] enable=True style= expression +font=courier 10 +fgcolor=Black +bgcolor=White +underline=False flash-delay= 500 bell=True [ParenMatch_cfgBindings] diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py index 4a25846238f6e1..c9f72ca37dab0a 100644 --- a/Lib/idlelib/configdialog.py +++ b/Lib/idlelib/configdialog.py @@ -1419,7 +1419,9 @@ def save_all_changed_extensions(self): ParenMatch: Style indicates what is highlighted when closer is entered: 'opener' - opener '({[' corresponding to closer; 'parens' - both chars; -'expression' (default) - also everything in between. Flash-delay is how +'expression' (default) - also everything in between. Bgcolor and fgcolor +are the matched background and foreground colors, respectively, and accept +named colors and hex values preceeded by a # sign. Flash-delay is how long to highlight if cursor is not moved (0 means forever). ''' } diff --git a/Lib/idlelib/parenmatch.py b/Lib/idlelib/parenmatch.py index c15cb818cd8fbd..41986d35e801e5 100644 --- a/Lib/idlelib/parenmatch.py +++ b/Lib/idlelib/parenmatch.py @@ -41,7 +41,14 @@ class ParenMatch: 'extensions','ParenMatch','flash-delay', type='int',default=500) BELL = idleConf.GetOption( 'extensions','ParenMatch','bell', type='bool',default=1) - HILITE_CONFIG = idleConf.GetHighlight(idleConf.CurrentTheme(),'hilite') + BG_COLOR = idleConf.GetOption("extensions", "ParenMatch", + "bgcolor", type="str", default="LightGray") + FG_COLOR = idleConf.GetOption("extensions", "ParenMatch", + "fgcolor", type="str", default="Black") + P_FONT = idleConf.GetOption("extensions", "ParenMatch", + "font", type="str", default="courier 10 italic") + P_UNDERLINE = idleConf.GetOption("extensions", "ParenMatch", + "underline", type="bool", default=1) RESTORE_VIRTUAL_EVENT_NAME = "<>" # We want the restore event be called before the usual return and @@ -133,7 +140,8 @@ def handle_restore_timer(self, timer_count): def create_tag_opener(self, indices): """Highlight the single paren that matches""" self.text.tag_add("paren", indices[0]) - self.text.tag_config("paren", self.HILITE_CONFIG) + self.text.tag_config("paren", background=self.BG_COLOR,foreground=self.FG_COLOR, + font=self.P_FONT,underline=self.P_UNDERLINE) def create_tag_parens(self, indices): """Highlight the left and right parens""" @@ -142,7 +150,8 @@ def create_tag_parens(self, indices): else: rightindex = indices[1] self.text.tag_add("paren", indices[0], indices[0]+"+1c", rightindex+"-1c", rightindex) - self.text.tag_config("paren", self.HILITE_CONFIG) + self.text.tag_config("paren", background=self.BG_COLOR,foreground=self.FG_COLOR, + font=self.P_FONT,underline=self.P_UNDERLINE) def create_tag_expression(self, indices): """Highlight the entire expression""" @@ -151,7 +160,8 @@ def create_tag_expression(self, indices): else: rightindex = indices[1] self.text.tag_add("paren", indices[0], rightindex) - self.text.tag_config("paren", self.HILITE_CONFIG) + self.text.tag_config("paren", background=self.BG_COLOR,foreground=self.FG_COLOR, + font=self.P_FONT,underline=self.P_UNDERLINE) # any one of the set_timeout_XXX methods can be used depending on # the style From d49170f8b19e30fb5283ff3e2cec131fff1f133e Mon Sep 17 00:00:00 2001 From: wohlganger Date: Thu, 29 Jun 2017 15:46:53 -0500 Subject: [PATCH 02/64] fixed whitespace issue --- Lib/idlelib/parenmatch.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Lib/idlelib/parenmatch.py b/Lib/idlelib/parenmatch.py index 41986d35e801e5..15e01a3e57ab1c 100644 --- a/Lib/idlelib/parenmatch.py +++ b/Lib/idlelib/parenmatch.py @@ -141,7 +141,7 @@ def create_tag_opener(self, indices): """Highlight the single paren that matches""" self.text.tag_add("paren", indices[0]) self.text.tag_config("paren", background=self.BG_COLOR,foreground=self.FG_COLOR, - font=self.P_FONT,underline=self.P_UNDERLINE) + font=self.P_FONT,underline=self.P_UNDERLINE) def create_tag_parens(self, indices): """Highlight the left and right parens""" @@ -151,7 +151,7 @@ def create_tag_parens(self, indices): rightindex = indices[1] self.text.tag_add("paren", indices[0], indices[0]+"+1c", rightindex+"-1c", rightindex) self.text.tag_config("paren", background=self.BG_COLOR,foreground=self.FG_COLOR, - font=self.P_FONT,underline=self.P_UNDERLINE) + font=self.P_FONT,underline=self.P_UNDERLINE) def create_tag_expression(self, indices): """Highlight the entire expression""" @@ -161,7 +161,7 @@ def create_tag_expression(self, indices): rightindex = indices[1] self.text.tag_add("paren", indices[0], rightindex) self.text.tag_config("paren", background=self.BG_COLOR,foreground=self.FG_COLOR, - font=self.P_FONT,underline=self.P_UNDERLINE) + font=self.P_FONT,underline=self.P_UNDERLINE) # any one of the set_timeout_XXX methods can be used depending on # the style From 8c3a9662bcd5c8617a0383a420536d46b472f341 Mon Sep 17 00:00:00 2001 From: wohlganger Date: Fri, 30 Jun 2017 08:18:50 -0500 Subject: [PATCH 03/64] only select from highlighting options --- Lib/idlelib/config-extensions.def | 5 +---- Lib/idlelib/configdialog.py | 7 +++---- Lib/idlelib/parenmatch.py | 21 +++++++-------------- 3 files changed, 11 insertions(+), 22 deletions(-) diff --git a/Lib/idlelib/config-extensions.def b/Lib/idlelib/config-extensions.def index f8abd38d9aa49d..6f1ec03981b7bd 100644 --- a/Lib/idlelib/config-extensions.def +++ b/Lib/idlelib/config-extensions.def @@ -73,10 +73,7 @@ format-paragraph= [ParenMatch] enable=True style= expression -font=courier 10 -fgcolor=Black -bgcolor=White -underline=False +hilite=hilite flash-delay= 500 bell=True [ParenMatch_cfgBindings] diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py index c9f72ca37dab0a..cfcf6f49b9acdc 100644 --- a/Lib/idlelib/configdialog.py +++ b/Lib/idlelib/configdialog.py @@ -1419,10 +1419,9 @@ def save_all_changed_extensions(self): ParenMatch: Style indicates what is highlighted when closer is entered: 'opener' - opener '({[' corresponding to closer; 'parens' - both chars; -'expression' (default) - also everything in between. Bgcolor and fgcolor -are the matched background and foreground colors, respectively, and accept -named colors and hex values preceeded by a # sign. Flash-delay is how -long to highlight if cursor is not moved (0 means forever). +'expression' (default) - also everything in between. Hilite determines +what highlighting is used. Flash-delay is how long to highlight if cursor +is not moved (0 means forever). ''' } diff --git a/Lib/idlelib/parenmatch.py b/Lib/idlelib/parenmatch.py index 15e01a3e57ab1c..8f22cb70c34100 100644 --- a/Lib/idlelib/parenmatch.py +++ b/Lib/idlelib/parenmatch.py @@ -41,14 +41,10 @@ class ParenMatch: 'extensions','ParenMatch','flash-delay', type='int',default=500) BELL = idleConf.GetOption( 'extensions','ParenMatch','bell', type='bool',default=1) - BG_COLOR = idleConf.GetOption("extensions", "ParenMatch", - "bgcolor", type="str", default="LightGray") - FG_COLOR = idleConf.GetOption("extensions", "ParenMatch", - "fgcolor", type="str", default="Black") - P_FONT = idleConf.GetOption("extensions", "ParenMatch", - "font", type="str", default="courier 10 italic") - P_UNDERLINE = idleConf.GetOption("extensions", "ParenMatch", - "underline", type="bool", default=1) + HILITE_CONFIG = idleConf.GetHighlight(idleConf.CurrentTheme(), + idleConf.GetOption( + 'extensions','ParenMatch','hilite', default='expression')) + RESTORE_VIRTUAL_EVENT_NAME = "<>" # We want the restore event be called before the usual return and @@ -140,8 +136,7 @@ def handle_restore_timer(self, timer_count): def create_tag_opener(self, indices): """Highlight the single paren that matches""" self.text.tag_add("paren", indices[0]) - self.text.tag_config("paren", background=self.BG_COLOR,foreground=self.FG_COLOR, - font=self.P_FONT,underline=self.P_UNDERLINE) + self.text.tag_config("paren", self.HILITE_CONFIG) def create_tag_parens(self, indices): """Highlight the left and right parens""" @@ -150,8 +145,7 @@ def create_tag_parens(self, indices): else: rightindex = indices[1] self.text.tag_add("paren", indices[0], indices[0]+"+1c", rightindex+"-1c", rightindex) - self.text.tag_config("paren", background=self.BG_COLOR,foreground=self.FG_COLOR, - font=self.P_FONT,underline=self.P_UNDERLINE) + self.text.tag_config("paren", self.HILITE_CONFIG) def create_tag_expression(self, indices): """Highlight the entire expression""" @@ -160,8 +154,7 @@ def create_tag_expression(self, indices): else: rightindex = indices[1] self.text.tag_add("paren", indices[0], rightindex) - self.text.tag_config("paren", background=self.BG_COLOR,foreground=self.FG_COLOR, - font=self.P_FONT,underline=self.P_UNDERLINE) + self.text.tag_config("paren", self.HILITE_CONFIG) # any one of the set_timeout_XXX methods can be used depending on # the style From 757bb38c457956b3b37c234ee2b10b80a5aa7b62 Mon Sep 17 00:00:00 2001 From: wohlganger Date: Fri, 30 Jun 2017 08:27:29 -0500 Subject: [PATCH 04/64] fixed whitespace --- Lib/idlelib/parenmatch.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/idlelib/parenmatch.py b/Lib/idlelib/parenmatch.py index 8f22cb70c34100..48f699ac676b0b 100644 --- a/Lib/idlelib/parenmatch.py +++ b/Lib/idlelib/parenmatch.py @@ -41,8 +41,8 @@ class ParenMatch: 'extensions','ParenMatch','flash-delay', type='int',default=500) BELL = idleConf.GetOption( 'extensions','ParenMatch','bell', type='bool',default=1) - HILITE_CONFIG = idleConf.GetHighlight(idleConf.CurrentTheme(), - idleConf.GetOption( + HILITE_CONFIG = idleConf.GetHighlight( + idleConf.CurrentTheme(), idleConf.GetOption( 'extensions','ParenMatch','hilite', default='expression')) From d7e136c91b1d5d7e5f3e476b3bdc3887e95ed074 Mon Sep 17 00:00:00 2001 From: wohlganger Date: Fri, 30 Jun 2017 08:39:03 -0500 Subject: [PATCH 05/64] whitespace --- Lib/idlelib/parenmatch.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/idlelib/parenmatch.py b/Lib/idlelib/parenmatch.py index 48f699ac676b0b..2dcd68ebcc1ce1 100644 --- a/Lib/idlelib/parenmatch.py +++ b/Lib/idlelib/parenmatch.py @@ -44,7 +44,7 @@ class ParenMatch: HILITE_CONFIG = idleConf.GetHighlight( idleConf.CurrentTheme(), idleConf.GetOption( 'extensions','ParenMatch','hilite', default='expression')) - + RESTORE_VIRTUAL_EVENT_NAME = "<>" # We want the restore event be called before the usual return and From 2adebb95c5706f7f9d530787e76a210f6541945f Mon Sep 17 00:00:00 2001 From: wohlganger Date: Mon, 10 Jul 2017 16:12:37 -0500 Subject: [PATCH 06/64] #27099 - turn builtin extensions to reg modules --- Lib/idlelib/autocomplete.py | 19 ++-- Lib/idlelib/autoexpand.py | 8 -- Lib/idlelib/calltips.py | 8 +- Lib/idlelib/codecontext.py | 31 ++++--- Lib/idlelib/colorizer.py | 1 + Lib/idlelib/config-extensions.def | 67 -------------- Lib/idlelib/config-highlight.def | 12 +++ Lib/idlelib/config-keys.def | 66 ++++++++++++- Lib/idlelib/config-main.def | 53 +++++++++++ Lib/idlelib/config.py | 23 ++++- Lib/idlelib/configdialog.py | 148 +++++++++++++++++++++++------- Lib/idlelib/editor.py | 49 +++++++--- Lib/idlelib/mainmenu.py | 11 ++- Lib/idlelib/paragraph.py | 10 +- Lib/idlelib/parenmatch.py | 31 +++---- Lib/idlelib/rstrip.py | 3 - Lib/idlelib/runscript.py | 2 +- Lib/idlelib/zoomheight.py | 8 +- 18 files changed, 362 insertions(+), 188 deletions(-) diff --git a/Lib/idlelib/autocomplete.py b/Lib/idlelib/autocomplete.py index cd212ccb143a3c..87706c19eae543 100644 --- a/Lib/idlelib/autocomplete.py +++ b/Lib/idlelib/autocomplete.py @@ -1,6 +1,5 @@ -"""autocomplete.py - An IDLE extension for automatically completing names. - -This extension can complete either attribute names or file names. It can pop +""" +This can complete either attribute names or file names. It can pop a window with all available names, for the user to select from. """ import os @@ -27,18 +26,10 @@ class AutoComplete: - menudefs = [ - ('edit', [ - ("Show Completions", "<>"), - ]) - ] - - popupwait = idleConf.GetOption("extensions", "AutoComplete", - "popupwait", type="int", default=0) - def __init__(self, editwin=None): self.editwin = editwin if editwin is not None: # not in subprocess or test + self.reset() self.text = editwin.text self.autocompletewindow = None # id of delayed call, and the index of the text insert when @@ -47,6 +38,10 @@ def __init__(self, editwin=None): self._delayed_completion_id = None self._delayed_completion_index = None + def reset(self): + self.popupwait = idleConf.GetOption("main", "EditorWindow", + "autocomplete_wait", type="int", default=0) + def _make_autocomplete_window(self): return autocomplete_w.AutoCompleteWindow(self.text) diff --git a/Lib/idlelib/autoexpand.py b/Lib/idlelib/autoexpand.py index 6b46bee69c95f5..10064c1f6e289b 100644 --- a/Lib/idlelib/autoexpand.py +++ b/Lib/idlelib/autoexpand.py @@ -10,7 +10,6 @@ place before requesting the next selection causes AutoExpand to reset its state. -This is an extension file and there is only one instance of AutoExpand. ''' import re import string @@ -20,13 +19,6 @@ ###$ unix class AutoExpand: - - menudefs = [ - ('edit', [ - ('E_xpand Word', '<>'), - ]), - ] - wordchars = string.ascii_letters + string.digits + "_" def __init__(self, editwin): diff --git a/Lib/idlelib/calltips.py b/Lib/idlelib/calltips.py index a8a3abe6c6982a..6e59a43f40cf72 100644 --- a/Lib/idlelib/calltips.py +++ b/Lib/idlelib/calltips.py @@ -1,4 +1,4 @@ -"""calltips.py - An IDLE Extension to Jog Your Memory +""" Call Tips are floating windows which display function, class, and method parameter and docstring information when you type an opening parenthesis, and @@ -17,12 +17,6 @@ class CallTips: - menudefs = [ - ('edit', [ - ("Show call tip", "<>"), - ]) - ] - def __init__(self, editwin=None): if editwin is None: # subprocess and test self.editwin = None diff --git a/Lib/idlelib/codecontext.py b/Lib/idlelib/codecontext.py index 09dc078d63f191..5de187a82584d5 100644 --- a/Lib/idlelib/codecontext.py +++ b/Lib/idlelib/codecontext.py @@ -1,4 +1,4 @@ -"""codecontext - Extension to display the block context above the edit window +"""codecontext - display the block context above the edit window Once code has scrolled off the top of a window, it can be difficult to determine which block you are in. This extension implements a pane at the top @@ -26,13 +26,7 @@ lambda s, c=re.compile(r"^(\s*)(\w*)"): c.match(s).groups() class CodeContext: - menudefs = [('options', [('!Code Conte_xt', '<>')])] - context_depth = idleConf.GetOption("extensions", "CodeContext", - "numlines", type="int", default=3) - bgcolor = idleConf.GetOption("extensions", "CodeContext", - "bgcolor", type="str", default="LightGray") - fgcolor = idleConf.GetOption("extensions", "CodeContext", - "fgcolor", type="str", default="Black") + def __init__(self, editwin): self.editwin = editwin self.text = editwin.text @@ -45,15 +39,28 @@ def __init__(self, editwin): # starts the toplevel 'block' of the module. self.info = [(0, -1, "", False)] self.topvisible = 1 - visible = idleConf.GetOption("extensions", "CodeContext", - "visible", type="bool", default=False) + visible = idleConf.GetOption("main", "Theme", + "contexton", type="bool", default=False) + self.reset() if visible: self.toggle_code_context_event() - self.editwin.setvar('<>', True) + # Start two update cycles, one for context lines, one for font changes. self.text.after(UPDATEINTERVAL, self.timer_event) self.text.after(FONTUPDATEINTERVAL, self.font_timer_event) + def reset(self): + self.menudefs = [('options', [('!Code Conte_xt', '<>')])] + self.context_depth = idleConf.GetOption("main", "Theme", + "numlines", type="int", default=3) + highlight=idleConf.GetHighlight(idleConf.CurrentTheme(),'codecontext') + self.fgcolor = highlight['foreground'] + self.bgcolor = highlight['background'] + if self.topvisible: + #need to rebuild widget to change color + self.toggle_code_context_event() + self.toggle_code_context_event() + def toggle_code_context_event(self, event=None): if not self.label: # Calculate the border width and horizontal padding required to @@ -86,7 +93,7 @@ def toggle_code_context_event(self, event=None): else: self.label.destroy() self.label = None - idleConf.SetOption("extensions", "CodeContext", "visible", + idleConf.SetOption("main", "Theme", "contexton", str(self.label is not None)) idleConf.SaveUserCfgFiles() return "break" diff --git a/Lib/idlelib/colorizer.py b/Lib/idlelib/colorizer.py index ff4084528804bd..89d81a0aefc556 100644 --- a/Lib/idlelib/colorizer.py +++ b/Lib/idlelib/colorizer.py @@ -92,6 +92,7 @@ def LoadTagDefs(self): "ERROR": idleConf.GetHighlight(theme, "error"), # The following is used by ReplaceDialog: "hit": idleConf.GetHighlight(theme, "hit"), + "paren": idleConf.GetHighlight(theme, "parenmatch"), } if DEBUG: print('tagdefs',self.tagdefs) diff --git a/Lib/idlelib/config-extensions.def b/Lib/idlelib/config-extensions.def index 6f1ec03981b7bd..649a2efa3e478b 100644 --- a/Lib/idlelib/config-extensions.def +++ b/Lib/idlelib/config-extensions.def @@ -31,70 +31,3 @@ # # See config-keys.def for notes on specifying keys and extend.txt for # information on creating IDLE extensions. - -[AutoComplete] -enable=True -popupwait=2000 -[AutoComplete_cfgBindings] -force-open-completions= -[AutoComplete_bindings] -autocomplete= -try-open-completions= - -[AutoExpand] -enable=True -[AutoExpand_cfgBindings] -expand-word= - -[CallTips] -enable=True -[CallTips_cfgBindings] -force-open-calltip= -[CallTips_bindings] -try-open-calltip= -refresh-calltip= - -[CodeContext] -enable=True -enable_shell=False -numlines=3 -visible=False -bgcolor=LightGray -fgcolor=Black -[CodeContext_bindings] -toggle-code-context= - -[FormatParagraph] -enable=True -max-width=72 -[FormatParagraph_cfgBindings] -format-paragraph= - -[ParenMatch] -enable=True -style= expression -hilite=hilite -flash-delay= 500 -bell=True -[ParenMatch_cfgBindings] -flash-paren= -[ParenMatch_bindings] -paren-closed= - -[RstripExtension] -enable=True -enable_shell=False -enable_editor=True - -[ScriptBinding] -enable=True -enable_shell=False -enable_editor=True -[ScriptBinding_cfgBindings] -run-module= -check-module= - -[ZoomHeight] -enable=True -[ZoomHeight_cfgBindings] -zoom-height= diff --git a/Lib/idlelib/config-highlight.def b/Lib/idlelib/config-highlight.def index 4146e28c4eda1f..531708e9c11fa3 100644 --- a/Lib/idlelib/config-highlight.def +++ b/Lib/idlelib/config-highlight.def @@ -22,6 +22,10 @@ hit-foreground= #ffffff hit-background= #000000 error-foreground= #000000 error-background= #ff7777 +codecontext-foreground = black +codecontext-background = LightGray +parenmatch-foreground = black +parenmatch-background = gray #cursor (only foreground can be set, restart IDLE) cursor-foreground= black #shell window @@ -53,6 +57,10 @@ hit-foreground= #ffffff hit-background= #000000 error-foreground= #000000 error-background= #ff7777 +codecontext-foreground = black +codecontext-background = LightGray +parenmatch-foreground = #aaaaaa +parenmatch-background = #ffffff #cursor (only foreground can be set, restart IDLE) cursor-foreground= black #shell window @@ -91,3 +99,7 @@ stdout-background = #002240 hit-foreground = #002240 comment-background = #002240 break-foreground = #FFFFFF +codecontext-foreground = #aaaaaa +codecontext-background = #000000 +parenmatch-foreground = #00dddd +parenmatch-background = #002240 diff --git a/Lib/idlelib/config-keys.def b/Lib/idlelib/config-keys.def index 64788f9adf015c..6b7e202c9ccab1 100644 --- a/Lib/idlelib/config-keys.def +++ b/Lib/idlelib/config-keys.def @@ -57,6 +57,19 @@ toggle-tabs= change-indentwidth= del-word-left= del-word-right= +autocomplete= +try-open-completions= +expand-word= +try-open-calltip= +refresh-calltip= +force-open-calltip= +toggle-code-context= +format-paragraph= +flash-paren= +paren-closed= +run-module= +check-module= +zoom-height= [IDLE Classic Unix] copy= @@ -108,6 +121,19 @@ toggle-tabs= change-indentwidth= del-word-left= del-word-right= +autocomplete= +try-open-completions= +expand-word= +try-open-calltip= +refresh-calltip= +force-open-calltip= +toggle-code-context= +format-paragraph= +flash-paren= +paren-closed= +run-module= +check-module= +zoom-height= [IDLE Modern Unix] copy = @@ -159,6 +185,19 @@ toggle-tabs = change-indentwidth = del-word-left = del-word-right = +autocomplete= +try-open-completions= +expand-word= +try-open-calltip= +refresh-calltip= +force-open-calltip= +toggle-code-context= +format-paragraph= +flash-paren= +paren-closed= +run-module= +check-module= +zoom-height= [IDLE Classic Mac] copy= @@ -210,6 +249,19 @@ toggle-tabs= change-indentwidth= del-word-left= del-word-right= +autocomplete= +try-open-completions= +expand-word= +try-open-calltip= +refresh-calltip= +force-open-calltip= +toggle-code-context= +format-paragraph= +flash-paren= +paren-closed= +run-module= +check-module= +zoom-height= [IDLE Classic OSX] toggle-tabs = @@ -262,4 +314,16 @@ python-context-help = save-copy-of-window-as-file = open-window-from-file = python-docs = - +autocomplete= +try-open-completions= +expand-word= +try-open-calltip= +refresh-calltip= +force-open-calltip= +toggle-code-context= +format-paragraph= +flash-paren= +paren-closed= +run-module= +check-module= +zoom-height= diff --git a/Lib/idlelib/config-main.def b/Lib/idlelib/config-main.def index 16f4b0959cf13c..20ef3687dc1682 100644 --- a/Lib/idlelib/config-main.def +++ b/Lib/idlelib/config-main.def @@ -82,6 +82,59 @@ name= name2= # name2 set in user config-main.cfg for keys added after 2016 July 1 +[AutoComplete] +enable=True +popupwait=2000 +[AutoComplete_cfgBindings] +force-open-completions= +[AutoComplete_bindings] +autocomplete= +try-open-completions= + +[AutoExpand] +enable=True + +[CallTips] +enable=True +[CallTips_cfgBindings] +force-open-calltip= + + +[CodeContext] +enable=True +enable_shell=False +numlines=3 +visible=False +bgcolor=LightGray +fgcolor=Black + + +[FormatParagraph] +enable=True +max-width=72 + + +[ParenMatch] +enable=True +style= expression +hilite=hilite +flash-delay= 500 +bell=True + +[RstripExtension] +enable=True +enable_shell=False +enable_editor=True + +[ScriptBinding] +enable=True +enable_shell=False +enable_editor=True + +[ZoomHeight] +enable=True + + [History] cyclic=1 diff --git a/Lib/idlelib/config.py b/Lib/idlelib/config.py index ed37f11a9cb8e0..fb6c5f3a2a7389 100644 --- a/Lib/idlelib/config.py +++ b/Lib/idlelib/config.py @@ -356,7 +356,12 @@ def GetThemeDict(self, type, themeName): 'stderr-foreground':'#000000', 'stderr-background':'#ffffff', 'console-foreground':'#000000', - 'console-background':'#ffffff' } + 'console-background':'#ffffff', + 'codecontext-foreground':'#000000', + 'codecontext-background':'#ffffff', + 'parenmatch-foreground':'000000', + 'parenmatch-background':'#ffffff', + } for element in theme: if not cfgParser.has_option(themeName, element): # Print warning that will return a default color @@ -660,7 +665,21 @@ def GetCoreKeys(self, keySetName=None): '<>': [''], '<>': [''], '<>': [''], - '<>': [''] + '<>': [''], + '<>':['>':['', '', ''], + '<>':[''], + '<>':[''], + '<>':['', ''], + '<>':[''], + '<>':[''], + '<>':[''], + '<>':[''], + '<>':[''], + '<>':['', '', ''], + '<>':[''], + '<>':[''], + '<>':[''] } if keySetName: if not (self.userCfg['keys'].has_section(keySetName) or diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py index f58299c769da7b..d8603c2894aa22 100644 --- a/Lib/idlelib/configdialog.py +++ b/Lib/idlelib/configdialog.py @@ -11,7 +11,7 @@ """ from tkinter import (Toplevel, Frame, LabelFrame, Listbox, Label, Button, Entry, Text, Scale, Radiobutton, Checkbutton, Canvas, - StringVar, BooleanVar, IntVar, TRUE, FALSE, + OptionMenu, StringVar, BooleanVar, IntVar, TRUE, FALSE, TOP, BOTTOM, RIGHT, LEFT, SOLID, GROOVE, NORMAL, DISABLED, NONE, BOTH, X, Y, W, E, EW, NS, NSEW, NW, HORIZONTAL, VERTICAL, ANCHOR, ACTIVE, END) @@ -72,6 +72,8 @@ def __init__(self, parent, title='', _htest=False, _utest=False): 'Shell Error Text': ('error', '11'), 'Shell Stdout Text': ('stdout', '12'), 'Shell Stderr Text': ('stderr', '13'), + 'Code Context Text': ('codecontext', '14'), + 'Matched Parenthetics': ('parenmatch', '15'), } self.create_widgets() self.resizable(height=FALSE, width=FALSE) @@ -218,8 +220,23 @@ def create_page_highlight(self): self.custom_theme = StringVar(parent) self.fg_bg_toggle = BooleanVar(parent) self.colour = StringVar(parent) + self.font_name = StringVar(parent) self.is_builtin_theme = BooleanVar(parent) self.highlight_target = StringVar(parent) + + self.parenstyle = StringVar(parent) + self.parenstyle.set(idleConf.GetOption( + 'main','Theme','parenstyle', default='opener')) + self.bell = BooleanVar(parent) + self.bell.set(idleConf.GetOption( + 'main','Theme','bell', default=True)) + + self.flash_delay = IntVar(parent) + self.flash_delay.set(idleConf.GetOption( + 'main','Theme','flash-delay', default=500)) + self.num_lines = IntVar(parent) + self.num_lines.set(idleConf.GetOption( + 'main','Theme','numlines', default=3)) ##widget creation #body frame @@ -229,6 +246,10 @@ def create_page_highlight(self): text=' Custom Highlighting ') frame_theme = LabelFrame(frame, borderwidth=2, relief=GROOVE, text=' Highlighting Theme ') + frame_paren = LabelFrame(frame, borderwidth=2, relief=GROOVE, + text=' Matched Parenthetics ') + frame_code = LabelFrame(frame, borderwidth=2, relief=GROOVE, + text=' Code Context ') #frame_custom self.text_highlight_sample=Text( frame_custom, relief=SOLID, borderwidth=1, @@ -238,10 +259,12 @@ def create_page_highlight(self): text.bind('', lambda e: 'break') text.bind('', lambda e: 'break') text_and_tags=( + ('Class CodeContext\n', 'codecontext'), ('#you can click here', 'comment'), ('\n', 'normal'), ('#to choose items', 'comment'), ('\n', 'normal'), ('def', 'keyword'), (' ', 'normal'), ('func', 'definition'), ('(param):\n ', 'normal'), + ('(parenthetics)','parenmatch'), ('"""string"""', 'string'), ('\n var0 = ', 'normal'), ("'string'", 'string'), ('\n var1 = ', 'normal'), ("'selected'", 'hilite'), ('\n var2 = ', 'normal'), @@ -297,10 +320,28 @@ def tem(event, elem=element): command=self.delete_custom_theme) self.new_custom_theme = Label(frame_theme, bd=2) + #frame_paren + self.opt_menu_pstyle = OptionMenu( + frame_paren, self.parenstyle, "opener","parens","expression")#todo: figure out how to make it work + + flash_label = Label(frame_paren, text='Time Displayed : \n(0 is until given input)') + + self.entry_flash = Entry( + frame_paren, textvariable=self.flash_delay, width=4) + self.check_bell = Checkbutton( + frame_paren, text="Bell", variable=self.bell) + #frame_code + lines_label = Label(frame_code, text='Lines : ') + self.entry_num_lines = Entry( + frame_code, textvariable=self.num_lines, width=3) + ##widget packing #body frame_custom.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH) - frame_theme.pack(side=LEFT, padx=5, pady=5, fill=Y) + frame_theme.pack(side=TOP, padx=5, pady=5, fill=X) + frame_paren.pack(side=TOP, padx=5, pady=5, fill=X) + frame_code.pack(side=TOP, padx=5, pady=5, fill=X) + #frame_custom self.frame_colour_set.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=X) frame_fg_bg_toggle.pack(side=TOP, padx=5, pady=0) @@ -320,6 +361,17 @@ def tem(event, elem=element): self.opt_menu_theme_custom.pack(side=TOP, fill=X, anchor=W, padx=5, pady=5) self.button_delete_custom_theme.pack(side=TOP, fill=X, padx=5, pady=5) self.new_custom_theme.pack(side=TOP, fill=X, pady=5) + + #frame_paren + self.opt_menu_pstyle.pack(side=TOP,anchor=W,padx=5, fill=X) + self.check_bell.pack(side=TOP, anchor=W,padx=5) + flash_label.pack(side=LEFT, anchor=W, padx=5) + self.entry_flash.pack(side=LEFT, fill=X,anchor=W,padx=5) + #frame_code + lines_label.pack(side=LEFT, anchor=W, padx=5) + self.entry_num_lines.pack(side=LEFT,anchor=W,padx=5) + + return frame def create_page_keys(self): @@ -422,6 +474,12 @@ def create_page_general(self): self.win_height = StringVar(parent) self.startup_edit = IntVar(parent) self.autosave = IntVar(parent) + self.autocomplete_wait = IntVar(parent) + self.autocomplete_wait.set(idleConf.GetOption( + 'main','General','autocomplete_wait', default=2000)) + self.formatp_maxw = IntVar(parent) + self.formatp_maxw.set(idleConf.GetOption( + 'main','General','formatp_maxw', default=72)) #widget creation #body @@ -432,6 +490,7 @@ def create_page_general(self): frame_save = LabelFrame(frame, borderwidth=2, relief=GROOVE, text=' autosave Preferences ') frame_win_size = Frame(frame, borderwidth=2, relief=GROOVE) + frame_extras = Frame(frame, borderwidth=2, relief=GROOVE) frame_help = LabelFrame(frame, borderwidth=2, relief=GROOVE, text=' Additional Help Sources ') #frame_run @@ -459,6 +518,13 @@ def create_page_general(self): win_height_title = Label(frame_win_size, text='Height') self.entry_win_height = Entry( frame_win_size, textvariable=self.win_height, width=3) + #frame extras + autocomplete_wait_title = Label(frame_extras, text='AutoComplete Popup Wait') + self.entry_autocomplete_wait = Entry( + frame_extras, textvariable=self.autocomplete_wait, width=6) + formatp_maxw_title = Label(frame_extras, text='Format Paragraph Max Width') + self.entry_formatp_maxw = Entry( + frame_extras, textvariable=self.formatp_maxw, width=3) #frame_help frame_helplist = Frame(frame_help) frame_helplist_buttons = Frame(frame_helplist) @@ -484,6 +550,7 @@ def create_page_general(self): frame_run.pack(side=TOP, padx=5, pady=5, fill=X) frame_save.pack(side=TOP, padx=5, pady=5, fill=X) frame_win_size.pack(side=TOP, padx=5, pady=5, fill=X) + frame_extras.pack(side=TOP, padx=5, pady=5, fill=X) frame_help.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH) #frame_run startup_title.pack(side=LEFT, anchor=W, padx=5, pady=5) @@ -499,6 +566,11 @@ def create_page_general(self): win_height_title.pack(side=RIGHT, anchor=E, pady=5) self.entry_win_width.pack(side=RIGHT, anchor=E, padx=10, pady=5) win_width_title.pack(side=RIGHT, anchor=E, pady=5) + #frame extras + self.entry_autocomplete_wait.pack(side=RIGHT, anchor=E, padx=10, pady=5) + autocomplete_wait_title.pack(side=RIGHT, anchor=E, pady=5) + self.entry_formatp_maxw.pack(side=RIGHT, anchor=E, padx=10, pady=5) + formatp_maxw_title.pack(side=RIGHT, anchor=E, pady=5) #frame_help frame_helplist_buttons.pack(side=RIGHT, padx=5, pady=5, fill=Y) frame_helplist.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH) @@ -529,6 +601,13 @@ def attach_var_callbacks(self): self.startup_edit.trace_add('write', self.var_changed_startup_edit) self.autosave.trace_add('write', self.var_changed_autosave) + self.parenstyle.trace_add('write', self.var_changed_parenstyle) + self.bell.trace_add('write', self.var_changed_bell) + self.flash_delay.trace_add('write', self.var_changed_flash_delay) + self.num_lines.trace_add('write', self.var_changed_num_lines) + self.formatp_maxw.trace_add('write', self.var_changed_formatp_maxw) + self.autocomplete_wait.trace_add('write',self.var_changed_autocomplete_wait) + def remove_var_callbacks(self): "Remove callbacks to prevent memory leaks." for var in ( @@ -537,7 +616,8 @@ def remove_var_callbacks(self): self.custom_theme, self.is_builtin_theme, self.highlight_target, self.keybinding, self.builtin_keys, self.custom_keys, self.are_keys_builtin, self.win_width, self.win_height, - self.startup_edit, self.autosave,): + self.startup_edit, self.autosave, + self.parenstyle, self.bell, self.flash_delay, self.num_lines): var.trace_remove('write', var.trace_info()[0][1]) def var_changed_font(self, *params): @@ -670,6 +750,16 @@ def var_changed_win_height(self, *params): value = self.win_height.get() changes.add_option('main', 'EditorWindow', 'height', value) + def var_changed_formatp_maxw(self, *params): + "Store change to format paragraph max width." + value = self.formatp_maxw.get() + changes.add_option('main', 'General', 'formatp_maxw', value) + + def var_changed_autocomplete_wait(self, *params): + "Store change to autocomplete popup wait time." + value = self.autocomplete_wait.get() + changes.add_option('main', 'General', 'autocomplete_wait', value) + def var_changed_startup_edit(self, *params): "Store change to toggle for starting IDLE in the editor or shell." value = self.startup_edit.get() @@ -680,6 +770,26 @@ def var_changed_autosave(self, *params): value = self.autosave.get() changes.add_option('main', 'General', 'autosave', value) + def var_changed_parenstyle(self, *params): + "Store change to parenthetics display style." + value=self.parenstyle.get() + self.add_changed_item('main', 'Theme', 'parenstyle', value) + + def var_changed_bell(self, *params): + "Store change to parenthtics bell (on/off)." + value=self.bell.get() + self.add_changed_item('main', 'Theme', 'bell', value) + + def var_changed_flash_delay(self, *params): + "Store change to parenthetics flash delay." + value=self.flash_delay.get() + self.add_changed_item('main', 'Theme', 'flash-delay', value) + + def var_changed_num_lines(self, *params): + "Store change to code context - number of lines displayed." + value=self.num_lines.get() + self.add_changed_item('main', 'Theme', 'numlines', value) + def set_theme_type(self): "Set available screen options based on builtin or custom theme." if self.is_builtin_theme.get(): @@ -1276,6 +1386,9 @@ def activate_config_changes(self): instance.set_notabs_indentwidth() instance.ApplyKeybindings() instance.reset_help_menu_entries() + instance.insParenMatch.reset() + instance.insCodeContext.reset() + instance.insAutoComplete.reset() def cancel(self): "Dismiss config dialog." @@ -1476,34 +1589,7 @@ def save_all_changed_extensions(self): [Cancel] only cancels changes made since the last save. ''' help_pages = { - 'Highlighting': ''' -Highlighting: -The IDLE Dark color theme is new in October 2015. It can only -be used with older IDLE releases if it is saved as a custom -theme, with a different name. -''', - 'Keys': ''' -Keys: -The IDLE Modern Unix key set is new in June 2016. It can only -be used with older IDLE releases if it is saved as a custom -key set, with a different name. -''', - 'Extensions': ''' -Extensions: - -Autocomplete: Popupwait is milleseconds to wait after key char, without -cursor movement, before popping up completion box. Key char is '.' after -identifier or a '/' (or '\\' on Windows) within a string. - -FormatParagraph: Max-width is max chars in lines after re-formatting. -Use with paragraphs in both strings and comment blocks. - -ParenMatch: Style indicates what is highlighted when closer is entered: -'opener' - opener '({[' corresponding to closer; 'parens' - both chars; -'expression' (default) - also everything in between. Hilite determines -what highlighting is used. Flash-delay is how long to highlight if cursor -is not moved (0 means forever). -''' + } diff --git a/Lib/idlelib/editor.py b/Lib/idlelib/editor.py index 43b105f726573d..4b575fa7c8e8cd 100644 --- a/Lib/idlelib/editor.py +++ b/Lib/idlelib/editor.py @@ -26,6 +26,15 @@ from idlelib import replace from idlelib import search from idlelib import windows +from idlelib.autocomplete import AutoComplete +from idlelib.autoexpand import AutoExpand +from idlelib.calltips import CallTips +from idlelib.codecontext import CodeContext +from idlelib.paragraph import FormatParagraph +from idlelib.parenmatch import ParenMatch +from idlelib.rstrip import RstripExtension +from idlelib.runscript import ScriptBinding +from idlelib.zoomheight import ZoomHeight # The default tab setting for a Text widget, in average-width characters. TK_TABWIDTH_DEFAULT = 8 @@ -270,6 +279,34 @@ def __init__(self, flist=None, filename=None, key=None, root=None): self.askinteger = tkSimpleDialog.askinteger self.showerror = tkMessageBox.showerror + + #init merged extentions binds - needs to be done after color is set + self.insAutoComplete=AutoComplete(self) + self.insAutoExpand=AutoExpand(self) + self.insCallTips=CallTips(self) + self.insCodeContext=CodeContext(self) + self.insFormatParagraph=FormatParagraph(self) + self.insParenMatch=ParenMatch(self) + self.insRstripExtension=RstripExtension(self) + self.insScriptBinding=ScriptBinding(self) + self.insZoomHeight=ZoomHeight(self) + + text.bind("<>",self.insAutoComplete.autocomplete_event) + text.bind("<>", self.insAutoComplete.try_open_completions_event) + text.bind("<>",self.insAutoComplete.force_open_completions_event) + text.bind("<>",self.insAutoExpand.expand_word_event) + text.bind("<>",self.insCodeContext.toggle_code_context_event) + text.bind("<>",self.insFormatParagraph.format_paragraph_event) + text.bind("<>",self.insParenMatch.flash_paren_event) + text.bind("<>",self.insParenMatch.paren_closed_event) + text.bind("<>",self.insScriptBinding.run_module_event) + text.bind("<>",self.insScriptBinding.check_module_event) + text.bind("<>",self.insZoomHeight.zoom_height_event) + text.bind("<>",self.insRstripExtension.do_rstrip) + text.bind("<>",self.insCallTips.try_open_calltip_event) + text.bind("<>",self.insCallTips.refresh_calltip_event) #must come after paren-closed to work right + text.bind("<>",self.insCallTips.force_open_calltip_event) + def _filename_to_unicode(self, filename): """Return filename as BMP unicode so diplayable in Tk.""" # Decode bytes to unicode. @@ -981,17 +1018,7 @@ def load_standard_extensions(self): def get_standard_extension_names(self): return idleConf.GetExtensions(editor_only=True) - extfiles = { # map config-extension section names to new file names - 'AutoComplete': 'autocomplete', - 'AutoExpand': 'autoexpand', - 'CallTips': 'calltips', - 'CodeContext': 'codecontext', - 'FormatParagraph': 'paragraph', - 'ParenMatch': 'parenmatch', - 'RstripExtension': 'rstrip', - 'ScriptBinding': 'runscript', - 'ZoomHeight': 'zoomheight', - } + extfiles = { } def load_extension(self, name): fname = self.extfiles.get(name, name) diff --git a/Lib/idlelib/mainmenu.py b/Lib/idlelib/mainmenu.py index 65345cd1f16188..14c283e4901af5 100644 --- a/Lib/idlelib/mainmenu.py +++ b/Lib/idlelib/mainmenu.py @@ -52,6 +52,11 @@ ('Find in Files...', '<>'), ('R_eplace...', '<>'), ('Go to _Line', '<>'), + ('S_how Completions', '<>'), + ('E_xpand Word', '<>'), + ('Show C_all Tip', '<>'), + ('Show Surrounding P_arens', '<>'), + ]), ('format', [ ('_Indent Region', '<>'), @@ -62,9 +67,13 @@ ('Untabify Region', '<>'), ('Toggle Tabs', '<>'), ('New Indent Width', '<>'), + ('F_ormat Paragraph', '<>'), + ('S_trip Trailing Whitespace', '<>'), ]), ('run', [ ('Python Shell', '<>'), + ('C_heck Module', '<>'), + ('R_un Module', '<>'), ]), ('shell', [ ('_View Last Restart', '<>'), @@ -80,7 +89,7 @@ ]), ('options', [ ('Configure _IDLE', '<>'), - None, + ('_Code Context', '<>'), ]), ('help', [ ('_About IDLE', '<>'), diff --git a/Lib/idlelib/paragraph.py b/Lib/idlelib/paragraph.py index f11bdaeb77ac38..b64b4a8690ef42 100644 --- a/Lib/idlelib/paragraph.py +++ b/Lib/idlelib/paragraph.py @@ -1,4 +1,4 @@ -"""Extension to format a paragraph or selection to a max width. +"""format a paragraph or selection to a max width. Does basic, standard text formatting, and also understands Python comment blocks. Thus, for editing Python source code, this @@ -21,12 +21,6 @@ class FormatParagraph: - menudefs = [ - ('format', [ # /s/edit/format dscherer@cmu.edu - ('Format Paragraph', '<>'), - ]) - ] - def __init__(self, editwin): self.editwin = editwin @@ -48,7 +42,7 @@ def format_paragraph_event(self, event, limit=None): if limit is None: # The default length limit is that defined by pep8 limit = idleConf.GetOption( - 'extensions', 'FormatParagraph', 'max-width', + 'main', 'General', 'formatp_maxw', type='int', default=72) text = self.editwin.text first, last = self.editwin.get_selection_indices() diff --git a/Lib/idlelib/parenmatch.py b/Lib/idlelib/parenmatch.py index 2dcd68ebcc1ce1..b47d63bad4237c 100644 --- a/Lib/idlelib/parenmatch.py +++ b/Lib/idlelib/parenmatch.py @@ -1,4 +1,4 @@ -"""ParenMatch -- An IDLE extension for parenthesis matching. +"""ParenMatch -- for parenthesis matching. When you hit a right paren, the cursor should move briefly to the left paren. Paren here is used generically; the matching applies to @@ -30,21 +30,6 @@ class ParenMatch: - Highlight when cursor is moved to the right of a closer. This might be too expensive to check. """ - menudefs = [ - ('edit', [ - ("Show surrounding parens", "<>"), - ]) - ] - STYLE = idleConf.GetOption( - 'extensions','ParenMatch','style', default='expression') - FLASH_DELAY = idleConf.GetOption( - 'extensions','ParenMatch','flash-delay', type='int',default=500) - BELL = idleConf.GetOption( - 'extensions','ParenMatch','bell', type='bool',default=1) - HILITE_CONFIG = idleConf.GetHighlight( - idleConf.CurrentTheme(), idleConf.GetOption( - 'extensions','ParenMatch','hilite', default='expression')) - RESTORE_VIRTUAL_EVENT_NAME = "<>" # We want the restore event be called before the usual return and @@ -60,9 +45,21 @@ def __init__(self, editwin): # and deactivate_restore (which calls event_delete). editwin.text.bind(self.RESTORE_VIRTUAL_EVENT_NAME, self.restore_event) - self.bell = self.text.bell if self.BELL else lambda: None self.counter = 0 self.is_restore_active = 0 + self.reset() + + def reset(self): + self.STYLE = idleConf.GetOption( + 'main','Theme','parenstyle', default='opener') + self.FLASH_DELAY = idleConf.GetOption( + 'main','Theme','flash-delay', type='int',default=500) + if idleConf.GetOption( + 'main','Theme','bell', type='bool',default=1): + self.bell=self.text.bell + else: + self.bell=lambda:None + self.HILITE_CONFIG = idleConf.GetHighlight(idleConf.CurrentTheme(), 'parenmatch') self.set_style(self.STYLE) def activate_restore(self): diff --git a/Lib/idlelib/rstrip.py b/Lib/idlelib/rstrip.py index 2ce3c7eafe5a81..82c7f7e8d8c8ab 100644 --- a/Lib/idlelib/rstrip.py +++ b/Lib/idlelib/rstrip.py @@ -2,9 +2,6 @@ class RstripExtension: - menudefs = [ - ('format', [None, ('Strip trailing whitespace', '<>'), ] ), ] - def __init__(self, editwin): self.editwin = editwin self.editwin.text.bind("<>", self.do_rstrip) diff --git a/Lib/idlelib/runscript.py b/Lib/idlelib/runscript.py index 3355f17d90bdb7..97c02fc83b5f66 100644 --- a/Lib/idlelib/runscript.py +++ b/Lib/idlelib/runscript.py @@ -1,4 +1,4 @@ -"""Extension to execute code outside the Python shell window. +"""execute code outside the Python shell window. This adds the following commands: diff --git a/Lib/idlelib/zoomheight.py b/Lib/idlelib/zoomheight.py index d01c9e964aa60f..fc062c2cdf409f 100644 --- a/Lib/idlelib/zoomheight.py +++ b/Lib/idlelib/zoomheight.py @@ -1,4 +1,4 @@ -# Sample extension: zoom a window to maximum height +# zoom a window to maximum height import re import sys @@ -8,12 +8,6 @@ class ZoomHeight: - menudefs = [ - ('windows', [ - ('_Zoom Height', '<>'), - ]) - ] - def __init__(self, editwin): self.editwin = editwin From 86c3d4c26cddaa7be013918447d52fb89ad4259c Mon Sep 17 00:00:00 2001 From: wohlganger Date: Mon, 10 Jul 2017 16:25:46 -0500 Subject: [PATCH 07/64] whitespace + fix I thought I already committed. --- Lib/idlelib/codecontext.py | 6 +++--- Lib/idlelib/configdialog.py | 38 ++++++++++++++++++------------------- Lib/idlelib/mainmenu.py | 2 +- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/Lib/idlelib/codecontext.py b/Lib/idlelib/codecontext.py index 5de187a82584d5..36703dd3316cc0 100644 --- a/Lib/idlelib/codecontext.py +++ b/Lib/idlelib/codecontext.py @@ -26,7 +26,7 @@ lambda s, c=re.compile(r"^(\s*)(\w*)"): c.match(s).groups() class CodeContext: - + def __init__(self, editwin): self.editwin = editwin self.text = editwin.text @@ -44,7 +44,7 @@ def __init__(self, editwin): self.reset() if visible: self.toggle_code_context_event() - + # Start two update cycles, one for context lines, one for font changes. self.text.after(UPDATEINTERVAL, self.timer_event) self.text.after(FONTUPDATEINTERVAL, self.font_timer_event) @@ -60,7 +60,7 @@ def reset(self): #need to rebuild widget to change color self.toggle_code_context_event() self.toggle_code_context_event() - + def toggle_code_context_event(self, event=None): if not self.label: # Calculate the border width and horizontal padding required to diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py index d8603c2894aa22..f85943b2b5af54 100644 --- a/Lib/idlelib/configdialog.py +++ b/Lib/idlelib/configdialog.py @@ -223,14 +223,14 @@ def create_page_highlight(self): self.font_name = StringVar(parent) self.is_builtin_theme = BooleanVar(parent) self.highlight_target = StringVar(parent) - + self.parenstyle = StringVar(parent) self.parenstyle.set(idleConf.GetOption( 'main','Theme','parenstyle', default='opener')) self.bell = BooleanVar(parent) self.bell.set(idleConf.GetOption( 'main','Theme','bell', default=True)) - + self.flash_delay = IntVar(parent) self.flash_delay.set(idleConf.GetOption( 'main','Theme','flash-delay', default=500)) @@ -323,9 +323,9 @@ def tem(event, elem=element): #frame_paren self.opt_menu_pstyle = OptionMenu( frame_paren, self.parenstyle, "opener","parens","expression")#todo: figure out how to make it work - + flash_label = Label(frame_paren, text='Time Displayed : \n(0 is until given input)') - + self.entry_flash = Entry( frame_paren, textvariable=self.flash_delay, width=4) self.check_bell = Checkbutton( @@ -334,14 +334,14 @@ def tem(event, elem=element): lines_label = Label(frame_code, text='Lines : ') self.entry_num_lines = Entry( frame_code, textvariable=self.num_lines, width=3) - + ##widget packing #body frame_custom.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH) frame_theme.pack(side=TOP, padx=5, pady=5, fill=X) frame_paren.pack(side=TOP, padx=5, pady=5, fill=X) frame_code.pack(side=TOP, padx=5, pady=5, fill=X) - + #frame_custom self.frame_colour_set.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=X) frame_fg_bg_toggle.pack(side=TOP, padx=5, pady=0) @@ -366,12 +366,12 @@ def tem(event, elem=element): self.opt_menu_pstyle.pack(side=TOP,anchor=W,padx=5, fill=X) self.check_bell.pack(side=TOP, anchor=W,padx=5) flash_label.pack(side=LEFT, anchor=W, padx=5) - self.entry_flash.pack(side=LEFT, fill=X,anchor=W,padx=5) + self.entry_flash.pack(side=LEFT, fill=X,anchor=W,padx=5) #frame_code lines_label.pack(side=LEFT, anchor=W, padx=5) self.entry_num_lines.pack(side=LEFT,anchor=W,padx=5) - - + + return frame def create_page_keys(self): @@ -521,10 +521,10 @@ def create_page_general(self): #frame extras autocomplete_wait_title = Label(frame_extras, text='AutoComplete Popup Wait') self.entry_autocomplete_wait = Entry( - frame_extras, textvariable=self.autocomplete_wait, width=6) - formatp_maxw_title = Label(frame_extras, text='Format Paragraph Max Width') + frame_extras, textvariable=self.autocomplete_wait, width=6) + formatp_maxw_title = Label(frame_extras, text='Format Paragraph Max Width') self.entry_formatp_maxw = Entry( - frame_extras, textvariable=self.formatp_maxw, width=3) + frame_extras, textvariable=self.formatp_maxw, width=3) #frame_help frame_helplist = Frame(frame_help) frame_helplist_buttons = Frame(frame_helplist) @@ -607,7 +607,7 @@ def attach_var_callbacks(self): self.num_lines.trace_add('write', self.var_changed_num_lines) self.formatp_maxw.trace_add('write', self.var_changed_formatp_maxw) self.autocomplete_wait.trace_add('write',self.var_changed_autocomplete_wait) - + def remove_var_callbacks(self): "Remove callbacks to prevent memory leaks." for var in ( @@ -773,23 +773,23 @@ def var_changed_autosave(self, *params): def var_changed_parenstyle(self, *params): "Store change to parenthetics display style." value=self.parenstyle.get() - self.add_changed_item('main', 'Theme', 'parenstyle', value) + changes.add_option('main', 'Theme', 'parenstyle', value) def var_changed_bell(self, *params): "Store change to parenthtics bell (on/off)." value=self.bell.get() - self.add_changed_item('main', 'Theme', 'bell', value) + changes.add_option('main', 'Theme', 'bell', value) def var_changed_flash_delay(self, *params): "Store change to parenthetics flash delay." value=self.flash_delay.get() - self.add_changed_item('main', 'Theme', 'flash-delay', value) + changes.add_option('main', 'Theme', 'flash-delay', value) def var_changed_num_lines(self, *params): "Store change to code context - number of lines displayed." value=self.num_lines.get() - self.add_changed_item('main', 'Theme', 'numlines', value) - + changes.add_option('main', 'Theme', 'numlines', value) + def set_theme_type(self): "Set available screen options based on builtin or custom theme." if self.is_builtin_theme.get(): @@ -1589,7 +1589,7 @@ def save_all_changed_extensions(self): [Cancel] only cancels changes made since the last save. ''' help_pages = { - + } diff --git a/Lib/idlelib/mainmenu.py b/Lib/idlelib/mainmenu.py index 14c283e4901af5..cd649cf5285be6 100644 --- a/Lib/idlelib/mainmenu.py +++ b/Lib/idlelib/mainmenu.py @@ -56,7 +56,7 @@ ('E_xpand Word', '<>'), ('Show C_all Tip', '<>'), ('Show Surrounding P_arens', '<>'), - + ]), ('format', [ ('_Indent Region', '<>'), From 1c246fcf1f3e4df13f4caba599c813efa0cdb4eb Mon Sep 17 00:00:00 2001 From: wohlganger Date: Tue, 11 Jul 2017 08:13:14 -0500 Subject: [PATCH 08/64] warning fixes, removed old config entries --- Lib/idlelib/autocomplete.py | 2 +- Lib/idlelib/config-keys.def | 10 +++---- Lib/idlelib/config-main.def | 53 ------------------------------------- 3 files changed, 6 insertions(+), 59 deletions(-) diff --git a/Lib/idlelib/autocomplete.py b/Lib/idlelib/autocomplete.py index 87706c19eae543..33eef9ae63e19f 100644 --- a/Lib/idlelib/autocomplete.py +++ b/Lib/idlelib/autocomplete.py @@ -39,7 +39,7 @@ def __init__(self, editwin=None): self._delayed_completion_index = None def reset(self): - self.popupwait = idleConf.GetOption("main", "EditorWindow", + self.popupwait = idleConf.GetOption("main", "General", "autocomplete_wait", type="int", default=0) def _make_autocomplete_window(self): diff --git a/Lib/idlelib/config-keys.def b/Lib/idlelib/config-keys.def index 6b7e202c9ccab1..ad110b50024a6d 100644 --- a/Lib/idlelib/config-keys.def +++ b/Lib/idlelib/config-keys.def @@ -63,7 +63,7 @@ expand-word= try-open-calltip= refresh-calltip= force-open-calltip= -toggle-code-context= +toggle-code-context= format-paragraph= flash-paren= paren-closed= @@ -127,7 +127,7 @@ expand-word= try-open-calltip= refresh-calltip= force-open-calltip= -toggle-code-context= +toggle-code-context= format-paragraph= flash-paren= paren-closed= @@ -191,7 +191,7 @@ expand-word= try-open-calltip= refresh-calltip= force-open-calltip= -toggle-code-context= +toggle-code-context= format-paragraph= flash-paren= paren-closed= @@ -255,7 +255,7 @@ expand-word= try-open-calltip= refresh-calltip= force-open-calltip= -toggle-code-context= +toggle-code-context= format-paragraph= flash-paren= paren-closed= @@ -320,7 +320,7 @@ expand-word= try-open-calltip= refresh-calltip= force-open-calltip= -toggle-code-context= +toggle-code-context= format-paragraph= flash-paren= paren-closed= diff --git a/Lib/idlelib/config-main.def b/Lib/idlelib/config-main.def index 20ef3687dc1682..16f4b0959cf13c 100644 --- a/Lib/idlelib/config-main.def +++ b/Lib/idlelib/config-main.def @@ -82,59 +82,6 @@ name= name2= # name2 set in user config-main.cfg for keys added after 2016 July 1 -[AutoComplete] -enable=True -popupwait=2000 -[AutoComplete_cfgBindings] -force-open-completions= -[AutoComplete_bindings] -autocomplete= -try-open-completions= - -[AutoExpand] -enable=True - -[CallTips] -enable=True -[CallTips_cfgBindings] -force-open-calltip= - - -[CodeContext] -enable=True -enable_shell=False -numlines=3 -visible=False -bgcolor=LightGray -fgcolor=Black - - -[FormatParagraph] -enable=True -max-width=72 - - -[ParenMatch] -enable=True -style= expression -hilite=hilite -flash-delay= 500 -bell=True - -[RstripExtension] -enable=True -enable_shell=False -enable_editor=True - -[ScriptBinding] -enable=True -enable_shell=False -enable_editor=True - -[ZoomHeight] -enable=True - - [History] cyclic=1 From 0418ad0a94f215ad9b5894b9e5b71474670f5c44 Mon Sep 17 00:00:00 2001 From: wohlganger Date: Tue, 11 Jul 2017 16:46:26 -0500 Subject: [PATCH 09/64] calltip append doc fix, default paren-fore fix, help update How did the change to the __doc__ of append not get fixed in the tests earlier? --- Lib/idlelib/config.py | 2 +- Lib/idlelib/help.html | 15 ++------------- Lib/idlelib/idle_test/test_calltips.py | 2 +- 3 files changed, 4 insertions(+), 15 deletions(-) diff --git a/Lib/idlelib/config.py b/Lib/idlelib/config.py index fb6c5f3a2a7389..c5690c8729e960 100644 --- a/Lib/idlelib/config.py +++ b/Lib/idlelib/config.py @@ -359,7 +359,7 @@ def GetThemeDict(self, type, themeName): 'console-background':'#ffffff', 'codecontext-foreground':'#000000', 'codecontext-background':'#ffffff', - 'parenmatch-foreground':'000000', + 'parenmatch-foreground':'#000000', 'parenmatch-background':'#ffffff', } for element in theme: diff --git a/Lib/idlelib/help.html b/Lib/idlelib/help.html index 0a3062e156421c..60a1035ece6538 100644 --- a/Lib/idlelib/help.html +++ b/Lib/idlelib/help.html @@ -636,19 +636,8 @@

25.5.4.2. Setting preferences¶

IDLE contains an extension facility. Preferences for extensions can be changed with Configure Extensions. See the beginning of config-extensions.def -in the idlelib directory for further information. The default extensions -are currently:

-
    -
  • FormatParagraph
  • -
  • AutoExpand
  • -
  • ZoomHeight
  • -
  • ScriptBinding
  • -
  • CallTips
  • -
  • ParenMatch
  • -
  • AutoComplete
  • -
  • CodeContext
  • -
  • RstripExtension
  • -
+in the idlelib directory for further information. There are currently no default extensions.

+ diff --git a/Lib/idlelib/idle_test/test_calltips.py b/Lib/idlelib/idle_test/test_calltips.py index 29b9f06faf868b..cf7c650a074daa 100644 --- a/Lib/idlelib/idle_test/test_calltips.py +++ b/Lib/idlelib/idle_test/test_calltips.py @@ -58,7 +58,7 @@ def gtest(obj, out): 'Create and return a new object. See help(type) for accurate signature.') gtest(list.__init__, 'Initialize self. See help(type(self)) for accurate signature.') - append_doc = "Append object to the end of the list." + append_doc = "L.append(object) -> None -- append object to end" gtest(list.append, append_doc) gtest([].append, append_doc) gtest(List.append, append_doc) From 097a0d8be848fbf5b5be1e6af0ab8acd8fc77d19 Mon Sep 17 00:00:00 2001 From: wohlganger Date: Wed, 12 Jul 2017 08:46:23 -0500 Subject: [PATCH 10/64] fix calltips Used incorrect / old python for testing. --- Lib/idlelib/idle_test/test_calltips.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/idlelib/idle_test/test_calltips.py b/Lib/idlelib/idle_test/test_calltips.py index cf7c650a074daa..29b9f06faf868b 100644 --- a/Lib/idlelib/idle_test/test_calltips.py +++ b/Lib/idlelib/idle_test/test_calltips.py @@ -58,7 +58,7 @@ def gtest(obj, out): 'Create and return a new object. See help(type) for accurate signature.') gtest(list.__init__, 'Initialize self. See help(type(self)) for accurate signature.') - append_doc = "L.append(object) -> None -- append object to end" + append_doc = "Append object to the end of the list." gtest(list.append, append_doc) gtest([].append, append_doc) gtest(List.append, append_doc) From 4232e77176ddc39040e103c63aa6c492e05ea135 Mon Sep 17 00:00:00 2001 From: wohlganger Date: Wed, 12 Jul 2017 09:17:55 -0500 Subject: [PATCH 11/64] removed circular import --- Lib/idlelib/editor.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/Lib/idlelib/editor.py b/Lib/idlelib/editor.py index 4b575fa7c8e8cd..83aeb6c7834b80 100644 --- a/Lib/idlelib/editor.py +++ b/Lib/idlelib/editor.py @@ -26,15 +26,7 @@ from idlelib import replace from idlelib import search from idlelib import windows -from idlelib.autocomplete import AutoComplete -from idlelib.autoexpand import AutoExpand -from idlelib.calltips import CallTips -from idlelib.codecontext import CodeContext -from idlelib.paragraph import FormatParagraph -from idlelib.parenmatch import ParenMatch -from idlelib.rstrip import RstripExtension -from idlelib.runscript import ScriptBinding -from idlelib.zoomheight import ZoomHeight + # The default tab setting for a Text widget, in average-width characters. TK_TABWIDTH_DEFAULT = 8 @@ -66,6 +58,16 @@ class EditorWindow(object): help_url = None def __init__(self, flist=None, filename=None, key=None, root=None): + from idlelib.autocomplete import AutoComplete + from idlelib.autoexpand import AutoExpand + from idlelib.calltips import CallTips + from idlelib.codecontext import CodeContext + from idlelib.paragraph import FormatParagraph + from idlelib.parenmatch import ParenMatch + from idlelib.rstrip import RstripExtension + from idlelib.runscript import ScriptBinding + from idlelib.zoomheight import ZoomHeight + if EditorWindow.help_url is None: dochome = os.path.join(sys.base_prefix, 'Doc', 'index.html') if sys.platform.count('linux'): From a7b29f5df03ce95a41ec26f081d326bdb55d9fb8 Mon Sep 17 00:00:00 2001 From: wohlganger Date: Wed, 12 Jul 2017 09:49:06 -0500 Subject: [PATCH 12/64] fix warnings due to missing default config options in idlelib.config-main.def --- Lib/idlelib/config-main.def | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Lib/idlelib/config-main.def b/Lib/idlelib/config-main.def index 16f4b0959cf13c..55e747f9d625d3 100644 --- a/Lib/idlelib/config-main.def +++ b/Lib/idlelib/config-main.def @@ -55,6 +55,7 @@ autosave= 0 print-command-posix=lpr %%s print-command-win=start /min notepad /p %%s delete-exitfunc= 1 +autocomplete_wait= 0 [EditorWindow] width= 80 @@ -74,6 +75,11 @@ num-spaces= 4 default= 1 name= IDLE Classic name2= +contexton= 0 +numlines= 3 +parenstyle= opener +flash-delay= 500 +bell= 1 # name2 set in user config-main.cfg for themes added after 2015 Oct 1 [Keys] From 252672b6d9d50a4f60f4c9a0cadc385f32401797 Mon Sep 17 00:00:00 2001 From: wohlganger Date: Wed, 19 Jul 2017 11:06:28 -0500 Subject: [PATCH 13/64] added format_pmaxw to config-main --- Lib/idlelib/config-main.def | 1 + 1 file changed, 1 insertion(+) diff --git a/Lib/idlelib/config-main.def b/Lib/idlelib/config-main.def index 55e747f9d625d3..71ff1f927254ec 100644 --- a/Lib/idlelib/config-main.def +++ b/Lib/idlelib/config-main.def @@ -56,6 +56,7 @@ print-command-posix=lpr %%s print-command-win=start /min notepad /p %%s delete-exitfunc= 1 autocomplete_wait= 0 +formatp_maxw= 72 [EditorWindow] width= 80 From 1a745919a6a7d4ac015f7a02d60190af6e44151d Mon Sep 17 00:00:00 2001 From: wohlganger Date: Thu, 20 Jul 2017 09:08:44 -0500 Subject: [PATCH 14/64] replaced help.html edit with idle.rst edit per terryjreedy request. --- Doc/library/idle.rst | 22 ++-------------------- Lib/idlelib/help.html | 15 +++++++++++++-- 2 files changed, 15 insertions(+), 22 deletions(-) diff --git a/Doc/library/idle.rst b/Doc/library/idle.rst index a945b6d771225b..13f286d7e30ebb 100644 --- a/Doc/library/idle.rst +++ b/Doc/library/idle.rst @@ -672,23 +672,5 @@ Extensions IDLE contains an extension facility. Preferences for extensions can be changed with Configure Extensions. See the beginning of config-extensions.def -in the idlelib directory for further information. The default extensions -are currently: - -* FormatParagraph - -* AutoExpand - -* ZoomHeight - -* ScriptBinding - -* CallTips - -* ParenMatch - -* AutoComplete - -* CodeContext - -* RstripExtension +in the idlelib directory for further information. There are currently no +default extensions. diff --git a/Lib/idlelib/help.html b/Lib/idlelib/help.html index 60a1035ece6538..0a3062e156421c 100644 --- a/Lib/idlelib/help.html +++ b/Lib/idlelib/help.html @@ -636,8 +636,19 @@

25.5.4.2. Setting preferences¶

IDLE contains an extension facility. Preferences for extensions can be changed with Configure Extensions. See the beginning of config-extensions.def -in the idlelib directory for further information. There are currently no default extensions.

- +in the idlelib directory for further information. The default extensions +are currently:

+
    +
  • FormatParagraph
  • +
  • AutoExpand
  • +
  • ZoomHeight
  • +
  • ScriptBinding
  • +
  • CallTips
  • +
  • ParenMatch
  • +
  • AutoComplete
  • +
  • CodeContext
  • +
  • RstripExtension
  • +
From f095c53d3dfffb716885db0b48b1c4a89d63b7a4 Mon Sep 17 00:00:00 2001 From: wohlganger Date: Fri, 4 Aug 2017 10:11:19 -0500 Subject: [PATCH 15/64] added conflict resolved var descriptions to docstring in create_page_highlight. --- Lib/idlelib/configdialog.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py index 1441b431aa364a..7518748f3d86db 100644 --- a/Lib/idlelib/configdialog.py +++ b/Lib/idlelib/configdialog.py @@ -230,6 +230,12 @@ def create_page_highlight(self): Note: this has no callback. is_builtin_theme: Selector for built-in or custom theme. highlight_target: Menu variable for the highlight tag target. + parenstyle: Menu option for style of paren highlight used. + bell: Menu variable for whether parens bells on error. + flash_delay: Menu variable for length of time paren + highlights display. + num_lines: Menu variable for number of lines Code Context + displays. Instance Data Attributes: theme_elements: Dictionary of tags for text highlighting. From a661bfaa2a95cd10194e45da0617c146cf7f11bb Mon Sep 17 00:00:00 2001 From: wohlganger Date: Fri, 4 Aug 2017 11:03:37 -0500 Subject: [PATCH 16/64] strip trailing whitespace --- Lib/idlelib/configdialog.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py index 7518748f3d86db..7703556f8ce486 100644 --- a/Lib/idlelib/configdialog.py +++ b/Lib/idlelib/configdialog.py @@ -309,7 +309,7 @@ def create_page_highlight(self): self.highlight_target = tracers.add( StringVar(parent), self.var_changed_highlight_target) - + self.parenstyle = tracers.add( StringVar(parent), self.var_changed_parenstyle) self.bell = tracers.add( @@ -327,7 +327,7 @@ def create_page_highlight(self): 'main','Theme','flash-delay', default=500)) self.num_lines.set(idleConf.GetOption( 'main','Theme','numlines', default=3)) - + # Widget creation: # body frame and section frames frame = Frame(self.note) @@ -336,12 +336,12 @@ def create_page_highlight(self): text=' Custom Highlighting ') frame_theme = LabelFrame(frame, borderwidth=2, relief=GROOVE, text=' Highlighting Theme ') - + frame_paren = LabelFrame(frame, borderwidth=2, relief=GROOVE, text=' Matched Parenthetics ') frame_code = LabelFrame(frame, borderwidth=2, relief=GROOVE, text=' Code Context ') - + #frame_custom text = self.highlight_sample = frame.highlight_sample = Text( frame_custom, relief=SOLID, borderwidth=1, @@ -580,7 +580,7 @@ def var_changed_color(self, *params): self.num_lines = tracers.add( StringVar(parent), self.var_changed_num_lines) - + def var_changed_highlight_target(self, *params): "Process selection of new target tag for highlighting." self.set_highlight_target() From 5a6a007c88b6b070cdff2f78f84ec8422f26736a Mon Sep 17 00:00:00 2001 From: wohlganger Date: Thu, 24 Aug 2017 11:09:10 -0500 Subject: [PATCH 17/64] re-introduced highlight options. Fixing code conflict ate highlighting options. Needed to be re-introduced. --- Lib/idlelib/configdialog.py | 81 +++++++++++++++++++++++++++++++++---- 1 file changed, 74 insertions(+), 7 deletions(-) diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py index dedf4deee849e3..caa64d33a3faad 100644 --- a/Lib/idlelib/configdialog.py +++ b/Lib/idlelib/configdialog.py @@ -207,7 +207,7 @@ def deactivate_current_config(self): for instance in win_instances: instance.RemoveKeybindings() - + def activate_config_changes(self): @@ -291,7 +291,7 @@ def load_extensions(self): for ext_name in self.extensions: opt_list = sorted(self.ext_defaultCfg.GetOptionList(ext_name)) - + # Bring 'enable' options to the beginning of the list. enables = [opt_name for opt_name in opt_list if opt_name.startswith('enable')] @@ -769,6 +769,8 @@ def create_page_highlight(self): 'Shell Error Text': ('error', '11'), 'Shell Stdout Text': ('stdout', '12'), 'Shell Stderr Text': ('stderr', '13'), + 'Code Context Text': ('codecontext', '14'), + 'Matched Parenthetics': ('parenmatch', '15'), } self.builtin_name = tracers.add( StringVar(self), self.var_changed_builtin_name) @@ -781,6 +783,23 @@ def create_page_highlight(self): BooleanVar(self), self.var_changed_theme_source) self.highlight_target = tracers.add( StringVar(self), self.var_changed_highlight_target) + self.parenstyle = tracers.add( + StringVar(self), self.var_changed_parenstyle) + self.bell = tracers.add( + StringVar(self), self.var_changed_bell) + self.flash_delay = tracers.add( + StringVar(self), self.var_changed_flash_delay) + self.num_lines = tracers.add( + StringVar(self), self.var_changed_num_lines) + + self.parenstyle.set(idleConf.GetOption( + 'main','Theme','parenstyle', default='opener')) + self.bell.set(idleConf.GetOption( + 'main','Theme','bell', default=True)) + self.flash_delay.set(idleConf.GetOption( + 'main','Theme','flash-delay', default=500)) + self.num_lines.set(idleConf.GetOption( + 'main','Theme','numlines', default=3)) # Create widgets: # body frame and section frames. @@ -788,6 +807,10 @@ def create_page_highlight(self): text=' Custom Highlighting ') frame_theme = LabelFrame(self, borderwidth=2, relief=GROOVE, text=' Highlighting Theme ') + frame_paren = LabelFrame(self, borderwidth=2, relief=GROOVE, + text=' Matched Parenthetics ') + frame_code = LabelFrame(self, borderwidth=2, relief=GROOVE, + text=' Code Context ') # frame_custom. text = self.highlight_sample = Text( frame_custom, relief=SOLID, borderwidth=1, @@ -795,12 +818,15 @@ def create_page_highlight(self): takefocus=FALSE, highlightthickness=0, wrap=NONE) text.bind('', lambda e: 'break') text.bind('', lambda e: 'break') - text_and_tags=(('\n', 'normal'), + text_and_tags=( + ('Class CodeContext\n', 'codecontext'), ('#you can click here', 'comment'), ('\n', 'normal'), ('#to choose items', 'comment'), ('\n', 'normal'), ('def', 'keyword'), (' ', 'normal'), - ('func', 'definition'), ('(param):\n ', 'normal'), - ('"""string"""', 'string'), ('\n var0 = ', 'normal'), + ('func', 'definition'), + ('(parenthetics)','parenmatch'), + (':\n','normal'), + (' """string"""', 'string'), ('\n var0 = ', 'normal'), ("'string'", 'string'), ('\n var1 = ', 'normal'), ("'selected'", 'hilite'), ('\n var2 = ', 'normal'), ("'found'", 'hit'), ('\n var3 = ', 'normal'), @@ -855,11 +881,24 @@ def tem(event, elem=element): frame_theme, text='Delete Custom Theme', command=self.delete_custom) self.theme_message = Label(frame_theme, bd=2) - + #frame_paren + self.opt_menu_pstyle = OptionMenu( + frame_paren, self.parenstyle, "opener","parens","expression") + flash_label = Label(frame_paren, text='Time Displayed : \n(0 is until given input)') + self.entry_flash = Entry( + frame_paren, textvariable=self.flash_delay, width=4) + self.check_bell = Checkbutton( + frame_paren, text="Bell", variable=self.bell) + #frame_code + lines_label = Label(frame_code, text='Lines : ') + self.entry_num_lines = Entry( + frame_code, textvariable=self.num_lines, width=3) # Pack widgets: # body. frame_custom.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH) - frame_theme.pack(side=LEFT, padx=5, pady=5, fill=Y) + frame_theme.pack(side=TOP, padx=5, pady=5, fill=X) + frame_paren.pack(side=TOP, padx=5, pady=5, fill=X) + frame_code.pack(side=TOP, padx=5, pady=5, fill=X) # frame_custom. self.frame_color_set.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=X) frame_fg_bg_toggle.pack(side=TOP, padx=5, pady=0) @@ -878,6 +917,14 @@ def tem(event, elem=element): self.customlist.pack(side=TOP, fill=X, anchor=W, padx=5, pady=5) self.button_delete_custom.pack(side=TOP, fill=X, padx=5, pady=5) self.theme_message.pack(side=TOP, fill=X, pady=5) + #frame_paren + self.opt_menu_pstyle.pack(side=TOP,anchor=W,padx=5, fill=X) + self.check_bell.pack(side=TOP, anchor=W,padx=5) + flash_label.pack(side=LEFT, anchor=W, padx=5) + self.entry_flash.pack(side=LEFT, fill=X,anchor=W,padx=5) + #frame_code + lines_label.pack(side=LEFT, anchor=W, padx=5) + self.entry_num_lines.pack(side=LEFT,anchor=W,padx=5) def load_theme_cfg(self): """Load current configuration settings for the theme options. @@ -984,6 +1031,26 @@ def var_changed_highlight_target(self, *params): "Process selection of new target tag for highlighting." self.set_highlight_target() + def var_changed_parenstyle(self, *params): + "Store change to parenthetics display style." + value=self.parenstyle.get() + changes.add_option('main', 'Theme', 'parenstyle', value) + + def var_changed_bell(self, *params): + "Store change to parenthtics bell (on/off)." + value=self.bell.get() + changes.add_option('main', 'Theme', 'bell', value) + + def var_changed_flash_delay(self, *params): + "Store change to parenthetics flash delay." + value=self.flash_delay.get() + changes.add_option('main', 'Theme', 'flash-delay', value) + + def var_changed_num_lines(self, *params): + "Store change to code context - number of lines displayed." + value=self.num_lines.get() + changes.add_option('main', 'Theme', 'numlines', value) + def set_theme_type(self): """Set available screen options based on builtin or custom theme. From 709b43470a70c33aab9146b4a911feb26efdbf7a Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Thu, 24 Aug 2017 13:49:24 -0400 Subject: [PATCH 18/64] News blurb. --- Misc/NEWS.d/next/IDLE/2017-08-24-13-48-16.bpo-27099.rENefC.rst | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 Misc/NEWS.d/next/IDLE/2017-08-24-13-48-16.bpo-27099.rENefC.rst diff --git a/Misc/NEWS.d/next/IDLE/2017-08-24-13-48-16.bpo-27099.rENefC.rst b/Misc/NEWS.d/next/IDLE/2017-08-24-13-48-16.bpo-27099.rENefC.rst new file mode 100644 index 00000000000000..539f06eb63b446 --- /dev/null +++ b/Misc/NEWS.d/next/IDLE/2017-08-24-13-48-16.bpo-27099.rENefC.rst @@ -0,0 +1,2 @@ +Convert IDLE's built-in 'extensions' to regular features. Inital patch by +Charles Wohlganger. From b9b1f9a85ca105fc03cd624f0016b360885d8acf Mon Sep 17 00:00:00 2001 From: wohlganger Date: Thu, 24 Aug 2017 13:47:53 -0500 Subject: [PATCH 19/64] Reinstated zoomheight as an extension. Replaced autoexpand with zoomheight as non-core keybinding test example. --- Lib/idlelib/config-extensions.def | 5 +++++ Lib/idlelib/config-keys.def | 5 ----- Lib/idlelib/config.py | 3 +-- Lib/idlelib/idle_test/test_configdialog.py | 5 +++-- Lib/idlelib/zoomheight.py | 11 ++++++++--- 5 files changed, 17 insertions(+), 12 deletions(-) diff --git a/Lib/idlelib/config-extensions.def b/Lib/idlelib/config-extensions.def index 649a2efa3e478b..973c28f540ea93 100644 --- a/Lib/idlelib/config-extensions.def +++ b/Lib/idlelib/config-extensions.def @@ -31,3 +31,8 @@ # # See config-keys.def for notes on specifying keys and extend.txt for # information on creating IDLE extensions. + +[ZoomHeight] +enable = True +[ZoomHeight_cfgBindings] +zoom-height= \ No newline at end of file diff --git a/Lib/idlelib/config-keys.def b/Lib/idlelib/config-keys.def index ad110b50024a6d..8ea04538edd49c 100644 --- a/Lib/idlelib/config-keys.def +++ b/Lib/idlelib/config-keys.def @@ -69,7 +69,6 @@ flash-paren= paren-closed= run-module= check-module= -zoom-height= [IDLE Classic Unix] copy= @@ -133,7 +132,6 @@ flash-paren= paren-closed= run-module= check-module= -zoom-height= [IDLE Modern Unix] copy = @@ -197,7 +195,6 @@ flash-paren= paren-closed= run-module= check-module= -zoom-height= [IDLE Classic Mac] copy= @@ -261,7 +258,6 @@ flash-paren= paren-closed= run-module= check-module= -zoom-height= [IDLE Classic OSX] toggle-tabs = @@ -326,4 +322,3 @@ flash-paren= paren-closed= run-module= check-module= -zoom-height= diff --git a/Lib/idlelib/config.py b/Lib/idlelib/config.py index b806a481b555cf..9c2ca2e460828d 100644 --- a/Lib/idlelib/config.py +++ b/Lib/idlelib/config.py @@ -672,8 +672,7 @@ def GetCoreKeys(self, keySetName=None): '<>':[''], '<>':['', '', ''], '<>':[''], - '<>':[''], - '<>':[''] + '<>':[''] } if keySetName: if not (self.userCfg['keys'].has_section(keySetName) or diff --git a/Lib/idlelib/idle_test/test_configdialog.py b/Lib/idlelib/idle_test/test_configdialog.py index df801c32b73a4b..1ce92412571384 100644 --- a/Lib/idlelib/idle_test/test_configdialog.py +++ b/Lib/idlelib/idle_test/test_configdialog.py @@ -823,19 +823,20 @@ def test_keybinding(self): d.custom_name.set('my custom keys') d.bindingslist.delete(0, 'end') d.bindingslist.insert(0, 'copy') - d.bindingslist.insert(1, 'expand-word') + d.bindingslist.insert(1, 'zoom-height') d.bindingslist.selection_set(0) d.bindingslist.selection_anchor(0) # Core binding - adds to keys. d.keybinding.set('') self.assertEqual(keyspage, {'my custom keys': {'copy': ''}}) + # Not a core binding - adds to extensions. d.bindingslist.selection_set(1) d.bindingslist.selection_anchor(1) d.keybinding.set('') self.assertEqual(extpage, - {'AutoExpand_cfgBindings': {'expand-word': ''}}) + {'zoomheight_cfgBindings': {'zoom-height': ''}}) def test_set_keys_type(self): eq = self.assertEqual diff --git a/Lib/idlelib/zoomheight.py b/Lib/idlelib/zoomheight.py index fc062c2cdf409f..cc8299bd73fec0 100644 --- a/Lib/idlelib/zoomheight.py +++ b/Lib/idlelib/zoomheight.py @@ -1,4 +1,4 @@ -# zoom a window to maximum height +# Sample extension - zoom a window to maximum height import re import sys @@ -6,8 +6,13 @@ from idlelib import macosx -class ZoomHeight: - +class zoomheight: + menudefs = [ + ('windows', [ + ('_Zoom Height', '<>'), + ]) + ] + def __init__(self, editwin): self.editwin = editwin From 8b76ccb2240e403317cbf37bdb9a04347b518aa6 Mon Sep 17 00:00:00 2001 From: wohlganger Date: Thu, 24 Aug 2017 14:08:57 -0500 Subject: [PATCH 20/64] fixed configdialog test (wrong cases), fixed whitespace --- Lib/idlelib/config-extensions.def | 4 ++-- Lib/idlelib/idle_test/test_configdialog.py | 2 +- Lib/idlelib/tree.py | 2 +- Lib/idlelib/zoomheight.py | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Lib/idlelib/config-extensions.def b/Lib/idlelib/config-extensions.def index 973c28f540ea93..d4e106329a95ce 100644 --- a/Lib/idlelib/config-extensions.def +++ b/Lib/idlelib/config-extensions.def @@ -32,7 +32,7 @@ # See config-keys.def for notes on specifying keys and extend.txt for # information on creating IDLE extensions. -[ZoomHeight] +[zoomheight] enable = True -[ZoomHeight_cfgBindings] +[zoomheight_cfgBindings] zoom-height= \ No newline at end of file diff --git a/Lib/idlelib/idle_test/test_configdialog.py b/Lib/idlelib/idle_test/test_configdialog.py index 1ce92412571384..d7bcd040e58e8c 100644 --- a/Lib/idlelib/idle_test/test_configdialog.py +++ b/Lib/idlelib/idle_test/test_configdialog.py @@ -830,7 +830,7 @@ def test_keybinding(self): d.keybinding.set('') self.assertEqual(keyspage, {'my custom keys': {'copy': ''}}) - + # Not a core binding - adds to extensions. d.bindingslist.selection_set(1) d.bindingslist.selection_anchor(1) diff --git a/Lib/idlelib/tree.py b/Lib/idlelib/tree.py index 292ce36184c76d..907e52d1a992a1 100644 --- a/Lib/idlelib/tree.py +++ b/Lib/idlelib/tree.py @@ -20,7 +20,7 @@ from tkinter.ttk import Scrollbar from idlelib.config import idleConf -from idlelib import zoomheight +from idlelib import ZoomHeight ICONDIR = "Icons" diff --git a/Lib/idlelib/zoomheight.py b/Lib/idlelib/zoomheight.py index cc8299bd73fec0..18d42608c98cf3 100644 --- a/Lib/idlelib/zoomheight.py +++ b/Lib/idlelib/zoomheight.py @@ -12,7 +12,7 @@ class zoomheight: ('_Zoom Height', '<>'), ]) ] - + def __init__(self, editwin): self.editwin = editwin From b986c1232a4eb717b82c3b3d84df1bffdae4d276 Mon Sep 17 00:00:00 2001 From: wohlganger Date: Thu, 24 Aug 2017 14:12:59 -0500 Subject: [PATCH 21/64] update idle.rst: zoomheight is an extension. --- Doc/library/idle.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Doc/library/idle.rst b/Doc/library/idle.rst index 13f286d7e30ebb..4f51f3f0ac2eaa 100644 --- a/Doc/library/idle.rst +++ b/Doc/library/idle.rst @@ -672,5 +672,6 @@ Extensions IDLE contains an extension facility. Preferences for extensions can be changed with Configure Extensions. See the beginning of config-extensions.def -in the idlelib directory for further information. There are currently no -default extensions. +in the idlelib directory for further information. The only current default +extension is zoomheight. It exists as an extension primarily to be an example +and for testing purposes. \ No newline at end of file From f86acaa61b35afe19f4edae1e91e13933fefed10 Mon Sep 17 00:00:00 2001 From: wohlganger Date: Thu, 24 Aug 2017 14:42:32 -0500 Subject: [PATCH 22/64] more fixes for zoomheight - trying to minimize changes from master --- Lib/idlelib/config-extensions.def | 4 ++-- Lib/idlelib/configdialog.py | 6 +++++- Lib/idlelib/editor.py | 9 ++++----- Lib/idlelib/tree.py | 2 +- Lib/idlelib/zoomheight.py | 4 ++-- 5 files changed, 14 insertions(+), 11 deletions(-) diff --git a/Lib/idlelib/config-extensions.def b/Lib/idlelib/config-extensions.def index d4e106329a95ce..973c28f540ea93 100644 --- a/Lib/idlelib/config-extensions.def +++ b/Lib/idlelib/config-extensions.def @@ -32,7 +32,7 @@ # See config-keys.def for notes on specifying keys and extend.txt for # information on creating IDLE extensions. -[zoomheight] +[ZoomHeight] enable = True -[zoomheight_cfgBindings] +[ZoomHeight_cfgBindings] zoom-height= \ No newline at end of file diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py index caa64d33a3faad..ecb75f1fa9f7c2 100644 --- a/Lib/idlelib/configdialog.py +++ b/Lib/idlelib/configdialog.py @@ -243,7 +243,11 @@ def create_page_extensions(self): which are boolean, and can be toggled with a True/False button. Methods: - + load_extensions: + extension_selected: Handle selection from list. + create_extension_frame: Hold widgets for one extension. + set_extension_value: Set in userCfg['extensions']. + save_all_changed_extensions: Call extension page Save(). """ parent = self.parent frame = Frame(self.note) diff --git a/Lib/idlelib/editor.py b/Lib/idlelib/editor.py index 83aeb6c7834b80..34024ea94c9af6 100644 --- a/Lib/idlelib/editor.py +++ b/Lib/idlelib/editor.py @@ -65,8 +65,7 @@ def __init__(self, flist=None, filename=None, key=None, root=None): from idlelib.paragraph import FormatParagraph from idlelib.parenmatch import ParenMatch from idlelib.rstrip import RstripExtension - from idlelib.runscript import ScriptBinding - from idlelib.zoomheight import ZoomHeight + from idlelib.runscript import ScriptBinding if EditorWindow.help_url is None: dochome = os.path.join(sys.base_prefix, 'Doc', 'index.html') @@ -291,7 +290,6 @@ def __init__(self, flist=None, filename=None, key=None, root=None): self.insParenMatch=ParenMatch(self) self.insRstripExtension=RstripExtension(self) self.insScriptBinding=ScriptBinding(self) - self.insZoomHeight=ZoomHeight(self) text.bind("<>",self.insAutoComplete.autocomplete_event) text.bind("<>", self.insAutoComplete.try_open_completions_event) @@ -303,7 +301,6 @@ def __init__(self, flist=None, filename=None, key=None, root=None): text.bind("<>",self.insParenMatch.paren_closed_event) text.bind("<>",self.insScriptBinding.run_module_event) text.bind("<>",self.insScriptBinding.check_module_event) - text.bind("<>",self.insZoomHeight.zoom_height_event) text.bind("<>",self.insRstripExtension.do_rstrip) text.bind("<>",self.insCallTips.try_open_calltip_event) text.bind("<>",self.insCallTips.refresh_calltip_event) #must come after paren-closed to work right @@ -1020,7 +1017,9 @@ def load_standard_extensions(self): def get_standard_extension_names(self): return idleConf.GetExtensions(editor_only=True) - extfiles = { } + extfiles = { + 'ZoomHeight': 'zoomheight', + } def load_extension(self, name): fname = self.extfiles.get(name, name) diff --git a/Lib/idlelib/tree.py b/Lib/idlelib/tree.py index 907e52d1a992a1..292ce36184c76d 100644 --- a/Lib/idlelib/tree.py +++ b/Lib/idlelib/tree.py @@ -20,7 +20,7 @@ from tkinter.ttk import Scrollbar from idlelib.config import idleConf -from idlelib import ZoomHeight +from idlelib import zoomheight ICONDIR = "Icons" diff --git a/Lib/idlelib/zoomheight.py b/Lib/idlelib/zoomheight.py index 18d42608c98cf3..ea1230a1f2454a 100644 --- a/Lib/idlelib/zoomheight.py +++ b/Lib/idlelib/zoomheight.py @@ -1,4 +1,4 @@ -# Sample extension - zoom a window to maximum height +# Sample extension: zoom a window to maximum height import re import sys @@ -6,7 +6,7 @@ from idlelib import macosx -class zoomheight: +class ZoomHeight: menudefs = [ ('windows', [ ('_Zoom Height', '<>'), From 5805462040b6a84182e0c5f7cc6f8355be399495 Mon Sep 17 00:00:00 2001 From: wohlganger Date: Thu, 24 Aug 2017 14:47:53 -0500 Subject: [PATCH 23/64] zoomheight config fix, zoomheight should be fully back to the way it was before. --- Lib/idlelib/config-extensions.def | 4 ++-- Lib/idlelib/zoomheight.py | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Lib/idlelib/config-extensions.def b/Lib/idlelib/config-extensions.def index 973c28f540ea93..5ee60649923c8c 100644 --- a/Lib/idlelib/config-extensions.def +++ b/Lib/idlelib/config-extensions.def @@ -33,6 +33,6 @@ # information on creating IDLE extensions. [ZoomHeight] -enable = True +enable=True [ZoomHeight_cfgBindings] -zoom-height= \ No newline at end of file +zoom-height= diff --git a/Lib/idlelib/zoomheight.py b/Lib/idlelib/zoomheight.py index ea1230a1f2454a..d01c9e964aa60f 100644 --- a/Lib/idlelib/zoomheight.py +++ b/Lib/idlelib/zoomheight.py @@ -7,6 +7,7 @@ class ZoomHeight: + menudefs = [ ('windows', [ ('_Zoom Height', '<>'), From 165b548c3acdd338cac305ee52da467a0efbbae5 Mon Sep 17 00:00:00 2001 From: wohlganger Date: Thu, 24 Aug 2017 14:54:19 -0500 Subject: [PATCH 24/64] whitespace --- Lib/idlelib/editor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/idlelib/editor.py b/Lib/idlelib/editor.py index 34024ea94c9af6..9ee3e9e16688cc 100644 --- a/Lib/idlelib/editor.py +++ b/Lib/idlelib/editor.py @@ -65,7 +65,7 @@ def __init__(self, flist=None, filename=None, key=None, root=None): from idlelib.paragraph import FormatParagraph from idlelib.parenmatch import ParenMatch from idlelib.rstrip import RstripExtension - from idlelib.runscript import ScriptBinding + from idlelib.runscript import ScriptBinding if EditorWindow.help_url is None: dochome = os.path.join(sys.base_prefix, 'Doc', 'index.html') From aee5d02c305001c15d41997c1b6b29dff5cf245a Mon Sep 17 00:00:00 2001 From: wohlganger Date: Thu, 24 Aug 2017 15:35:14 -0500 Subject: [PATCH 25/64] fixed force-open completions missing from config-keys and config. Fixed zoomheight configdialog test. --- Lib/idlelib/config-keys.def | 5 +++++ Lib/idlelib/config.py | 1 + Lib/idlelib/idle_test/test_configdialog.py | 2 +- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/Lib/idlelib/config-keys.def b/Lib/idlelib/config-keys.def index 8ea04538edd49c..1a1b67f98e26d7 100644 --- a/Lib/idlelib/config-keys.def +++ b/Lib/idlelib/config-keys.def @@ -59,6 +59,7 @@ del-word-left= del-word-right= autocomplete= try-open-completions= +force-open-completions= expand-word= try-open-calltip= refresh-calltip= @@ -122,6 +123,7 @@ del-word-left= del-word-right= autocomplete= try-open-completions= +force-open-completions= expand-word= try-open-calltip= refresh-calltip= @@ -185,6 +187,7 @@ del-word-left = del-word-right = autocomplete= try-open-completions= +force-open-completions= expand-word= try-open-calltip= refresh-calltip= @@ -248,6 +251,7 @@ del-word-left= del-word-right= autocomplete= try-open-completions= +force-open-completions= expand-word= try-open-calltip= refresh-calltip= @@ -312,6 +316,7 @@ open-window-from-file = python-docs = autocomplete= try-open-completions= +force-open-completions= expand-word= try-open-calltip= refresh-calltip= diff --git a/Lib/idlelib/config.py b/Lib/idlelib/config.py index 9c2ca2e460828d..0dcf5c9439b848 100644 --- a/Lib/idlelib/config.py +++ b/Lib/idlelib/config.py @@ -662,6 +662,7 @@ def GetCoreKeys(self, keySetName=None): '<>': [''], '<>':['>':['', '', ''], + '<>':[''], '<>':[''], '<>':[''], '<>':['', ''], diff --git a/Lib/idlelib/idle_test/test_configdialog.py b/Lib/idlelib/idle_test/test_configdialog.py index d7bcd040e58e8c..f1b7d5b308276a 100644 --- a/Lib/idlelib/idle_test/test_configdialog.py +++ b/Lib/idlelib/idle_test/test_configdialog.py @@ -836,7 +836,7 @@ def test_keybinding(self): d.bindingslist.selection_anchor(1) d.keybinding.set('') self.assertEqual(extpage, - {'zoomheight_cfgBindings': {'zoom-height': ''}}) + {'ZoomHeight_cfgBindings': {'zoom-height': ''}}) def test_set_keys_type(self): eq = self.assertEqual From cb161bd96dc5039f7ff85af5834d3e24d4fe2775 Mon Sep 17 00:00:00 2001 From: wohlganger Date: Fri, 25 Aug 2017 08:17:17 -0500 Subject: [PATCH 26/64] Changes made per terryjreedy request. Sorry about the formatting; I think that happened when I was trying to fix for the conflicts. --- Lib/idlelib/configdialog.py | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py index ecb75f1fa9f7c2..fab8b137652ad6 100644 --- a/Lib/idlelib/configdialog.py +++ b/Lib/idlelib/configdialog.py @@ -207,16 +207,12 @@ def deactivate_current_config(self): for instance in win_instances: instance.RemoveKeybindings() - - - def activate_config_changes(self): """Apply configuration changes to current windows. Dynamically update the current parent window instances with some of the configuration changes. """ - win_instances = self.parent.instance_dict.keys() for instance in win_instances: instance.ResetColorizer() @@ -1169,7 +1165,6 @@ def create_new(self, new_theme_name): save_new set_theme_type """ - if self.theme_source.get(): theme_type = 'default' theme_name = self.builtin_name.get() @@ -2079,7 +2074,33 @@ def detach(self): [Cancel] only cancels changes made since the last save. ''' help_pages = { - + 'Highlighting': ''' +Highlighting: +The IDLE Dark color theme is new in October 2015. It can only +be used with older IDLE releases if it is saved as a custom +theme, with a different name. +''', + 'Keys': ''' +Keys: +The IDLE Modern Unix key set is new in June 2016. It can only +be used with older IDLE releases if it is saved as a custom +key set, with a different name. +''', + 'Extensions': ''' +Extensions: + +Autocomplete: Popupwait is milleseconds to wait after key char, without +cursor movement, before popping up completion box. Key char is '.' after +identifier or a '/' (or '\\' on Windows) within a string. + +FormatParagraph: Max-width is max chars in lines after re-formatting. +Use with paragraphs in both strings and comment blocks. + +ParenMatch: Style indicates what is highlighted when closer is entered: +'opener' - opener '({[' corresponding to closer; 'parens' - both chars; +'expression' (default) - also everything in between. Flash-delay is how +long to highlight if cursor is not moved (0 means forever). +''' } From dc09a2b2edb137cbd95d3f680956c9fb18c43e40 Mon Sep 17 00:00:00 2001 From: wohlganger Date: Fri, 25 Aug 2017 08:51:16 -0500 Subject: [PATCH 27/64] re-added autocomplete wait and format paragraph max width options to general tab --- Lib/idlelib/configdialog.py | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py index fab8b137652ad6..80e0eaff86e159 100644 --- a/Lib/idlelib/configdialog.py +++ b/Lib/idlelib/configdialog.py @@ -791,7 +791,6 @@ def create_page_highlight(self): StringVar(self), self.var_changed_flash_delay) self.num_lines = tracers.add( StringVar(self), self.var_changed_num_lines) - self.parenstyle.set(idleConf.GetOption( 'main','Theme','parenstyle', default='opener')) self.bell.set(idleConf.GetOption( @@ -800,7 +799,6 @@ def create_page_highlight(self): 'main','Theme','flash-delay', default=500)) self.num_lines.set(idleConf.GetOption( 'main','Theme','numlines', default=3)) - # Create widgets: # body frame and section frames. frame_custom = LabelFrame(self, borderwidth=2, relief=GROOVE, @@ -1830,6 +1828,11 @@ def create_page_general(self): self.win_height = tracers.add( StringVar(self), ('main', 'EditorWindow', 'height')) + self.formatp_maxw = tracers.add( + IntVar(self), ('main', 'General', 'formatp_maxw')) + self.autocomplete_wait = tracers.add( + IntVar(self), ('write', 'General', 'autocomplete_wait')) + # Create widgets: # Section frames. frame_run = LabelFrame(self, borderwidth=2, relief=GROOVE, @@ -1837,6 +1840,7 @@ def create_page_general(self): frame_save = LabelFrame(self, borderwidth=2, relief=GROOVE, text=' autosave Preferences ') frame_win_size = Frame(self, borderwidth=2, relief=GROOVE) + frame_extras = Frame(self, borderwidth=2, relief=GROOVE) frame_help = LabelFrame(self, borderwidth=2, relief=GROOVE, text=' Additional Help Sources ') # frame_run. @@ -1864,6 +1868,13 @@ def create_page_general(self): win_height_title = Label(frame_win_size, text='Height') self.win_height_int = Entry( frame_win_size, textvariable=self.win_height, width=3) + #frame extras + autocomplete_wait_title = Label(frame_extras, text='AutoComplete Popup Wait') + self.entry_autocomplete_wait = Entry( + frame_extras, textvariable=self.autocomplete_wait, width=6) + formatp_maxw_title = Label(frame_extras, text='Format Paragraph Max Width') + self.entry_formatp_maxw = Entry( + frame_extras, textvariable=self.formatp_maxw, width=3) # frame_help. frame_helplist = Frame(frame_help) frame_helplist_buttons = Frame(frame_helplist) @@ -1889,6 +1900,7 @@ def create_page_general(self): frame_run.pack(side=TOP, padx=5, pady=5, fill=X) frame_save.pack(side=TOP, padx=5, pady=5, fill=X) frame_win_size.pack(side=TOP, padx=5, pady=5, fill=X) + frame_extras.pack(side=TOP, padx=5, pady=5, fill=X) frame_help.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH) # frame_run. startup_title.pack(side=LEFT, anchor=W, padx=5, pady=5) @@ -1904,6 +1916,11 @@ def create_page_general(self): win_height_title.pack(side=RIGHT, anchor=E, pady=5) self.win_width_int.pack(side=RIGHT, anchor=E, padx=10, pady=5) win_width_title.pack(side=RIGHT, anchor=E, pady=5) + #frame extras + self.entry_autocomplete_wait.pack(side=RIGHT, anchor=E, padx=10, pady=5) + autocomplete_wait_title.pack(side=RIGHT, anchor=E, pady=5) + self.entry_formatp_maxw.pack(side=RIGHT, anchor=E, padx=10, pady=5) + formatp_maxw_title.pack(side=RIGHT, anchor=E, pady=5) # frame_help. frame_helplist_buttons.pack(side=RIGHT, padx=5, pady=5, fill=Y) frame_helplist.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH) From 60ff82f253be57c0e125addf7b9afa18e47be5bb Mon Sep 17 00:00:00 2001 From: wohlganger Date: Wed, 30 Aug 2017 09:21:42 -0500 Subject: [PATCH 28/64] changes per terryjreedy still need to move the imports --- Lib/idlelib/autocomplete.py | 27 ++++--- Lib/idlelib/autoexpand.py | 1 + Lib/idlelib/codecontext.py | 32 ++++----- Lib/idlelib/colorizer.py | 1 - Lib/idlelib/config-extensions.def | 20 ++++-- Lib/idlelib/config-highlight.def | 12 ---- Lib/idlelib/config-keys.def | 105 +++++++++++---------------- Lib/idlelib/config-main.def | 5 -- Lib/idlelib/config.py | 15 +--- Lib/idlelib/configdialog.py | 115 +++++++++++------------------- Lib/idlelib/editor.py | 33 +++++---- Lib/idlelib/mainmenu.py | 3 + Lib/idlelib/parenmatch.py | 8 +-- Lib/idlelib/zoomheight.py | 8 +-- 14 files changed, 156 insertions(+), 229 deletions(-) diff --git a/Lib/idlelib/autocomplete.py b/Lib/idlelib/autocomplete.py index 33eef9ae63e19f..02b7564b8888fd 100644 --- a/Lib/idlelib/autocomplete.py +++ b/Lib/idlelib/autocomplete.py @@ -1,6 +1,6 @@ -""" -This can complete either attribute names or file names. It can pop -a window with all available names, for the user to select from. +"""Complete either attribute names or file names. +Either on demand or after a user-selected delay after a key character, +pop up a list of candidates. """ import os import string @@ -26,19 +26,18 @@ class AutoComplete: - def __init__(self, editwin=None): + def __init__(self, editwin): self.editwin = editwin - if editwin is not None: # not in subprocess or test - self.reset() - self.text = editwin.text - self.autocompletewindow = None - # id of delayed call, and the index of the text insert when - # the delayed call was issued. If _delayed_completion_id is - # None, there is no delayed call. - self._delayed_completion_id = None - self._delayed_completion_index = None + self.reload() + self.text = editwin.text + self.autocompletewindow = None + # id of delayed call, and the index of the text insert when + # the delayed call was issued. If _delayed_completion_id is + # None, there is no delayed call. + self._delayed_completion_id = None + self._delayed_completion_index = None - def reset(self): + def reload(self): self.popupwait = idleConf.GetOption("main", "General", "autocomplete_wait", type="int", default=0) diff --git a/Lib/idlelib/autoexpand.py b/Lib/idlelib/autoexpand.py index 10064c1f6e289b..0b0e8454939663 100644 --- a/Lib/idlelib/autoexpand.py +++ b/Lib/idlelib/autoexpand.py @@ -10,6 +10,7 @@ place before requesting the next selection causes AutoExpand to reset its state. +There is only one instance of Autoexpand. ''' import re import string diff --git a/Lib/idlelib/codecontext.py b/Lib/idlelib/codecontext.py index 36703dd3316cc0..5fab5101c88bb7 100644 --- a/Lib/idlelib/codecontext.py +++ b/Lib/idlelib/codecontext.py @@ -26,7 +26,10 @@ lambda s, c=re.compile(r"^(\s*)(\w*)"): c.match(s).groups() class CodeContext: - + bgcolor = idleConf.GetOption("extensions", "CodeContext", + "bgcolor", type="str", default="LightGray") + fgcolor = idleConf.GetOption("extensions", "CodeContext", + "fgcolor", type="str", default="Black") def __init__(self, editwin): self.editwin = editwin self.text = editwin.text @@ -39,27 +42,20 @@ def __init__(self, editwin): # starts the toplevel 'block' of the module. self.info = [(0, -1, "", False)] self.topvisible = 1 - visible = idleConf.GetOption("main", "Theme", - "contexton", type="bool", default=False) - self.reset() - if visible: - self.toggle_code_context_event() - + self.reload() # Start two update cycles, one for context lines, one for font changes. self.text.after(UPDATEINTERVAL, self.timer_event) self.text.after(FONTUPDATEINTERVAL, self.font_timer_event) - def reset(self): - self.menudefs = [('options', [('!Code Conte_xt', '<>')])] - self.context_depth = idleConf.GetOption("main", "Theme", - "numlines", type="int", default=3) - highlight=idleConf.GetHighlight(idleConf.CurrentTheme(),'codecontext') - self.fgcolor = highlight['foreground'] - self.bgcolor = highlight['background'] - if self.topvisible: - #need to rebuild widget to change color - self.toggle_code_context_event() - self.toggle_code_context_event() + def reload(self): + self.context_depth = idleConf.GetOption("extensions", "CodeContext", + "numlines", type="int", default=3) + self.bgcolor = idleConf.GetOption("extensions", "CodeContext", + "bgcolor", type="str", default="LightGray") + self.fgcolor = idleConf.GetOption("extensions", "CodeContext", + "fgcolor", type="str", default="Black") + + def toggle_code_context_event(self, event=None): if not self.label: diff --git a/Lib/idlelib/colorizer.py b/Lib/idlelib/colorizer.py index 89d81a0aefc556..ff4084528804bd 100644 --- a/Lib/idlelib/colorizer.py +++ b/Lib/idlelib/colorizer.py @@ -92,7 +92,6 @@ def LoadTagDefs(self): "ERROR": idleConf.GetHighlight(theme, "error"), # The following is used by ReplaceDialog: "hit": idleConf.GetHighlight(theme, "hit"), - "paren": idleConf.GetHighlight(theme, "parenmatch"), } if DEBUG: print('tagdefs',self.tagdefs) diff --git a/Lib/idlelib/config-extensions.def b/Lib/idlelib/config-extensions.def index 5ee60649923c8c..4c53eadea551a8 100644 --- a/Lib/idlelib/config-extensions.def +++ b/Lib/idlelib/config-extensions.def @@ -32,7 +32,19 @@ # See config-keys.def for notes on specifying keys and extend.txt for # information on creating IDLE extensions. -[ZoomHeight] -enable=True -[ZoomHeight_cfgBindings] -zoom-height= +[AutoComplete] +popupwait=2000 + +[CodeContext] +numlines=3 +visible=False +bgcolor=LightGray +fgcolor=Black + +[FormatParagraph] +max-width=72 + +[ParenMatch] +style=expression +flash-delay=500 +bell=True diff --git a/Lib/idlelib/config-highlight.def b/Lib/idlelib/config-highlight.def index 531708e9c11fa3..4146e28c4eda1f 100644 --- a/Lib/idlelib/config-highlight.def +++ b/Lib/idlelib/config-highlight.def @@ -22,10 +22,6 @@ hit-foreground= #ffffff hit-background= #000000 error-foreground= #000000 error-background= #ff7777 -codecontext-foreground = black -codecontext-background = LightGray -parenmatch-foreground = black -parenmatch-background = gray #cursor (only foreground can be set, restart IDLE) cursor-foreground= black #shell window @@ -57,10 +53,6 @@ hit-foreground= #ffffff hit-background= #000000 error-foreground= #000000 error-background= #ff7777 -codecontext-foreground = black -codecontext-background = LightGray -parenmatch-foreground = #aaaaaa -parenmatch-background = #ffffff #cursor (only foreground can be set, restart IDLE) cursor-foreground= black #shell window @@ -99,7 +91,3 @@ stdout-background = #002240 hit-foreground = #002240 comment-background = #002240 break-foreground = #FFFFFF -codecontext-foreground = #aaaaaa -codecontext-background = #000000 -parenmatch-foreground = #00dddd -parenmatch-background = #002240 diff --git a/Lib/idlelib/config-keys.def b/Lib/idlelib/config-keys.def index 1a1b67f98e26d7..935ef06a37a884 100644 --- a/Lib/idlelib/config-keys.def +++ b/Lib/idlelib/config-keys.def @@ -57,19 +57,14 @@ toggle-tabs= change-indentwidth= del-word-left= del-word-right= -autocomplete= -try-open-completions= -force-open-completions= -expand-word= -try-open-calltip= -refresh-calltip= -force-open-calltip= -toggle-code-context= -format-paragraph= -flash-paren= -paren-closed= -run-module= -check-module= +force-open-completions= +expand-word= +force-open-calltip= +format-paragraph= +flash-paren= +run-module= +check-module= +zoom-height= [IDLE Classic Unix] copy= @@ -121,19 +116,14 @@ toggle-tabs= change-indentwidth= del-word-left= del-word-right= -autocomplete= -try-open-completions= -force-open-completions= -expand-word= -try-open-calltip= -refresh-calltip= -force-open-calltip= -toggle-code-context= -format-paragraph= -flash-paren= -paren-closed= -run-module= -check-module= +force-open-completions= +expand-word= +force-open-calltip= +format-paragraph= +flash-paren= +run-module= +check-module= +zoom-height= [IDLE Modern Unix] copy = @@ -185,19 +175,14 @@ toggle-tabs = change-indentwidth = del-word-left = del-word-right = -autocomplete= -try-open-completions= -force-open-completions= -expand-word= -try-open-calltip= -refresh-calltip= -force-open-calltip= -toggle-code-context= -format-paragraph= -flash-paren= -paren-closed= -run-module= -check-module= +force-open-completions= +expand-word= +force-open-calltip= +format-paragraph= +flash-paren= +run-module= +check-module= +zoom-height= [IDLE Classic Mac] copy= @@ -249,19 +234,14 @@ toggle-tabs= change-indentwidth= del-word-left= del-word-right= -autocomplete= -try-open-completions= -force-open-completions= -expand-word= -try-open-calltip= -refresh-calltip= -force-open-calltip= -toggle-code-context= -format-paragraph= -flash-paren= -paren-closed= -run-module= -check-module= +force-open-completions= +expand-word= +force-open-calltip= +format-paragraph= +flash-paren= +run-module= +check-module= +zoom-height= [IDLE Classic OSX] toggle-tabs = @@ -314,16 +294,11 @@ python-context-help = save-copy-of-window-as-file = open-window-from-file = python-docs = -autocomplete= -try-open-completions= -force-open-completions= -expand-word= -try-open-calltip= -refresh-calltip= -force-open-calltip= -toggle-code-context= -format-paragraph= -flash-paren= -paren-closed= -run-module= -check-module= +force-open-completions= +expand-word= +force-open-calltip= +format-paragraph= +flash-paren= +run-module= +check-module= +zoom-height= diff --git a/Lib/idlelib/config-main.def b/Lib/idlelib/config-main.def index 71ff1f927254ec..24ec7af16b4c37 100644 --- a/Lib/idlelib/config-main.def +++ b/Lib/idlelib/config-main.def @@ -76,11 +76,6 @@ num-spaces= 4 default= 1 name= IDLE Classic name2= -contexton= 0 -numlines= 3 -parenstyle= opener -flash-delay= 500 -bell= 1 # name2 set in user config-main.cfg for themes added after 2015 Oct 1 [Keys] diff --git a/Lib/idlelib/config.py b/Lib/idlelib/config.py index 0dcf5c9439b848..758e3be5c029d5 100644 --- a/Lib/idlelib/config.py +++ b/Lib/idlelib/config.py @@ -360,10 +360,6 @@ def GetThemeDict(self, type, themeName): 'stderr-background':'#ffffff', 'console-foreground':'#000000', 'console-background':'#ffffff', - 'codecontext-foreground':'#000000', - 'codecontext-background':'#ffffff', - 'parenmatch-foreground':'#000000', - 'parenmatch-background':'#ffffff', } for element in theme: if not cfgParser.has_option(themeName, element): @@ -660,20 +656,13 @@ def GetCoreKeys(self, keySetName=None): '<>': [''], '<>': [''], '<>': [''], - '<>':['>':['', '', ''], '<>':[''], '<>':[''], - '<>':[''], - '<>':['', ''], '<>':[''], - '<>':[''], - '<>':[''], - '<>':[''], '<>':[''], - '<>':['', '', ''], '<>':[''], - '<>':[''] + '<>':[''], + '<>':[''] } if keySetName: if not (self.userCfg['keys'].has_section(keySetName) or diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py index 6883ebbdee89ca..ed0fc8e3fff743 100644 --- a/Lib/idlelib/configdialog.py +++ b/Lib/idlelib/configdialog.py @@ -769,8 +769,6 @@ def create_page_highlight(self): 'Shell Error Text': ('error', '11'), 'Shell Stdout Text': ('stdout', '12'), 'Shell Stderr Text': ('stderr', '13'), - 'Code Context Text': ('codecontext', '14'), - 'Matched Parenthetics': ('parenmatch', '15'), } self.builtin_name = tracers.add( StringVar(self), self.var_changed_builtin_name) @@ -783,32 +781,13 @@ def create_page_highlight(self): BooleanVar(self), self.var_changed_theme_source) self.highlight_target = tracers.add( StringVar(self), self.var_changed_highlight_target) - self.parenstyle = tracers.add( - StringVar(self), self.var_changed_parenstyle) - self.bell = tracers.add( - StringVar(self), self.var_changed_bell) - self.flash_delay = tracers.add( - StringVar(self), self.var_changed_flash_delay) - self.num_lines = tracers.add( - StringVar(self), self.var_changed_num_lines) - self.parenstyle.set(idleConf.GetOption( - 'main','Theme','parenstyle', default='opener')) - self.bell.set(idleConf.GetOption( - 'main','Theme','bell', default=True)) - self.flash_delay.set(idleConf.GetOption( - 'main','Theme','flash-delay', default=500)) - self.num_lines.set(idleConf.GetOption( - 'main','Theme','numlines', default=3)) + # Create widgets: # body frame and section frames. frame_custom = LabelFrame(self, borderwidth=2, relief=GROOVE, text=' Custom Highlighting ') frame_theme = LabelFrame(self, borderwidth=2, relief=GROOVE, text=' Highlighting Theme ') - frame_paren = LabelFrame(self, borderwidth=2, relief=GROOVE, - text=' Matched Parenthetics ') - frame_code = LabelFrame(self, borderwidth=2, relief=GROOVE, - text=' Code Context ') # frame_custom. text = self.highlight_sample = Text( frame_custom, relief=SOLID, borderwidth=1, @@ -816,13 +795,13 @@ def create_page_highlight(self): takefocus=FALSE, highlightthickness=0, wrap=NONE) text.bind('', lambda e: 'break') text.bind('', lambda e: 'break') - text_and_tags=( - ('Class CodeContext\n', 'codecontext'), + text_and_tags=(('\n', 'normal'), + ('#you can click here', 'comment'), ('\n', 'normal'), ('#to choose items', 'comment'), ('\n', 'normal'), ('def', 'keyword'), (' ', 'normal'), - ('func', 'definition'), - ('(parenthetics)','parenmatch'), + ('func', 'definition'), ('(param):\n ', 'normal'), + ('"""string"""', 'string'), ('\n var0 = ', 'normal'), (':\n','normal'), (' """string"""', 'string'), ('\n var0 = ', 'normal'), ("'string'", 'string'), ('\n var1 = ', 'normal'), @@ -881,24 +860,10 @@ def tem(event, elem=element): frame_theme, text='Delete Custom Theme', command=self.delete_custom) self.theme_message = Label(frame_theme, borderwidth=2) - #frame_paren - self.opt_menu_pstyle = OptionMenu( - frame_paren, self.parenstyle, "opener","parens","expression") - flash_label = Label(frame_paren, text='Time Displayed : \n(0 is until given input)') - self.entry_flash = Entry( - frame_paren, textvariable=self.flash_delay, width=4) - self.check_bell = Checkbutton( - frame_paren, text="Bell", variable=self.bell) - #frame_code - lines_label = Label(frame_code, text='Lines : ') - self.entry_num_lines = Entry( - frame_code, textvariable=self.num_lines, width=3) # Pack widgets: # body. frame_custom.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH) frame_theme.pack(side=TOP, padx=5, pady=5, fill=X) - frame_paren.pack(side=TOP, padx=5, pady=5, fill=X) - frame_code.pack(side=TOP, padx=5, pady=5, fill=X) # frame_custom. self.frame_color_set.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=X) frame_fg_bg_toggle.pack(side=TOP, padx=5, pady=0) @@ -917,14 +882,6 @@ def tem(event, elem=element): self.customlist.pack(side=TOP, fill=X, anchor=W, padx=5, pady=5) self.button_delete_custom.pack(side=TOP, fill=X, padx=5, pady=5) self.theme_message.pack(side=TOP, fill=X, pady=5) - #frame_paren - self.opt_menu_pstyle.pack(side=TOP,anchor=W,padx=5, fill=X) - self.check_bell.pack(side=TOP, anchor=W,padx=5) - flash_label.pack(side=LEFT, anchor=W, padx=5) - self.entry_flash.pack(side=LEFT, fill=X,anchor=W,padx=5) - #frame_code - lines_label.pack(side=LEFT, anchor=W, padx=5) - self.entry_num_lines.pack(side=LEFT,anchor=W,padx=5) def load_theme_cfg(self): """Load current configuration settings for the theme options. @@ -1029,26 +986,6 @@ def var_changed_highlight_target(self, *params): "Process selection of new target tag for highlighting." self.set_highlight_target() - def var_changed_parenstyle(self, *params): - "Store change to parenthetics display style." - value=self.parenstyle.get() - changes.add_option('main', 'Theme', 'parenstyle', value) - - def var_changed_bell(self, *params): - "Store change to parenthtics bell (on/off)." - value=self.bell.get() - changes.add_option('main', 'Theme', 'bell', value) - - def var_changed_flash_delay(self, *params): - "Store change to parenthetics flash delay." - value=self.flash_delay.get() - changes.add_option('main', 'Theme', 'flash-delay', value) - - def var_changed_num_lines(self, *params): - "Store change to code context - number of lines displayed." - value=self.num_lines.get() - changes.add_option('main', 'Theme', 'numlines', value) - def set_theme_type(self): """Set available screen options based on builtin or custom theme. @@ -1836,19 +1773,32 @@ def create_page_general(self): StringVar(self), ('main', 'EditorWindow', 'width')) self.win_height = tracers.add( StringVar(self), ('main', 'EditorWindow', 'height')) + self.parenstyle = tracers.add( + StringVar(self), ('extensions', 'ParenMatch', 'style')) + self.bell = tracers.add( + BooleanVar(self), ('extensions', 'ParenMatch', 'bell')) + self.flash_delay = tracers.add( + IntVar(self), ('extensions', 'ParenMatch', 'flash-delay')) + self.num_lines = tracers.add( + IntVar(self), ('extensions', 'CodeContext', 'numlines')) self.formatp_maxw = tracers.add( - IntVar(self), ('main', 'General', 'formatp_maxw')) + IntVar(self), ('extensions', 'FormatParagraph', 'max-width')) self.autocomplete_wait = tracers.add( - IntVar(self), ('write', 'General', 'autocomplete_wait')) + IntVar(self), ('extensions', 'Autocomplete', 'popupwait')) + # Create widgets: # Section frames. frame_window = LabelFrame(self, borderwidth=2, relief=GROOVE, text=' Window Preferences') frame_editor = LabelFrame(self, borderwidth=2, relief=GROOVE, text=' Editor Preferences') + frame_extras = Frame(self, borderwidth=2, relief=GROOVE) + frame_paren = LabelFrame(self, borderwidth=2, relief=GROOVE, + text=' Matched Parenthetics ') + frame_code = LabelFrame(self, borderwidth=2, relief=GROOVE, + text=' Code Context ') frame_help = LabelFrame(self, borderwidth=2, relief=GROOVE, text=' Additional Help Sources ') - frame_extras = Frame(self, borderwidth=2, relief=GROOVE) # Frame_window. frame_run = Frame(frame_window, borderwidth=0) startup_title = Label(frame_run, text='At Startup') @@ -1868,6 +1818,18 @@ def create_page_general(self): win_height_title = Label(frame_win_size, text='Height') self.win_height_int = Entry( frame_win_size, textvariable=self.win_height, width=3) + #frame_paren + self.opt_menu_pstyle = OptionMenu( + frame_paren, self.parenstyle, "opener","parens","expression") + flash_label = Label(frame_paren, text='Time Displayed : \n(0 is until given input)') + self.entry_flash = Entry( + frame_paren, textvariable=self.flash_delay, width=4) + self.check_bell = Checkbutton( + frame_paren, text="Bell", variable=self.bell) + #frame_code + lines_label = Label(frame_code, text='Lines : ') + self.entry_num_lines = Entry( + frame_code, textvariable=self.num_lines, width=3) #frame extras autocomplete_wait_title = Label(frame_extras, text='AutoComplete Popup Wait') self.entry_autocomplete_wait = Entry( @@ -1908,7 +1870,8 @@ def create_page_general(self): # Body. frame_window.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH) frame_editor.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH) - + frame_paren.pack(side=TOP, padx=5, pady=5, fill=X) + frame_code.pack(side=TOP, padx=5, pady=5, fill=X) frame_extras.pack(side=TOP, padx=5, pady=5, fill=X) frame_help.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH) # frame_run. @@ -1923,6 +1886,14 @@ def create_page_general(self): win_height_title.pack(side=RIGHT, anchor=E, pady=5) self.win_width_int.pack(side=RIGHT, anchor=E, padx=10, pady=5) win_width_title.pack(side=RIGHT, anchor=E, pady=5) + #frame_paren + self.opt_menu_pstyle.pack(side=TOP,anchor=W,padx=5, fill=X) + self.check_bell.pack(side=TOP, anchor=W,padx=5) + flash_label.pack(side=LEFT, anchor=W, padx=5) + self.entry_flash.pack(side=LEFT, fill=X,anchor=W,padx=5) + #frame_code + lines_label.pack(side=LEFT, anchor=W, padx=5) + self.entry_num_lines.pack(side=LEFT,anchor=W,padx=5) #frame extras self.entry_autocomplete_wait.pack(side=RIGHT, anchor=E, padx=10, pady=5) autocomplete_wait_title.pack(side=RIGHT, anchor=E, pady=5) diff --git a/Lib/idlelib/editor.py b/Lib/idlelib/editor.py index 9ee3e9e16688cc..e605d2da52fcfc 100644 --- a/Lib/idlelib/editor.py +++ b/Lib/idlelib/editor.py @@ -66,6 +66,7 @@ def __init__(self, flist=None, filename=None, key=None, root=None): from idlelib.parenmatch import ParenMatch from idlelib.rstrip import RstripExtension from idlelib.runscript import ScriptBinding + from idlelib.zoomheight import ZoomHeight if EditorWindow.help_url is None: dochome = os.path.join(sys.base_prefix, 'Doc', 'index.html') @@ -282,14 +283,15 @@ def __init__(self, flist=None, filename=None, key=None, root=None): #init merged extentions binds - needs to be done after color is set - self.insAutoComplete=AutoComplete(self) - self.insAutoExpand=AutoExpand(self) - self.insCallTips=CallTips(self) - self.insCodeContext=CodeContext(self) - self.insFormatParagraph=FormatParagraph(self) - self.insParenMatch=ParenMatch(self) - self.insRstripExtension=RstripExtension(self) - self.insScriptBinding=ScriptBinding(self) + self.insAutoComplete = AutoComplete(self) + self.insAutoExpand = AutoExpand(self) + self.insCallTips = CallTips(self) + self.insCodeContext = CodeContext(self) + self.insFormatParagraph = FormatParagraph(self) + self.insParenMatch = ParenMatch(self) + self.insRstripExtension = RstripExtension(self) + self.insScriptBinding = ScriptBinding(self) + self.insZoomHeight = ZoomHeight(self) text.bind("<>",self.insAutoComplete.autocomplete_event) text.bind("<>", self.insAutoComplete.try_open_completions_event) @@ -305,6 +307,7 @@ def __init__(self, flist=None, filename=None, key=None, root=None): text.bind("<>",self.insCallTips.try_open_calltip_event) text.bind("<>",self.insCallTips.refresh_calltip_event) #must come after paren-closed to work right text.bind("<>",self.insCallTips.force_open_calltip_event) + text.bind("<>",self.insZoomHeight.zoom_height_event) def _filename_to_unicode(self, filename): """Return filename as BMP unicode so diplayable in Tk.""" @@ -1008,17 +1011,19 @@ def unload_extensions(self): def load_standard_extensions(self): for name in self.get_standard_extension_names(): - try: - self.load_extension(name) - except: - print("Failed to load extension", repr(name)) - traceback.print_exc() + if name not in {'CodeContext','FormatParagraph','ParenMatch','AutoComplete'}: + # specific exclusions because we are storing config for mainlined old + # extensions in config-extensions.def for backward compatibility + try: + self.load_extension(name) + except: + print("Failed to load extension", repr(name)) + traceback.print_exc() def get_standard_extension_names(self): return idleConf.GetExtensions(editor_only=True) extfiles = { - 'ZoomHeight': 'zoomheight', } def load_extension(self, name): diff --git a/Lib/idlelib/mainmenu.py b/Lib/idlelib/mainmenu.py index cd649cf5285be6..d1dcb83d9324f4 100644 --- a/Lib/idlelib/mainmenu.py +++ b/Lib/idlelib/mainmenu.py @@ -91,6 +91,9 @@ ('Configure _IDLE', '<>'), ('_Code Context', '<>'), ]), + ('windows', [ + ('Zoom Height', '<>'), + ]), ('help', [ ('_About IDLE', '<>'), None, diff --git a/Lib/idlelib/parenmatch.py b/Lib/idlelib/parenmatch.py index 04dca0e0ee8b2d..d4dbc2f26eae56 100644 --- a/Lib/idlelib/parenmatch.py +++ b/Lib/idlelib/parenmatch.py @@ -51,15 +51,15 @@ def __init__(self, editwin): def reset(self): self.STYLE = idleConf.GetOption( - 'main','Theme','parenstyle', default='opener') + 'extensions','ParenMatch','style', default='opener') self.FLASH_DELAY = idleConf.GetOption( - 'main','Theme','flash-delay', type='int',default=500) + 'extensions','ParenMatch','flash-delay', type='int',default=500) if idleConf.GetOption( - 'main','Theme','bell', type='bool',default=1): + 'extensions','ParenMatch','bell', type='bool',default=1): self.bell=self.text.bell else: self.bell=lambda:None - self.HILITE_CONFIG = idleConf.GetHighlight(idleConf.CurrentTheme(), 'parenmatch') + self.HILITE_CONFIG = idleConf.GetHighlight(idleConf.CurrentTheme(), 'hilite') self.set_style(self.STYLE) def activate_restore(self): diff --git a/Lib/idlelib/zoomheight.py b/Lib/idlelib/zoomheight.py index d01c9e964aa60f..6f3c577b95dcb7 100644 --- a/Lib/idlelib/zoomheight.py +++ b/Lib/idlelib/zoomheight.py @@ -1,4 +1,4 @@ -# Sample extension: zoom a window to maximum height +# Zoom a window to maximum height import re import sys @@ -8,12 +8,6 @@ class ZoomHeight: - menudefs = [ - ('windows', [ - ('_Zoom Height', '<>'), - ]) - ] - def __init__(self, editwin): self.editwin = editwin From e3f6f169d7808a2ddd7725c6ac67b3fcd30f7ade Mon Sep 17 00:00:00 2001 From: wohlganger Date: Wed, 30 Aug 2017 13:52:22 -0500 Subject: [PATCH 29/64] fix warnings, errors, bugs AutoComplete, CodeContext, FormatParagraph, ParenMatch needed special exclusions to have settings read from config-extensions.def . Special exclusions needed for calltips, completions, paren-closed events to prevent them being configurable. --- Lib/idlelib/config.py | 33 ++++++++++++++-------- Lib/idlelib/configdialog.py | 3 ++ Lib/idlelib/editor.py | 55 ++++++++++++++++++------------------- 3 files changed, 50 insertions(+), 41 deletions(-) diff --git a/Lib/idlelib/config.py b/Lib/idlelib/config.py index 758e3be5c029d5..07a138d413f898 100644 --- a/Lib/idlelib/config.py +++ b/Lib/idlelib/config.py @@ -439,6 +439,10 @@ def GetExtensions(self, active_only=True, """ extns = self.RemoveKeyBindNames( self.GetSectionList('default', 'extensions')) + for extn in ['AutoComplete','CodeContext','FormatParagraph','ParenMatch']: + extns.remove(extn) + # specific exclusions because we are storing config for mainlined old + # extensions in config-extensions.def for backward compatibility userExtns = self.RemoveKeyBindNames( self.GetSectionList('user', 'extensions')) for extn in userExtns: @@ -656,10 +660,13 @@ def GetCoreKeys(self, keySetName=None): '<>': [''], '<>': [''], '<>': [''], + '<>':[''], + '<>':['', '', ''], '<>':[''], '<>':[''], '<>':[''], '<>':[''], + '<>':['', '', ''], '<>':[''], '<>':[''], '<>':[''] @@ -675,18 +682,20 @@ def GetCoreKeys(self, keySetName=None): _warn(warning, 'keys', keySetName) else: for event in keyBindings: - binding = self.GetKeyBinding(keySetName, event) - if binding: - keyBindings[event] = binding - else: #we are going to return a default, print warning - warning = ( - '\n Warning: config.py - IdleConf.GetCoreKeys -\n' - ' problem retrieving key binding for event %r\n' - ' from key set %r.\n' - ' returning default value: %r' % - (event, keySetName, keyBindings[event]) - ) - _warn(warning, 'keys', keySetName, event) + if event not in {'<>','<>','<>'}: + # do not allow calltips, etc. events/keys to be configured. + binding = self.GetKeyBinding(keySetName, event) + if binding: + keyBindings[event] = binding + else: #we are going to return a default, print warning + warning = ( + '\n Warning: config.py - IdleConf.GetCoreKeys -\n' + ' problem retrieving key binding for event %r\n' + ' from key set %r.\n' + ' returning default value: %r' % + (event, keySetName, keyBindings[event]) + ) + _warn(warning, 'keys', keySetName, event) return keyBindings def GetExtraHelpSourceList(self, configSet): diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py index ed0fc8e3fff743..df24fd82631765 100644 --- a/Lib/idlelib/configdialog.py +++ b/Lib/idlelib/configdialog.py @@ -1643,6 +1643,9 @@ def load_keys_list(self, keyset_name): keyset = idleConf.GetKeySet(keyset_name) bind_names = list(keyset.keys()) bind_names.sort() + for key in ('<>','<>','<>'): + #do not allow these event keys to be configured + bind_names.remove(key) self.bindingslist.delete(0, END) for bind_name in bind_names: key = ' '.join(keyset[bind_name]) diff --git a/Lib/idlelib/editor.py b/Lib/idlelib/editor.py index e605d2da52fcfc..f40e29881077b8 100644 --- a/Lib/idlelib/editor.py +++ b/Lib/idlelib/editor.py @@ -53,21 +53,20 @@ class EditorWindow(object): from idlelib import mainmenu from tkinter import Toplevel from idlelib.statusbar import MultiStatusBar + from idlelib.autocomplete import AutoComplete + from idlelib.autoexpand import AutoExpand + from idlelib.calltips import CallTips + from idlelib.codecontext import CodeContext + from idlelib.paragraph import FormatParagraph + from idlelib.parenmatch import ParenMatch + from idlelib.rstrip import RstripExtension + from idlelib.runscript import ScriptBinding + from idlelib.zoomheight import ZoomHeight filesystemencoding = sys.getfilesystemencoding() # for file names help_url = None def __init__(self, flist=None, filename=None, key=None, root=None): - from idlelib.autocomplete import AutoComplete - from idlelib.autoexpand import AutoExpand - from idlelib.calltips import CallTips - from idlelib.codecontext import CodeContext - from idlelib.paragraph import FormatParagraph - from idlelib.parenmatch import ParenMatch - from idlelib.rstrip import RstripExtension - from idlelib.runscript import ScriptBinding - from idlelib.zoomheight import ZoomHeight - if EditorWindow.help_url is None: dochome = os.path.join(sys.base_prefix, 'Doc', 'index.html') if sys.platform.count('linux'): @@ -282,16 +281,16 @@ def __init__(self, flist=None, filename=None, key=None, root=None): self.showerror = tkMessageBox.showerror - #init merged extentions binds - needs to be done after color is set - self.insAutoComplete = AutoComplete(self) - self.insAutoExpand = AutoExpand(self) - self.insCallTips = CallTips(self) - self.insCodeContext = CodeContext(self) - self.insFormatParagraph = FormatParagraph(self) - self.insParenMatch = ParenMatch(self) - self.insRstripExtension = RstripExtension(self) - self.insScriptBinding = ScriptBinding(self) - self.insZoomHeight = ZoomHeight(self) + # merged extentions binds - depends on frame.text being packed (called from self.ResetColorizer()) + self.insAutoComplete = self.AutoComplete(self) + self.insAutoExpand = self.AutoExpand(self) + self.insCallTips = self.CallTips(self) + self.insCodeContext = self.CodeContext(self) + self.insFormatParagraph = self.FormatParagraph(self) + self.insParenMatch = self.ParenMatch(self) + self.insRstripExtension = self.RstripExtension(self) + self.insScriptBinding = self.ScriptBinding(self) + self.insZoomHeight = self.ZoomHeight(self) text.bind("<>",self.insAutoComplete.autocomplete_event) text.bind("<>", self.insAutoComplete.try_open_completions_event) @@ -305,7 +304,8 @@ def __init__(self, flist=None, filename=None, key=None, root=None): text.bind("<>",self.insScriptBinding.check_module_event) text.bind("<>",self.insRstripExtension.do_rstrip) text.bind("<>",self.insCallTips.try_open_calltip_event) - text.bind("<>",self.insCallTips.refresh_calltip_event) #must come after paren-closed to work right + #refresh-calltips must come after paren-closed to work right + text.bind("<>",self.insCallTips.refresh_calltip_event) text.bind("<>",self.insCallTips.force_open_calltip_event) text.bind("<>",self.insZoomHeight.zoom_height_event) @@ -1011,14 +1011,11 @@ def unload_extensions(self): def load_standard_extensions(self): for name in self.get_standard_extension_names(): - if name not in {'CodeContext','FormatParagraph','ParenMatch','AutoComplete'}: - # specific exclusions because we are storing config for mainlined old - # extensions in config-extensions.def for backward compatibility - try: - self.load_extension(name) - except: - print("Failed to load extension", repr(name)) - traceback.print_exc() + try: + self.load_extension(name) + except: + print("Failed to load extension", repr(name)) + traceback.print_exc() def get_standard_extension_names(self): return idleConf.GetExtensions(editor_only=True) From a253f0cfdb32a4fbc65705c288c0f1a4d92a4816 Mon Sep 17 00:00:00 2001 From: wohlganger Date: Wed, 30 Aug 2017 15:20:11 -0500 Subject: [PATCH 30/64] whitespace --- Lib/idlelib/autocomplete.py | 2 +- Lib/idlelib/config.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Lib/idlelib/autocomplete.py b/Lib/idlelib/autocomplete.py index 02b7564b8888fd..bc67e55079e4ca 100644 --- a/Lib/idlelib/autocomplete.py +++ b/Lib/idlelib/autocomplete.py @@ -1,4 +1,4 @@ -"""Complete either attribute names or file names. +"""Complete either attribute names or file names. Either on demand or after a user-selected delay after a key character, pop up a list of candidates. """ diff --git a/Lib/idlelib/config.py b/Lib/idlelib/config.py index 07a138d413f898..7da9fcaa6d7616 100644 --- a/Lib/idlelib/config.py +++ b/Lib/idlelib/config.py @@ -440,9 +440,9 @@ def GetExtensions(self, active_only=True, extns = self.RemoveKeyBindNames( self.GetSectionList('default', 'extensions')) for extn in ['AutoComplete','CodeContext','FormatParagraph','ParenMatch']: - extns.remove(extn) + extns.remove(extn) # specific exclusions because we are storing config for mainlined old - # extensions in config-extensions.def for backward compatibility + # extensions in config-extensions.def for backward compatibility userExtns = self.RemoveKeyBindNames( self.GetSectionList('user', 'extensions')) for extn in userExtns: @@ -683,7 +683,7 @@ def GetCoreKeys(self, keySetName=None): else: for event in keyBindings: if event not in {'<>','<>','<>'}: - # do not allow calltips, etc. events/keys to be configured. + # do not allow calltips, etc. events/keys to be configured. binding = self.GetKeyBinding(keySetName, event) if binding: keyBindings[event] = binding From fe8eb7a06bb3515db2c00d78e80fa998f3ec18ca Mon Sep 17 00:00:00 2001 From: wohlganger Date: Wed, 30 Aug 2017 15:41:37 -0500 Subject: [PATCH 31/64] bugfix if the cfg file had one of the old extensions in it, it was showing up in the extensions tab. This fixes that. --- Lib/idlelib/configdialog.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py index df24fd82631765..34c7ea06503f79 100644 --- a/Lib/idlelib/configdialog.py +++ b/Lib/idlelib/configdialog.py @@ -287,7 +287,11 @@ def load_extensions(self): "Fill self.extensions with data from the default and user configs." self.extensions = {} for ext_name in idleConf.GetExtensions(active_only=False): - self.extensions[ext_name] = [] + if ext_name not in {'AutoComplete', 'AutoExpand', 'CallTips', 'CodeContext', 'FormatParagraph', + 'ParenMatch', 'RStripExtension', 'ScriptBinding', 'ZoomHeight'}: + # These extensions were converted to built-ins, and need to be filtered out so they don't + # appear in the Extensions tab. + self.extensions[ext_name] = [] for ext_name in self.extensions: opt_list = sorted(self.ext_defaultCfg.GetOptionList(ext_name)) From caa777403acf09e010c742d6904fee7974faa4a4 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Thu, 31 Aug 2017 12:27:56 -0400 Subject: [PATCH 32/64] Make changes need to get IDLE to run --- Lib/idlelib/autocomplete.py | 28 ++++++++++++++++------------ Lib/idlelib/config.py | 8 ++++---- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/Lib/idlelib/autocomplete.py b/Lib/idlelib/autocomplete.py index bc67e55079e4ca..ed0b45cd1fa675 100644 --- a/Lib/idlelib/autocomplete.py +++ b/Lib/idlelib/autocomplete.py @@ -26,20 +26,21 @@ class AutoComplete: - def __init__(self, editwin): + def __init__(self, editwin=None): self.editwin = editwin - self.reload() - self.text = editwin.text - self.autocompletewindow = None - # id of delayed call, and the index of the text insert when - # the delayed call was issued. If _delayed_completion_id is - # None, there is no delayed call. - self._delayed_completion_id = None - self._delayed_completion_index = None + if editwin is not None: # not in subprocess or test + self.text = editwin.text + self.autocompletewindow = None + # id of delayed call, and the index of the text insert when + # the delayed call was issued. If _delayed_completion_id is + # None, there is no delayed call. + self._delayed_completion_id = None + self._delayed_completion_index = None - def reload(self): - self.popupwait = idleConf.GetOption("main", "General", - "autocomplete_wait", type="int", default=0) + @classmethod + def reload(cls): + cls.popupwait = idleConf.GetOption( + "extensions", "AutoComplete", "popupwait", type="int", default=0) def _make_autocomplete_window(self): return autocomplete_w.AutoCompleteWindow(self.text) @@ -222,6 +223,9 @@ def get_entity(self, name): return eval(name, namespace) +AutoComplete.reload() + + if __name__ == '__main__': from unittest import main main('idlelib.idle_test.test_autocomplete', verbosity=2) diff --git a/Lib/idlelib/config.py b/Lib/idlelib/config.py index 7da9fcaa6d7616..0c35953a15e11e 100644 --- a/Lib/idlelib/config.py +++ b/Lib/idlelib/config.py @@ -439,15 +439,15 @@ def GetExtensions(self, active_only=True, """ extns = self.RemoveKeyBindNames( self.GetSectionList('default', 'extensions')) - for extn in ['AutoComplete','CodeContext','FormatParagraph','ParenMatch']: - extns.remove(extn) - # specific exclusions because we are storing config for mainlined old - # extensions in config-extensions.def for backward compatibility userExtns = self.RemoveKeyBindNames( self.GetSectionList('user', 'extensions')) for extn in userExtns: if extn not in extns: #user has added own extension extns.append(extn) + for extn in ['AutoComplete','CodeContext','FormatParagraph','ParenMatch']: + extns.remove(extn) + # specific exclusions because we are storing config for mainlined old + # extensions in config-extensions.def for backward compatibility if active_only: activeExtns = [] for extn in extns: From c29184e935cf2a37894f55246de02d8c03fef138 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Thu, 31 Aug 2017 14:02:45 -0400 Subject: [PATCH 33/64] Edit extension/feature files, making existing tests pass. --- Lib/idlelib/autocomplete.py | 1 + Lib/idlelib/autoexpand.py | 4 +--- Lib/idlelib/calltips.py | 6 +++--- Lib/idlelib/codecontext.py | 15 +++++++++------ Lib/idlelib/config-main.def | 2 -- Lib/idlelib/editor.py | 4 +++- Lib/idlelib/paragraph.py | 16 ++++++++++------ Lib/idlelib/parenmatch.py | 24 +++++++++++++----------- Lib/idlelib/runscript.py | 27 +++++++-------------------- Lib/idlelib/zoomheight.py | 2 +- 10 files changed, 48 insertions(+), 53 deletions(-) diff --git a/Lib/idlelib/autocomplete.py b/Lib/idlelib/autocomplete.py index ed0b45cd1fa675..edf445f08b5869 100644 --- a/Lib/idlelib/autocomplete.py +++ b/Lib/idlelib/autocomplete.py @@ -1,4 +1,5 @@ """Complete either attribute names or file names. + Either on demand or after a user-selected delay after a key character, pop up a list of candidates. """ diff --git a/Lib/idlelib/autoexpand.py b/Lib/idlelib/autoexpand.py index 0b0e8454939663..42e733a1a9e735 100644 --- a/Lib/idlelib/autoexpand.py +++ b/Lib/idlelib/autoexpand.py @@ -15,9 +15,6 @@ import re import string -###$ event <> -###$ win -###$ unix class AutoExpand: wordchars = string.ascii_letters + string.digits + "_" @@ -93,6 +90,7 @@ def getprevword(self): i = i-1 return line[i:] + if __name__ == '__main__': import unittest unittest.main('idlelib.idle_test.test_autoexpand', verbosity=2) diff --git a/Lib/idlelib/calltips.py b/Lib/idlelib/calltips.py index cbbe546df99e5d..ec8f6169895b88 100644 --- a/Lib/idlelib/calltips.py +++ b/Lib/idlelib/calltips.py @@ -1,9 +1,8 @@ -""" +"""Pop up a reminder of how to call a function. Call Tips are floating windows which display function, class, and method parameter and docstring information when you type an opening parenthesis, and which disappear when you type a closing parenthesis. - """ import inspect import re @@ -15,6 +14,7 @@ from idlelib.hyperparser import HyperParser import __main__ + class CallTips: def __init__(self, editwin=None): @@ -97,6 +97,7 @@ def fetch_tip(self, expression): else: return get_argspec(get_entity(expression)) + def get_entity(expression): """Return the object corresponding to expression evaluated in a namespace spanning sys.modules and __main.dict__. @@ -120,7 +121,6 @@ def get_entity(expression): _invalid_method = "invalid method signature" _argument_positional = "\n['/' marks preceding arguments as positional-only]\n" - def get_argspec(ob): '''Return a string describing the signature of a callable object, or ''. diff --git a/Lib/idlelib/codecontext.py b/Lib/idlelib/codecontext.py index 5fab5101c88bb7..779b5b88cf35fa 100644 --- a/Lib/idlelib/codecontext.py +++ b/Lib/idlelib/codecontext.py @@ -25,6 +25,7 @@ getspacesfirstword =\ lambda s, c=re.compile(r"^(\s*)(\w*)"): c.match(s).groups() + class CodeContext: bgcolor = idleConf.GetOption("extensions", "CodeContext", "bgcolor", type="str", default="LightGray") @@ -47,16 +48,15 @@ def __init__(self, editwin): self.text.after(UPDATEINTERVAL, self.timer_event) self.text.after(FONTUPDATEINTERVAL, self.font_timer_event) - def reload(self): - self.context_depth = idleConf.GetOption("extensions", "CodeContext", + @classmethod + def reload(cls): + cls.context_depth = idleConf.GetOption("extensions", "CodeContext", "numlines", type="int", default=3) - self.bgcolor = idleConf.GetOption("extensions", "CodeContext", + cls.bgcolor = idleConf.GetOption("extensions", "CodeContext", "bgcolor", type="str", default="LightGray") - self.fgcolor = idleConf.GetOption("extensions", "CodeContext", + cls.fgcolor = idleConf.GetOption("extensions", "CodeContext", "fgcolor", type="str", default="Black") - - def toggle_code_context_event(self, event=None): if not self.label: # Calculate the border width and horizontal padding required to @@ -180,3 +180,6 @@ def font_timer_event(self): self.textfont = newtextfont self.label["font"] = self.textfont self.text.after(FONTUPDATEINTERVAL, self.font_timer_event) + + +CodeContext.reload() diff --git a/Lib/idlelib/config-main.def b/Lib/idlelib/config-main.def index 24ec7af16b4c37..16f4b0959cf13c 100644 --- a/Lib/idlelib/config-main.def +++ b/Lib/idlelib/config-main.def @@ -55,8 +55,6 @@ autosave= 0 print-command-posix=lpr %%s print-command-win=start /min notepad /p %%s delete-exitfunc= 1 -autocomplete_wait= 0 -formatp_maxw= 72 [EditorWindow] width= 80 diff --git a/Lib/idlelib/editor.py b/Lib/idlelib/editor.py index f40e29881077b8..4f4554c0f0bdb4 100644 --- a/Lib/idlelib/editor.py +++ b/Lib/idlelib/editor.py @@ -60,13 +60,15 @@ class EditorWindow(object): from idlelib.paragraph import FormatParagraph from idlelib.parenmatch import ParenMatch from idlelib.rstrip import RstripExtension - from idlelib.runscript import ScriptBinding from idlelib.zoomheight import ZoomHeight filesystemencoding = sys.getfilesystemencoding() # for file names help_url = None def __init__(self, flist=None, filename=None, key=None, root=None): + # Delay import: runscript imports pyshell imports EditorWindow. + from idlelib.runscript import ScriptBinding + if EditorWindow.help_url is None: dochome = os.path.join(sys.base_prefix, 'Doc', 'index.html') if sys.platform.count('linux'): diff --git a/Lib/idlelib/paragraph.py b/Lib/idlelib/paragraph.py index b64b4a8690ef42..cf8dfdb641f69d 100644 --- a/Lib/idlelib/paragraph.py +++ b/Lib/idlelib/paragraph.py @@ -1,4 +1,4 @@ -"""format a paragraph or selection to a max width. +"""Format a paragraph, comment block, or selection to a max width. Does basic, standard text formatting, and also understands Python comment blocks. Thus, for editing Python source code, this @@ -24,6 +24,11 @@ class FormatParagraph: def __init__(self, editwin): self.editwin = editwin + @classmethod + def reload(cls): + cls.max_width = idleConf.GetOption('extensions', 'FormatParagraph', + 'max-width', type='int', default=72) + def close(self): self.editwin = None @@ -39,11 +44,7 @@ def format_paragraph_event(self, event, limit=None): The length limit parameter is for testing with a known value. """ - if limit is None: - # The default length limit is that defined by pep8 - limit = idleConf.GetOption( - 'main', 'General', 'formatp_maxw', - type='int', default=72) + limit = self.max_width if limit is None else limit text = self.editwin.text first, last = self.editwin.get_selection_indices() if first and last: @@ -69,6 +70,9 @@ def format_paragraph_event(self, event, limit=None): text.see("insert") return "break" + +FormatParagraph.reload() + def find_paragraph(text, mark): """Returns the start/stop indices enclosing the paragraph that mark is in. diff --git a/Lib/idlelib/parenmatch.py b/Lib/idlelib/parenmatch.py index d4dbc2f26eae56..12212150c6dc8e 100644 --- a/Lib/idlelib/parenmatch.py +++ b/Lib/idlelib/parenmatch.py @@ -45,22 +45,21 @@ def __init__(self, editwin): # and deactivate_restore (which calls event_delete). editwin.text.bind(self.RESTORE_VIRTUAL_EVENT_NAME, self.restore_event) + self.bell = self.text.bell if self.BELL else lambda: None self.counter = 0 self.is_restore_active = 0 - self.reset() + self.set_style(self.STYLE) - def reset(self): - self.STYLE = idleConf.GetOption( + @classmethod + def reload(cls): + cls.STYLE = idleConf.GetOption( 'extensions','ParenMatch','style', default='opener') - self.FLASH_DELAY = idleConf.GetOption( + cls.FLASH_DELAY = idleConf.GetOption( 'extensions','ParenMatch','flash-delay', type='int',default=500) - if idleConf.GetOption( - 'extensions','ParenMatch','bell', type='bool',default=1): - self.bell=self.text.bell - else: - self.bell=lambda:None - self.HILITE_CONFIG = idleConf.GetHighlight(idleConf.CurrentTheme(), 'hilite') - self.set_style(self.STYLE) + cls.BELL = idleConf.GetOption( + 'extensions','ParenMatch','bell', type='bool', default=1) + cls.HILITE_CONFIG = idleConf.GetHighlight(idleConf.CurrentTheme(), + 'hilite') def activate_restore(self): "Activate mechanism to restore text from highlighting." @@ -181,6 +180,9 @@ def set_timeout_last(self): lambda self=self, c=self.counter: self.handle_restore_timer(c)) +ParenMatch.reload() + + if __name__ == '__main__': import unittest unittest.main('idlelib.idle_test.test_parenmatch', verbosity=2) diff --git a/Lib/idlelib/runscript.py b/Lib/idlelib/runscript.py index 97c02fc83b5f66..45bf56345825a1 100644 --- a/Lib/idlelib/runscript.py +++ b/Lib/idlelib/runscript.py @@ -1,22 +1,14 @@ -"""execute code outside the Python shell window. +"""Execute code from an editor. -This adds the following commands: +Check module: do a full syntax check of the current module. +Also run the tabnanny to catch any inconsistent tabs. -- Check module does a full syntax check of the current module. - It also runs the tabnanny to catch any inconsistent tabs. - -- Run module executes the module's code in the __main__ namespace. The window - must have been saved previously. The module is added to sys.modules, and is - also added to the __main__ namespace. - -XXX GvR Redesign this interface (yet again) as follows: - -- Present a dialog box for ``Run Module'' - -- Allow specify command line arguments in the dialog box +Run module: also execute the module's code in the __main__ namespace. +The window must have been saved previously. The module is added to +sys.modules, and is also added to the __main__ namespace. +TODO: Specify command line arguments in a dialog box. """ - import os import tabnanny import tokenize @@ -40,11 +32,6 @@ class ScriptBinding: - menudefs = [ - ('run', [None, - ('Check Module', '<>'), - ('Run Module', '<>'), ]), ] - def __init__(self, editwin): self.editwin = editwin # Provide instance variables referenced by debugger diff --git a/Lib/idlelib/zoomheight.py b/Lib/idlelib/zoomheight.py index 6f3c577b95dcb7..74fbc888a80d3f 100644 --- a/Lib/idlelib/zoomheight.py +++ b/Lib/idlelib/zoomheight.py @@ -1,4 +1,4 @@ -# Zoom a window to maximum height +"Zoom a window to maximum height." import re import sys From c62f72629be55484fade5feb685bd3cf36c96d54 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Thu, 31 Aug 2017 14:26:11 -0400 Subject: [PATCH 34/64] Redo feature bindings. --- Lib/idlelib/editor.py | 54 +++++++++++++++++++++---------------------- Lib/idlelib/rstrip.py | 1 - 2 files changed, 27 insertions(+), 28 deletions(-) diff --git a/Lib/idlelib/editor.py b/Lib/idlelib/editor.py index 4f4554c0f0bdb4..d87f3076c04055 100644 --- a/Lib/idlelib/editor.py +++ b/Lib/idlelib/editor.py @@ -96,7 +96,8 @@ def __init__(self, flist=None, filename=None, key=None, root=None): # Safari requires real file:-URLs EditorWindow.help_url = 'file://' + EditorWindow.help_url else: - EditorWindow.help_url = "https://docs.python.org/%d.%d/" % sys.version_info[:2] + EditorWindow.help_url = ("https://docs.python.org/%d.%d/" + % sys.version_info[:2]) self.flist = flist root = root or flist.root self.root = root @@ -283,33 +284,32 @@ def __init__(self, flist=None, filename=None, key=None, root=None): self.showerror = tkMessageBox.showerror - # merged extentions binds - depends on frame.text being packed (called from self.ResetColorizer()) - self.insAutoComplete = self.AutoComplete(self) - self.insAutoExpand = self.AutoExpand(self) - self.insCallTips = self.CallTips(self) - self.insCodeContext = self.CodeContext(self) - self.insFormatParagraph = self.FormatParagraph(self) - self.insParenMatch = self.ParenMatch(self) - self.insRstripExtension = self.RstripExtension(self) - self.insScriptBinding = self.ScriptBinding(self) - self.insZoomHeight = self.ZoomHeight(self) - - text.bind("<>",self.insAutoComplete.autocomplete_event) - text.bind("<>", self.insAutoComplete.try_open_completions_event) - text.bind("<>",self.insAutoComplete.force_open_completions_event) - text.bind("<>",self.insAutoExpand.expand_word_event) - text.bind("<>",self.insCodeContext.toggle_code_context_event) - text.bind("<>",self.insFormatParagraph.format_paragraph_event) - text.bind("<>",self.insParenMatch.flash_paren_event) - text.bind("<>",self.insParenMatch.paren_closed_event) - text.bind("<>",self.insScriptBinding.run_module_event) - text.bind("<>",self.insScriptBinding.check_module_event) - text.bind("<>",self.insRstripExtension.do_rstrip) - text.bind("<>",self.insCallTips.try_open_calltip_event) + # Former extension bindings depends on frame.text being packed + # (called from self.ResetColorizer()). + autocomplete = self.AutoComplete(self) + text.bind("<>", autocomplete.autocomplete_event) + text.bind("<>", + autocomplete.try_open_completions_event) + text.bind("<>", + autocomplete.force_open_completions_event) + text.bind("<>", self.AutoExpand(self).expand_word_event) + text.bind("<>", + self.CodeContext(self).toggle_code_context_event) + text.bind("<>", + self.FormatParagraph(self).format_paragraph_event) + parenmath = self.ParenMatch(self) + text.bind("<>", parenmatch.flash_paren_event) + text.bind("<>", parenmatch.paren_closed_event) + scriptbinding = self.ScriptBinding(self) + text.bind("<>", scriptbinding.check_module_event) + text.bind("<>", scriptbinding.run_module_event) + text.bind("<>", self.RstripExtension().do_rstrip) + calltips = self.CallTips(self) + text.bind("<>", calltips.try_open_calltip_event) #refresh-calltips must come after paren-closed to work right - text.bind("<>",self.insCallTips.refresh_calltip_event) - text.bind("<>",self.insCallTips.force_open_calltip_event) - text.bind("<>",self.insZoomHeight.zoom_height_event) + text.bind("<>", calltips.refresh_calltip_event) + text.bind("<>", calltips.force_open_calltip_event) + text.bind("<>", self.ZoomHeight(self).zoom_height_event) def _filename_to_unicode(self, filename): """Return filename as BMP unicode so diplayable in Tk.""" diff --git a/Lib/idlelib/rstrip.py b/Lib/idlelib/rstrip.py index 82c7f7e8d8c8ab..18c86f9b2c8896 100644 --- a/Lib/idlelib/rstrip.py +++ b/Lib/idlelib/rstrip.py @@ -4,7 +4,6 @@ class RstripExtension: def __init__(self, editwin): self.editwin = editwin - self.editwin.text.bind("<>", self.do_rstrip) def do_rstrip(self, event=None): From d0e009f3dc4bca30ac167208ba8f3f8982edac0a Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Thu, 31 Aug 2017 15:50:18 -0400 Subject: [PATCH 35/64] First draft of dummy extension. --- Lib/idlelib/config-extensions.def | 48 ++++++++++++++++++++----------- Lib/idlelib/editor.py | 3 +- Lib/idlelib/zzdummy.py | 41 ++++++++++++++++++++++++++ 3 files changed, 74 insertions(+), 18 deletions(-) create mode 100644 Lib/idlelib/zzdummy.py diff --git a/Lib/idlelib/config-extensions.def b/Lib/idlelib/config-extensions.def index 4c53eadea551a8..0643ac395f465e 100644 --- a/Lib/idlelib/config-extensions.def +++ b/Lib/idlelib/config-extensions.def @@ -1,5 +1,25 @@ # config-extensions.def # +# The following sections are for features that are no longer extensions. +# Their options values are left here for back-compatibility. + +[AutoComplete] +popupwait= 2000 + +[CodeContext] +numlines= 3 +visible= False +bgcolor= LightGray +fgcolor= Black + +[FormatParagraph] +max-width= 72 # PEP8 standard + +[ParenMatch] +style= expression +flash-delay= 500 +bell= True + # IDLE reads several config files to determine user preferences. This # file is the default configuration file for IDLE extensions settings. # @@ -19,7 +39,7 @@ # extension that may be sensibly re-configured. # # If there are no keybindings for a menus' virtual events, include lines -# like <>= (See [CodeContext], below.) +# like <>=. # # Currently it is necessary to manually modify this file to change # extension key bindings and default values. To customize, create @@ -32,19 +52,13 @@ # See config-keys.def for notes on specifying keys and extend.txt for # information on creating IDLE extensions. -[AutoComplete] -popupwait=2000 - -[CodeContext] -numlines=3 -visible=False -bgcolor=LightGray -fgcolor=Black - -[FormatParagraph] -max-width=72 - -[ParenMatch] -style=expression -flash-delay=500 -bell=True +# A fake extension for testing and example purposes. +# When enabled and invoked, inserts or deletes z-text at beginning of +# every line. +[ZzDummy] +enable= False +enable_shell = False +enable_editor = True +z-text= Z +[ZzDummy_bindings] +z-in= diff --git a/Lib/idlelib/editor.py b/Lib/idlelib/editor.py index d87f3076c04055..9d98962f5bcc90 100644 --- a/Lib/idlelib/editor.py +++ b/Lib/idlelib/editor.py @@ -1022,7 +1022,8 @@ def load_standard_extensions(self): def get_standard_extension_names(self): return idleConf.GetExtensions(editor_only=True) - extfiles = { + extfiles = { # Use for experimental features implemented as extension. + 'ZzDummy': 'zzdummy', } def load_extension(self, name): diff --git a/Lib/idlelib/zzdummy.py b/Lib/idlelib/zzdummy.py new file mode 100644 index 00000000000000..c814b80edc2900 --- /dev/null +++ b/Lib/idlelib/zzdummy.py @@ -0,0 +1,41 @@ +"Example extension, also used for testing." + +from idlelib.config import idleConf + +ztext = idleConf.GetOption('extensions', 'ZzDummy', 'z-text') + + +class ZzDummy: + + menudefs = [ + ('format', [ + ('Z in', '<>'), + ('Z out', '<>'), + ] ) + ] + + def __init__(self, editwin): + self.text = editwin.text + z_in = False + + @classmethod + def reload(cls): + cls.ztext = idleConf.GetOption('extensions', 'ZzDummy', 'z-text') + + def z_in_event(self, event): + """ + """ + text = self.text + text.undo_block_start() + for line in range(1, text.index('end')): + text.insert('%d.0', ztest) + text.undo_block_stop() + return "break" + + +ZzDummy.reload() + +##if __name__ == "__main__": +## import unittest +## unittest.main('idlelib.idle_test.test_zzdummy', +## verbosity=2, exit=False) From 9e889c22738305781b99c85368f7d8f1801f2583 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Thu, 31 Aug 2017 16:42:52 -0400 Subject: [PATCH 36/64] Fix that eliminate startup error and all but 2 test_idle errors. --- Lib/idlelib/config-extensions.def | 15 ++++++++------- Lib/idlelib/configdialog.py | 3 --- Lib/idlelib/editor.py | 6 +++--- Lib/idlelib/idle_test/test_configdialog.py | 4 ++-- Lib/idlelib/zzdummy.py | 13 +++++++------ 5 files changed, 20 insertions(+), 21 deletions(-) diff --git a/Lib/idlelib/config-extensions.def b/Lib/idlelib/config-extensions.def index 0643ac395f465e..0fdb92b37f4396 100644 --- a/Lib/idlelib/config-extensions.def +++ b/Lib/idlelib/config-extensions.def @@ -13,7 +13,7 @@ bgcolor= LightGray fgcolor= Black [FormatParagraph] -max-width= 72 # PEP8 standard +max-width= 72 [ParenMatch] style= expression @@ -52,13 +52,14 @@ bell= True # See config-keys.def for notes on specifying keys and extend.txt for # information on creating IDLE extensions. -# A fake extension for testing and example purposes. -# When enabled and invoked, inserts or deletes z-text at beginning of -# every line. -[ZzDummy] -enable= False +# A fake extension for testing and example purposes. When enabled and +# invoked, inserts or deletes z-text at beginning of every line. +[ZzDummy] +enable= True enable_shell = False enable_editor = True z-text= Z [ZzDummy_bindings] -z-in= +z-in= +[ZzDummy_cfgBindings] +z-out= diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py index 34c7ea06503f79..ebcfeaae2931d7 100644 --- a/Lib/idlelib/configdialog.py +++ b/Lib/idlelib/configdialog.py @@ -1647,9 +1647,6 @@ def load_keys_list(self, keyset_name): keyset = idleConf.GetKeySet(keyset_name) bind_names = list(keyset.keys()) bind_names.sort() - for key in ('<>','<>','<>'): - #do not allow these event keys to be configured - bind_names.remove(key) self.bindingslist.delete(0, END) for bind_name in bind_names: key = ' '.join(keyset[bind_name]) diff --git a/Lib/idlelib/editor.py b/Lib/idlelib/editor.py index 9d98962f5bcc90..ad69cbdb3d9fd1 100644 --- a/Lib/idlelib/editor.py +++ b/Lib/idlelib/editor.py @@ -297,13 +297,13 @@ def __init__(self, flist=None, filename=None, key=None, root=None): self.CodeContext(self).toggle_code_context_event) text.bind("<>", self.FormatParagraph(self).format_paragraph_event) - parenmath = self.ParenMatch(self) + parenmatch = self.ParenMatch(self) text.bind("<>", parenmatch.flash_paren_event) text.bind("<>", parenmatch.paren_closed_event) - scriptbinding = self.ScriptBinding(self) + scriptbinding = ScriptBinding(self) text.bind("<>", scriptbinding.check_module_event) text.bind("<>", scriptbinding.run_module_event) - text.bind("<>", self.RstripExtension().do_rstrip) + text.bind("<>", self.RstripExtension(self).do_rstrip) calltips = self.CallTips(self) text.bind("<>", calltips.try_open_calltip_event) #refresh-calltips must come after paren-closed to work right diff --git a/Lib/idlelib/idle_test/test_configdialog.py b/Lib/idlelib/idle_test/test_configdialog.py index 695f0d7afc2526..7925c6d5148ec9 100644 --- a/Lib/idlelib/idle_test/test_configdialog.py +++ b/Lib/idlelib/idle_test/test_configdialog.py @@ -824,7 +824,7 @@ def test_keybinding(self): d.custom_name.set('my custom keys') d.bindingslist.delete(0, 'end') d.bindingslist.insert(0, 'copy') - d.bindingslist.insert(1, 'zoom-height') + d.bindingslist.insert(1, 'z-out') d.bindingslist.selection_set(0) d.bindingslist.selection_anchor(0) # Core binding - adds to keys. @@ -837,7 +837,7 @@ def test_keybinding(self): d.bindingslist.selection_anchor(1) d.keybinding.set('') self.assertEqual(extpage, - {'ZoomHeight_cfgBindings': {'zoom-height': ''}}) + {'ZzDummy_cfgBindings': {'z-out': ''}}) def test_set_keys_type(self): eq = self.assertEqual diff --git a/Lib/idlelib/zzdummy.py b/Lib/idlelib/zzdummy.py index c814b80edc2900..8084499646653d 100644 --- a/Lib/idlelib/zzdummy.py +++ b/Lib/idlelib/zzdummy.py @@ -7,12 +7,12 @@ class ZzDummy: - menudefs = [ - ('format', [ - ('Z in', '<>'), - ('Z out', '<>'), - ] ) - ] +## menudefs = [ +## ('format', [ +## ('Z in', '<>'), +## ('Z out', '<>'), +## ] ) +## ] def __init__(self, editwin): self.text = editwin.text @@ -32,6 +32,7 @@ def z_in_event(self, event): text.undo_block_stop() return "break" + def z_out_event(self, event): pass ZzDummy.reload() From 6afdea3c254c1d8d247f5125f4ca5e85351f68e3 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Fri, 1 Sep 2017 23:21:14 -0400 Subject: [PATCH 37/64] Move widgets, load widgets, add tests, make previous tests pass. --- Lib/idlelib/config-extensions.def | 4 +- Lib/idlelib/configdialog.py | 158 ++++++++++++--------- Lib/idlelib/idle_test/test_config.py | 59 +++----- Lib/idlelib/idle_test/test_configdialog.py | 42 ++++-- 4 files changed, 148 insertions(+), 115 deletions(-) diff --git a/Lib/idlelib/config-extensions.def b/Lib/idlelib/config-extensions.def index 0fdb92b37f4396..e8d417bac0d497 100644 --- a/Lib/idlelib/config-extensions.def +++ b/Lib/idlelib/config-extensions.def @@ -59,7 +59,7 @@ enable= True enable_shell = False enable_editor = True z-text= Z -[ZzDummy_bindings] -z-in= [ZzDummy_cfgBindings] +z-in= +[ZzDummy_bindings] z-out= diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py index ebcfeaae2931d7..fd26b780abb811 100644 --- a/Lib/idlelib/configdialog.py +++ b/Lib/idlelib/configdialog.py @@ -10,12 +10,12 @@ """ from tkinter import (Toplevel, Listbox, Text, Scale, Canvas, - OptionMenu, StringVar, BooleanVar, IntVar, TRUE, FALSE, + StringVar, BooleanVar, IntVar, TRUE, FALSE, TOP, BOTTOM, RIGHT, LEFT, SOLID, GROOVE, NORMAL, DISABLED, NONE, BOTH, X, Y, W, E, EW, NS, NSEW, NW, HORIZONTAL, VERTICAL, ANCHOR, ACTIVE, END) from tkinter.ttk import (Button, Checkbutton, Entry, Frame, Label, LabelFrame, - Notebook, Radiobutton, Scrollbar, Style) + OptionMenu, Notebook, Radiobutton, Scrollbar, Style) import tkinter.colorchooser as tkColorChooser import tkinter.font as tkFont from tkinter import messagebox @@ -287,8 +287,8 @@ def load_extensions(self): "Fill self.extensions with data from the default and user configs." self.extensions = {} for ext_name in idleConf.GetExtensions(active_only=False): - if ext_name not in {'AutoComplete', 'AutoExpand', 'CallTips', 'CodeContext', 'FormatParagraph', - 'ParenMatch', 'RStripExtension', 'ScriptBinding', 'ZoomHeight'}: + if ext_name not in {'AutoComplete', 'CodeContext', + 'FormatParagraph', 'ParenMatch'}: # These extensions were converted to built-ins, and need to be filtered out so they don't # appear in the Extensions tab. self.extensions[ext_name] = [] @@ -1771,24 +1771,25 @@ def create_page_general(self): """ self.startup_edit = tracers.add( IntVar(self), ('main', 'General', 'editor-on-startup')) - self.autosave = tracers.add( - IntVar(self), ('main', 'General', 'autosave')) self.win_width = tracers.add( StringVar(self), ('main', 'EditorWindow', 'width')) self.win_height = tracers.add( StringVar(self), ('main', 'EditorWindow', 'height')) - self.parenstyle = tracers.add( + self.autocomplete_wait = tracers.add( + IntVar(self), ('extensions', 'AutoComplete', 'popupwait')) + self.paren_style = tracers.add( StringVar(self), ('extensions', 'ParenMatch', 'style')) - self.bell = tracers.add( - BooleanVar(self), ('extensions', 'ParenMatch', 'bell')) self.flash_delay = tracers.add( IntVar(self), ('extensions', 'ParenMatch', 'flash-delay')) - self.num_lines = tracers.add( + self.paren_bell = tracers.add( + BooleanVar(self), ('extensions', 'ParenMatch', 'bell')) + + self.autosave = tracers.add( + IntVar(self), ('main', 'General', 'autosave')) + self.format_width = tracers.add( + IntVar(self), ('extensions', 'FormatParagraph', 'max-width')) + self.context_lines = tracers.add( IntVar(self), ('extensions', 'CodeContext', 'numlines')) - self.formatp_maxw = tracers.add( - IntVar(self), ('extensions', 'FormatParagraph', 'max-width')) - self.autocomplete_wait = tracers.add( - IntVar(self), ('extensions', 'Autocomplete', 'popupwait')) # Create widgets: # Section frames. @@ -1796,11 +1797,6 @@ def create_page_general(self): text=' Window Preferences') frame_editor = LabelFrame(self, borderwidth=2, relief=GROOVE, text=' Editor Preferences') - frame_extras = Frame(self, borderwidth=2, relief=GROOVE) - frame_paren = LabelFrame(self, borderwidth=2, relief=GROOVE, - text=' Matched Parenthetics ') - frame_code = LabelFrame(self, borderwidth=2, relief=GROOVE, - text=' Code Context ') frame_help = LabelFrame(self, borderwidth=2, relief=GROOVE, text=' Additional Help Sources ') # Frame_window. @@ -1813,7 +1809,7 @@ def create_page_general(self): frame_run, variable=self.startup_edit, value=0, text='Open Shell Window') - frame_win_size = Frame(frame_window, borderwidth=0,) + frame_win_size = Frame(frame_window, borderwidth=0) win_size_title = Label( frame_win_size, text='Initial Window Size (in characters)') win_width_title = Label(frame_win_size, text='Width') @@ -1822,25 +1818,27 @@ def create_page_general(self): win_height_title = Label(frame_win_size, text='Height') self.win_height_int = Entry( frame_win_size, textvariable=self.win_height, width=3) - #frame_paren - self.opt_menu_pstyle = OptionMenu( - frame_paren, self.parenstyle, "opener","parens","expression") - flash_label = Label(frame_paren, text='Time Displayed : \n(0 is until given input)') - self.entry_flash = Entry( - frame_paren, textvariable=self.flash_delay, width=4) - self.check_bell = Checkbutton( - frame_paren, text="Bell", variable=self.bell) - #frame_code - lines_label = Label(frame_code, text='Lines : ') - self.entry_num_lines = Entry( - frame_code, textvariable=self.num_lines, width=3) - #frame extras - autocomplete_wait_title = Label(frame_extras, text='AutoComplete Popup Wait') - self.entry_autocomplete_wait = Entry( - frame_extras, textvariable=self.autocomplete_wait, width=6) - formatp_maxw_title = Label(frame_extras, text='Format Paragraph Max Width') - self.entry_formatp_maxw = Entry( - frame_extras, textvariable=self.formatp_maxw, width=3) + + frame_autocomplete = Frame(frame_window, borderwidth=0,) + auto_wait_title = Label(frame_autocomplete, + text='Completions Popup Wait (milliseconds)') + self.auto_wait_int = Entry(frame_autocomplete, width=6, + textvariable=self.autocomplete_wait) + + frame_paren1 = Frame(frame_window, borderwidth=0) + paren_style_title = Label(frame_paren1, text='Paren Match Style') + self.paren_style_type = OptionMenu( + frame_paren1, self.paren_style, 'expression', + "opener","parens","expression") + frame_paren2 = Frame(frame_window, borderwidth=0) + paren_time_title = Label( + frame_paren2, text='Time Match Displayed (milliseconds)\n' + '(0 is until next input)') + self.paren_flash_time = Entry( + frame_paren2, textvariable=self.flash_delay, width=6) + self.bell_on = Checkbutton( + frame_paren2, text="Bell on Mismatch", variable=self.paren_bell) + # Frame_editor. frame_save = Frame(frame_editor, borderwidth=0) run_save_title = Label(frame_save, text='At Start of Run (F5) ') @@ -1850,6 +1848,19 @@ def create_page_general(self): self.save_auto_on = Radiobutton( frame_save, variable=self.autosave, value=1, text='No Prompt') + + frame_format = Frame(frame_editor, borderwidth=0) + format_width_title = Label(frame_format, + text='Format Paragraph Max Width') + self.format_width_int = Entry( + frame_format, textvariable=self.format_width, width=4) + + frame_context = Frame(frame_editor, borderwidth=0) + context_title = Label(frame_context, text='Context Lines :') + self.context_int = Entry( + frame_context, textvariable=self.context_lines, width=3) + + # frame_help. frame_helplist = Frame(frame_help) frame_helplist_buttons = Frame(frame_helplist) @@ -1874,9 +1885,6 @@ def create_page_general(self): # Body. frame_window.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH) frame_editor.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH) - frame_paren.pack(side=TOP, padx=5, pady=5, fill=X) - frame_code.pack(side=TOP, padx=5, pady=5, fill=X) - frame_extras.pack(side=TOP, padx=5, pady=5, fill=X) frame_help.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH) # frame_run. frame_run.pack(side=TOP, padx=5, pady=0, fill=X) @@ -1890,24 +1898,33 @@ def create_page_general(self): win_height_title.pack(side=RIGHT, anchor=E, pady=5) self.win_width_int.pack(side=RIGHT, anchor=E, padx=10, pady=5) win_width_title.pack(side=RIGHT, anchor=E, pady=5) - #frame_paren - self.opt_menu_pstyle.pack(side=TOP,anchor=W,padx=5, fill=X) - self.check_bell.pack(side=TOP, anchor=W,padx=5) - flash_label.pack(side=LEFT, anchor=W, padx=5) - self.entry_flash.pack(side=LEFT, fill=X,anchor=W,padx=5) - #frame_code - lines_label.pack(side=LEFT, anchor=W, padx=5) - self.entry_num_lines.pack(side=LEFT,anchor=W,padx=5) - #frame extras - self.entry_autocomplete_wait.pack(side=RIGHT, anchor=E, padx=10, pady=5) - autocomplete_wait_title.pack(side=RIGHT, anchor=E, pady=5) - self.entry_formatp_maxw.pack(side=RIGHT, anchor=E, padx=10, pady=5) - formatp_maxw_title.pack(side=RIGHT, anchor=E, pady=5) + # frame_autocomplete. + frame_autocomplete.pack(side=TOP, padx=5, pady=0, fill=X) + auto_wait_title.pack(side=LEFT, anchor=W, padx=5, pady=5) + self.auto_wait_int.pack(side=TOP, padx=10, pady=5) + # frame_paren. + frame_paren1.pack(side=TOP, padx=5, pady=0, fill=X) + paren_style_title.pack(side=LEFT, anchor=W, padx=5, pady=5) + self.paren_style_type.pack(side=TOP, padx=10, pady=5) + frame_paren2.pack(side=TOP, padx=5, pady=0, fill=X) + paren_time_title.pack(side=LEFT, anchor=W, padx=5) + self.bell_on.pack(side=RIGHT, anchor=E, padx=15, pady=5) + self.paren_flash_time.pack(side=TOP, anchor=W, padx=15, pady=5) + # frame_save. frame_save.pack(side=TOP, padx=5, pady=0, fill=X) run_save_title.pack(side=LEFT, anchor=W, padx=5, pady=5) self.save_auto_on.pack(side=RIGHT, anchor=W, padx=5, pady=5) self.save_ask_on.pack(side=RIGHT, anchor=W, padx=5, pady=5) + # frame_format. + frame_format.pack(side=TOP, padx=5, pady=0, fill=X) + format_width_title.pack(side=LEFT, anchor=W, padx=5, pady=5) + self.format_width_int.pack(side=TOP, padx=10, pady=5) + # frame_context. + frame_context.pack(side=TOP, padx=5, pady=0, fill=X) + context_title.pack(side=LEFT, anchor=W, padx=5, pady=5) + self.context_int.pack(side=TOP, padx=5, pady=5) + # frame_help. frame_helplist_buttons.pack(side=RIGHT, padx=5, pady=5, fill=Y) frame_helplist.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH) @@ -1919,17 +1936,30 @@ def create_page_general(self): def load_general_cfg(self): "Load current configuration settings for the general options." - # Set startup state. + # Set variables for all windows. self.startup_edit.set(idleConf.GetOption( - 'main', 'General', 'editor-on-startup', default=0, type='bool')) - # Set autosave state. - self.autosave.set(idleConf.GetOption( - 'main', 'General', 'autosave', default=0, type='bool')) - # Set initial window size. + 'main', 'General', 'editor-on-startup', type='bool')) self.win_width.set(idleConf.GetOption( 'main', 'EditorWindow', 'width', type='int')) self.win_height.set(idleConf.GetOption( 'main', 'EditorWindow', 'height', type='int')) + self.autocomplete_wait.set(idleConf.GetOption( + 'extensions', 'AutoComplete', 'popupwait', type='int')) + self.paren_style.set(idleConf.GetOption( + 'extensions', 'ParenMatch', 'style')) + self.flash_delay.set(idleConf.GetOption( + 'extensions', 'ParenMatch', 'flash-delay', type='int')) + self.paren_bell.set(idleConf.GetOption( + 'extensions', 'ParenMatch', 'bell')) + + # Set variables for editor windows. + self.autosave.set(idleConf.GetOption( + 'main', 'General', 'autosave', default=0, type='bool')) + self.format_width.set(idleConf.GetOption( + 'extensions', 'FormatParagraph', 'max-width', type='int')) + self.context_lines.set(idleConf.GetOption( + 'extensions', 'CodeContext', 'numlines', type='int')) + # Set additional help sources. self.user_helplist = idleConf.GetAllExtraHelpSourcesList() self.helplist.delete(0, 'end') @@ -2090,10 +2120,10 @@ def detach(self): be used with older IDLE releases if it is saved as a custom key set, with a different name. ''', - 'Extensions': ''' -Extensions: + 'General': ''' +General: -Autocomplete: Popupwait is milleseconds to wait after key char, without +AutoComplete: Popupwait is milleseconds to wait after key char, without cursor movement, before popping up completion box. Key char is '.' after identifier or a '/' (or '\\' on Windows) within a string. diff --git a/Lib/idlelib/idle_test/test_config.py b/Lib/idlelib/idle_test/test_config.py index bbf06504fc2552..aa33a0f642fdcb 100644 --- a/Lib/idlelib/idle_test/test_config.py +++ b/Lib/idlelib/idle_test/test_config.py @@ -437,78 +437,57 @@ def test_get_extensions(self): eq = self.assertEqual eq(conf.GetExtensions(), - ['AutoComplete', 'AutoExpand', 'CallTips', 'CodeContext', - 'FormatParagraph', 'ParenMatch', 'RstripExtension', 'ScriptBinding', - 'ZoomHeight']) + ['ZzDummy']) eq(conf.GetExtensions(active_only=False), - ['AutoComplete', 'AutoExpand', 'CallTips', 'CodeContext', - 'FormatParagraph', 'ParenMatch', 'RstripExtension', 'ScriptBinding', - 'ZoomHeight', 'DISABLE']) + ['ZzDummy', 'DISABLE']) eq(conf.GetExtensions(editor_only=True), - ['AutoComplete', 'AutoExpand', 'CallTips', 'CodeContext', - 'FormatParagraph', 'ParenMatch', 'RstripExtension', 'ScriptBinding', - 'ZoomHeight']) + ['ZzDummy']) eq(conf.GetExtensions(shell_only=True), - ['AutoComplete', 'AutoExpand', 'CallTips', 'FormatParagraph', - 'ParenMatch', 'ZoomHeight']) + []) eq(conf.GetExtensions(active_only=False, editor_only=True), - ['AutoComplete', 'AutoExpand', 'CallTips', 'CodeContext', - 'FormatParagraph', 'ParenMatch', 'RstripExtension', - 'ScriptBinding', 'ZoomHeight', 'DISABLE']) - eq(conf.GetExtensions(active_only=False, shell_only=True), - ['AutoComplete', 'AutoExpand', 'CallTips', 'CodeContext', - 'FormatParagraph', 'ParenMatch', 'RstripExtension', 'ScriptBinding', - 'ZoomHeight', 'DISABLE']) + ['ZzDummy', 'DISABLE']) # Add user extensions conf.SetOption('extensions', 'Foobar', 'enable', 'True') eq(conf.GetExtensions(), - ['AutoComplete', 'AutoExpand', 'CallTips', 'CodeContext', - 'FormatParagraph', 'ParenMatch', 'RstripExtension', - 'ScriptBinding', 'ZoomHeight', 'Foobar']) # User extensions didn't sort + ['ZzDummy', 'Foobar']) # User extensions didn't sort eq(conf.GetExtensions(active_only=False), - ['AutoComplete', 'AutoExpand', 'CallTips', 'CodeContext', - 'FormatParagraph', 'ParenMatch', 'RstripExtension', - 'ScriptBinding', 'ZoomHeight', 'DISABLE', 'Foobar']) + ['ZzDummy', 'DISABLE', 'Foobar']) def test_remove_key_bind_names(self): conf = self.mock_config() self.assertCountEqual( conf.RemoveKeyBindNames(conf.GetSectionList('default', 'extensions')), - ['AutoComplete', 'AutoExpand', 'CallTips', 'CodeContext', - 'FormatParagraph', 'ParenMatch', 'RstripExtension', 'ScriptBinding', - 'ZoomHeight']) + ['AutoComplete', 'CodeContext', 'FormatParagraph', 'ParenMatch','ZzDummy']) def test_get_extn_name_for_event(self): conf = self.mock_config() eq = self.assertEqual - eq(conf.GetExtnNameForEvent('force-open-completions'), 'AutoComplete') - eq(conf.GetExtnNameForEvent('expand-word'), 'AutoExpand') - eq(conf.GetExtnNameForEvent('force-open-calltip'), 'CallTips') - eq(conf.GetExtnNameForEvent('zoom-height'), 'ZoomHeight') + eq(conf.GetExtnNameForEvent('z-in'), 'ZzDummy') + eq(conf.GetExtnNameForEvent('z-out'), None) def test_get_extension_keys(self): conf = self.mock_config() eq = self.assertEqual - eq(conf.GetExtensionKeys('AutoComplete'), - {'<>': ['']}) - eq(conf.GetExtensionKeys('ParenMatch'), - {'<>': ['']}) - - key = [''] if sys.platform == 'darwin' else [''] - eq(conf.GetExtensionKeys('ZoomHeight'), {'<>': key}) + eq(conf.GetExtensionKeys('ZzDummy'), + {'<>': ['']}) +# need option key test +## key = [''] if sys.platform == 'darwin' else [''] +## eq(conf.GetExtensionKeys('ZoomHeight'), {'<>': key}) def test_get_extension_bindings(self): conf = self.mock_config() self.assertEqual(conf.GetExtensionBindings('NotExists'), {}) - key = [''] if sys.platform == 'darwin' else [''] + #key = [''] if sys.platform == 'darwin' else [''] + expect = {'<>': [''], + '<>': ['']} self.assertEqual( - conf.GetExtensionBindings('ZoomHeight'), {'<>': key}) + conf.GetExtensionBindings('ZzDummy'), expect) # Add non-configuarable bindings conf.defaultCfg['extensions'].add_section('Foobar') diff --git a/Lib/idlelib/idle_test/test_configdialog.py b/Lib/idlelib/idle_test/test_configdialog.py index 7925c6d5148ec9..3d6a858e6fd2b9 100644 --- a/Lib/idlelib/idle_test/test_configdialog.py +++ b/Lib/idlelib/idle_test/test_configdialog.py @@ -824,7 +824,7 @@ def test_keybinding(self): d.custom_name.set('my custom keys') d.bindingslist.delete(0, 'end') d.bindingslist.insert(0, 'copy') - d.bindingslist.insert(1, 'z-out') + d.bindingslist.insert(1, 'z-in') d.bindingslist.selection_set(0) d.bindingslist.selection_anchor(0) # Core binding - adds to keys. @@ -837,7 +837,7 @@ def test_keybinding(self): d.bindingslist.selection_anchor(1) d.keybinding.set('') self.assertEqual(extpage, - {'ZzDummy_cfgBindings': {'z-out': ''}}) + {'ZzDummy_cfgBindings': {'z-in': ''}}) def test_set_keys_type(self): eq = self.assertEqual @@ -1126,13 +1126,6 @@ def test_startup(self): self.assertEqual(mainpage, {'General': {'editor-on-startup': '0'}}) - def test_autosave(self): - d = self.page - d.save_auto_on.invoke() - self.assertEqual(mainpage, {'General': {'autosave': '1'}}) - d.save_ask_on.invoke() - self.assertEqual(mainpage, {'General': {'autosave': '0'}}) - def test_editor_size(self): d = self.page d.win_height_int.insert(0, '1') @@ -1141,6 +1134,37 @@ def test_editor_size(self): d.win_width_int.insert(0, '1') self.assertEqual(mainpage, {'EditorWindow': {'width': '180'}}) + def test_autocomplete_wait(self): + self.page.auto_wait_int.insert(0, '1') + self.assertEqual(extpage, {'AutoComplete': {'popupwait': '12000'}}) + + def test_parenmatch(self): + d = self.page + eq = self.assertEqual + d.paren_style_type['menu'].invoke(0) + eq(extpage, {'ParenMatch': {'style': 'opener'}}) + changes.clear() + d.paren_flash_time.insert(0, '2') + eq(extpage, {'ParenMatch': {'flash-delay': '2500'}}) + changes.clear() + d.bell_on.invoke() + eq(extpage, {'ParenMatch': {'bell': 'False'}}) + + def test_autosave(self): + d = self.page + d.save_auto_on.invoke() + self.assertEqual(mainpage, {'General': {'autosave': '1'}}) + d.save_ask_on.invoke() + self.assertEqual(mainpage, {'General': {'autosave': '0'}}) + + def test_paragraph(self): + self.page.format_width_int.insert(0, '1') + self.assertEqual(extpage, {'FormatParagraph': {'max-width': '172'}}) + + def test_context(self): + self.page.context_int.insert(0, '1') + self.assertEqual(extpage, {'CodeContext': {'numlines': '13'}}) + def test_source_selected(self): d = self.page d.set = d.set_add_delete_state From 7de27db56b352bc87f682b2507243200fc15da99 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Tue, 5 Sep 2017 00:49:47 -0400 Subject: [PATCH 38/64] Add format-paragraph to core keys. --- Lib/idlelib/config.py | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/Lib/idlelib/config.py b/Lib/idlelib/config.py index 0c35953a15e11e..e44818024ef63a 100644 --- a/Lib/idlelib/config.py +++ b/Lib/idlelib/config.py @@ -660,16 +660,17 @@ def GetCoreKeys(self, keySetName=None): '<>': [''], '<>': [''], '<>': [''], - '<>':[''], - '<>':['', '', ''], - '<>':[''], - '<>':[''], - '<>':[''], - '<>':[''], - '<>':['', '', ''], - '<>':[''], - '<>':[''], - '<>':[''] + '<>': [''], + '<>': ['', '', ''], + '<>': [''], + '<>': [''], + '<>': [''], + '<>': [''], + '<>': [''], + '<>': ['', '', ''], + '<>': [''], + '<>': [''], + '<>': [''], } if keySetName: if not (self.userCfg['keys'].has_section(keySetName) or From 2265b53e1ba2f53aca8c0820ed02ecef15a3000e Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Tue, 5 Sep 2017 02:05:12 -0400 Subject: [PATCH 39/64] Fix autocomplete and long lines. --- Lib/idlelib/autocomplete.py | 1 + Lib/idlelib/config.py | 12 +++++++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/Lib/idlelib/autocomplete.py b/Lib/idlelib/autocomplete.py index edf445f08b5869..06c771cc08e1f1 100644 --- a/Lib/idlelib/autocomplete.py +++ b/Lib/idlelib/autocomplete.py @@ -31,6 +31,7 @@ def __init__(self, editwin=None): self.editwin = editwin if editwin is not None: # not in subprocess or test self.text = editwin.text + self.text.event_add('<>', '') self.autocompletewindow = None # id of delayed call, and the index of the text insert when # the delayed call was issued. If _delayed_completion_id is diff --git a/Lib/idlelib/config.py b/Lib/idlelib/config.py index e44818024ef63a..105a177c46b505 100644 --- a/Lib/idlelib/config.py +++ b/Lib/idlelib/config.py @@ -661,13 +661,17 @@ def GetCoreKeys(self, keySetName=None): '<>': [''], '<>': [''], '<>': [''], - '<>': ['', '', ''], + '<>': ['', + '', + ''], '<>': [''], '<>': [''], '<>': [''], '<>': [''], '<>': [''], - '<>': ['', '', ''], + '<>': ['', + '', + ''], '<>': [''], '<>': [''], '<>': [''], @@ -683,7 +687,9 @@ def GetCoreKeys(self, keySetName=None): _warn(warning, 'keys', keySetName) else: for event in keyBindings: - if event not in {'<>','<>','<>'}: + if event not in {'<>', + '<>', + '<>'}: # do not allow calltips, etc. events/keys to be configured. binding = self.GetKeyBinding(keySetName, event) if binding: From 03eed248bc51e59abdf6409e07b9264198e69312 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Fri, 8 Sep 2017 21:23:36 -0400 Subject: [PATCH 40/64] Put fixed-key event-adds in EditorWindow.__init__ for now. --- Lib/idlelib/autocomplete.py | 1 - Lib/idlelib/config.py | 7 ------- Lib/idlelib/editor.py | 10 ++++++++++ 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/Lib/idlelib/autocomplete.py b/Lib/idlelib/autocomplete.py index 06c771cc08e1f1..edf445f08b5869 100644 --- a/Lib/idlelib/autocomplete.py +++ b/Lib/idlelib/autocomplete.py @@ -31,7 +31,6 @@ def __init__(self, editwin=None): self.editwin = editwin if editwin is not None: # not in subprocess or test self.text = editwin.text - self.text.event_add('<>', '') self.autocompletewindow = None # id of delayed call, and the index of the text insert when # the delayed call was issued. If _delayed_completion_id is diff --git a/Lib/idlelib/config.py b/Lib/idlelib/config.py index 105a177c46b505..d37fd9afce37c3 100644 --- a/Lib/idlelib/config.py +++ b/Lib/idlelib/config.py @@ -660,18 +660,11 @@ def GetCoreKeys(self, keySetName=None): '<>': [''], '<>': [''], '<>': [''], - '<>': [''], - '<>': ['', - '', - ''], '<>': [''], '<>': [''], '<>': [''], '<>': [''], '<>': [''], - '<>': ['', - '', - ''], '<>': [''], '<>': [''], '<>': [''], diff --git a/Lib/idlelib/editor.py b/Lib/idlelib/editor.py index ad69cbdb3d9fd1..ea738999638919 100644 --- a/Lib/idlelib/editor.py +++ b/Lib/idlelib/editor.py @@ -284,6 +284,16 @@ def __init__(self, flist=None, filename=None, key=None, root=None): self.showerror = tkMessageBox.showerror + # Add pseudoevents for former extension fixed keys. + # (This probably needs to be done once in the process.) + text.event_add('<>', '') + text.event_add('<>', '' + '', '') + text.event_add('<>', '') + text.event_add('<>', '') + text.event_add('<>', '', + '', '') + # Former extension bindings depends on frame.text being packed # (called from self.ResetColorizer()). autocomplete = self.AutoComplete(self) From 207b0cac586371559e0db55e0f40485e9f837e00 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Fri, 8 Sep 2017 22:01:56 -0400 Subject: [PATCH 41/64] Don't warn if new core keys not in user key config. --- Lib/idlelib/config.py | 37 ++++++++++++++++++++----------------- Lib/idlelib/editor.py | 7 +++---- 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/Lib/idlelib/config.py b/Lib/idlelib/config.py index d37fd9afce37c3..2233b54228a336 100644 --- a/Lib/idlelib/config.py +++ b/Lib/idlelib/config.py @@ -599,7 +599,12 @@ def IsCoreBinding(self, virtualEvent): return ('<<'+virtualEvent+'>>') in self.GetCoreKeys() # TODO make keyBindins a file or class attribute used for test above -# and copied in function below +# and copied in function below. + + former_extension_events = { # Those with user-configurable keys. + '<>', '<>', + '<>', '<>', '<>', + '<>', '<>', '<>'} def GetCoreKeys(self, keySetName=None): """Return dict of core virtual-key keybindings for keySetName. @@ -669,6 +674,7 @@ def GetCoreKeys(self, keySetName=None): '<>': [''], '<>': [''], } + if keySetName: if not (self.userCfg['keys'].has_section(keySetName) or self.defaultCfg['keys'].has_section(keySetName)): @@ -680,22 +686,19 @@ def GetCoreKeys(self, keySetName=None): _warn(warning, 'keys', keySetName) else: for event in keyBindings: - if event not in {'<>', - '<>', - '<>'}: - # do not allow calltips, etc. events/keys to be configured. - binding = self.GetKeyBinding(keySetName, event) - if binding: - keyBindings[event] = binding - else: #we are going to return a default, print warning - warning = ( - '\n Warning: config.py - IdleConf.GetCoreKeys -\n' - ' problem retrieving key binding for event %r\n' - ' from key set %r.\n' - ' returning default value: %r' % - (event, keySetName, keyBindings[event]) - ) - _warn(warning, 'keys', keySetName, event) + binding = self.GetKeyBinding(keySetName, event) + if binding: + keyBindings[event] = binding + # Otherwise return default in keyBindings. + elif event not in self.former_extension_events: + warning = ( + '\n Warning: config.py - IdleConf.GetCoreKeys -\n' + ' problem retrieving key binding for event %r\n' + ' from key set %r.\n' + ' returning default value: %r' % + (event, keySetName, keyBindings[event]) + ) + _warn(warning, 'keys', keySetName, event) return keyBindings def GetExtraHelpSourceList(self, configSet): diff --git a/Lib/idlelib/editor.py b/Lib/idlelib/editor.py index ea738999638919..03ee1128636820 100644 --- a/Lib/idlelib/editor.py +++ b/Lib/idlelib/editor.py @@ -283,11 +283,10 @@ def __init__(self, flist=None, filename=None, key=None, root=None): self.askinteger = tkSimpleDialog.askinteger self.showerror = tkMessageBox.showerror - # Add pseudoevents for former extension fixed keys. # (This probably needs to be done once in the process.) text.event_add('<>', '') - text.event_add('<>', '' + text.event_add('<>', '', '', '') text.event_add('<>', '') text.event_add('<>', '') @@ -303,8 +302,6 @@ def __init__(self, flist=None, filename=None, key=None, root=None): text.bind("<>", autocomplete.force_open_completions_event) text.bind("<>", self.AutoExpand(self).expand_word_event) - text.bind("<>", - self.CodeContext(self).toggle_code_context_event) text.bind("<>", self.FormatParagraph(self).format_paragraph_event) parenmatch = self.ParenMatch(self) @@ -320,6 +317,8 @@ def __init__(self, flist=None, filename=None, key=None, root=None): text.bind("<>", calltips.refresh_calltip_event) text.bind("<>", calltips.force_open_calltip_event) text.bind("<>", self.ZoomHeight(self).zoom_height_event) + text.bind("<>", + self.CodeContext(self).toggle_code_context_event) def _filename_to_unicode(self, filename): """Return filename as BMP unicode so diplayable in Tk.""" From 00acd22b068560e0d7cea1d8cddca0dadc371f6c Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Fri, 8 Sep 2017 22:16:29 -0400 Subject: [PATCH 42/64] Make code context inoperative in outwin, shell. --- Lib/idlelib/outwin.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Lib/idlelib/outwin.py b/Lib/idlelib/outwin.py index 5f7c09fb92cdad..6c2a792d86b99a 100644 --- a/Lib/idlelib/outwin.py +++ b/Lib/idlelib/outwin.py @@ -77,6 +77,7 @@ class OutputWindow(EditorWindow): def __init__(self, *args): EditorWindow.__init__(self, *args) self.text.bind("<>", self.goto_file_line) + self.text.unbind("<>") # Customize EditorWindow def ispythonsource(self, filename): From 2d2bfbd5e765c8badeaa214f9a456fb06a5d225d Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Fri, 8 Sep 2017 23:19:13 -0400 Subject: [PATCH 43/64] Change new bindings for MacOsx. --- Lib/idlelib/config-keys.def | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Lib/idlelib/config-keys.def b/Lib/idlelib/config-keys.def index 935ef06a37a884..fd235194dfc950 100644 --- a/Lib/idlelib/config-keys.def +++ b/Lib/idlelib/config-keys.def @@ -235,13 +235,13 @@ change-indentwidth= del-word-left= del-word-right= force-open-completions= -expand-word= +expand-word= force-open-calltip= -format-paragraph= +format-paragraph= flash-paren= run-module= -check-module= -zoom-height= +check-module= +zoom-height= [IDLE Classic OSX] toggle-tabs = @@ -295,10 +295,10 @@ save-copy-of-window-as-file = open-window-from-file = python-docs = force-open-completions= -expand-word= +expand-word= force-open-calltip= -format-paragraph= +format-paragraph= flash-paren= run-module= -check-module= -zoom-height= +check-module= +zoom-height= From fe76cd32f89ec5df485a5313664a3a884916e67c Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Sat, 9 Sep 2017 16:51:15 -0400 Subject: [PATCH 44/64] Disable test dependent on extension Alt keys. --- Lib/idlelib/idle_test/test_config.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Lib/idlelib/idle_test/test_config.py b/Lib/idlelib/idle_test/test_config.py index aa33a0f642fdcb..84b45a6376749d 100644 --- a/Lib/idlelib/idle_test/test_config.py +++ b/Lib/idlelib/idle_test/test_config.py @@ -521,9 +521,11 @@ def test_get_current_keyset(self): sys.platform = 'some-linux' self.assertEqual(conf.GetCurrentKeySet(), conf.GetKeySet(conf.CurrentKeys())) - # This should not be the same, sicne replace Date: Sat, 9 Sep 2017 18:43:02 -0400 Subject: [PATCH 45/64] Minor edits. --- Lib/idlelib/config.py | 3 ++- Lib/idlelib/configdialog.py | 13 ++++--------- Lib/idlelib/editor.py | 4 +--- 3 files changed, 7 insertions(+), 13 deletions(-) diff --git a/Lib/idlelib/config.py b/Lib/idlelib/config.py index 2233b54228a336..c3e57bc692d801 100644 --- a/Lib/idlelib/config.py +++ b/Lib/idlelib/config.py @@ -444,7 +444,8 @@ def GetExtensions(self, active_only=True, for extn in userExtns: if extn not in extns: #user has added own extension extns.append(extn) - for extn in ['AutoComplete','CodeContext','FormatParagraph','ParenMatch']: + for extn in ('AutoComplete','CodeContext', + 'FormatParagraph','ParenMatch'): extns.remove(extn) # specific exclusions because we are storing config for mainlined old # extensions in config-extensions.def for backward compatibility diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py index fd26b780abb811..8260da6c990ef1 100644 --- a/Lib/idlelib/configdialog.py +++ b/Lib/idlelib/configdialog.py @@ -287,11 +287,8 @@ def load_extensions(self): "Fill self.extensions with data from the default and user configs." self.extensions = {} for ext_name in idleConf.GetExtensions(active_only=False): - if ext_name not in {'AutoComplete', 'CodeContext', - 'FormatParagraph', 'ParenMatch'}: - # These extensions were converted to built-ins, and need to be filtered out so they don't - # appear in the Extensions tab. - self.extensions[ext_name] = [] + # Former built-in extensions are already filtered out. + self.extensions[ext_name] = [] for ext_name in self.extensions: opt_list = sorted(self.ext_defaultCfg.GetOptionList(ext_name)) @@ -799,15 +796,13 @@ def create_page_highlight(self): takefocus=FALSE, highlightthickness=0, wrap=NONE) text.bind('', lambda e: 'break') text.bind('', lambda e: 'break') - text_and_tags=(('\n', 'normal'), - + text_and_tags=( + ('\n', 'normal'), ('#you can click here', 'comment'), ('\n', 'normal'), ('#to choose items', 'comment'), ('\n', 'normal'), ('def', 'keyword'), (' ', 'normal'), ('func', 'definition'), ('(param):\n ', 'normal'), ('"""string"""', 'string'), ('\n var0 = ', 'normal'), - (':\n','normal'), - (' """string"""', 'string'), ('\n var0 = ', 'normal'), ("'string'", 'string'), ('\n var1 = ', 'normal'), ("'selected'", 'hilite'), ('\n var2 = ', 'normal'), ("'found'", 'hit'), ('\n var3 = ', 'normal'), diff --git a/Lib/idlelib/editor.py b/Lib/idlelib/editor.py index 03ee1128636820..855d375055653a 100644 --- a/Lib/idlelib/editor.py +++ b/Lib/idlelib/editor.py @@ -27,12 +27,10 @@ from idlelib import search from idlelib import windows - # The default tab setting for a Text widget, in average-width characters. TK_TABWIDTH_DEFAULT = 8 _py_version = ' (%s)' % platform.python_version() - def _sphinx_version(): "Format sys.version_info to produce the Sphinx version string used to install the chm docs" major, minor, micro, level, serial = sys.version_info @@ -1031,7 +1029,7 @@ def load_standard_extensions(self): def get_standard_extension_names(self): return idleConf.GetExtensions(editor_only=True) - extfiles = { # Use for experimental features implemented as extension. + extfiles = { # Map built-in config-extension section names to file names. 'ZzDummy': 'zzdummy', } From 6845bb0a53ad34c1625a4d85373250eb9175041b Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Sat, 9 Sep 2017 22:40:31 -0400 Subject: [PATCH 46/64] File out news blurb explaining effect for users. --- .../2017-08-24-13-48-16.bpo-27099.rENefC.rst | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/Misc/NEWS.d/next/IDLE/2017-08-24-13-48-16.bpo-27099.rENefC.rst b/Misc/NEWS.d/next/IDLE/2017-08-24-13-48-16.bpo-27099.rENefC.rst index 539f06eb63b446..9b59fbabe531b0 100644 --- a/Misc/NEWS.d/next/IDLE/2017-08-24-13-48-16.bpo-27099.rENefC.rst +++ b/Misc/NEWS.d/next/IDLE/2017-08-24-13-48-16.bpo-27099.rENefC.rst @@ -1,2 +1,20 @@ -Convert IDLE's built-in 'extensions' to regular features. Inital patch by -Charles Wohlganger. +Convert IDLE's built-in 'extensions' to regular features. + +About 10 IDLE features were implemented as supposedly optional +extensions. Their different behavior could be confusing or worse for +users and not good for maintenance. Hence the conversion. + +The main difference for users is that user configurable key bindings +for builtin features are now handled uniformly. Now, editing a binding +in a keyset only affects its value in the keyset. All bindings are +defined together in the system-specific default keysets in config- +extensions.def. All custom keysets are saved as a whole in config- +extension.cfg. All take effect as soon as one clicks Apply or Ok. + +The affected events are '<>', '<>', +'<>', '<>', '<>', +'<>', '<>', and '<>'. Any +(global) customizations made before 3.6.3 will not affect their keyset- +specific customization after 3.6.3. and vice versa. + +Inital patch by Charles Wohlganger. From b7afebb2523d4bd712f1037b4ed6a0bda7e913b4 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Sun, 10 Sep 2017 16:47:33 -0400 Subject: [PATCH 47/64] Don't compute ('') during integer input in entry box. --- Lib/idlelib/configdialog.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py index 8260da6c990ef1..604719f0453b5c 100644 --- a/Lib/idlelib/configdialog.py +++ b/Lib/idlelib/configdialog.py @@ -1764,6 +1764,7 @@ def create_page_general(self): (*)helplist: ListBox scroll_helplist: Scrollbar """ + # Integer values need StringVar because int('') raises. self.startup_edit = tracers.add( IntVar(self), ('main', 'General', 'editor-on-startup')) self.win_width = tracers.add( @@ -1771,20 +1772,20 @@ def create_page_general(self): self.win_height = tracers.add( StringVar(self), ('main', 'EditorWindow', 'height')) self.autocomplete_wait = tracers.add( - IntVar(self), ('extensions', 'AutoComplete', 'popupwait')) + StringVar(self), ('extensions', 'AutoComplete', 'popupwait')) self.paren_style = tracers.add( StringVar(self), ('extensions', 'ParenMatch', 'style')) self.flash_delay = tracers.add( - IntVar(self), ('extensions', 'ParenMatch', 'flash-delay')) + StringVar(self), ('extensions', 'ParenMatch', 'flash-delay')) self.paren_bell = tracers.add( BooleanVar(self), ('extensions', 'ParenMatch', 'bell')) self.autosave = tracers.add( IntVar(self), ('main', 'General', 'autosave')) self.format_width = tracers.add( - IntVar(self), ('extensions', 'FormatParagraph', 'max-width')) + StringVar(self), ('extensions', 'FormatParagraph', 'max-width')) self.context_lines = tracers.add( - IntVar(self), ('extensions', 'CodeContext', 'numlines')) + StringVar(self), ('extensions', 'CodeContext', 'numlines')) # Create widgets: # Section frames. From 7a5336681b5826af386b757422266631f93a9702 Mon Sep 17 00:00:00 2001 From: wohlganger Date: Tue, 12 Sep 2017 11:11:14 -0500 Subject: [PATCH 48/64] fix conflict --- Doc/library/idle.rst | 7 ------- 1 file changed, 7 deletions(-) diff --git a/Doc/library/idle.rst b/Doc/library/idle.rst index d622c6a7d63528..0faeb6df934d33 100644 --- a/Doc/library/idle.rst +++ b/Doc/library/idle.rst @@ -694,14 +694,7 @@ Extensions ^^^^^^^^^^ IDLE contains an extension facility. Preferences for extensions can be -<<<<<<< HEAD -changed with Configure Extensions. See the beginning of config-extensions.def -in the idlelib directory for further information. The only current default -extension is zoomheight. It exists as an extension primarily to be an example -and for testing purposes. -======= changed with the Extensions tab of the preferences dialog. See the beginning of config-extensions.def in the idlelib directory for further information. The only current default extension is zzdummy, an example also used for testing. ->>>>>>> upstream/master From d3b6848734a73b120af820057b716adcb8ece5cd Mon Sep 17 00:00:00 2001 From: wohlganger Date: Tue, 12 Sep 2017 11:17:02 -0500 Subject: [PATCH 49/64] fix conflict --- Lib/idlelib/idle_test/test_configdialog.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/Lib/idlelib/idle_test/test_configdialog.py b/Lib/idlelib/idle_test/test_configdialog.py index dfe7426863a4bc..71df0ba4b22102 100644 --- a/Lib/idlelib/idle_test/test_configdialog.py +++ b/Lib/idlelib/idle_test/test_configdialog.py @@ -1128,13 +1128,6 @@ def test_startup(self): def test_editor_size(self): d = self.page -<<<<<<< HEAD - d.win_height_int.insert(0, '1') - self.assertEqual(mainpage, {'EditorWindow': {'height': '140'}}) - changes.clear() - d.win_width_int.insert(0, '1') - self.assertEqual(mainpage, {'EditorWindow': {'width': '180'}}) -======= d.win_height_int.delete(0, 'end') d.win_height_int.insert(0, '11') self.assertEqual(mainpage, {'EditorWindow': {'height': '11'}}) @@ -1177,7 +1170,6 @@ def test_context(self): self.page.context_int.delete(0, 'end') self.page.context_int.insert(0, '1') self.assertEqual(extpage, {'CodeContext': {'numlines': '1'}}) ->>>>>>> upstream/master def test_autocomplete_wait(self): self.page.auto_wait_int.insert(0, '1') From eb58d399c830f36ba03dc1a59a57f33739bb30ca Mon Sep 17 00:00:00 2001 From: wohlganger Date: Tue, 12 Sep 2017 11:19:16 -0500 Subject: [PATCH 50/64] fix conflict, match upstream --- Lib/idlelib/idle_test/test_configdialog.py | 66 ---------------------- 1 file changed, 66 deletions(-) diff --git a/Lib/idlelib/idle_test/test_configdialog.py b/Lib/idlelib/idle_test/test_configdialog.py index 71df0ba4b22102..83b164d70ac338 100644 --- a/Lib/idlelib/idle_test/test_configdialog.py +++ b/Lib/idlelib/idle_test/test_configdialog.py @@ -1136,72 +1136,6 @@ def test_editor_size(self): d.win_width_int.insert(0, '11') self.assertEqual(mainpage, {'EditorWindow': {'width': '11'}}) - def test_autocomplete_wait(self): - self.page.auto_wait_int.delete(0, 'end') - self.page.auto_wait_int.insert(0, '11') - self.assertEqual(extpage, {'AutoComplete': {'popupwait': '11'}}) - - def test_parenmatch(self): - d = self.page - eq = self.assertEqual - d.paren_style_type['menu'].invoke(0) - eq(extpage, {'ParenMatch': {'style': 'opener'}}) - changes.clear() - d.paren_flash_time.delete(0, 'end') - d.paren_flash_time.insert(0, '11') - eq(extpage, {'ParenMatch': {'flash-delay': '11'}}) - changes.clear() - d.bell_on.invoke() - eq(extpage, {'ParenMatch': {'bell': 'False'}}) - - def test_autosave(self): - d = self.page - d.save_auto_on.invoke() - self.assertEqual(mainpage, {'General': {'autosave': '1'}}) - d.save_ask_on.invoke() - self.assertEqual(mainpage, {'General': {'autosave': '0'}}) - - def test_paragraph(self): - self.page.format_width_int.delete(0, 'end') - self.page.format_width_int.insert(0, '11') - self.assertEqual(extpage, {'FormatParagraph': {'max-width': '11'}}) - - def test_context(self): - self.page.context_int.delete(0, 'end') - self.page.context_int.insert(0, '1') - self.assertEqual(extpage, {'CodeContext': {'numlines': '1'}}) - - def test_autocomplete_wait(self): - self.page.auto_wait_int.insert(0, '1') - self.assertEqual(extpage, {'AutoComplete': {'popupwait': '12000'}}) - - def test_parenmatch(self): - d = self.page - eq = self.assertEqual - d.paren_style_type['menu'].invoke(0) - eq(extpage, {'ParenMatch': {'style': 'opener'}}) - changes.clear() - d.paren_flash_time.insert(0, '2') - eq(extpage, {'ParenMatch': {'flash-delay': '2500'}}) - changes.clear() - d.bell_on.invoke() - eq(extpage, {'ParenMatch': {'bell': 'False'}}) - - def test_autosave(self): - d = self.page - d.save_auto_on.invoke() - self.assertEqual(mainpage, {'General': {'autosave': '1'}}) - d.save_ask_on.invoke() - self.assertEqual(mainpage, {'General': {'autosave': '0'}}) - - def test_paragraph(self): - self.page.format_width_int.insert(0, '1') - self.assertEqual(extpage, {'FormatParagraph': {'max-width': '172'}}) - - def test_context(self): - self.page.context_int.insert(0, '1') - self.assertEqual(extpage, {'CodeContext': {'numlines': '13'}}) - def test_source_selected(self): d = self.page d.set = d.set_add_delete_state From 119bbfca2a8c09b4e1fad2e7ff61ecd0fec5127b Mon Sep 17 00:00:00 2001 From: wohlganger Date: Tue, 12 Sep 2017 11:21:03 -0500 Subject: [PATCH 51/64] match upstream --- Lib/idlelib/idle_test/test_configdialog.py | 35 ++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/Lib/idlelib/idle_test/test_configdialog.py b/Lib/idlelib/idle_test/test_configdialog.py index 83b164d70ac338..cae7186415b546 100644 --- a/Lib/idlelib/idle_test/test_configdialog.py +++ b/Lib/idlelib/idle_test/test_configdialog.py @@ -1136,6 +1136,41 @@ def test_editor_size(self): d.win_width_int.insert(0, '11') self.assertEqual(mainpage, {'EditorWindow': {'width': '11'}}) + def test_autocomplete_wait(self): + self.page.auto_wait_int.delete(0, 'end') + self.page.auto_wait_int.insert(0, '11') + self.assertEqual(extpage, {'AutoComplete': {'popupwait': '11'}}) + + def test_parenmatch(self): + d = self.page + eq = self.assertEqual + d.paren_style_type['menu'].invoke(0) + eq(extpage, {'ParenMatch': {'style': 'opener'}}) + changes.clear() + d.paren_flash_time.delete(0, 'end') + d.paren_flash_time.insert(0, '11') + eq(extpage, {'ParenMatch': {'flash-delay': '11'}}) + changes.clear() + d.bell_on.invoke() + eq(extpage, {'ParenMatch': {'bell': 'False'}}) + + def test_autosave(self): + d = self.page + d.save_auto_on.invoke() + self.assertEqual(mainpage, {'General': {'autosave': '1'}}) + d.save_ask_on.invoke() + self.assertEqual(mainpage, {'General': {'autosave': '0'}}) + + def test_paragraph(self): + self.page.format_width_int.delete(0, 'end') + self.page.format_width_int.insert(0, '11') + self.assertEqual(extpage, {'FormatParagraph': {'max-width': '11'}}) + + def test_context(self): + self.page.context_int.delete(0, 'end') + self.page.context_int.insert(0, '1') + self.assertEqual(extpage, {'CodeContext': {'numlines': '1'}}) + def test_source_selected(self): d = self.page d.set = d.set_add_delete_state From 814efbeaf4aaf7c3d3e16502c6b51e9476553809 Mon Sep 17 00:00:00 2001 From: wohlganger Date: Tue, 12 Sep 2017 14:10:13 -0500 Subject: [PATCH 52/64] Auto-insert-parens extension includes changes to config-extensions.def and test_config.py for it to run and pass tests. New tests for it in test_parenclose. Coverage tool indicates it covers everything but __init__. All idlelib tests pass, except one for calltips, which I'm fairly sure fails outside of this patch anyway. --- Lib/idlelib/ParenClose.py | 95 ++++++++++++++++++++++++ Lib/idlelib/config-extensions.def | 10 +++ Lib/idlelib/idle_test/test_config.py | 19 ++--- Lib/idlelib/idle_test/test_parenclose.py | 78 +++++++++++++++++++ 4 files changed, 193 insertions(+), 9 deletions(-) create mode 100644 Lib/idlelib/ParenClose.py create mode 100644 Lib/idlelib/idle_test/test_parenclose.py diff --git a/Lib/idlelib/ParenClose.py b/Lib/idlelib/ParenClose.py new file mode 100644 index 00000000000000..d745bd294cb018 --- /dev/null +++ b/Lib/idlelib/ParenClose.py @@ -0,0 +1,95 @@ +""" +Parens and Ticks Closing Extension + +When you hit left paren or tick, +automatically creates the closing paren or tick. + +Author: Charles M. Wohlganger + charles.wohlganger@gmail.com + +Last Updated: 12-Aug-2017 by Charles Wohlganger + +Add to the end of config-extensions.def : + + [ParenClose] + enable = True + paren_close = True + tick_close = True + skip_closures = True + comment_space = True + [ParenClose_cfgBindings] + p-open = + t-open = + p-close = + +""" + +from idlelib.config import idleConf + + +class ParenClose: + ''' + When loaded as an extension to IDLE, and paren_close is True, the symbols + ([{ will have their closures printed after them and the insertion cursor + moved between the two. The same is true for tick closures and the symbols + ' and ". When \'\'\' or """ are typed and tick_close is True, it will also + produce the closing symbols. If skip_closures is True, then when a closure + symbol is typed and the same one is to the right of it, that symbols is + deleted before the new one is typed, effectively skipping over the closure. + ''' + def __init__(self, editwin=None): #setting default to none makes testing easier + if editwin: + self.text = editwin.text + else: + self.text=None + self.paren_close = idleConf.GetOption( + 'extensions', 'ParenClose', 'paren_close', default=True) + self.tick_close = idleConf.GetOption( + 'extensions', 'ParenClose', 'tick_close', default=True) + self.skip_closures = idleConf.GetOption( + 'extensions', 'ParenClose', 'skip_closures', default=True) + self.comment_space = idleConf.GetOption( + 'extensions', 'ParenClose', 'comment_space', default=True) + #sometimes idleConf loads boolean False as string "False" + if self.paren_close == 'False': + self.paren_close = False + if self.tick_close == 'False': + self.tick_close = False + if self.skip_closures == 'False': + self.skip_closures = False + if self.comment_space == 'False': + self.comment_space = False + + def p_open_event(self, event): + if self.paren_close: + closer = {'(': ')', '[': ']', '{': '}'}[event.char] + pos = self.text.index('insert') + self.text.insert(pos, closer) + self.text.mark_set('insert', pos) + + def p_close_event(self, event): + pos = self.text.index('insert') + if self.skip_closures \ + and self.text.get(pos, pos + ' +1c') == event.char: + self.text.delete(pos, pos + '+1c') + + def t_open_event(self, event): + if self.tick_close: + pos = self.text.index('insert') + if self.text.get(pos + ' -2c', pos) == event.char * 2 \ + and self.text.get(pos, pos + ' +1c') != event.char: + # Instead of one tick, add two ticks if there are two; + # user wants to make docstring or multiline. + self.text.insert(pos, event.char * 3) + self.text.mark_set('insert', pos) + else: + if self.skip_closures \ + and self.text.get(pos, pos + ' +1c') == event.char: + self.text.delete(pos, pos + ' +1c') + else: + self.text.insert(pos, event.char) + self.text.mark_set('insert', pos) + +if __name__ == '__main__': + import unittest + unittest.main('idlelib.idle_test.test_parenclose', verbosity=2) diff --git a/Lib/idlelib/config-extensions.def b/Lib/idlelib/config-extensions.def index e8d417bac0d497..7224d42678972d 100644 --- a/Lib/idlelib/config-extensions.def +++ b/Lib/idlelib/config-extensions.def @@ -63,3 +63,13 @@ z-text= Z z-in= [ZzDummy_bindings] z-out= + +[ParenClose] +enable = True +paren_close = True +tick_close = True +skip_closures = True +[ParenClose_cfgBindings] +p-open = +t-open = +p-close = diff --git a/Lib/idlelib/idle_test/test_config.py b/Lib/idlelib/idle_test/test_config.py index 84b45a6376749d..d6fa4fb959bf71 100644 --- a/Lib/idlelib/idle_test/test_config.py +++ b/Lib/idlelib/idle_test/test_config.py @@ -436,30 +436,31 @@ def test_get_extensions(self): conf.SetOption('extensions', 'DISABLE', 'enable', 'False') eq = self.assertEqual - eq(conf.GetExtensions(), - ['ZzDummy']) + eq((conf.GetExtensions()), + ['ZzDummy', 'ParenClose']) eq(conf.GetExtensions(active_only=False), - ['ZzDummy', 'DISABLE']) + ['ZzDummy', 'ParenClose', 'DISABLE']) eq(conf.GetExtensions(editor_only=True), - ['ZzDummy']) + ['ZzDummy', 'ParenClose']) eq(conf.GetExtensions(shell_only=True), - []) + ['ParenClose']) eq(conf.GetExtensions(active_only=False, editor_only=True), - ['ZzDummy', 'DISABLE']) + ['ZzDummy', 'ParenClose', 'DISABLE']) # Add user extensions conf.SetOption('extensions', 'Foobar', 'enable', 'True') eq(conf.GetExtensions(), - ['ZzDummy', 'Foobar']) # User extensions didn't sort + ['ZzDummy', 'ParenClose', 'Foobar']) # User extensions didn't sort eq(conf.GetExtensions(active_only=False), - ['ZzDummy', 'DISABLE', 'Foobar']) + ['ZzDummy', 'ParenClose', 'DISABLE', 'Foobar']) def test_remove_key_bind_names(self): conf = self.mock_config() self.assertCountEqual( conf.RemoveKeyBindNames(conf.GetSectionList('default', 'extensions')), - ['AutoComplete', 'CodeContext', 'FormatParagraph', 'ParenMatch','ZzDummy']) + ['AutoComplete', 'CodeContext', 'FormatParagraph', 'ParenClose', + 'ParenMatch','ZzDummy']) def test_get_extn_name_for_event(self): conf = self.mock_config() diff --git a/Lib/idlelib/idle_test/test_parenclose.py b/Lib/idlelib/idle_test/test_parenclose.py new file mode 100644 index 00000000000000..62dc72e282547b --- /dev/null +++ b/Lib/idlelib/idle_test/test_parenclose.py @@ -0,0 +1,78 @@ +'''Test idlelib.parenclose. +''' +from idlelib.ParenClose import ParenClose +import unittest +from unittest.mock import Mock +from tkinter import Text # mocktk doesn't do text position addition correctly + + +class MockEvent(object): + def __init__(self, char): + self.char = char + + +class ParenCloseTest(unittest.TestCase): + def test_parenClose(self): + self.p_open_event = ParenClose.p_open_event + self.p_close_event = ParenClose.p_close_event + self.t_open_event = ParenClose.t_open_event + self.text = Text() + p_open = self.p_open_event + p_close = self.p_close_event + t_open = self.t_open_event + for paren_close_set, tick_close_set, skip_closures_set, \ + insert, func, pos, char, result, posend in [ + (1, 1, 1, 'def abuse', p_open, '1.9', '(', + 'def abuse()', '1.10'), + (1, 1, 1, 'spam = ', p_open, '1.7', '[', + 'spam = []', '1.8'), + (1, 1, 1, 'eggs = ', p_open, '1.7', '{', + 'eggs = {}', '1.8'), + (1, 1, 1, 'def abuse2()', p_close, '1.11', ')', + 'def abuse2()', '1.12'), + (1, 1, 1, 'spam2 = []', p_close, '1.9', ']', + 'spam2 = []', '1.10'), + (1, 1, 1, 'eggs2 = {}', p_close, '1.9', '}', + 'eggs2 = {}', '1.10'), + (1, 1, 1, "color = ", t_open, '1.8', "'", + "color = ''", '1.9'), + (1, 1, 1, "velocity = ", t_open, '1.11', '"', + 'velocity = ""', '1.12'), + (1, 1, 1, "more_spam = ''", t_open, '1.14', "'", + "more_spam = ''''''", '1.15'), + (1, 1, 1, 'more_eggs = ""', t_open, '1.14', '"', + 'more_eggs = """"""', '1.15'), + (1, 1, 1, "more_spam2 = ''''''", t_open, '1.16', "'", + "more_spam2 = ''''''", '1.17'), + (1, 1, 1, 'more_eggs2 = """"""', t_open, '1.16', '"', + 'more_eggs2 = """"""', '1.17'), + (0, 1, 1, 'no_spam = ', p_open, '1.10', '(', + 'no_spam = (', '1.11'), + (1, 0, 1, 'no_velocity = ', t_open, '1.14', "'", + "no_velocity = '", '1.15'), + (1, 0, 1, 'no_more_eggs = ""', t_open, '1.17', '"', + 'no_more_eggs = """', '1.18'), + (1, 1, 0, 'tinny = []', p_close, '1.9', ']', + 'tinny = []]', '1.10'), + (1, 1, 0, 'gone = ""', t_open, '1.8', '"', + 'gone = """"', '1.9')]: + # reset text and self + self.paren_close = paren_close_set + self.tick_close = tick_close_set + self.skip_closures = skip_closures_set + self.text.delete('1.0', 'end') + # write text, move to current position, write character + self.text.insert('1.0', insert) + self.text.mark_set('insert', pos) + event = MockEvent(char) + func(self, event) + p = self.text.index('insert') + self.text.insert(p, char) + # checking position and text at the same time + # makes it easier to spot where errors occur + p = self.text.index('insert') + actual = self.text.get('1.0', 'end') + self.assertTupleEqual((actual, p), (result+'\n', posend)) + +if __name__ == '__main__': + unittest.main(verbosity=2) From 7f596b98d1a4722f6004563ede78824a414e1ca8 Mon Sep 17 00:00:00 2001 From: wohlganger Date: Tue, 12 Sep 2017 14:30:58 -0500 Subject: [PATCH 53/64] removed unused old options sorry i missed this before uploading --- Lib/idlelib/ParenClose.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Lib/idlelib/ParenClose.py b/Lib/idlelib/ParenClose.py index d745bd294cb018..c974662cd0f1b5 100644 --- a/Lib/idlelib/ParenClose.py +++ b/Lib/idlelib/ParenClose.py @@ -48,8 +48,6 @@ def __init__(self, editwin=None): #setting default to none makes testing easier 'extensions', 'ParenClose', 'tick_close', default=True) self.skip_closures = idleConf.GetOption( 'extensions', 'ParenClose', 'skip_closures', default=True) - self.comment_space = idleConf.GetOption( - 'extensions', 'ParenClose', 'comment_space', default=True) #sometimes idleConf loads boolean False as string "False" if self.paren_close == 'False': self.paren_close = False @@ -57,8 +55,6 @@ def __init__(self, editwin=None): #setting default to none makes testing easier self.tick_close = False if self.skip_closures == 'False': self.skip_closures = False - if self.comment_space == 'False': - self.comment_space = False def p_open_event(self, event): if self.paren_close: From 26323e2ab11e958b930831cdde7add34f40b52e3 Mon Sep 17 00:00:00 2001 From: wohlganger Date: Wed, 13 Sep 2017 10:31:40 -0500 Subject: [PATCH 54/64] Changes made per csabella request. ' and " changed to quotedbl and quoteright. Fixed keybindings appearing in config. Comment style fix. GetOption bool. Code deduplication on p_close_event, p_open_event, t_open_event. Needed to change test to use instantiated ParenClose object rather than class methods as a result of previous changes. Probably makes for a better test. --- Lib/idlelib/config-extensions.def | 4 +- Lib/idlelib/idle_test/parenmatch.py | 188 +++++++++++++++++++++++ Lib/idlelib/idle_test/test_parenclose.py | 44 +++--- 3 files changed, 211 insertions(+), 25 deletions(-) create mode 100644 Lib/idlelib/idle_test/parenmatch.py diff --git a/Lib/idlelib/config-extensions.def b/Lib/idlelib/config-extensions.def index 7224d42678972d..0808820927e3e2 100644 --- a/Lib/idlelib/config-extensions.def +++ b/Lib/idlelib/config-extensions.def @@ -69,7 +69,7 @@ enable = True paren_close = True tick_close = True skip_closures = True -[ParenClose_cfgBindings] +[ParenClose_bindings] p-open = -t-open = +t-open = p-close = diff --git a/Lib/idlelib/idle_test/parenmatch.py b/Lib/idlelib/idle_test/parenmatch.py new file mode 100644 index 00000000000000..12212150c6dc8e --- /dev/null +++ b/Lib/idlelib/idle_test/parenmatch.py @@ -0,0 +1,188 @@ +"""ParenMatch -- for parenthesis matching. + +When you hit a right paren, the cursor should move briefly to the left +paren. Paren here is used generically; the matching applies to +parentheses, square brackets, and curly braces. +""" +from idlelib.hyperparser import HyperParser +from idlelib.config import idleConf + +_openers = {')':'(',']':'[','}':'{'} +CHECK_DELAY = 100 # milliseconds + +class ParenMatch: + """Highlight matching openers and closers, (), [], and {}. + + There are three supported styles of paren matching. When a right + paren (opener) is typed: + + opener -- highlight the matching left paren (closer); + parens -- highlight the left and right parens (opener and closer); + expression -- highlight the entire expression from opener to closer. + (For back compatibility, 'default' is a synonym for 'opener'). + + Flash-delay is the maximum milliseconds the highlighting remains. + Any cursor movement (key press or click) before that removes the + highlight. If flash-delay is 0, there is no maximum. + + TODO: + - Augment bell() with mismatch warning in status window. + - Highlight when cursor is moved to the right of a closer. + This might be too expensive to check. + """ + + RESTORE_VIRTUAL_EVENT_NAME = "<>" + # We want the restore event be called before the usual return and + # backspace events. + RESTORE_SEQUENCES = ("", "", + "", "") + + 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.bell = self.text.bell if self.BELL else lambda: None + self.counter = 0 + self.is_restore_active = 0 + self.set_style(self.STYLE) + + @classmethod + def reload(cls): + cls.STYLE = idleConf.GetOption( + 'extensions','ParenMatch','style', default='opener') + cls.FLASH_DELAY = idleConf.GetOption( + 'extensions','ParenMatch','flash-delay', type='int',default=500) + cls.BELL = idleConf.GetOption( + 'extensions','ParenMatch','bell', type='bool', default=1) + cls.HILITE_CONFIG = idleConf.GetHighlight(idleConf.CurrentTheme(), + 'hilite') + + def activate_restore(self): + "Activate mechanism to restore text from highlighting." + 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): + "Remove restore event bindings." + 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): + "Set tag and timeout functions." + self.STYLE = style + self.create_tag = ( + self.create_tag_opener if style in {"opener", "default"} else + self.create_tag_parens if style == "parens" else + self.create_tag_expression) # "expression" or unknown + + self.set_timeout = (self.set_timeout_last if self.FLASH_DELAY else + self.set_timeout_none) + + def flash_paren_event(self, event): + "Handle editor 'show surrounding parens' event (menu or shortcut)." + indices = (HyperParser(self.editwin, "insert") + .get_surrounding_brackets()) + if indices is None: + self.bell() + return "break" + self.activate_restore() + self.create_tag(indices) + self.set_timeout() + return "break" + + def paren_closed_event(self, event): + "Handle user input of closer." + # If user bound non-closer to <>, 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.bell() + return + self.activate_restore() + self.create_tag(indices) + self.set_timeout() + return + + def restore_event(self, event=None): + "Remove effect of doing match." + 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() + + # any one of the create_tag_XXX methods can be used depending on + # the style + + def create_tag_opener(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_parens(self, indices): + """Highlight the left and right parens""" + if self.text.get(indices[1]) in (')', ']', '}'): + rightindex = indices[1]+"+1c" + else: + rightindex = indices[1] + self.text.tag_add("paren", indices[0], indices[0]+"+1c", rightindex+"-1c", rightindex) + 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 FLASH_DELAY millisecs""" + # 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)) + + +ParenMatch.reload() + + +if __name__ == '__main__': + import unittest + unittest.main('idlelib.idle_test.test_parenmatch', verbosity=2) diff --git a/Lib/idlelib/idle_test/test_parenclose.py b/Lib/idlelib/idle_test/test_parenclose.py index 62dc72e282547b..3bb73f84dfb28f 100644 --- a/Lib/idlelib/idle_test/test_parenclose.py +++ b/Lib/idlelib/idle_test/test_parenclose.py @@ -13,13 +13,11 @@ def __init__(self, char): class ParenCloseTest(unittest.TestCase): def test_parenClose(self): - self.p_open_event = ParenClose.p_open_event - self.p_close_event = ParenClose.p_close_event - self.t_open_event = ParenClose.t_open_event - self.text = Text() - p_open = self.p_open_event - p_close = self.p_close_event - t_open = self.t_open_event + p = ParenClose() + p.text = Text() + p_open = p.p_open_event + p_close = p.p_close_event + t_open = p.t_open_event for paren_close_set, tick_close_set, skip_closures_set, \ insert, func, pos, char, result, posend in [ (1, 1, 1, 'def abuse', p_open, '1.9', '(', @@ -56,23 +54,23 @@ def test_parenClose(self): 'tinny = []]', '1.10'), (1, 1, 0, 'gone = ""', t_open, '1.8', '"', 'gone = """"', '1.9')]: - # reset text and self - self.paren_close = paren_close_set - self.tick_close = tick_close_set - self.skip_closures = skip_closures_set - self.text.delete('1.0', 'end') - # write text, move to current position, write character - self.text.insert('1.0', insert) - self.text.mark_set('insert', pos) + # Reset text and self. + p.paren_close = paren_close_set + p.tick_close = tick_close_set + p.skip_closures = skip_closures_set + p.text.delete('1.0', 'end') + # Write text, move to current position, write character. + p.text.insert('1.0', insert) + p.text.mark_set('insert', pos) event = MockEvent(char) - func(self, event) - p = self.text.index('insert') - self.text.insert(p, char) - # checking position and text at the same time - # makes it easier to spot where errors occur - p = self.text.index('insert') - actual = self.text.get('1.0', 'end') - self.assertTupleEqual((actual, p), (result+'\n', posend)) + func(event) + pos = p.text.index('insert') + p.text.insert(pos, char) + # Checking position and text at the same time + # makes it easier to spot where errors occur. + pos = p.text.index('insert') + actual = p.text.get('1.0', 'end') + self.assertTupleEqual((actual, pos), (result+'\n', posend)) if __name__ == '__main__': unittest.main(verbosity=2) From 7862d8a02fd4b63b88767c68a23f14f3ac73c56f Mon Sep 17 00:00:00 2001 From: wohlganger Date: Wed, 13 Sep 2017 10:59:39 -0500 Subject: [PATCH 55/64] fix stupid mistake copied wrong file to wrong place between where I can test code and my git folder. --- Lib/idlelib/ParenClose.py | 36 ++---- Lib/idlelib/idle_test/parenmatch.py | 188 ---------------------------- 2 files changed, 13 insertions(+), 211 deletions(-) delete mode 100644 Lib/idlelib/idle_test/parenmatch.py diff --git a/Lib/idlelib/ParenClose.py b/Lib/idlelib/ParenClose.py index c974662cd0f1b5..968b96b16bbd52 100644 --- a/Lib/idlelib/ParenClose.py +++ b/Lib/idlelib/ParenClose.py @@ -7,7 +7,7 @@ Author: Charles M. Wohlganger charles.wohlganger@gmail.com -Last Updated: 12-Aug-2017 by Charles Wohlganger +Last Updated: 13-Aug-2017 by Charles Wohlganger Add to the end of config-extensions.def : @@ -19,7 +19,7 @@ comment_space = True [ParenClose_cfgBindings] p-open = - t-open = + t-open = p-close = """ @@ -37,54 +37,44 @@ class ParenClose: symbol is typed and the same one is to the right of it, that symbols is deleted before the new one is typed, effectively skipping over the closure. ''' - def __init__(self, editwin=None): #setting default to none makes testing easier + def __init__(self, editwin=None): # Setting default to none makes testing easier. if editwin: self.text = editwin.text else: self.text=None self.paren_close = idleConf.GetOption( - 'extensions', 'ParenClose', 'paren_close', default=True) + 'extensions', 'ParenClose', 'paren_close', type = bool, default=True) self.tick_close = idleConf.GetOption( - 'extensions', 'ParenClose', 'tick_close', default=True) + 'extensions', 'ParenClose', 'tick_close', type = bool, default=True) self.skip_closures = idleConf.GetOption( - 'extensions', 'ParenClose', 'skip_closures', default=True) - #sometimes idleConf loads boolean False as string "False" - if self.paren_close == 'False': - self.paren_close = False - if self.tick_close == 'False': - self.tick_close = False - if self.skip_closures == 'False': - self.skip_closures = False + 'extensions', 'ParenClose', 'skip_closures', type = bool, default=True) def p_open_event(self, event): if self.paren_close: - closer = {'(': ')', '[': ']', '{': '}'}[event.char] + closer = {'(': ')', '[': ']', '{': '}', "'":"'", '"':'"'}[event.char] pos = self.text.index('insert') self.text.insert(pos, closer) self.text.mark_set('insert', pos) - + def p_close_event(self, event): pos = self.text.index('insert') if self.skip_closures \ and self.text.get(pos, pos + ' +1c') == event.char: self.text.delete(pos, pos + '+1c') + return True + return False def t_open_event(self, event): if self.tick_close: pos = self.text.index('insert') if self.text.get(pos + ' -2c', pos) == event.char * 2 \ and self.text.get(pos, pos + ' +1c') != event.char: - # Instead of one tick, add two ticks if there are two; + # Instead of one tick, add three ticks if there are two; # user wants to make docstring or multiline. self.text.insert(pos, event.char * 3) self.text.mark_set('insert', pos) - else: - if self.skip_closures \ - and self.text.get(pos, pos + ' +1c') == event.char: - self.text.delete(pos, pos + ' +1c') - else: - self.text.insert(pos, event.char) - self.text.mark_set('insert', pos) + elif not self.p_close_event(event): + self.p_open_event(event) if __name__ == '__main__': import unittest diff --git a/Lib/idlelib/idle_test/parenmatch.py b/Lib/idlelib/idle_test/parenmatch.py deleted file mode 100644 index 12212150c6dc8e..00000000000000 --- a/Lib/idlelib/idle_test/parenmatch.py +++ /dev/null @@ -1,188 +0,0 @@ -"""ParenMatch -- for parenthesis matching. - -When you hit a right paren, the cursor should move briefly to the left -paren. Paren here is used generically; the matching applies to -parentheses, square brackets, and curly braces. -""" -from idlelib.hyperparser import HyperParser -from idlelib.config import idleConf - -_openers = {')':'(',']':'[','}':'{'} -CHECK_DELAY = 100 # milliseconds - -class ParenMatch: - """Highlight matching openers and closers, (), [], and {}. - - There are three supported styles of paren matching. When a right - paren (opener) is typed: - - opener -- highlight the matching left paren (closer); - parens -- highlight the left and right parens (opener and closer); - expression -- highlight the entire expression from opener to closer. - (For back compatibility, 'default' is a synonym for 'opener'). - - Flash-delay is the maximum milliseconds the highlighting remains. - Any cursor movement (key press or click) before that removes the - highlight. If flash-delay is 0, there is no maximum. - - TODO: - - Augment bell() with mismatch warning in status window. - - Highlight when cursor is moved to the right of a closer. - This might be too expensive to check. - """ - - RESTORE_VIRTUAL_EVENT_NAME = "<>" - # We want the restore event be called before the usual return and - # backspace events. - RESTORE_SEQUENCES = ("", "", - "", "") - - 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.bell = self.text.bell if self.BELL else lambda: None - self.counter = 0 - self.is_restore_active = 0 - self.set_style(self.STYLE) - - @classmethod - def reload(cls): - cls.STYLE = idleConf.GetOption( - 'extensions','ParenMatch','style', default='opener') - cls.FLASH_DELAY = idleConf.GetOption( - 'extensions','ParenMatch','flash-delay', type='int',default=500) - cls.BELL = idleConf.GetOption( - 'extensions','ParenMatch','bell', type='bool', default=1) - cls.HILITE_CONFIG = idleConf.GetHighlight(idleConf.CurrentTheme(), - 'hilite') - - def activate_restore(self): - "Activate mechanism to restore text from highlighting." - 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): - "Remove restore event bindings." - 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): - "Set tag and timeout functions." - self.STYLE = style - self.create_tag = ( - self.create_tag_opener if style in {"opener", "default"} else - self.create_tag_parens if style == "parens" else - self.create_tag_expression) # "expression" or unknown - - self.set_timeout = (self.set_timeout_last if self.FLASH_DELAY else - self.set_timeout_none) - - def flash_paren_event(self, event): - "Handle editor 'show surrounding parens' event (menu or shortcut)." - indices = (HyperParser(self.editwin, "insert") - .get_surrounding_brackets()) - if indices is None: - self.bell() - return "break" - self.activate_restore() - self.create_tag(indices) - self.set_timeout() - return "break" - - def paren_closed_event(self, event): - "Handle user input of closer." - # If user bound non-closer to <>, 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.bell() - return - self.activate_restore() - self.create_tag(indices) - self.set_timeout() - return - - def restore_event(self, event=None): - "Remove effect of doing match." - 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() - - # any one of the create_tag_XXX methods can be used depending on - # the style - - def create_tag_opener(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_parens(self, indices): - """Highlight the left and right parens""" - if self.text.get(indices[1]) in (')', ']', '}'): - rightindex = indices[1]+"+1c" - else: - rightindex = indices[1] - self.text.tag_add("paren", indices[0], indices[0]+"+1c", rightindex+"-1c", rightindex) - 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 FLASH_DELAY millisecs""" - # 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)) - - -ParenMatch.reload() - - -if __name__ == '__main__': - import unittest - unittest.main('idlelib.idle_test.test_parenmatch', verbosity=2) From 2f19ca89ff481227fbee1dae6fb3a35e193f796d Mon Sep 17 00:00:00 2001 From: wohlganger Date: Wed, 13 Sep 2017 11:33:22 -0500 Subject: [PATCH 56/64] added mutual delete / backspace. minor fixes --- Lib/idlelib/ParenClose.py | 34 ++++++++++++--- Lib/idlelib/config-extensions.def | 5 ++- Lib/idlelib/idle_test/test_parenclose.py | 55 ++++++++++++++++-------- 3 files changed, 68 insertions(+), 26 deletions(-) diff --git a/Lib/idlelib/ParenClose.py b/Lib/idlelib/ParenClose.py index 968b96b16bbd52..526ace85d8ef60 100644 --- a/Lib/idlelib/ParenClose.py +++ b/Lib/idlelib/ParenClose.py @@ -15,13 +15,14 @@ enable = True paren_close = True tick_close = True - skip_closures = True - comment_space = True + skip_closures = True + mutual_delete = True [ParenClose_cfgBindings] p-open = t-open = p-close = - + back = + delete = """ from idlelib.config import idleConf @@ -37,7 +38,11 @@ class ParenClose: symbol is typed and the same one is to the right of it, that symbols is deleted before the new one is typed, effectively skipping over the closure. ''' - def __init__(self, editwin=None): # Setting default to none makes testing easier. + + closers = {'(': ')', '[': ']', '{': '}', "'":"'", '"':'"'} + + def __init__(self, editwin=None): + # Setting default to none makes testing easier. if editwin: self.text = editwin.text else: @@ -48,12 +53,29 @@ def __init__(self, editwin=None): # Setting default to none makes testing easier 'extensions', 'ParenClose', 'tick_close', type = bool, default=True) self.skip_closures = idleConf.GetOption( 'extensions', 'ParenClose', 'skip_closures', type = bool, default=True) + self.mutual_delete = idleConf.GetOption( + 'extensions', 'ParenClose', 'mutual_delete', default=True) + def delcheck(self, pos): + symbol1 = self.text.get(pos + '-1c', pos) + symbol2 = self.text.get(pos, pos + '+1c') + + return self.mutual_delete and symbol1 in self.closers \ + and self.closers[symbol1] == symbol2 + + def back_event(self, event): + pos = self.text.index('insert') + if self.delcheck(pos): + self.text.delete(pos, pos + '+1c') + def delete_event(self, event): + pos = self.text.index('insert') + if self.delcheck(pos): + self.text.delete(pos + '-1c', pos) + def p_open_event(self, event): if self.paren_close: - closer = {'(': ')', '[': ']', '{': '}', "'":"'", '"':'"'}[event.char] pos = self.text.index('insert') - self.text.insert(pos, closer) + self.text.insert(pos, self.closers[event.char]) self.text.mark_set('insert', pos) def p_close_event(self, event): diff --git a/Lib/idlelib/config-extensions.def b/Lib/idlelib/config-extensions.def index 0808820927e3e2..dd27e5a9470dad 100644 --- a/Lib/idlelib/config-extensions.def +++ b/Lib/idlelib/config-extensions.def @@ -69,7 +69,10 @@ enable = True paren_close = True tick_close = True skip_closures = True +mutual_delete = True [ParenClose_bindings] p-open = -t-open = +t-open = p-close = +back = +delete = diff --git a/Lib/idlelib/idle_test/test_parenclose.py b/Lib/idlelib/idle_test/test_parenclose.py index 3bb73f84dfb28f..5e1830a98de46b 100644 --- a/Lib/idlelib/idle_test/test_parenclose.py +++ b/Lib/idlelib/idle_test/test_parenclose.py @@ -18,46 +18,63 @@ def test_parenClose(self): p_open = p.p_open_event p_close = p.p_close_event t_open = p.t_open_event - for paren_close_set, tick_close_set, skip_closures_set, \ + back = p.back_event + delete = p.delete_event + for paren_close_set, tick_close_set, skip_closures_set, mutual_set, \ insert, func, pos, char, result, posend in [ - (1, 1, 1, 'def abuse', p_open, '1.9', '(', + (1, 1, 1, 1, 'def abuse', p_open, '1.9', '(', 'def abuse()', '1.10'), - (1, 1, 1, 'spam = ', p_open, '1.7', '[', + (1, 1, 1, 1, 'spam = ', p_open, '1.7', '[', 'spam = []', '1.8'), - (1, 1, 1, 'eggs = ', p_open, '1.7', '{', + (1, 1, 1, 1, 'eggs = ', p_open, '1.7', '{', 'eggs = {}', '1.8'), - (1, 1, 1, 'def abuse2()', p_close, '1.11', ')', + (1, 1, 1, 1, 'def abuse2()', p_close, '1.11', ')', 'def abuse2()', '1.12'), - (1, 1, 1, 'spam2 = []', p_close, '1.9', ']', + (1, 1, 1, 1, 'spam2 = []', p_close, '1.9', ']', 'spam2 = []', '1.10'), - (1, 1, 1, 'eggs2 = {}', p_close, '1.9', '}', + (1, 1, 1, 1, 'eggs2 = {}', p_close, '1.9', '}', 'eggs2 = {}', '1.10'), - (1, 1, 1, "color = ", t_open, '1.8', "'", + (1, 1, 1, 1, "color = ", t_open, '1.8', "'", "color = ''", '1.9'), - (1, 1, 1, "velocity = ", t_open, '1.11', '"', + (1, 1, 1, 1, "velocity = ", t_open, '1.11', '"', 'velocity = ""', '1.12'), - (1, 1, 1, "more_spam = ''", t_open, '1.14', "'", + (1, 1, 1, 1, "more_spam = ''", t_open, '1.14', "'", "more_spam = ''''''", '1.15'), - (1, 1, 1, 'more_eggs = ""', t_open, '1.14', '"', + (1, 1, 1, 1, 'more_eggs = ""', t_open, '1.14', '"', 'more_eggs = """"""', '1.15'), - (1, 1, 1, "more_spam2 = ''''''", t_open, '1.16', "'", + (1, 1, 1, 1, "more_spam2 = ''''''", t_open, '1.16', "'", "more_spam2 = ''''''", '1.17'), - (1, 1, 1, 'more_eggs2 = """"""', t_open, '1.16', '"', + (1, 1, 1, 1, 'more_eggs2 = """"""', t_open, '1.16', '"', 'more_eggs2 = """"""', '1.17'), - (0, 1, 1, 'no_spam = ', p_open, '1.10', '(', + (0, 1, 1, 1, 'no_spam = ', p_open, '1.10', '(', 'no_spam = (', '1.11'), - (1, 0, 1, 'no_velocity = ', t_open, '1.14', "'", + (1, 0, 1, 1, 'no_velocity = ', t_open, '1.14', "'", "no_velocity = '", '1.15'), - (1, 0, 1, 'no_more_eggs = ""', t_open, '1.17', '"', + (1, 0, 1, 1, 'no_more_eggs = ""', t_open, '1.17', '"', 'no_more_eggs = """', '1.18'), - (1, 1, 0, 'tinny = []', p_close, '1.9', ']', + (1, 1, 0, 1, 'tinny = []', p_close, '1.9', ']', 'tinny = []]', '1.10'), - (1, 1, 0, 'gone = ""', t_open, '1.8', '"', - 'gone = """"', '1.9')]: + (1, 1, 0, 1, 'gone = ""', t_open, '1.8', '"', + 'gone = """"', '1.9'), + (0, 0, 0, 1, 'nurb = ""', back, '1.8', '', + 'nurb = "', '1.8'), + (0, 0, 0, 0, "honk = ''", back, '1.8', '', + "honk = ''", '1.8'), + (0, 0, 0, 1, 'chiz = []', delete, '1.8', '', + 'chiz = ]', '1.7'), + (0, 0, 0, 1, 'pink = {}', back, '1.8', '', + 'pink = {', '1.8') + + ]: + # The actual delete and backspace events do not occur in this + # test. We are checking to make sure everything is correct before + # they would delete or backspace. + # Reset text and self. p.paren_close = paren_close_set p.tick_close = tick_close_set p.skip_closures = skip_closures_set + p.mutual_delete = mutual_set p.text.delete('1.0', 'end') # Write text, move to current position, write character. p.text.insert('1.0', insert) From 6d2fa269df4ea0a9d581d9a20435f8606b0d9186 Mon Sep 17 00:00:00 2001 From: wohlganger Date: Wed, 13 Sep 2017 11:54:32 -0500 Subject: [PATCH 57/64] PEP8 fixes. --- Lib/idlelib/ParenClose.py | 33 ++++++++++++++---------- Lib/idlelib/idle_test/test_parenclose.py | 10 +++---- 2 files changed, 24 insertions(+), 19 deletions(-) diff --git a/Lib/idlelib/ParenClose.py b/Lib/idlelib/ParenClose.py index 526ace85d8ef60..d4dbc79a1ea03b 100644 --- a/Lib/idlelib/ParenClose.py +++ b/Lib/idlelib/ParenClose.py @@ -15,7 +15,7 @@ enable = True paren_close = True tick_close = True - skip_closures = True + skip_closures = True mutual_delete = True [ParenClose_cfgBindings] p-open = @@ -39,29 +39,33 @@ class ParenClose: deleted before the new one is typed, effectively skipping over the closure. ''' - closers = {'(': ')', '[': ']', '{': '}', "'":"'", '"':'"'} - + closers = {'(': ')', '[': ']', '{': '}', "'": "'", '"': '"'} + def __init__(self, editwin=None): # Setting default to none makes testing easier. if editwin: self.text = editwin.text else: - self.text=None + self.text = None self.paren_close = idleConf.GetOption( - 'extensions', 'ParenClose', 'paren_close', type = bool, default=True) + 'extensions', 'ParenClose', 'paren_close', + type=bool, default=True) self.tick_close = idleConf.GetOption( - 'extensions', 'ParenClose', 'tick_close', type = bool, default=True) + 'extensions', 'ParenClose', 'tick_close', + type=bool, default=True) self.skip_closures = idleConf.GetOption( - 'extensions', 'ParenClose', 'skip_closures', type = bool, default=True) + 'extensions', 'ParenClose', 'skip_closures', + type=bool, default=True) self.mutual_delete = idleConf.GetOption( - 'extensions', 'ParenClose', 'mutual_delete', default=True) + 'extensions', 'ParenClose', 'mutual_delete', + type=bool, default=True) + def delcheck(self, pos): symbol1 = self.text.get(pos + '-1c', pos) symbol2 = self.text.get(pos, pos + '+1c') - - return self.mutual_delete and symbol1 in self.closers \ - and self.closers[symbol1] == symbol2 - + return (self.mutual_delete and symbol1 in self.closers + and self.closers[symbol1] == symbol2) + def back_event(self, event): pos = self.text.index('insert') if self.delcheck(pos): @@ -71,13 +75,13 @@ def delete_event(self, event): pos = self.text.index('insert') if self.delcheck(pos): self.text.delete(pos + '-1c', pos) - + def p_open_event(self, event): if self.paren_close: pos = self.text.index('insert') self.text.insert(pos, self.closers[event.char]) self.text.mark_set('insert', pos) - + def p_close_event(self, event): pos = self.text.index('insert') if self.skip_closures \ @@ -98,6 +102,7 @@ def t_open_event(self, event): elif not self.p_close_event(event): self.p_open_event(event) + if __name__ == '__main__': import unittest unittest.main('idlelib.idle_test.test_parenclose', verbosity=2) diff --git a/Lib/idlelib/idle_test/test_parenclose.py b/Lib/idlelib/idle_test/test_parenclose.py index 5e1830a98de46b..ee6cd34d27995f 100644 --- a/Lib/idlelib/idle_test/test_parenclose.py +++ b/Lib/idlelib/idle_test/test_parenclose.py @@ -2,7 +2,6 @@ ''' from idlelib.ParenClose import ParenClose import unittest -from unittest.mock import Mock from tkinter import Text # mocktk doesn't do text position addition correctly @@ -66,10 +65,10 @@ def test_parenClose(self): 'pink = {', '1.8') ]: - # The actual delete and backspace events do not occur in this - # test. We are checking to make sure everything is correct before - # they would delete or backspace. - + # The actual delete and backspace events do not occur in + # this test. We are checking to make sure everything is + # correct before they would delete or backspace. + # Reset text and self. p.paren_close = paren_close_set p.tick_close = tick_close_set @@ -89,5 +88,6 @@ def test_parenClose(self): actual = p.text.get('1.0', 'end') self.assertTupleEqual((actual, pos), (result+'\n', posend)) + if __name__ == '__main__': unittest.main(verbosity=2) From 15f8d82070912700decbc82c94e4bf28d1473b17 Mon Sep 17 00:00:00 2001 From: wohlganger Date: Wed, 13 Sep 2017 16:22:56 -0500 Subject: [PATCH 58/64] replace type option of bool with 'bool' idleConf expects a string bool, not type bool. whoops. --- Lib/idlelib/ParenClose.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Lib/idlelib/ParenClose.py b/Lib/idlelib/ParenClose.py index d4dbc79a1ea03b..d5199f5e61edaa 100644 --- a/Lib/idlelib/ParenClose.py +++ b/Lib/idlelib/ParenClose.py @@ -49,16 +49,16 @@ def __init__(self, editwin=None): self.text = None self.paren_close = idleConf.GetOption( 'extensions', 'ParenClose', 'paren_close', - type=bool, default=True) + type='bool', default=True) self.tick_close = idleConf.GetOption( 'extensions', 'ParenClose', 'tick_close', - type=bool, default=True) + type='bool', default=True) self.skip_closures = idleConf.GetOption( 'extensions', 'ParenClose', 'skip_closures', - type=bool, default=True) + type='bool', default=True) self.mutual_delete = idleConf.GetOption( 'extensions', 'ParenClose', 'mutual_delete', - type=bool, default=True) + type='bool', default=True) def delcheck(self, pos): symbol1 = self.text.get(pos + '-1c', pos) From caa9240c6593eadb661edb0fc6a24e8f74850bf1 Mon Sep 17 00:00:00 2001 From: wohlganger Date: Fri, 15 Sep 2017 15:56:07 -0500 Subject: [PATCH 59/64] whitespace --- Lib/idlelib/idle_test/test_parenclose.py | 1 - 1 file changed, 1 deletion(-) diff --git a/Lib/idlelib/idle_test/test_parenclose.py b/Lib/idlelib/idle_test/test_parenclose.py index ee6cd34d27995f..8eda69e40f5792 100644 --- a/Lib/idlelib/idle_test/test_parenclose.py +++ b/Lib/idlelib/idle_test/test_parenclose.py @@ -63,7 +63,6 @@ def test_parenClose(self): 'chiz = ]', '1.7'), (0, 0, 0, 1, 'pink = {}', back, '1.8', '', 'pink = {', '1.8') - ]: # The actual delete and backspace events do not occur in # this test. We are checking to make sure everything is From 21f448234496bfa1cbd0b0807aa25ef5920adbbc Mon Sep 17 00:00:00 2001 From: wohlganger Date: Fri, 15 Sep 2017 16:12:31 -0500 Subject: [PATCH 60/64] whitespace fixed with reindent.py --- Lib/idlelib/idle_test/test_parenclose.py | 42 ++++++++++++------------ 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/Lib/idlelib/idle_test/test_parenclose.py b/Lib/idlelib/idle_test/test_parenclose.py index 8eda69e40f5792..dd932db112627f 100644 --- a/Lib/idlelib/idle_test/test_parenclose.py +++ b/Lib/idlelib/idle_test/test_parenclose.py @@ -64,28 +64,28 @@ def test_parenClose(self): (0, 0, 0, 1, 'pink = {}', back, '1.8', '', 'pink = {', '1.8') ]: - # The actual delete and backspace events do not occur in - # this test. We are checking to make sure everything is - # correct before they would delete or backspace. + # The actual delete and backspace events do not occur in + # this test. We are checking to make sure everything is + # correct before they would delete or backspace. - # Reset text and self. - p.paren_close = paren_close_set - p.tick_close = tick_close_set - p.skip_closures = skip_closures_set - p.mutual_delete = mutual_set - p.text.delete('1.0', 'end') - # Write text, move to current position, write character. - p.text.insert('1.0', insert) - p.text.mark_set('insert', pos) - event = MockEvent(char) - func(event) - pos = p.text.index('insert') - p.text.insert(pos, char) - # Checking position and text at the same time - # makes it easier to spot where errors occur. - pos = p.text.index('insert') - actual = p.text.get('1.0', 'end') - self.assertTupleEqual((actual, pos), (result+'\n', posend)) + # Reset text and self. + p.paren_close = paren_close_set + p.tick_close = tick_close_set + p.skip_closures = skip_closures_set + p.mutual_delete = mutual_set + p.text.delete('1.0', 'end') + # Write text, move to current position, write character. + p.text.insert('1.0', insert) + p.text.mark_set('insert', pos) + event = MockEvent(char) + func(event) + pos = p.text.index('insert') + p.text.insert(pos, char) + # Checking position and text at the same time + # makes it easier to spot where errors occur. + pos = p.text.index('insert') + actual = p.text.get('1.0', 'end') + self.assertTupleEqual((actual, pos), (result+'\n', posend)) if __name__ == '__main__': From 25ba0f85cc6715030e1268bee3281e475e52e003 Mon Sep 17 00:00:00 2001 From: wohlganger Date: Fri, 15 Sep 2017 16:51:51 -0500 Subject: [PATCH 61/64] trying to fix Tk for travis testing parenclose --- Lib/idlelib/idle_test/test_parenclose.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/idlelib/idle_test/test_parenclose.py b/Lib/idlelib/idle_test/test_parenclose.py index dd932db112627f..87d74df4345d6b 100644 --- a/Lib/idlelib/idle_test/test_parenclose.py +++ b/Lib/idlelib/idle_test/test_parenclose.py @@ -2,7 +2,7 @@ ''' from idlelib.ParenClose import ParenClose import unittest -from tkinter import Text # mocktk doesn't do text position addition correctly +from tkinter import Text, Tk class MockEvent(object): @@ -13,7 +13,7 @@ def __init__(self, char): class ParenCloseTest(unittest.TestCase): def test_parenClose(self): p = ParenClose() - p.text = Text() + p.text = Text(Tk()) p_open = p.p_open_event p_close = p.p_close_event t_open = p.t_open_event From a4cd0e3016cde50c2de1710e9d64d1f8938341db Mon Sep 17 00:00:00 2001 From: wohlganger Date: Mon, 18 Sep 2017 11:20:10 -0500 Subject: [PATCH 62/64] Set parenclose test as gui required --- Lib/idlelib/idle_test/test_parenclose.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Lib/idlelib/idle_test/test_parenclose.py b/Lib/idlelib/idle_test/test_parenclose.py index 87d74df4345d6b..109a618272c943 100644 --- a/Lib/idlelib/idle_test/test_parenclose.py +++ b/Lib/idlelib/idle_test/test_parenclose.py @@ -2,8 +2,10 @@ ''' from idlelib.ParenClose import ParenClose import unittest -from tkinter import Text, Tk - +# Can't use mock text; it doesn't handle positioning correctly. +from tkinter import Text +from test.support import requires +requires('gui') class MockEvent(object): def __init__(self, char): @@ -13,7 +15,7 @@ def __init__(self, char): class ParenCloseTest(unittest.TestCase): def test_parenClose(self): p = ParenClose() - p.text = Text(Tk()) + p.text = Text() p_open = p.p_open_event p_close = p.p_close_event t_open = p.t_open_event From cffa9d64f884e619d1f6213f878712fba53bdfe8 Mon Sep 17 00:00:00 2001 From: wohlganger Date: Wed, 20 Sep 2017 09:16:36 -0500 Subject: [PATCH 63/64] Added option to distinguish between mutual delete and mutual backspace In use, it was irritating for me to have both perform the action. This way, users can pick one or both or none. --- Lib/idlelib/ParenClose.py | 21 ++++++---- Lib/idlelib/config-extensions.def | 1 + Lib/idlelib/idle_test/test_parenclose.py | 53 +++++++++++++----------- 3 files changed, 43 insertions(+), 32 deletions(-) diff --git a/Lib/idlelib/ParenClose.py b/Lib/idlelib/ParenClose.py index d5199f5e61edaa..10beb626c4aefd 100644 --- a/Lib/idlelib/ParenClose.py +++ b/Lib/idlelib/ParenClose.py @@ -37,6 +37,8 @@ class ParenClose: produce the closing symbols. If skip_closures is True, then when a closure symbol is typed and the same one is to the right of it, that symbols is deleted before the new one is typed, effectively skipping over the closure. + If the cursor is between two closures and mutual_delete or mutual_backspace + is True, when the respective command is given, both closures are deleted. ''' closers = {'(': ')', '[': ']', '{': '}', "'": "'", '"': '"'} @@ -59,22 +61,27 @@ def __init__(self, editwin=None): self.mutual_delete = idleConf.GetOption( 'extensions', 'ParenClose', 'mutual_delete', type='bool', default=True) + self.mutual_backspace = idleConf.GetOption( + 'extensions', 'ParenClose', 'mutual_backspace', + type='bool', default=True) def delcheck(self, pos): symbol1 = self.text.get(pos + '-1c', pos) symbol2 = self.text.get(pos, pos + '+1c') - return (self.mutual_delete and symbol1 in self.closers + return (symbol1 in self.closers and self.closers[symbol1] == symbol2) def back_event(self, event): - pos = self.text.index('insert') - if self.delcheck(pos): - self.text.delete(pos, pos + '+1c') + if self.mutual_backspace: + pos = self.text.index('insert') + if self.delcheck(pos): + self.text.delete(pos, pos + '+1c') def delete_event(self, event): - pos = self.text.index('insert') - if self.delcheck(pos): - self.text.delete(pos + '-1c', pos) + if self.mutual_delete: + pos = self.text.index('insert') + if self.delcheck(pos): + self.text.delete(pos + '-1c', pos) def p_open_event(self, event): if self.paren_close: diff --git a/Lib/idlelib/config-extensions.def b/Lib/idlelib/config-extensions.def index 5dbe58bc679324..56806d012e5230 100644 --- a/Lib/idlelib/config-extensions.def +++ b/Lib/idlelib/config-extensions.def @@ -70,6 +70,7 @@ paren_close = True tick_close = True skip_closures = True mutual_delete = True +mutual_backspace = True [ParenClose_bindings] p-open = t-open = diff --git a/Lib/idlelib/idle_test/test_parenclose.py b/Lib/idlelib/idle_test/test_parenclose.py index 109a618272c943..fec67d7da83086 100644 --- a/Lib/idlelib/idle_test/test_parenclose.py +++ b/Lib/idlelib/idle_test/test_parenclose.py @@ -21,49 +21,51 @@ def test_parenClose(self): t_open = p.t_open_event back = p.back_event delete = p.delete_event - for paren_close_set, tick_close_set, skip_closures_set, mutual_set, \ - insert, func, pos, char, result, posend in [ - (1, 1, 1, 1, 'def abuse', p_open, '1.9', '(', + for paren_close_set, tick_close_set, skip_closures_set, mutual_b_set, \ + mutual_d_set, insert, func, pos, char, result, posend in [ + (1, 1, 1, 1, 1, 'def abuse', p_open, '1.9', '(', 'def abuse()', '1.10'), - (1, 1, 1, 1, 'spam = ', p_open, '1.7', '[', + (1, 1, 1, 1, 1, 'spam = ', p_open, '1.7', '[', 'spam = []', '1.8'), - (1, 1, 1, 1, 'eggs = ', p_open, '1.7', '{', + (1, 1, 1, 1, 1, 'eggs = ', p_open, '1.7', '{', 'eggs = {}', '1.8'), - (1, 1, 1, 1, 'def abuse2()', p_close, '1.11', ')', + (1, 1, 1, 1, 1, 'def abuse2()', p_close, '1.11', ')', 'def abuse2()', '1.12'), - (1, 1, 1, 1, 'spam2 = []', p_close, '1.9', ']', + (1, 1, 1, 1, 1, 'spam2 = []', p_close, '1.9', ']', 'spam2 = []', '1.10'), - (1, 1, 1, 1, 'eggs2 = {}', p_close, '1.9', '}', + (1, 1, 1, 1, 1, 'eggs2 = {}', p_close, '1.9', '}', 'eggs2 = {}', '1.10'), - (1, 1, 1, 1, "color = ", t_open, '1.8', "'", + (1, 1, 1, 1, 1, "color = ", t_open, '1.8', "'", "color = ''", '1.9'), - (1, 1, 1, 1, "velocity = ", t_open, '1.11', '"', + (1, 1, 1, 1, 1, "velocity = ", t_open, '1.11', '"', 'velocity = ""', '1.12'), - (1, 1, 1, 1, "more_spam = ''", t_open, '1.14', "'", + (1, 1, 1, 1, 1, "more_spam = ''", t_open, '1.14', "'", "more_spam = ''''''", '1.15'), - (1, 1, 1, 1, 'more_eggs = ""', t_open, '1.14', '"', + (1, 1, 1, 1, 1, 'more_eggs = ""', t_open, '1.14', '"', 'more_eggs = """"""', '1.15'), - (1, 1, 1, 1, "more_spam2 = ''''''", t_open, '1.16', "'", + (1, 1, 1, 1, 1, "more_spam2 = ''''''", t_open, '1.16', "'", "more_spam2 = ''''''", '1.17'), - (1, 1, 1, 1, 'more_eggs2 = """"""', t_open, '1.16', '"', + (1, 1, 1, 1, 1, 'more_eggs2 = """"""', t_open, '1.16', '"', 'more_eggs2 = """"""', '1.17'), - (0, 1, 1, 1, 'no_spam = ', p_open, '1.10', '(', + (0, 1, 1, 1, 1, 'no_spam = ', p_open, '1.10', '(', 'no_spam = (', '1.11'), - (1, 0, 1, 1, 'no_velocity = ', t_open, '1.14', "'", + (1, 0, 1, 1, 1, 'no_velocity = ', t_open, '1.14', "'", "no_velocity = '", '1.15'), - (1, 0, 1, 1, 'no_more_eggs = ""', t_open, '1.17', '"', + (1, 0, 1, 1, 1, 'no_more_eggs = ""', t_open, '1.17', '"', 'no_more_eggs = """', '1.18'), - (1, 1, 0, 1, 'tinny = []', p_close, '1.9', ']', + (1, 1, 0, 1, 1, 'tinny = []', p_close, '1.9', ']', 'tinny = []]', '1.10'), - (1, 1, 0, 1, 'gone = ""', t_open, '1.8', '"', + (1, 1, 0, 1, 1, 'gone = ""', t_open, '1.8', '"', 'gone = """"', '1.9'), - (0, 0, 0, 1, 'nurb = ""', back, '1.8', '', + (0, 0, 0, 0, 1, "honk = []", back, '1.8', '', + "honk = []", '1.8'), + (0, 0, 0, 1, 0, "henk = ()", delete, '1.8', '', + "henk = ()", '1.8'), + (0, 0, 0, 1, 1, 'nurb = ""', back, '1.8', '', 'nurb = "', '1.8'), - (0, 0, 0, 0, "honk = ''", back, '1.8', '', - "honk = ''", '1.8'), - (0, 0, 0, 1, 'chiz = []', delete, '1.8', '', + (0, 0, 0, 1, 1, 'chiz = []', delete, '1.8', '', 'chiz = ]', '1.7'), - (0, 0, 0, 1, 'pink = {}', back, '1.8', '', + (0, 0, 0, 1, 1, 'pink = {}', back, '1.8', '', 'pink = {', '1.8') ]: # The actual delete and backspace events do not occur in @@ -74,7 +76,8 @@ def test_parenClose(self): p.paren_close = paren_close_set p.tick_close = tick_close_set p.skip_closures = skip_closures_set - p.mutual_delete = mutual_set + p.mutual_delete = mutual_d_set + p.mutual_backspace = mutual_b_set p.text.delete('1.0', 'end') # Write text, move to current position, write character. p.text.insert('1.0', insert) From 9aaa395e155414487864facc1f94ef9c09590d38 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Fri, 6 Apr 2018 01:26:36 -0400 Subject: [PATCH 64/64] Update ParenClose.py Trigger a retest to see if CI produces same odd failures as on another PR. --- Lib/idlelib/ParenClose.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Lib/idlelib/ParenClose.py b/Lib/idlelib/ParenClose.py index 10beb626c4aefd..c4e2de419da999 100644 --- a/Lib/idlelib/ParenClose.py +++ b/Lib/idlelib/ParenClose.py @@ -4,11 +4,6 @@ When you hit left paren or tick, automatically creates the closing paren or tick. -Author: Charles M. Wohlganger - charles.wohlganger@gmail.com - -Last Updated: 13-Aug-2017 by Charles Wohlganger - Add to the end of config-extensions.def : [ParenClose]