diff -r b42871520ea8 Lib/idlelib/ClearWindow.py --- a/Lib/idlelib/ClearWindow.py Wed Feb 19 20:22:04 2014 +0200 +++ b/Lib/idlelib/ClearWindow.py Wed Feb 19 20:38:38 2014 +0200 @@ -3,6 +3,7 @@ from tkinter import INSERT, TclError from idlelib.UndoDelegator import DeleteCommand +from idlelib.Squeezer import ExpandingButton class ClearWindow: @@ -28,11 +29,12 @@ def clear_window(self): """clear everything before the last prompt in the shell window""" endpos = self._find_last_prompt_start() + squeezer_ext = self.editwin.extensions.get('Squeezer', None) # to make this action undo-able, it is implemented as an # UndoDelegator.Command sub-class and added to the EditorWindow's # UndoDelegator instance - dc = ClearWindowDeleteCommand('1.0', endpos) + dc = ClearWindowDeleteCommand('1.0', endpos, squeezer_ext=squeezer_ext) self.editwin.undo.addcmd(dc) def _find_last_prompt_start(self): @@ -53,13 +55,36 @@ class ClearWindowDeleteCommand(DeleteCommand): - def __init__(self, index1, index2): + def __init__(self, index1, index2, squeezer_ext=None): DeleteCommand.__init__(self, index1, index2) + self.squeezer_ext = squeezer_ext self.dump = None self.expandingbuttons_dump = None def do(self, text): + if self.squeezer_ext is not None: + # dump and remove the Squeezer extensions's "squeezed text" buttons + buttons_in_range = [] + buttons_not_in_range = [] + for eb in self.squeezer_ext.expandingbuttons: + eb_index = text.index(eb) + is_eb_in_range = (text.compare(self.index1, '<=', eb_index) and + text.compare(eb_index, '<', self.index2)) + if is_eb_in_range: + buttons_in_range.append(eb) + else: + buttons_not_in_range.append(eb) + + self.expandingbuttons_dump = [ + (eb.s, eb.tags, text.index(eb)) + for eb in buttons_in_range + ] + for eb in buttons_in_range: + text.delete(eb) + + self.squeezer_ext.expandingbuttons = buttons_not_in_range + # dump and remove all of the text self.dump = text.dump(self.index1, self.index2, all=True) text.delete(self.index1, self.index2) @@ -83,4 +108,20 @@ for tag_name in tag_starts: text.tag_add(tag_name, tag_starts[tag_name], self.index2) + if self.squeezer_ext is not None: + # restore the Squeezer extensions's "squeezed text" buttons + for (s, tags, index) in self.expandingbuttons_dump: + eb = ExpandingButton(s, tags, + self.squeezer_ext.count_lines(s), + self.squeezer_ext) + text.window_create(index, window=eb, padx=3, pady=5) + text.update() + self.squeezer_ext.expandingbuttons.append(eb) + + # keep squeezer_ext.expandingbuttons sorted by their index in the + # Text widget + def get_index_as_ints(expanding_button): + return tuple(map(int, text.index(expanding_button).split('.'))) + self.squeezer_ext.expandingbuttons.sort(key=get_index_as_ints) + text.see(INSERT) diff -r b42871520ea8 Lib/idlelib/idle_test/test_clearwindow.py --- a/Lib/idlelib/idle_test/test_clearwindow.py Wed Feb 19 20:22:04 2014 +0200 +++ b/Lib/idlelib/idle_test/test_clearwindow.py Wed Feb 19 20:38:38 2014 +0200 @@ -5,6 +5,7 @@ from unittest.mock import Mock, NonCallableMagicMock, patch, sentinel from test.support import requires +from idlelib.Squeezer import ExpandingButton from idlelib.ClearWindow import ClearWindow, ClearWindowDeleteCommand @@ -73,6 +74,22 @@ self.assertIsInstance(cmd, ClearWindowDeleteCommand) self.assertEquals(cmd.index1, '1.0') self.assertEquals(cmd.index2, sentinel.IDX) + self.assertEquals(cmd.squeezer_ext, None) + + def test_clear_window_method_with_squeezer_extension(self): + """test ClearWindow.clear_window() passes Squeezer to the delete cmd""" + editwin = self.make_mock_editor_window() + editwin.extensions = {'Squeezer': sentinel.SQUEEZER_EXT} + clearwindow = ClearWindow(editwin) + clearwindow._find_last_prompt_start = Mock(return_value=sentinel.IDX) + + clearwindow.clear_window() + self.assertEquals(editwin.undo.addcmd.call_count, 1) + cmd = editwin.undo.addcmd.call_args[0][0] + self.assertIsInstance(cmd, ClearWindowDeleteCommand) + self.assertEquals(cmd.index1, '1.0') + self.assertEquals(cmd.index2, sentinel.IDX) + self.assertEquals(cmd.squeezer_ext, sentinel.SQUEEZER_EXT) def test_find_last_prompt_start_no_iomark(self): """test _find_last_prompt_start() with no 'iomark' mark in the text""" @@ -89,6 +106,7 @@ self.assertIsInstance(cmd, ClearWindowDeleteCommand) self.assertEquals(cmd.index1, '1.0') self.assertEquals(cmd.index2, '3.0') + self.assertEquals(cmd.squeezer_ext, None) def test_find_last_prompt_start_no_prompt(self): """test _find_last_prompt_start() with no 'prompt' tag in the text""" @@ -105,6 +123,7 @@ self.assertIsInstance(cmd, ClearWindowDeleteCommand) self.assertEquals(cmd.index1, '1.0') self.assertEquals(cmd.index2, '3.0') + self.assertEquals(cmd.squeezer_ext, None) def test_find_last_prompt_start_iomark_and_prompt(self): """test _find_last_prompt_start() with both 'prompt' and 'iomark'""" @@ -120,6 +139,7 @@ self.assertIsInstance(cmd, ClearWindowDeleteCommand) self.assertEquals(cmd.index1, '1.0') self.assertEquals(cmd.index2, '3.0') + self.assertEquals(cmd.squeezer_ext, None) def test_find_last_prompt_start_no_iomark_or_prompt(self): """test _find_last_prompt_start() with no 'prompt' or 'iomark'""" @@ -142,9 +162,11 @@ class TestClearWindowDeleteCommand(unittest.TestCase): """tests for the ClearWindowDeleteCommand class""" def test_init(self): - cmd = ClearWindowDeleteCommand(sentinel.index1, sentinel.index2) + cmd = ClearWindowDeleteCommand(sentinel.index1, sentinel.index2, + squeezer_ext=sentinel.squeezer_ext) self.assertEquals(cmd.index1, sentinel.index1) self.assertEquals(cmd.index2, sentinel.index2) + self.assertEquals(cmd.squeezer_ext, sentinel.squeezer_ext) def test_do(self): requires('gui') @@ -220,3 +242,76 @@ self.assertEquals(text_widget.get('1.0', 'end-1c'), orig_text) self.assertEquals(get_tag_ranges(), orig_tag_ranges) + + def test_squeezer_expanding_buttons(self): + """an extensive scenario testing the Squeezer support""" + requires('gui') + + text_widget = Text() + + squeezer = Mock() + squeezer.expandingbuttons = [] + def mock_count_lines(text): + return text.count('\n') + (0 if text.endswith('\n') else 1) + squeezer.count_lines = mock_count_lines + squeezer.editwin.text = text_widget + squeezer.get_preview_command = Mock(return_value='notepad.exe %(fn)s') + squeezer.get_show_tooltip = Mock(return_value=True) + squeezer.get_tooltip_delay = Mock(return_value=1500) + + def add_expanding_button(index, text, tags): + eb = ExpandingButton(text, tags, + mock_count_lines(text), + squeezer) + text_widget.window_create(index, window=eb, padx=3, pady=5) + text_widget.update() + squeezer.expandingbuttons.append(eb) + + text_widget.insert(INSERT, 'text\n') + add_expanding_button(INSERT, 'squeezed1', ('stdout',)) + text_widget.insert(INSERT, '\n') + text_widget.insert(INSERT, 'text\n') + add_expanding_button(INSERT, 'squeezed2', ('stdout',)) + text_widget.insert(INSERT, '\n') + text_widget.insert(INSERT, 'text\n') + add_expanding_button(INSERT, 'squeezed3', ('stdout',)) + text_widget.insert(INSERT, '\n') + text_widget.insert(INSERT, 'text\n') + add_expanding_button(INSERT, 'squeezed4', ('stdout',)) + text_widget.insert(INSERT, '\n') + text_widget.insert(INSERT, 'text\n') + + self.assertEquals(len(squeezer.expandingbuttons), 4) + + cmd = ClearWindowDeleteCommand('3.0', '7.0', squeezer_ext=squeezer) + cmd.do(text_widget) + + self.assertEquals(text_widget.get('1.0', 'end-1c').count('text'), 3) + self.assertEquals(len(squeezer.expandingbuttons), 2) + self.assertEquals(squeezer.expandingbuttons[0].s, 'squeezed1') + self.assertEquals(squeezer.expandingbuttons[1].s, 'squeezed4') + + cmd.undo(text_widget) + + self.assertEquals(text_widget.get('1.0', 'end-1c').count('text'), 5) + self.assertEquals(len(squeezer.expandingbuttons), 4) + self.assertEquals(squeezer.expandingbuttons[0].s, 'squeezed1') + self.assertEquals(squeezer.expandingbuttons[1].s, 'squeezed2') + self.assertEquals(squeezer.expandingbuttons[2].s, 'squeezed3') + self.assertEquals(squeezer.expandingbuttons[3].s, 'squeezed4') + + cmd.redo(text_widget) + + self.assertEquals(text_widget.get('1.0', 'end-1c').count('text'), 3) + self.assertEquals(len(squeezer.expandingbuttons), 2) + self.assertEquals(squeezer.expandingbuttons[0].s, 'squeezed1') + self.assertEquals(squeezer.expandingbuttons[1].s, 'squeezed4') + + cmd.undo(text_widget) + + self.assertEquals(text_widget.get('1.0', 'end-1c').count('text'), 5) + self.assertEquals(len(squeezer.expandingbuttons), 4) + self.assertEquals(squeezer.expandingbuttons[0].s, 'squeezed1') + self.assertEquals(squeezer.expandingbuttons[1].s, 'squeezed2') + self.assertEquals(squeezer.expandingbuttons[2].s, 'squeezed3') + self.assertEquals(squeezer.expandingbuttons[3].s, 'squeezed4')