bpo-36390: Gather IDLE Format menu functions into format.py (#14827)
Add two indent spec methods from editor and Rstrip to existing file. Tests are not added for indent methods because they need change in lights of 3.x's prohibition on mixing tabs and spaces.
This commit is contained in:
parent
8cb65d1381
commit
1b38922434
|
@ -53,9 +53,8 @@ class EditorWindow(object):
|
||||||
from idlelib.autoexpand import AutoExpand
|
from idlelib.autoexpand import AutoExpand
|
||||||
from idlelib.calltip import Calltip
|
from idlelib.calltip import Calltip
|
||||||
from idlelib.codecontext import CodeContext
|
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.parenmatch import ParenMatch
|
||||||
from idlelib.rstrip import Rstrip
|
|
||||||
from idlelib.squeezer import Squeezer
|
from idlelib.squeezer import Squeezer
|
||||||
from idlelib.zoomheight import ZoomHeight
|
from idlelib.zoomheight import ZoomHeight
|
||||||
|
|
||||||
|
@ -173,14 +172,15 @@ class EditorWindow(object):
|
||||||
text.bind("<<newline-and-indent>>",self.newline_and_indent_event)
|
text.bind("<<newline-and-indent>>",self.newline_and_indent_event)
|
||||||
text.bind("<<smart-indent>>",self.smart_indent_event)
|
text.bind("<<smart-indent>>",self.smart_indent_event)
|
||||||
self.fregion = fregion = self.FormatRegion(self)
|
self.fregion = fregion = self.FormatRegion(self)
|
||||||
|
# self.fregion used in smart_indent_event to access indent_region.
|
||||||
text.bind("<<indent-region>>", fregion.indent_region_event)
|
text.bind("<<indent-region>>", fregion.indent_region_event)
|
||||||
text.bind("<<dedent-region>>", fregion.dedent_region_event)
|
text.bind("<<dedent-region>>", fregion.dedent_region_event)
|
||||||
text.bind("<<comment-region>>", fregion.comment_region_event)
|
text.bind("<<comment-region>>", fregion.comment_region_event)
|
||||||
text.bind("<<uncomment-region>>", fregion.uncomment_region_event)
|
text.bind("<<uncomment-region>>", fregion.uncomment_region_event)
|
||||||
text.bind("<<tabify-region>>", fregion.tabify_region_event)
|
text.bind("<<tabify-region>>", fregion.tabify_region_event)
|
||||||
text.bind("<<untabify-region>>", fregion.untabify_region_event)
|
text.bind("<<untabify-region>>", fregion.untabify_region_event)
|
||||||
text.bind("<<toggle-tabs>>", self.toggle_tabs_event)
|
text.bind("<<toggle-tabs>>", self.Indents.toggle_tabs_event)
|
||||||
text.bind("<<change-indentwidth>>",self.change_indentwidth_event)
|
text.bind("<<change-indentwidth>>", self.Indents.change_indentwidth_event)
|
||||||
text.bind("<Left>", self.move_at_edge_if_selection(0))
|
text.bind("<Left>", self.move_at_edge_if_selection(0))
|
||||||
text.bind("<Right>", self.move_at_edge_if_selection(1))
|
text.bind("<Right>", self.move_at_edge_if_selection(1))
|
||||||
text.bind("<<del-word-left>>", self.del_word_left)
|
text.bind("<<del-word-left>>", self.del_word_left)
|
||||||
|
@ -1424,20 +1424,6 @@ class EditorWindow(object):
|
||||||
return _icis(_startindex + "+%dc" % offset)
|
return _icis(_startindex + "+%dc" % offset)
|
||||||
return inner
|
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
|
# XXX this isn't bound to anything -- see tabwidth comments
|
||||||
## def change_tabwidth_event(self, event):
|
## def change_tabwidth_event(self, event):
|
||||||
## new = self._asktabwidth()
|
## new = self._asktabwidth()
|
||||||
|
@ -1446,18 +1432,6 @@ class EditorWindow(object):
|
||||||
## self.set_indentation_params(0, guess=0)
|
## self.set_indentation_params(0, guess=0)
|
||||||
## return "break"
|
## 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.
|
# Make string that displays as n leading blanks.
|
||||||
|
|
||||||
def _make_blanks(self, n):
|
def _make_blanks(self, n):
|
||||||
|
|
|
@ -6,6 +6,7 @@ comment, uncomment, tabify, and untabify.
|
||||||
File renamed from paragraph.py with functions added from editor.py.
|
File renamed from paragraph.py with functions added from editor.py.
|
||||||
"""
|
"""
|
||||||
import re
|
import re
|
||||||
|
from tkinter.messagebox import askyesno
|
||||||
from tkinter.simpledialog import askinteger
|
from tkinter.simpledialog import askinteger
|
||||||
from idlelib.config import idleConf
|
from idlelib.config import idleConf
|
||||||
|
|
||||||
|
@ -195,7 +196,7 @@ def get_comment_header(line):
|
||||||
return m.group(1)
|
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]*')
|
_line_indent_re = re.compile(r'[ \t]*')
|
||||||
|
|
||||||
def get_line_indent(line, tabwidth):
|
def get_line_indent(line, tabwidth):
|
||||||
|
@ -209,7 +210,7 @@ def get_line_indent(line, tabwidth):
|
||||||
|
|
||||||
|
|
||||||
class FormatRegion:
|
class FormatRegion:
|
||||||
"Format selected text."
|
"Format selected text (region)."
|
||||||
|
|
||||||
def __init__(self, editwin):
|
def __init__(self, editwin):
|
||||||
self.editwin = editwin
|
self.editwin = editwin
|
||||||
|
@ -352,6 +353,65 @@ class FormatRegion:
|
||||||
maxvalue=16)
|
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__":
|
if __name__ == "__main__":
|
||||||
from unittest import main
|
from unittest import main
|
||||||
main('idlelib.idle_test.test_format', verbosity=2, exit=False)
|
main('idlelib.idle_test.test_format', verbosity=2, exit=False)
|
||||||
|
|
|
@ -6,6 +6,7 @@ from unittest import mock
|
||||||
from test.support import requires
|
from test.support import requires
|
||||||
from tkinter import Tk, Text
|
from tkinter import Tk, Text
|
||||||
from idlelib.editor import EditorWindow
|
from idlelib.editor import EditorWindow
|
||||||
|
from idlelib.idle_test.mock_idle import Editor as MockEditor
|
||||||
|
|
||||||
|
|
||||||
class Is_Get_Test(unittest.TestCase):
|
class Is_Get_Test(unittest.TestCase):
|
||||||
|
@ -573,5 +574,50 @@ class C1():
|
||||||
self.assertEqual(ask(), 10)
|
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__':
|
if __name__ == '__main__':
|
||||||
unittest.main(verbosity=2, exit=2)
|
unittest.main(verbosity=2, exit=2)
|
||||||
|
|
|
@ -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)
|
|
|
@ -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,)
|
|
|
@ -1,2 +1,2 @@
|
||||||
Rename paragraph.py to format.py and add region formatting methods
|
Gather Format menu functions into format.py. Combine
|
||||||
from editor.py. Add tests for the latter.
|
paragraph.py, rstrip.py, and format methods from editor.py.
|
||||||
|
|
Loading…
Reference in New Issue