diff --git a/gitfourchette/mainwindow.py b/gitfourchette/mainwindow.py index d6ecbd78..adbd8269 100644 --- a/gitfourchette/mainwindow.py +++ b/gitfourchette/mainwindow.py @@ -19,7 +19,7 @@ from gitfourchette.application import GFApplication from gitfourchette.codeview.codeview import CodeView from gitfourchette.dropzone import DropAction, DropZone -from gitfourchette.exttools.toolprocess import ToolProcess +from gitfourchette.exttools.toolprocess import ToolProcess, setUpToolCommand from gitfourchette.exttools.usercommand import UserCommand from gitfourchette.forms.aboutdialog import AboutDialog from gitfourchette.forms.clonedialog import CloneDialog @@ -34,7 +34,7 @@ from gitfourchette.porcelain import * from gitfourchette.qt import * from gitfourchette.repowidget import RepoWidget -from gitfourchette.settings import PrefEffects, TabBarClick +from gitfourchette.settings import PrefEffects, TabBarClick, getExternalEditorName from gitfourchette.syntax import LexJobCache from gitfourchette.tasks import TaskBook, RepoTaskRunner from gitfourchette.tasks.newrepotasks import NewRepo @@ -1254,6 +1254,14 @@ def workdirProxy(): tip=_("Open a terminal in the repo’s working directory"), ), + ActionDef( + _("Op&en Repo in {0}", getExternalEditorName()), + lambda: self.openRepoInEditor(workdirProxy()), + icon="prefs-diff", + shortcuts=GlobalShortcuts.NO_SHORTCUT, + tip=_("Open this repo’s working directory in {0}", getExternalEditorName()), + ), + ActionDef( _("Cop&y Repo Path"), lambda: self.repolessCopyPath(workdirProxy()), @@ -1273,6 +1281,13 @@ def workdirProxy(): ), ] + def openRepoInEditor(self, workdir: str) -> None: + if settings.prefs.externalEditor == "": + setUpToolCommand(self, "externalEditor") + return + + ToolProcess.startTextEditor(self, workdir) + def repolessSetNickname(self, workdir: str): defaultName = Path(workdir).name oldNickname = settings.history.getRepoNickname(workdir, strict=True) diff --git a/gitfourchette/settings.py b/gitfourchette/settings.py index 82620e41..8f1ec449 100644 --- a/gitfourchette/settings.py +++ b/gitfourchette/settings.py @@ -253,6 +253,7 @@ class PrefEffects: "language", "commands", "confirmCommands", + "externalEditor" } "Pref keys that trigger a rebuild of the main menu." diff --git a/test/test_mainwindow.py b/test/test_mainwindow.py index 9b388c78..a8e7dff9 100644 --- a/test/test_mainwindow.py +++ b/test/test_mainwindow.py @@ -156,7 +156,9 @@ def testMainWindowMenuItems(tempDir, mainWindow): def testTabBarActions(tempDir, mainWindow): editorPath = getTestDataPath("editor-shim.py") scratchPath = f"{tempDir.name}/scratch file.txt" - GFApplication.applyPrefs(terminal=f'"{editorPath}" "{scratchPath}" "hello world" $COMMAND') + GFApplication.applyPrefs( + terminal=f'"{editorPath}" "{scratchPath}" "hello world" $COMMAND' + ) # Open two repos to test background and foreground tab actions wd0 = unpackRepo(tempDir, renameTo="repo0") @@ -172,11 +174,13 @@ def testTabBarActions(tempDir, mainWindow): assert isinstance(widget0, RepoWidget) assert isinstance(widget1, RepoStub) - for tabIndex, wd in enumerate([wd0, wd1]): + def getMenu(tIndex: int) -> QMenu: tabBar = mainWindow.tabs.tabs - tabRect = tabBar.tabRect(tabIndex) - menu = summonContextMenu(tabBar, tabRect.center()) + tabRect = tabBar.tabRect(tIndex) + return summonContextMenu(tabBar, tabRect.center()) + for tabIndex, wd in enumerate([wd0, wd1]): + menu = getMenu(tabIndex) triggerMenuAction(menu, "copy repo path") assert QApplication.clipboard().text() == wd @@ -190,6 +194,25 @@ def testTabBarActions(tempDir, mainWindow): assert terminalShimResult[0] == "hello world" assert terminalShimResult[1].endswith(".sh") # path to launcher script + # Test open repo in editor, with editor not set. + triggerMenuAction(menu, "open repo in external editor") + rejectQMessageBox(mainWindow, r"text editor.+isn.t configured") + + # Test open repo in editor, with editor is set + GFApplication.applyPrefs( + externalEditor=f'"{editorPath}" "{scratchPath}" "hello world"' + ) + menu.close() + menu = getMenu(tabIndex) # reload menu, and verify editor changed + is working. + triggerMenuAction(menu, "open repo in editor-shim.py") + waitForFile(scratchPath) + editorShimResult = readTextFile(scratchPath, unlink=True).splitlines() + assert editorShimResult[0] == "hello world" # ensure editor opens + assert Path(wd).samefile(editorShimResult[1]) + GFApplication.applyPrefs( + externalEditor="" + ) # revert to no external editor, for the next wd. + menu.close()