From cfaacb2cdea34b96a3d1c5701bbe22bf129446d6 Mon Sep 17 00:00:00 2001 From: Cheryl Sabella Date: Fri, 12 Jun 2020 20:06:26 -0400 Subject: [PATCH] bpo-39666: Refactor common code between hyperparser and editor --- Lib/idlelib/editor.py | 31 +-------- Lib/idlelib/hyperparser.py | 79 ++++++++++++---------- Lib/idlelib/idle_test/test_autocomplete.py | 3 + Lib/idlelib/idle_test/test_hyperparser.py | 2 +- Lib/idlelib/idle_test/test_parenmatch.py | 3 + 5 files changed, 51 insertions(+), 67 deletions(-) diff --git a/Lib/idlelib/editor.py b/Lib/idlelib/editor.py index a178eaf93c0..22d9bb3b697 100644 --- a/Lib/idlelib/editor.py +++ b/Lib/idlelib/editor.py @@ -20,6 +20,7 @@ from idlelib import configdialog from idlelib import grep from idlelib import help from idlelib import help_about +from idlelib import hyperparser from idlelib import macosx from idlelib.multicall import MultiCallCreator from idlelib import pyparse @@ -248,13 +249,6 @@ class EditorWindow(object): idleConf.blink_off_time = self.text['insertofftime'] self.update_cursor_blink() - # When searching backwards for a reliable place to begin parsing, - # first start num_context_lines[0] lines back, then - # num_context_lines[1] lines back if that didn't work, and so on. - # The last value should be huge (larger than the # of lines in a - # conceivable file). - # Making the initial values larger slows things down more often. - self.num_context_lines = 50, 500, 5000000 self.per = per = self.Percolator(text) self.undo = undo = self.UndoDelegator() per.insertfilter(undo) @@ -1396,28 +1390,7 @@ class EditorWindow(object): # Adjust indentation for continuations and block open/close. # First need to find the last statement. - lno = index2line(text.index('insert')) - y = pyparse.Parser(self.indentwidth, self.tabwidth) - if not self.prompt_last_line: - for context in self.num_context_lines: - startat = max(lno - context, 1) - startatindex = repr(startat) + ".0" - rawtext = text.get(startatindex, "insert") - y.set_code(rawtext) - bod = y.find_good_parse_start( - self._build_char_in_string_func(startatindex)) - if bod is not None or startat == 1: - break - y.set_lo(bod or 0) - else: - r = text.tag_prevrange("console", "insert") - if r: - startatindex = r[1] - else: - startatindex = "1.0" - rawtext = text.get(startatindex, "insert") - y.set_code(rawtext) - y.set_lo(0) + y = hyperparser.parser(self) c = y.get_continuation_type() if c != pyparse.C_NONE: diff --git a/Lib/idlelib/hyperparser.py b/Lib/idlelib/hyperparser.py index 77baca782b3..bd8e6da0061 100644 --- a/Lib/idlelib/hyperparser.py +++ b/Lib/idlelib/hyperparser.py @@ -22,54 +22,59 @@ _IS_ASCII_ID_FIRST_CHAR = \ [(chr(x) in _ASCII_ID_FIRST_CHARS) for x in range(128)] +def parser(editwin, index="insert", lineend=""): + "Create a PyParse instance and find the last statement." + text = editwin.text + + p = pyparse.Parser(editwin.indentwidth, editwin.tabwidth) + lineno = editwin.getlineno(index) + stopatindex = index if not lineend else f"{lineno}.end" + + if editwin.prompt_last_line: + r = text.tag_prevrange("console", index) + startatindex = r[1] if r else "1.0" + p.set_code(text.get(startatindex, stopatindex) + lineend) + return p + + # When searching backwards for a reliable place to begin parsing, + # first start num_context_lines[0] lines back, then + # num_context_lines[1] lines back if that didn't work, and so on. + # The last value should be huge (larger than the # of lines in a + # conceivable file). + # Making the initial values larger slows things down more often. + num_context_lines = 50, 500, 5000000 + for context in num_context_lines: + startat = max(lineno - context, 1) + startatindex = f"{startat}.0" + p.set_code(text.get(startatindex, stopatindex) + lineend) + bod = p.find_good_parse_start( + editwin._build_char_in_string_func(startatindex)) + if bod is not None or startat == 1: + break + p.set_lo(bod or 0) + return p + + class HyperParser: def __init__(self, editwin, index): "To initialize, analyze the surroundings of the given index." self.editwin = editwin - self.text = text = editwin.text + self.text = editwin.text - parser = pyparse.Parser(editwin.indentwidth, editwin.tabwidth) - - def index2line(index): - return int(float(index)) - lno = index2line(text.index(index)) - - if not editwin.prompt_last_line: - for context in editwin.num_context_lines: - startat = max(lno - context, 1) - startatindex = repr(startat) + ".0" - stopatindex = "%d.end" % lno - # We add the newline because PyParse requires a newline - # at end. We add a space so that index won't be at end - # of line, so that its status will be the same as the - # char before it, if should. - parser.set_code(text.get(startatindex, stopatindex)+' \n') - bod = parser.find_good_parse_start( - editwin._build_char_in_string_func(startatindex)) - if bod is not None or startat == 1: - break - parser.set_lo(bod or 0) - else: - r = text.tag_prevrange("console", index) - if r: - startatindex = r[1] - else: - startatindex = "1.0" - stopatindex = "%d.end" % lno - # We add the newline because PyParse requires it. We add a - # space so that index won't be at end of line, so that its - # status will be the same as the char before it, if should. - parser.set_code(text.get(startatindex, stopatindex)+' \n') - parser.set_lo(0) + # We add the newline because PyParse requires a newline + # at end. We add a space so that index won't be at end + # of line, so that its status will be the same as the + # char before it. + p = parser(editwin, index=index, lineend=" \n") # We want what the parser has, minus the last newline and space. - self.rawtext = parser.code[:-2] + self.rawtext = p.code[:-2] # Parser.code apparently preserves the statement we are in, so # that stopatindex can be used to synchronize the string with # the text box indices. - self.stopatindex = stopatindex - self.bracketing = parser.get_last_stmt_bracketing() + self.stopatindex = f"{editwin.getlineno(index)}.end" + self.bracketing = p.get_last_stmt_bracketing() # find which pairs of bracketing are openers. These always # correspond to a character of rawtext. self.isopener = [i>0 and self.bracketing[i][1] > diff --git a/Lib/idlelib/idle_test/test_autocomplete.py b/Lib/idlelib/idle_test/test_autocomplete.py index 1841495fcf1..35e3e10b1dc 100644 --- a/Lib/idlelib/idle_test/test_autocomplete.py +++ b/Lib/idlelib/idle_test/test_autocomplete.py @@ -21,6 +21,9 @@ class DummyEditwin: self.tabwidth = 8 self.prompt_last_line = '>>>' # Currently not used by autocomplete. + def getlineno(self, index): + return int(float(self.text.index(index))) + class AutoCompleteTest(unittest.TestCase): diff --git a/Lib/idlelib/idle_test/test_hyperparser.py b/Lib/idlelib/idle_test/test_hyperparser.py index 343843c4166..439829a9d3f 100644 --- a/Lib/idlelib/idle_test/test_hyperparser.py +++ b/Lib/idlelib/idle_test/test_hyperparser.py @@ -12,10 +12,10 @@ class DummyEditwin: self.indentwidth = 8 self.tabwidth = 8 self.prompt_last_line = '>>>' - self.num_context_lines = 50, 500, 1000 _build_char_in_string_func = EditorWindow._build_char_in_string_func is_char_in_string = EditorWindow.is_char_in_string + getlineno = EditorWindow.getlineno class HyperParserTest(unittest.TestCase): diff --git a/Lib/idlelib/idle_test/test_parenmatch.py b/Lib/idlelib/idle_test/test_parenmatch.py index 4a41d8433d5..9af7833750a 100644 --- a/Lib/idlelib/idle_test/test_parenmatch.py +++ b/Lib/idlelib/idle_test/test_parenmatch.py @@ -19,6 +19,9 @@ class DummyEditwin: self.tabwidth = 8 self.prompt_last_line = '>>>' # Currently not used by parenmatch. + def getlineno(self, index): + return int(float(self.text.index(index))) + class ParenMatchTest(unittest.TestCase):