From 85f3f1af9ca2600f7e13b2cd51de6a531066147e Mon Sep 17 00:00:00 2001 From: dalthviz Date: Wed, 5 Jul 2017 02:46:27 -0500 Subject: [PATCH 1/5] Adds disambiguation for consoles. --- spyder/plugins/ipythonconsole.py | 34 ++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/spyder/plugins/ipythonconsole.py b/spyder/plugins/ipythonconsole.py index 03ab6b5d456..359bcc758bc 100644 --- a/spyder/plugins/ipythonconsole.py +++ b/spyder/plugins/ipythonconsole.py @@ -52,7 +52,7 @@ from spyder.utils.ipython.style import create_qss_style from spyder.utils.qthelpers import create_action, MENU_SEPARATOR from spyder.utils import icon_manager as ima -from spyder.utils import encoding, programs +from spyder.utils import encoding, programs, sourcecode from spyder.utils.misc import get_error_match, remove_backslashes from spyder.widgets.findreplace import FindReplace from spyder.widgets.ipythonconsole import ClientWidget @@ -607,6 +607,7 @@ def __init__(self, parent, testing=False): self.master_clients = 0 self.clients = [] + self.filenames = [] self.mainwindow_close = False self.create_new_client_if_empty = True self.testing = testing @@ -740,6 +741,7 @@ def refresh_plugin(self): sw = client.shellwidget self.variableexplorer.set_shellwidget_from_id(id(sw)) self.help.set_shell(sw) + self.update_tabs_text() self.update_plugin_title.emit() def get_plugin_actions(self): @@ -907,7 +909,7 @@ def write_to_stdin(self, line): @Slot() @Slot(bool) - def create_new_client(self, give_focus=True): + def create_new_client(self, give_focus=True, filename=None): """Create a new client""" self.master_clients += 1 client_id = dict(int_id=to_text_string(self.master_clients), @@ -920,7 +922,7 @@ def create_new_client(self, give_focus=True): interpreter_versions=self.interpreter_versions(), connection_file=cf, menu_actions=self.menu_actions) - self.add_tab(client, name=client.get_name()) + self.add_tab(client, name=client.get_name(), filename=filename) if cf is None: error_msg = _("The directory {} is not writable and it is " @@ -1192,8 +1194,10 @@ def close_client(self, index=None, client=None, force=False): # Note: client index may have changed after closing related widgets self.tabwidget.removeTab(self.tabwidget.indexOf(client)) self.clients.remove(client) + self.filenames.pop(index) if not self.tabwidget.count() and self.create_new_client_if_empty: self.create_new_client() + self.update_tabs_text() self.update_plugin_title.emit() def get_client_index_from_id(self, client_id): @@ -1265,7 +1269,7 @@ def create_client_from_path(self, path): def create_client_for_file(self, filename): """Create a client to execute code related to a file.""" # Create client - self.create_new_client() + self.create_new_client(filename=filename) # Don't increase the count of master clients self.master_clients -= 1 @@ -1273,7 +1277,8 @@ def create_client_for_file(self, filename): # Rename client tab with filename client = self.get_current_client() client.allow_rename = False - self.rename_client_tab(client, filename) + fname = self.get_tab_text(filename) + self.rename_client_tab(client, fname) def get_client_for_file(self, filename): """Get client associated with a given file.""" @@ -1355,25 +1360,42 @@ def restart_kernel(self): client.restart_kernel() #------ Public API (for tabs) --------------------------------------------- - def add_tab(self, widget, name): + def add_tab(self, widget, name, filename=None): """Add tab""" self.clients.append(widget) index = self.tabwidget.addTab(widget, name) + self.filenames.insert(index, filename) self.tabwidget.setCurrentIndex(index) if self.dockwidget and not self.ismaximized: self.dockwidget.setVisible(True) self.dockwidget.raise_() self.activateWindow() widget.get_control().setFocus() + self.update_tabs_text() def move_tab(self, index_from, index_to): """ Move tab (tabs themselves have already been moved by the tabwidget) """ + filename = self.filenames.pop(index_from) client = self.clients.pop(index_from) + self.filenames.insert(index_to, filename) self.clients.insert(index_to, client) self.update_plugin_title.emit() + def get_tab_text(self, fname): + """Get tab text without ambiguation.""" + files_path_list = [filename for filename in self.filenames + if filename is not None] + return sourcecode.get_file_title(files_path_list, fname) + + def update_tabs_text(self): + """Update the text from the tabs.""" + for index, fname in enumerate(self.filenames): + if fname is not None: + client = self.clients[index] + self.rename_client_tab(client, self.get_tab_text(fname)) + def rename_client_tab(self, client, given_name): """Rename client's tab""" index = self.get_client_index_from_id(id(client)) From d885e867cb3dd78b10af8310c368b764d56b5553 Mon Sep 17 00:00:00 2001 From: dalthviz Date: Wed, 5 Jul 2017 16:00:49 -0500 Subject: [PATCH 2/5] Adds a test. --- spyder/plugins/ipythonconsole.py | 12 +++---- spyder/plugins/tests/test_ipythonconsole.py | 36 +++++++++++++++++++++ spyder/utils/sourcecode.py | 2 +- spyder/widgets/editor.py | 2 +- 4 files changed, 44 insertions(+), 8 deletions(-) diff --git a/spyder/plugins/ipythonconsole.py b/spyder/plugins/ipythonconsole.py index 359bcc758bc..88e15eb4e15 100644 --- a/spyder/plugins/ipythonconsole.py +++ b/spyder/plugins/ipythonconsole.py @@ -1277,8 +1277,8 @@ def create_client_for_file(self, filename): # Rename client tab with filename client = self.get_current_client() client.allow_rename = False - fname = self.get_tab_text(filename) - self.rename_client_tab(client, fname) + tab_text = self.disambiguate_fname(filename) + self.rename_client_tab(client, tab_text) def get_client_for_file(self, filename): """Get client associated with a given file.""" @@ -1383,18 +1383,18 @@ def move_tab(self, index_from, index_to): self.clients.insert(index_to, client) self.update_plugin_title.emit() - def get_tab_text(self, fname): - """Get tab text without ambiguation.""" + def disambiguate_fname(self, fname): + """Generate a file name without ambiguation.""" files_path_list = [filename for filename in self.filenames if filename is not None] - return sourcecode.get_file_title(files_path_list, fname) + return sourcecode.disambiguate_fname(files_path_list, fname) def update_tabs_text(self): """Update the text from the tabs.""" for index, fname in enumerate(self.filenames): if fname is not None: client = self.clients[index] - self.rename_client_tab(client, self.get_tab_text(fname)) + self.rename_client_tab(client, self.disambiguate_fname(fname)) def rename_client_tab(self, client, given_name): """Rename client's tab""" diff --git a/spyder/plugins/tests/test_ipythonconsole.py b/spyder/plugins/tests/test_ipythonconsole.py index 3c5efba939e..f262de4aa24 100644 --- a/spyder/plugins/tests/test_ipythonconsole.py +++ b/spyder/plugins/tests/test_ipythonconsole.py @@ -35,6 +35,7 @@ #============================================================================== SHELL_TIMEOUT = 20000 PYQT_WHEEL = PYQT_VERSION > '5.6' +TEMP_DIRECTORY = tempfile.gettempdir() #============================================================================== @@ -76,6 +77,40 @@ def close_console(): #============================================================================== # Tests #============================================================================== +@flaky(max_runs=3) +def test_console_disambiguation(ipyconsole, qtbot): + """Test the disambiguation of dedicated consoles.""" + # Create directories and file for TEMP_DIRECTORY/a/b/c.py + # and TEMP_DIRECTORY/a/d/c.py + dir_b = osp.join(TEMP_DIRECTORY, 'a', 'b') + filename_b = osp.join(dir_b, 'c.py') + if not osp.isdir(dir_b): + os.makedirs(dir_b) + if not osp.isfile(filename_b): + file_c = open(filename_b, 'w+') + file_c.close() + dir_d = osp.join(TEMP_DIRECTORY, 'a', 'd') + filename_d = osp.join(dir_d, 'c.py') + if not osp.isdir(dir_d): + os.makedirs(dir_d) + if not osp.isfile(filename_d): + file_e = open(filename_d, 'w+') + file_e.close() + + # Create new client and assert name without disambiguation + ipyconsole.create_client_for_file(filename_b) + client = ipyconsole.get_current_client() + assert client.get_name() == 'c.py/A' + + # Create new client and assert name with disambiguation + ipyconsole.create_client_for_file(filename_d) + client = ipyconsole.get_current_client() + assert client.get_name() == 'c.py - d/A' + ipyconsole.tabwidget.setCurrentIndex(1) + client = ipyconsole.get_current_client() + assert client.get_name() == 'c.py - b/A' + + @flaky(max_runs=3) def test_console_coloring(ipyconsole, qtbot): @@ -99,6 +134,7 @@ def test_console_coloring(ipyconsole, qtbot): assert console_background_color.strip() == editor_background_color.strip() assert console_font_color.strip() == editor_font_color.strip() + @flaky(max_runs=3) @pytest.mark.skipif(os.name == 'nt', reason="It doesn't work on Windows") def test_get_env(ipyconsole, qtbot): diff --git a/spyder/utils/sourcecode.py b/spyder/utils/sourcecode.py index 367a83a2736..f3816ce50bd 100644 --- a/spyder/utils/sourcecode.py +++ b/spyder/utils/sourcecode.py @@ -183,7 +183,7 @@ def differentiate_prefix(path_components0, path_components1): path_0 = '/' return path_0 -def get_file_title(files_path_list, filename): +def disambiguate_fname(files_path_list, filename): """Get tab title without ambiguation.""" fname = os.path.basename(filename) same_name_files = get_same_name_files(files_path_list, fname) diff --git a/spyder/widgets/editor.py b/spyder/widgets/editor.py index f549bb2ee2f..1544f900b30 100644 --- a/spyder/widgets/editor.py +++ b/spyder/widgets/editor.py @@ -1135,7 +1135,7 @@ def get_tab_text(self, index, is_modified=None, is_readonly=None): """Return tab title.""" files_path_list = [finfo.filename for finfo in self.data] fname = self.data[index].filename - fname = sourcecode.get_file_title(files_path_list, fname) + fname = sourcecode.disambiguate_fname(files_path_list, fname) return self.__modified_readonly_title(fname, is_modified, is_readonly) From 1eda93f9c71708dbbcde5bf47d71894426324eb4 Mon Sep 17 00:00:00 2001 From: dalthviz Date: Wed, 5 Jul 2017 16:34:32 -0500 Subject: [PATCH 3/5] Fixes sourcecodes test. --- spyder/utils/tests/test_sourcecode.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spyder/utils/tests/test_sourcecode.py b/spyder/utils/tests/test_sourcecode.py index fccfd8142f9..2ab19659872 100644 --- a/spyder/utils/tests/test_sourcecode.py +++ b/spyder/utils/tests/test_sourcecode.py @@ -84,7 +84,7 @@ def test_shortest_path(): shortest_path = os.path.join(*['c:','','documents','test','test.py']) assert sourcecode.shortest_path(files_path_list) == shortest_path -def test_get_file_title(): +def test_disambiguate_fname(): files_path_list = [] if sys.platform.startswith('linux'): fname0 = os.path.join(*['','','documents','test','test.py']) @@ -98,8 +98,8 @@ def test_get_file_title(): files_path_list.append(fname1) title0 = 'test.py - ' + os.path.join(*['test']) title1 = 'test.py - ' + os.path.join(*['projects','test']) - assert sourcecode.get_file_title(files_path_list, fname0) == title0 - assert sourcecode.get_file_title(files_path_list, fname1) == title1 + assert sourcecode.disambiguate_fname(files_path_list, fname0) == title0 + assert sourcecode.disambiguate_fname(files_path_list, fname1) == title1 if __name__ == '__main__': pytest.main() From c218b90ea968508d1e8af490306ef00aa5397011 Mon Sep 17 00:00:00 2001 From: dalthviz Date: Wed, 5 Jul 2017 17:28:42 -0500 Subject: [PATCH 4/5] Improvements. --- spyder/plugins/ipythonconsole.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/spyder/plugins/ipythonconsole.py b/spyder/plugins/ipythonconsole.py index 88e15eb4e15..dbd1ebc1ecb 100644 --- a/spyder/plugins/ipythonconsole.py +++ b/spyder/plugins/ipythonconsole.py @@ -909,6 +909,8 @@ def write_to_stdin(self, line): @Slot() @Slot(bool) + @Slot(object) + @Slot(bool, object) def create_new_client(self, give_focus=True, filename=None): """Create a new client""" self.master_clients += 1 @@ -1386,13 +1388,13 @@ def move_tab(self, index_from, index_to): def disambiguate_fname(self, fname): """Generate a file name without ambiguation.""" files_path_list = [filename for filename in self.filenames - if filename is not None] + if filename] return sourcecode.disambiguate_fname(files_path_list, fname) def update_tabs_text(self): """Update the text from the tabs.""" for index, fname in enumerate(self.filenames): - if fname is not None: + if fname: client = self.clients[index] self.rename_client_tab(client, self.disambiguate_fname(fname)) From b611aa3d97110a02dc163a954afa885df86cf5ea Mon Sep 17 00:00:00 2001 From: dalthviz Date: Wed, 5 Jul 2017 17:54:05 -0500 Subject: [PATCH 5/5] str instead of object. --- spyder/plugins/ipythonconsole.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/spyder/plugins/ipythonconsole.py b/spyder/plugins/ipythonconsole.py index dbd1ebc1ecb..19524640e19 100644 --- a/spyder/plugins/ipythonconsole.py +++ b/spyder/plugins/ipythonconsole.py @@ -909,9 +909,9 @@ def write_to_stdin(self, line): @Slot() @Slot(bool) - @Slot(object) - @Slot(bool, object) - def create_new_client(self, give_focus=True, filename=None): + @Slot(str) + @Slot(bool, str) + def create_new_client(self, give_focus=True, filename=''): """Create a new client""" self.master_clients += 1 client_id = dict(int_id=to_text_string(self.master_clients), @@ -1362,7 +1362,7 @@ def restart_kernel(self): client.restart_kernel() #------ Public API (for tabs) --------------------------------------------- - def add_tab(self, widget, name, filename=None): + def add_tab(self, widget, name, filename=''): """Add tab""" self.clients.append(widget) index = self.tabwidget.addTab(widget, name)