diff --git a/Lib/idlelib/editor.py b/Lib/idlelib/editor.py index f02498da3521f1..24b1ffc67975a5 100644 --- a/Lib/idlelib/editor.py +++ b/Lib/idlelib/editor.py @@ -53,9 +53,8 @@ class EditorWindow(object): from idlelib.autoexpand import AutoExpand from idlelib.calltip import Calltip from idlelib.codecontext import CodeContext - from idlelib.format import FormatParagraph, FormatRegion + from idlelib.format import FormatParagraph, FormatRegion, Indents, Rstrip from idlelib.parenmatch import ParenMatch - from idlelib.rstrip import Rstrip from idlelib.squeezer import Squeezer from idlelib.zoomheight import ZoomHeight @@ -173,14 +172,15 @@ def __init__(self, flist=None, filename=None, key=None, root=None): text.bind("<>",self.newline_and_indent_event) text.bind("<>",self.smart_indent_event) self.fregion = fregion = self.FormatRegion(self) + # self.fregion used in smart_indent_event to access indent_region. text.bind("<>", fregion.indent_region_event) text.bind("<>", fregion.dedent_region_event) text.bind("<>", fregion.comment_region_event) text.bind("<>", fregion.uncomment_region_event) text.bind("<>", fregion.tabify_region_event) text.bind("<>", fregion.untabify_region_event) - text.bind("<>", self.toggle_tabs_event) - text.bind("<>",self.change_indentwidth_event) + text.bind("<>", self.Indents.toggle_tabs_event) + text.bind("<>", self.Indents.change_indentwidth_event) text.bind("", self.move_at_edge_if_selection(0)) text.bind("", self.move_at_edge_if_selection(1)) text.bind("<>", self.del_word_left) @@ -1424,20 +1424,6 @@ def inner(offset, _startindex=startindex, return _icis(_startindex + "+%dc" % offset) return inner - def toggle_tabs_event(self, event): - if self.askyesno( - "Toggle tabs", - "Turn tabs " + ("on", "off")[self.usetabs] + - "?\nIndent width " + - ("will be", "remains at")[self.usetabs] + " 8." + - "\n Note: a tab is always 8 columns", - parent=self.text): - self.usetabs = not self.usetabs - # Try to prevent inconsistent indentation. - # User must change indent width manually after using tabs. - self.indentwidth = 8 - return "break" - # XXX this isn't bound to anything -- see tabwidth comments ## def change_tabwidth_event(self, event): ## new = self._asktabwidth() @@ -1446,18 +1432,6 @@ def toggle_tabs_event(self, event): ## self.set_indentation_params(0, guess=0) ## return "break" - def change_indentwidth_event(self, event): - new = self.askinteger( - "Indent width", - "New indent width (2-16)\n(Always use 8 when using tabs)", - parent=self.text, - initialvalue=self.indentwidth, - minvalue=2, - maxvalue=16) - if new and new != self.indentwidth and not self.usetabs: - self.indentwidth = new - return "break" - # Make string that displays as n leading blanks. def _make_blanks(self, n): diff --git a/Lib/idlelib/format.py b/Lib/idlelib/format.py index e11ca3a9d26faa..bced4c1770eb01 100644 --- a/Lib/idlelib/format.py +++ b/Lib/idlelib/format.py @@ -6,6 +6,7 @@ File renamed from paragraph.py with functions added from editor.py. """ import re +from tkinter.messagebox import askyesno from tkinter.simpledialog import askinteger from idlelib.config import idleConf @@ -195,7 +196,7 @@ def get_comment_header(line): return m.group(1) -# Copy from editor.py; importing it would cause an import cycle. +# Copied from editor.py; importing it would cause an import cycle. _line_indent_re = re.compile(r'[ \t]*') def get_line_indent(line, tabwidth): @@ -209,7 +210,7 @@ def get_line_indent(line, tabwidth): class FormatRegion: - "Format selected text." + "Format selected text (region)." def __init__(self, editwin): self.editwin = editwin @@ -352,6 +353,65 @@ def _asktabwidth(self): maxvalue=16) +# With mixed indents not allowed, these are semi-useless and not unittested. +class Indents: # pragma: no cover + "Change future indents." + + def __init__(self, editwin): + self.editwin = editwin + + def toggle_tabs_event(self, event): + editwin = self.editwin + usetabs = editwin.usetabs + if askyesno( + "Toggle tabs", + "Turn tabs " + ("on", "off")[usetabs] + + "?\nIndent width " + + ("will be", "remains at")[usetabs] + " 8." + + "\n Note: a tab is always 8 columns", + parent=editwin.text): + editwin.usetabs = not usetabs + # Try to prevent inconsistent indentation. + # User must change indent width manually after using tabs. + editwin.indentwidth = 8 + return "break" + + def change_indentwidth_event(self, event): + editwin = self.editwin + new = askinteger( + "Indent width", + "New indent width (2-16)\n(Always use 8 when using tabs)", + parent=editwin.text, + initialvalue=editwin.indentwidth, + minvalue=2, + maxvalue=16) + if new and new != editwin.indentwidth and not editwin.usetabs: + editwin.indentwidth = new + return "break" + + +class Rstrip: # 'Strip Trailing Whitespace" on "Format" menu. + def __init__(self, editwin): + self.editwin = editwin + + def do_rstrip(self, event=None): + text = self.editwin.text + undo = self.editwin.undo + undo.undo_block_start() + + end_line = int(float(text.index('end'))) + for cur in range(1, end_line): + txt = text.get('%i.0' % cur, '%i.end' % cur) + raw = len(txt) + cut = len(txt.rstrip()) + # Since text.delete() marks file as changed, even if not, + # only call it when needed to actually delete something. + if cut < raw: + text.delete('%i.%i' % (cur, cut), '%i.end' % cur) + + undo.undo_block_stop() + + if __name__ == "__main__": from unittest import main main('idlelib.idle_test.test_format', verbosity=2, exit=False) diff --git a/Lib/idlelib/idle_test/test_format.py b/Lib/idlelib/idle_test/test_format.py index a2d27ed69dd1a1..c7b123e9d513af 100644 --- a/Lib/idlelib/idle_test/test_format.py +++ b/Lib/idlelib/idle_test/test_format.py @@ -6,6 +6,7 @@ from test.support import requires from tkinter import Tk, Text from idlelib.editor import EditorWindow +from idlelib.idle_test.mock_idle import Editor as MockEditor class Is_Get_Test(unittest.TestCase): @@ -573,5 +574,50 @@ def test_ask_tabwidth(self, askinteger): self.assertEqual(ask(), 10) +class rstripTest(unittest.TestCase): + + def test_rstrip_line(self): + editor = MockEditor() + text = editor.text + do_rstrip = ft.Rstrip(editor).do_rstrip + eq = self.assertEqual + + do_rstrip() + eq(text.get('1.0', 'insert'), '') + text.insert('1.0', ' ') + do_rstrip() + eq(text.get('1.0', 'insert'), '') + text.insert('1.0', ' \n') + do_rstrip() + eq(text.get('1.0', 'insert'), '\n') + + def test_rstrip_multiple(self): + editor = MockEditor() + # Comment above, uncomment 3 below to test with real Editor & Text. + #from idlelib.editor import EditorWindow as Editor + #from tkinter import Tk + #editor = Editor(root=Tk()) + text = editor.text + do_rstrip = ft.Rstrip(editor).do_rstrip + + original = ( + "Line with an ending tab \n" + "Line ending in 5 spaces \n" + "Linewithnospaces\n" + " indented line\n" + " indented line with trailing space \n" + " ") + stripped = ( + "Line with an ending tab\n" + "Line ending in 5 spaces\n" + "Linewithnospaces\n" + " indented line\n" + " indented line with trailing space\n") + + text.insert('1.0', original) + do_rstrip() + self.assertEqual(text.get('1.0', 'insert'), stripped) + + if __name__ == '__main__': unittest.main(verbosity=2, exit=2) diff --git a/Lib/idlelib/idle_test/test_rstrip.py b/Lib/idlelib/idle_test/test_rstrip.py deleted file mode 100644 index 2bc7c6f035e96b..00000000000000 --- a/Lib/idlelib/idle_test/test_rstrip.py +++ /dev/null @@ -1,53 +0,0 @@ -"Test rstrip, coverage 100%." - -from idlelib import rstrip -import unittest -from idlelib.idle_test.mock_idle import Editor - -class rstripTest(unittest.TestCase): - - def test_rstrip_line(self): - editor = Editor() - text = editor.text - do_rstrip = rstrip.Rstrip(editor).do_rstrip - - do_rstrip() - self.assertEqual(text.get('1.0', 'insert'), '') - text.insert('1.0', ' ') - do_rstrip() - self.assertEqual(text.get('1.0', 'insert'), '') - text.insert('1.0', ' \n') - do_rstrip() - self.assertEqual(text.get('1.0', 'insert'), '\n') - - def test_rstrip_multiple(self): - editor = Editor() - # Comment above, uncomment 3 below to test with real Editor & Text. - #from idlelib.editor import EditorWindow as Editor - #from tkinter import Tk - #editor = Editor(root=Tk()) - text = editor.text - do_rstrip = rstrip.Rstrip(editor).do_rstrip - - original = ( - "Line with an ending tab \n" - "Line ending in 5 spaces \n" - "Linewithnospaces\n" - " indented line\n" - " indented line with trailing space \n" - " ") - stripped = ( - "Line with an ending tab\n" - "Line ending in 5 spaces\n" - "Linewithnospaces\n" - " indented line\n" - " indented line with trailing space\n") - - text.insert('1.0', original) - do_rstrip() - self.assertEqual(text.get('1.0', 'insert'), stripped) - - - -if __name__ == '__main__': - unittest.main(verbosity=2) diff --git a/Lib/idlelib/rstrip.py b/Lib/idlelib/rstrip.py deleted file mode 100644 index f93b5e8fc20021..00000000000000 --- a/Lib/idlelib/rstrip.py +++ /dev/null @@ -1,29 +0,0 @@ -'Provides "Strip trailing whitespace" under the "Format" menu.' - -class Rstrip: - - def __init__(self, editwin): - self.editwin = editwin - - def do_rstrip(self, event=None): - - text = self.editwin.text - undo = self.editwin.undo - - undo.undo_block_start() - - end_line = int(float(text.index('end'))) - for cur in range(1, end_line): - txt = text.get('%i.0' % cur, '%i.end' % cur) - raw = len(txt) - cut = len(txt.rstrip()) - # Since text.delete() marks file as changed, even if not, - # only call it when needed to actually delete something. - if cut < raw: - text.delete('%i.%i' % (cur, cut), '%i.end' % cur) - - undo.undo_block_stop() - -if __name__ == "__main__": - from unittest import main - main('idlelib.idle_test.test_rstrip', verbosity=2,) diff --git a/Misc/NEWS.d/next/IDLE/2019-03-21-08-35-00.bpo-36390.OdDCGk.rst b/Misc/NEWS.d/next/IDLE/2019-03-21-08-35-00.bpo-36390.OdDCGk.rst index fabc75fd1c6eab..74bbda34024998 100644 --- a/Misc/NEWS.d/next/IDLE/2019-03-21-08-35-00.bpo-36390.OdDCGk.rst +++ b/Misc/NEWS.d/next/IDLE/2019-03-21-08-35-00.bpo-36390.OdDCGk.rst @@ -1,2 +1,2 @@ -Rename paragraph.py to format.py and add region formatting methods -from editor.py. Add tests for the latter. +Gather Format menu functions into format.py. Combine +paragraph.py, rstrip.py, and format methods from editor.py.