Repository: spyder-ide/spyder

Issue:
Feature request: Pasting a file in the working directory doesn't work.
<!--- **PLEASE READ:** When submitting here, please ensure you've completed the following checklist and checked the boxes to confirm. Issue reports without it may be closed. Thanks! --->

### Issue Report Checklist

* [ ] Searched the [issues page](https://github.com/spyder-ide/spyder/issues?q=is%3Aissue) for similar reports
* [ ] Read the relevant sections of the [Spyder Troubleshooting Guide](https://github.com/spyder-ide/spyder/wiki/Troubleshooting-Guide-and-FAQ) and followed its advice
* [ ] Reproduced the issue after updating with ``conda update spyder`` (or ``pip``, if not using Anaconda)
* [ ] Could not reproduce inside ``jupyter qtconsole`` (if console-related)
* [ ] Tried basic troubleshooting (if a bug/error)
    * [ ] Restarted Spyder
    * [ ] Reset preferences with ``spyder --reset``
    * [ ] Reinstalled the latest version of [Anaconda](https://www.anaconda.com/download/)
    * [ ] Tried the other applicable steps from the Troubleshooting Guide
* [ ] Completed the **Problem Description**, **Steps to Reproduce** and **Version** sections below


## Problem Description
It would be cool to paste a file path and have it open automatically in the editor. Sometimes I have a file path in the clipboard and I don't know what to do with it.


### What steps reproduce the problem?

1. Paste a file path in the working directory
2. Nothing happens
3. 

### What is the expected output? What do you see instead?



### Paste Traceback/Error Below (if applicable)
<!--- Copy from error dialog or View > Panes > Internal Console --->

```python-traceback

PASTE TRACEBACK HERE

```

## Versions
<!--- You can get this information from Help > About Spyder...
or (if Spyder won't launch) the "conda list" command
from the Anaconda Prompt/Terminal/command line. --->

* Spyder version: 
* Python version: 
* Qt version: 
* PyQt version: 
* Operating System name/version: 

### Dependencies
<!--- Please go to the menu entry Help > Dependencies,
press the Copy to clipboard button and paste below --->

```
PASTE DEPENDENCIES HERE
```



Discussion:

Resolving patch:
diff --git a/spyder/plugins/workingdirectory/container.py b/spyder/plugins/workingdirectory/container.py
--- a/spyder/plugins/workingdirectory/container.py
+++ b/spyder/plugins/workingdirectory/container.py
@@ -12,6 +12,7 @@
 import logging
 import os
 import os.path as osp
+import re
 
 # Third party imports
 from qtpy.compat import getexistingdirectory
@@ -57,6 +58,9 @@ class WorkingDirectoryToolbar(ApplicationToolbar):
 
 
 class WorkingDirectoryComboBox(PathComboBox):
+    """Working directory combo box."""
+
+    edit_goto = Signal(str, int, str)
 
     def __init__(self, parent):
         super().__init__(
@@ -77,7 +81,61 @@ def enterEvent(self, event):
         """Set current path as the tooltip of the widget on hover."""
         self.setToolTip(self.currentText())
 
+    def focusOutEvent(self, event):
+        """Handle focus out event restoring the last valid selected path."""
+        if self.add_current_text_if_valid():
+            self.selected()
+            self.hide_completer()
+        hide_status = getattr(self.lineEdit(), 'hide_status_icon', None)
+        if hide_status:
+            hide_status()
+        super().focusOutEvent(event)
+
+    # ---- Own methods
+    def valid_text(self):
+        """Get valid version of current text."""
+        directory = self.currentText()
+        file = None
+        line_number = None
+
+        if directory:
+            # Search for path/to/file.py:10 where 10 is the line number
+            match = re.fullmatch(r"(?:(\d+):)?(.+)", directory[::-1])
+            if match:
+                line_number, directory = match.groups()
+                if line_number:
+                    line_number = int(line_number[::-1])
+                directory = directory[::-1]
+
+            directory = osp.abspath(directory)
+
+            # If the directory is actually a file, open containing directory
+            if osp.isfile(directory):
+                file = osp.basename(directory)
+                directory = osp.dirname(directory)
+
+            # If the directory name is malformed, open parent directory
+            if not osp.isdir(directory):
+                directory = osp.dirname(directory)
+
+            if self.is_valid(directory):
+                return directory, file, line_number
+
+        return self.selected_text, file, line_number
+
+    def add_current_text_if_valid(self):
+        """Add current text to combo box history if valid."""
+        directory, file, line_number = self.valid_text()
+        if file:
+            self.edit_goto.emit(file, line_number, "")
+        if directory != self.currentText():
+            self.add_text(directory)
+        if directory:
+            return True
 
+
+# ---- Container
+# ----------------------------------------------------------------------------
 class WorkingDirectorySpacer(QWidget):
     ID = 'working_directory_spacer'
 
@@ -107,6 +165,20 @@ class WorkingDirectoryContainer(PluginMainContainer):
         The new new working directory path.
     """
 
+    edit_goto = Signal(str, int, str)
+    """
+    This signal is emitted when a file has been requested.
+
+    Parameters
+    ----------
+    filename: str
+        The file to open.
+    line: int
+        The line to go to.
+    word: str
+        The word to go to in the line.
+    """
+
     # ---- PluginMainContainer API
     # ------------------------------------------------------------------------
     def setup(self):
@@ -128,6 +200,7 @@ def setup(self):
 
         # Signals
         self.pathedit.open_dir.connect(self.chdir)
+        self.pathedit.edit_goto.connect(self.edit_goto)
         self.pathedit.activated[str].connect(self.chdir)
 
         # Actions
diff --git a/spyder/plugins/workingdirectory/plugin.py b/spyder/plugins/workingdirectory/plugin.py
--- a/spyder/plugins/workingdirectory/plugin.py
+++ b/spyder/plugins/workingdirectory/plugin.py
@@ -96,6 +96,8 @@ def on_preferences_available(self):
     def on_editor_available(self):
         editor = self.get_plugin(Plugins.Editor)
         editor.sig_dir_opened.connect(self._editor_change_dir)
+        container = self.get_container()
+        container.edit_goto.connect(editor.load)
 
     @on_plugin_available(plugin=Plugins.Explorer)
     def on_explorer_available(self):
diff --git a/spyder/widgets/comboboxes.py b/spyder/widgets/comboboxes.py
--- a/spyder/widgets/comboboxes.py
+++ b/spyder/widgets/comboboxes.py
@@ -324,10 +324,13 @@ def tab_complete(self):
         If there is a single option available one tab completes the option.
         """
         opts = self._complete_options()
+        if len(opts) == 0:
+            return
         if len(opts) == 1:
             self.set_current_text(opts[0] + os.sep)
             self.hide_completer()
         else:
+            self.set_current_text(osp.commonprefix(opts))
             self.completer().complete()
 
     def is_valid(self, qstr=None):


Test patch:
diff --git a/spyder/plugins/workingdirectory/tests/test_workingdirectory.py b/spyder/plugins/workingdirectory/tests/test_workingdirectory.py
--- a/spyder/plugins/workingdirectory/tests/test_workingdirectory.py
+++ b/spyder/plugins/workingdirectory/tests/test_workingdirectory.py
@@ -6,6 +6,7 @@
 """Tests for workingdirectory plugin."""
 
 # Standard library imports
+import os
 import os.path as osp
 import sys
 from unittest.mock import Mock
@@ -13,6 +14,7 @@
 # Third-party imports
 import pytest
 from qtpy.QtWidgets import QMainWindow
+from qtpy.QtCore import Qt
 
 # Local imports
 from spyder.app.cli_options import get_options
@@ -116,5 +118,34 @@ def test_get_workingdir_cli(setup_workingdirectory):
     CONF.reset_to_defaults()
 
 
+def test_file_goto(qtbot, setup_workingdirectory):
+    """
+    Test that putting a file in the workingdirectory emits a edit_goto signal.
+    """
+    container = setup_workingdirectory.get_container()
+    
+    signal_res = {}
+    
+    def test_slot(filename, line, word):
+        signal_res["filename"] = filename
+        signal_res["line"] = line
+    
+    container.edit_goto.connect(test_slot)
+    
+    pathedit = container.pathedit
+    wd = setup_workingdirectory.get_workdir()
+    filename = osp.join(wd, "myfile_workingdirectory_test.py")
+    with open(filename, "w") as f:
+        f.write("\n" * 5)
+    with qtbot.waitSignal(container.edit_goto):
+        pathedit.add_text(filename + ":1")
+        qtbot.keyClick(pathedit, Qt.Key_Return)
+    
+    assert signal_res["filename"] in filename
+    assert signal_res["line"] == 1
+    
+    os.remove(filename)
+
+
 if __name__ == "__main__":
     pytest.main()