From 2fab8277825afe88f0c7f6160d0dd7d17b557074 Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Thu, 11 Nov 2021 11:51:11 +0100 Subject: [PATCH] Support external editors for locator, projects and file system tree When setting an external editor as the default editor in the MIME type settings. Other operations like File > Open, or when using navigation shortcuts like Follow Symbol, and stepping with the debugger will still always open the file within Qt Creator. The Open with... menu can still be used to explicitly open a file in an external editor afterwards. One of the use cases is to be able to open some files that would otherwise unhelpfully be opened in Qt Creator's binary editor in the systems default editor. Fixes: QTCREATORBUG-13880 Change-Id: I852f097da8badd10de78b74e7078987447eebe98 Reviewed-by: Qt CI Bot Reviewed-by: David Schulz Reviewed-by: Christian Stenger --- .../cmakelocatorfilter.cpp | 6 +- .../editormanager/editormanager.cpp | 108 ++++++++++++------ .../coreplugin/editormanager/editormanager.h | 3 +- .../coreplugin/foldernavigationwidget.cpp | 4 +- .../coreplugin/locator/basefilefilter.cpp | 4 +- src/plugins/cppeditor/cpplocatorfilter.cpp | 7 +- src/plugins/cppeditor/symbolsfindfilter.cpp | 7 +- src/plugins/languageclient/locatorfilter.cpp | 10 +- .../projectexplorer/projectexplorer.cpp | 4 +- .../projectexplorer/projecttreewidget.cpp | 4 +- 10 files changed, 107 insertions(+), 50 deletions(-) diff --git a/src/plugins/cmakeprojectmanager/cmakelocatorfilter.cpp b/src/plugins/cmakeprojectmanager/cmakelocatorfilter.cpp index f3af0318a09..9364b155952 100644 --- a/src/plugins/cmakeprojectmanager/cmakelocatorfilter.cpp +++ b/src/plugins/cmakeprojectmanager/cmakelocatorfilter.cpp @@ -185,7 +185,9 @@ void OpenCMakeTargetLocatorFilter::accept(Core::LocatorFilterEntry selection, const auto file = FilePath::fromVariant(extraData.value("file")); if (line >= 0) - Core::EditorManager::openEditorAt({file, line}); + Core::EditorManager::openEditorAt({file, line}, + {}, + Core::EditorManager::AllowExternalEditor); else - Core::EditorManager::openEditor(file); + Core::EditorManager::openEditor(file, {}, Core::EditorManager::AllowExternalEditor); } diff --git a/src/plugins/coreplugin/editormanager/editormanager.cpp b/src/plugins/coreplugin/editormanager/editormanager.cpp index 9d4e82f11b6..e328bd50435 100644 --- a/src/plugins/coreplugin/editormanager/editormanager.cpp +++ b/src/plugins/coreplugin/editormanager/editormanager.cpp @@ -135,6 +135,14 @@ using namespace Core; using namespace Core::Internal; using namespace Utils; +static void checkEditorFlags(EditorManager::OpenEditorFlags flags) +{ + if (flags & EditorManager::OpenInOtherSplit) { + QTC_CHECK(!(flags & EditorManager::SwitchSplitIfAlreadyVisible)); + QTC_CHECK(!(flags & EditorManager::AllowExternalEditor)); + } +} + //===================EditorManager===================== /*! @@ -812,13 +820,21 @@ IEditor *EditorManagerPrivate::openEditor(EditorView *view, const FilePath &file return activateEditor(view, editor, flags); } + if (skipOpeningBigTextFile(filePath)) + return nullptr; + FilePath realFp = autoSaveName(filePath); if (!filePath.exists() || !realFp.exists() || filePath.lastModified() >= realFp.lastModified()) { realFp.removeFile(); realFp = filePath; } - EditorFactoryList factories = EditorManagerPrivate::findFactories(Id(), filePath); + EditorTypeList factories = EditorType::preferredEditorTypes(filePath); + if (!(flags & EditorManager::AllowExternalEditor)) { + factories = Utils::filtered(factories, [](EditorType *type) { + return type->asEditorFactory() != nullptr; + }); + } if (factories.isEmpty()) { Utils::MimeType mimeType = Utils::mimeTypeForFile(filePath); QMessageBox msgbox(QMessageBox::Critical, EditorManager::tr("File Error"), @@ -829,48 +845,56 @@ IEditor *EditorManagerPrivate::openEditor(EditorView *view, const FilePath &file return nullptr; } if (editorId.isValid()) { - IEditorFactory *factory = Utils::findOrDefault(IEditorFactory::allEditorFactories(), - Utils::equal(&IEditorFactory::id, editorId)); + EditorType *factory = EditorType::editorTypeForId(editorId); if (factory) { + QTC_CHECK(factory->asEditorFactory() || (flags & EditorManager::AllowExternalEditor)); factories.removeOne(factory); factories.push_front(factory); } } - if (skipOpeningBigTextFile(filePath)) - return nullptr; - IEditor *editor = nullptr; auto overrideCursor = Utils::OverrideCursor(QCursor(Qt::WaitCursor)); - IEditorFactory *factory = factories.takeFirst(); + EditorType *factory = factories.takeFirst(); while (factory) { - editor = createEditor(factory, filePath); - if (!editor) { - factory = factories.takeFirst(); - continue; - } - QString errorString; - IDocument::OpenResult openResult = editor->document()->open(&errorString, filePath, realFp); - if (openResult == IDocument::OpenResult::Success) - break; - overrideCursor.reset(); - delete editor; - editor = nullptr; - - if (openResult == IDocument::OpenResult::ReadError) { - QMessageBox msgbox(QMessageBox::Critical, EditorManager::tr("File Error"), - tr("Could not open \"%1\" for reading. " - "Either the file does not exist or you do not have " - "the permissions to open it.") - .arg(realFp.toUserOutput()), - QMessageBox::Ok, ICore::dialogParent()); - msgbox.exec(); - return nullptr; + if (factory->asEditorFactory()) { + editor = createEditor(factory->asEditorFactory(), filePath); + if (!editor) { + factory = factories.isEmpty() ? nullptr : factories.takeFirst(); + continue; + } + IDocument::OpenResult openResult = editor->document()->open(&errorString, + filePath, + realFp); + if (openResult == IDocument::OpenResult::Success) + break; + overrideCursor.reset(); + delete editor; + editor = nullptr; + if (openResult == IDocument::OpenResult::ReadError) { + QMessageBox msgbox(QMessageBox::Critical, + EditorManager::tr("File Error"), + tr("Could not open \"%1\" for reading. " + "Either the file does not exist or you do not have " + "the permissions to open it.") + .arg(realFp.toUserOutput()), + QMessageBox::Ok, + ICore::dialogParent()); + msgbox.exec(); + return nullptr; + } + // can happen e.g. when trying to open an completely empty .qrc file + QTC_CHECK(openResult == IDocument::OpenResult::CannotHandle); + } else { + QTC_ASSERT(factory->asExternalEditor(), + factory = factories.isEmpty() ? nullptr : factories.takeFirst(); + continue); + if (factory->asExternalEditor()->startEditor(filePath, &errorString)) + break; } - QTC_CHECK(openResult == IDocument::OpenResult::CannotHandle); if (errorString.isEmpty()) errorString = tr("Could not open \"%1\": Unknown error.").arg(realFp.toUserOutput()); @@ -881,12 +905,12 @@ IEditor *EditorManagerPrivate::openEditor(EditorView *view, const FilePath &file QMessageBox::Open | QMessageBox::Cancel, ICore::dialogParent()); - IEditorFactory *selectedFactory = nullptr; + EditorType *selectedFactory = nullptr; if (!factories.isEmpty()) { auto button = qobject_cast(msgbox.button(QMessageBox::Open)); QTC_ASSERT(button, return nullptr); auto menu = new QMenu(button); - foreach (IEditorFactory *factory, factories) { + foreach (EditorType *factory, factories) { QAction *action = menu->addAction(factory->displayName()); connect(action, &QAction::triggered, &msgbox, [&selectedFactory, factory, &msgbox]() { selectedFactory = factory; @@ -3020,8 +3044,11 @@ bool EditorManager::closeEditors(const QList &editorsToClose, bool ask */ void EditorManager::activateEditorForEntry(DocumentModel::Entry *entry, OpenEditorFlags flags) { + QTC_CHECK(!(flags & EditorManager::AllowExternalEditor)); + EditorManagerPrivate::activateEditorForEntry(EditorManagerPrivate::currentEditorView(), - entry, flags); + entry, + flags); } /*! @@ -3031,7 +3058,9 @@ void EditorManager::activateEditorForEntry(DocumentModel::Entry *entry, OpenEdit */ void EditorManager::activateEditor(IEditor *editor, OpenEditorFlags flags) { - QTC_ASSERT(editor, return); + QTC_CHECK(!(flags & EditorManager::AllowExternalEditor)); + + QTC_ASSERT(editor, return ); EditorView *view = EditorManagerPrivate::viewForEditor(editor); // an IEditor doesn't have to belong to a view, it might be kept in storage by the editor model if (!view) @@ -3045,7 +3074,11 @@ void EditorManager::activateEditor(IEditor *editor, OpenEditorFlags flags) */ IEditor *EditorManager::activateEditorForDocument(IDocument *document, OpenEditorFlags flags) { - return EditorManagerPrivate::activateEditorForDocument(EditorManagerPrivate::currentEditorView(), document, flags); + QTC_CHECK(!(flags & EditorManager::AllowExternalEditor)); + + return EditorManagerPrivate::activateEditorForDocument(EditorManagerPrivate::currentEditorView(), + document, + flags); } /*! @@ -3066,6 +3099,7 @@ IEditor *EditorManager::activateEditorForDocument(IDocument *document, OpenEdito IEditor *EditorManager::openEditor(const FilePath &filePath, Id editorId, OpenEditorFlags flags, bool *newEditor) { + checkEditorFlags(flags); if (flags & EditorManager::OpenInOtherSplit) EditorManager::gotoOtherSplit(); @@ -3097,6 +3131,7 @@ IEditor *EditorManager::openEditorAt(const Link &link, OpenEditorFlags flags, bool *newEditor) { + checkEditorFlags(flags); if (flags & EditorManager::OpenInOtherSplit) EditorManager::gotoOtherSplit(); @@ -3251,6 +3286,9 @@ IEditor *EditorManager::openEditorWithContents(Id editorId, const QString &uniqueId, OpenEditorFlags flags) { + QTC_CHECK(!(flags & EditorManager::AllowExternalEditor)); + checkEditorFlags(flags); + if (debugEditorManager) qDebug() << Q_FUNC_INFO << editorId.name() << titlePattern << uniqueId << contents; diff --git a/src/plugins/coreplugin/editormanager/editormanager.h b/src/plugins/coreplugin/editormanager/editormanager.h index 9658c4794e7..4b722c6dce9 100644 --- a/src/plugins/coreplugin/editormanager/editormanager.h +++ b/src/plugins/coreplugin/editormanager/editormanager.h @@ -84,7 +84,8 @@ public: DoNotSwitchToDesignMode = 16, DoNotSwitchToEditMode = 32, SwitchSplitIfAlreadyVisible = 64, - DoNotRaise = 128 + DoNotRaise = 128, + AllowExternalEditor = 256 }; Q_DECLARE_FLAGS(OpenEditorFlags, OpenEditorFlag) diff --git a/src/plugins/coreplugin/foldernavigationwidget.cpp b/src/plugins/coreplugin/foldernavigationwidget.cpp index 1f5dd3a7eba..4726b3264a4 100644 --- a/src/plugins/coreplugin/foldernavigationwidget.cpp +++ b/src/plugins/coreplugin/foldernavigationwidget.cpp @@ -616,7 +616,9 @@ void FolderNavigationWidget::openItem(const QModelIndex &index) if (m_fileSystemModel->isDir(index)) return; const QString path = m_fileSystemModel->filePath(index); - Core::EditorManager::openEditor(FilePath::fromString(path)); + Core::EditorManager::openEditor(FilePath::fromString(path), + {}, + Core::EditorManager::AllowExternalEditor); } void FolderNavigationWidget::createNewFolder(const QModelIndex &parent) diff --git a/src/plugins/coreplugin/locator/basefilefilter.cpp b/src/plugins/coreplugin/locator/basefilefilter.cpp index 4457419c0fe..8c34e305aac 100644 --- a/src/plugins/coreplugin/locator/basefilefilter.cpp +++ b/src/plugins/coreplugin/locator/basefilefilter.cpp @@ -237,11 +237,11 @@ void BaseFileFilter::openEditorAt(const LocatorFilterEntry& selection) const LineColumn lineColumn = LineColumn::extractFromFileName(postfix, postfixPos); if (postfixPos >= 0) { const Link link(selection.filePath, lineColumn.line, lineColumn.column); - EditorManager::openEditorAt(link); + EditorManager::openEditorAt(link, {}, Core::EditorManager::AllowExternalEditor); return; } } - EditorManager::openEditor(selection.filePath); + EditorManager::openEditor(selection.filePath, {}, Core::EditorManager::AllowExternalEditor); } /*! diff --git a/src/plugins/cppeditor/cpplocatorfilter.cpp b/src/plugins/cppeditor/cpplocatorfilter.cpp index 605513a4fd7..1def7351788 100644 --- a/src/plugins/cppeditor/cpplocatorfilter.cpp +++ b/src/plugins/cppeditor/cpplocatorfilter.cpp @@ -143,8 +143,11 @@ void CppLocatorFilter::accept(Core::LocatorFilterEntry selection, Q_UNUSED(selectionStart) Q_UNUSED(selectionLength) IndexItem::Ptr info = qvariant_cast(selection.internalData); - Core::EditorManager::openEditorAt( - {Utils::FilePath::fromString(info->fileName()), info->line(), info->column()}); + Core::EditorManager::openEditorAt({Utils::FilePath::fromString(info->fileName()), + info->line(), + info->column()}, + {}, + Core::EditorManager::AllowExternalEditor); } CppClassesFilter::CppClassesFilter(CppLocatorData *locatorData) diff --git a/src/plugins/cppeditor/symbolsfindfilter.cpp b/src/plugins/cppeditor/symbolsfindfilter.cpp index bd034245a23..05e16624a15 100644 --- a/src/plugins/cppeditor/symbolsfindfilter.cpp +++ b/src/plugins/cppeditor/symbolsfindfilter.cpp @@ -179,8 +179,11 @@ void SymbolsFindFilter::openEditor(const SearchResultItem &item) if (!item.userData().canConvert()) return; IndexItem::Ptr info = item.userData().value(); - EditorManager::openEditorAt( - {FilePath::fromString(info->fileName()), info->line(), info->column()}); + EditorManager::openEditorAt({FilePath::fromString(info->fileName()), + info->line(), + info->column()}, + {}, + Core::EditorManager::AllowExternalEditor); } QWidget *SymbolsFindFilter::createConfigWidget() diff --git a/src/plugins/languageclient/locatorfilter.cpp b/src/plugins/languageclient/locatorfilter.cpp index 11588cc2ad2..28d01d2c82d 100644 --- a/src/plugins/languageclient/locatorfilter.cpp +++ b/src/plugins/languageclient/locatorfilter.cpp @@ -226,9 +226,11 @@ void DocumentLocatorFilter::accept(Core::LocatorFilterEntry selection, if (selection.internalData.canConvert()) { auto lineColumn = qvariant_cast(selection.internalData); const Utils::Link link(m_currentUri.toFilePath(), lineColumn.line + 1, lineColumn.column); - Core::EditorManager::openEditorAt(link); + Core::EditorManager::openEditorAt(link, {}, Core::EditorManager::AllowExternalEditor); } else if (selection.internalData.canConvert()) { - Core::EditorManager::openEditorAt(qvariant_cast(selection.internalData)); + Core::EditorManager::openEditorAt(qvariant_cast(selection.internalData), + {}, + Core::EditorManager::AllowExternalEditor); } } @@ -329,7 +331,9 @@ void WorkspaceLocatorFilter::accept(Core::LocatorFilterEntry selection, int * /*selectionLength*/) const { if (selection.internalData.canConvert()) - Core::EditorManager::openEditorAt(qvariant_cast(selection.internalData)); + Core::EditorManager::openEditorAt(qvariant_cast(selection.internalData), + {}, + Core::EditorManager::AllowExternalEditor); } void WorkspaceLocatorFilter::handleResponse(Client *client, diff --git a/src/plugins/projectexplorer/projectexplorer.cpp b/src/plugins/projectexplorer/projectexplorer.cpp index ca1a621c18f..9efd7f2858b 100644 --- a/src/plugins/projectexplorer/projectexplorer.cpp +++ b/src/plugins/projectexplorer/projectexplorer.cpp @@ -3643,7 +3643,9 @@ void ProjectExplorerPluginPrivate::updateLocationSubMenus() : tr("%1 in %2").arg(li.displayName).arg(li.path.toUserOutput()); auto *action = new QAction(displayName, nullptr); connect(action, &QAction::triggered, this, [line, path]() { - Core::EditorManager::openEditorAt(Link(path, line)); + Core::EditorManager::openEditorAt(Link(path, line), + {}, + Core::EditorManager::AllowExternalEditor); }); projectMenu->addAction(action); diff --git a/src/plugins/projectexplorer/projecttreewidget.cpp b/src/plugins/projectexplorer/projecttreewidget.cpp index 1bc948531e3..6c5b70718ce 100644 --- a/src/plugins/projectexplorer/projecttreewidget.cpp +++ b/src/plugins/projectexplorer/projecttreewidget.cpp @@ -563,7 +563,9 @@ void ProjectTreeWidget::openItem(const QModelIndex &mainIndex) Node *node = m_model->nodeForIndex(mainIndex); if (!node || !node->asFileNode()) return; - IEditor *editor = EditorManager::openEditor(node->filePath()); + IEditor *editor = EditorManager::openEditor(node->filePath(), + {}, + Core::EditorManager::AllowExternalEditor); if (editor && node->line() >= 0) editor->gotoLine(node->line()); }