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 <qt_ci_bot@qt-project.org>
Reviewed-by: David Schulz <david.schulz@qt.io>
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
This commit is contained in:
Eike Ziller
2021-11-11 11:51:11 +01:00
parent 0bbc465f61
commit 2fab827782
10 changed files with 107 additions and 50 deletions

View File

@@ -185,7 +185,9 @@ void OpenCMakeTargetLocatorFilter::accept(Core::LocatorFilterEntry selection,
const auto file = FilePath::fromVariant(extraData.value("file")); const auto file = FilePath::fromVariant(extraData.value("file"));
if (line >= 0) if (line >= 0)
Core::EditorManager::openEditorAt({file, line}); Core::EditorManager::openEditorAt({file, line},
{},
Core::EditorManager::AllowExternalEditor);
else else
Core::EditorManager::openEditor(file); Core::EditorManager::openEditor(file, {}, Core::EditorManager::AllowExternalEditor);
} }

View File

@@ -135,6 +135,14 @@ using namespace Core;
using namespace Core::Internal; using namespace Core::Internal;
using namespace Utils; using namespace Utils;
static void checkEditorFlags(EditorManager::OpenEditorFlags flags)
{
if (flags & EditorManager::OpenInOtherSplit) {
QTC_CHECK(!(flags & EditorManager::SwitchSplitIfAlreadyVisible));
QTC_CHECK(!(flags & EditorManager::AllowExternalEditor));
}
}
//===================EditorManager===================== //===================EditorManager=====================
/*! /*!
@@ -812,13 +820,21 @@ IEditor *EditorManagerPrivate::openEditor(EditorView *view, const FilePath &file
return activateEditor(view, editor, flags); return activateEditor(view, editor, flags);
} }
if (skipOpeningBigTextFile(filePath))
return nullptr;
FilePath realFp = autoSaveName(filePath); FilePath realFp = autoSaveName(filePath);
if (!filePath.exists() || !realFp.exists() || filePath.lastModified() >= realFp.lastModified()) { if (!filePath.exists() || !realFp.exists() || filePath.lastModified() >= realFp.lastModified()) {
realFp.removeFile(); realFp.removeFile();
realFp = filePath; 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()) { if (factories.isEmpty()) {
Utils::MimeType mimeType = Utils::mimeTypeForFile(filePath); Utils::MimeType mimeType = Utils::mimeTypeForFile(filePath);
QMessageBox msgbox(QMessageBox::Critical, EditorManager::tr("File Error"), QMessageBox msgbox(QMessageBox::Critical, EditorManager::tr("File Error"),
@@ -829,48 +845,56 @@ IEditor *EditorManagerPrivate::openEditor(EditorView *view, const FilePath &file
return nullptr; return nullptr;
} }
if (editorId.isValid()) { if (editorId.isValid()) {
IEditorFactory *factory = Utils::findOrDefault(IEditorFactory::allEditorFactories(), EditorType *factory = EditorType::editorTypeForId(editorId);
Utils::equal(&IEditorFactory::id, editorId));
if (factory) { if (factory) {
QTC_CHECK(factory->asEditorFactory() || (flags & EditorManager::AllowExternalEditor));
factories.removeOne(factory); factories.removeOne(factory);
factories.push_front(factory); factories.push_front(factory);
} }
} }
if (skipOpeningBigTextFile(filePath))
return nullptr;
IEditor *editor = nullptr; IEditor *editor = nullptr;
auto overrideCursor = Utils::OverrideCursor(QCursor(Qt::WaitCursor)); auto overrideCursor = Utils::OverrideCursor(QCursor(Qt::WaitCursor));
IEditorFactory *factory = factories.takeFirst(); EditorType *factory = factories.takeFirst();
while (factory) { while (factory) {
editor = createEditor(factory, filePath);
if (!editor) {
factory = factories.takeFirst();
continue;
}
QString errorString; QString errorString;
IDocument::OpenResult openResult = editor->document()->open(&errorString, filePath, realFp);
if (openResult == IDocument::OpenResult::Success)
break;
overrideCursor.reset(); if (factory->asEditorFactory()) {
delete editor; editor = createEditor(factory->asEditorFactory(), filePath);
editor = nullptr; if (!editor) {
factory = factories.isEmpty() ? nullptr : factories.takeFirst();
if (openResult == IDocument::OpenResult::ReadError) { continue;
QMessageBox msgbox(QMessageBox::Critical, EditorManager::tr("File Error"), }
tr("Could not open \"%1\" for reading. " IDocument::OpenResult openResult = editor->document()->open(&errorString,
"Either the file does not exist or you do not have " filePath,
"the permissions to open it.") realFp);
.arg(realFp.toUserOutput()), if (openResult == IDocument::OpenResult::Success)
QMessageBox::Ok, ICore::dialogParent()); break;
msgbox.exec(); overrideCursor.reset();
return nullptr; 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()) if (errorString.isEmpty())
errorString = tr("Could not open \"%1\": Unknown error.").arg(realFp.toUserOutput()); 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, QMessageBox::Open | QMessageBox::Cancel,
ICore::dialogParent()); ICore::dialogParent());
IEditorFactory *selectedFactory = nullptr; EditorType *selectedFactory = nullptr;
if (!factories.isEmpty()) { if (!factories.isEmpty()) {
auto button = qobject_cast<QPushButton *>(msgbox.button(QMessageBox::Open)); auto button = qobject_cast<QPushButton *>(msgbox.button(QMessageBox::Open));
QTC_ASSERT(button, return nullptr); QTC_ASSERT(button, return nullptr);
auto menu = new QMenu(button); auto menu = new QMenu(button);
foreach (IEditorFactory *factory, factories) { foreach (EditorType *factory, factories) {
QAction *action = menu->addAction(factory->displayName()); QAction *action = menu->addAction(factory->displayName());
connect(action, &QAction::triggered, &msgbox, [&selectedFactory, factory, &msgbox]() { connect(action, &QAction::triggered, &msgbox, [&selectedFactory, factory, &msgbox]() {
selectedFactory = factory; selectedFactory = factory;
@@ -3020,8 +3044,11 @@ bool EditorManager::closeEditors(const QList<IEditor*> &editorsToClose, bool ask
*/ */
void EditorManager::activateEditorForEntry(DocumentModel::Entry *entry, OpenEditorFlags flags) void EditorManager::activateEditorForEntry(DocumentModel::Entry *entry, OpenEditorFlags flags)
{ {
QTC_CHECK(!(flags & EditorManager::AllowExternalEditor));
EditorManagerPrivate::activateEditorForEntry(EditorManagerPrivate::currentEditorView(), 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) 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); 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 // an IEditor doesn't have to belong to a view, it might be kept in storage by the editor model
if (!view) if (!view)
@@ -3045,7 +3074,11 @@ void EditorManager::activateEditor(IEditor *editor, OpenEditorFlags flags)
*/ */
IEditor *EditorManager::activateEditorForDocument(IDocument *document, 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, IEditor *EditorManager::openEditor(const FilePath &filePath, Id editorId,
OpenEditorFlags flags, bool *newEditor) OpenEditorFlags flags, bool *newEditor)
{ {
checkEditorFlags(flags);
if (flags & EditorManager::OpenInOtherSplit) if (flags & EditorManager::OpenInOtherSplit)
EditorManager::gotoOtherSplit(); EditorManager::gotoOtherSplit();
@@ -3097,6 +3131,7 @@ IEditor *EditorManager::openEditorAt(const Link &link,
OpenEditorFlags flags, OpenEditorFlags flags,
bool *newEditor) bool *newEditor)
{ {
checkEditorFlags(flags);
if (flags & EditorManager::OpenInOtherSplit) if (flags & EditorManager::OpenInOtherSplit)
EditorManager::gotoOtherSplit(); EditorManager::gotoOtherSplit();
@@ -3251,6 +3286,9 @@ IEditor *EditorManager::openEditorWithContents(Id editorId,
const QString &uniqueId, const QString &uniqueId,
OpenEditorFlags flags) OpenEditorFlags flags)
{ {
QTC_CHECK(!(flags & EditorManager::AllowExternalEditor));
checkEditorFlags(flags);
if (debugEditorManager) if (debugEditorManager)
qDebug() << Q_FUNC_INFO << editorId.name() << titlePattern << uniqueId << contents; qDebug() << Q_FUNC_INFO << editorId.name() << titlePattern << uniqueId << contents;

View File

@@ -84,7 +84,8 @@ public:
DoNotSwitchToDesignMode = 16, DoNotSwitchToDesignMode = 16,
DoNotSwitchToEditMode = 32, DoNotSwitchToEditMode = 32,
SwitchSplitIfAlreadyVisible = 64, SwitchSplitIfAlreadyVisible = 64,
DoNotRaise = 128 DoNotRaise = 128,
AllowExternalEditor = 256
}; };
Q_DECLARE_FLAGS(OpenEditorFlags, OpenEditorFlag) Q_DECLARE_FLAGS(OpenEditorFlags, OpenEditorFlag)

View File

@@ -616,7 +616,9 @@ void FolderNavigationWidget::openItem(const QModelIndex &index)
if (m_fileSystemModel->isDir(index)) if (m_fileSystemModel->isDir(index))
return; return;
const QString path = m_fileSystemModel->filePath(index); 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) void FolderNavigationWidget::createNewFolder(const QModelIndex &parent)

View File

@@ -237,11 +237,11 @@ void BaseFileFilter::openEditorAt(const LocatorFilterEntry& selection)
const LineColumn lineColumn = LineColumn::extractFromFileName(postfix, postfixPos); const LineColumn lineColumn = LineColumn::extractFromFileName(postfix, postfixPos);
if (postfixPos >= 0) { if (postfixPos >= 0) {
const Link link(selection.filePath, lineColumn.line, lineColumn.column); const Link link(selection.filePath, lineColumn.line, lineColumn.column);
EditorManager::openEditorAt(link); EditorManager::openEditorAt(link, {}, Core::EditorManager::AllowExternalEditor);
return; return;
} }
} }
EditorManager::openEditor(selection.filePath); EditorManager::openEditor(selection.filePath, {}, Core::EditorManager::AllowExternalEditor);
} }
/*! /*!

View File

@@ -143,8 +143,11 @@ void CppLocatorFilter::accept(Core::LocatorFilterEntry selection,
Q_UNUSED(selectionStart) Q_UNUSED(selectionStart)
Q_UNUSED(selectionLength) Q_UNUSED(selectionLength)
IndexItem::Ptr info = qvariant_cast<IndexItem::Ptr>(selection.internalData); IndexItem::Ptr info = qvariant_cast<IndexItem::Ptr>(selection.internalData);
Core::EditorManager::openEditorAt( Core::EditorManager::openEditorAt({Utils::FilePath::fromString(info->fileName()),
{Utils::FilePath::fromString(info->fileName()), info->line(), info->column()}); info->line(),
info->column()},
{},
Core::EditorManager::AllowExternalEditor);
} }
CppClassesFilter::CppClassesFilter(CppLocatorData *locatorData) CppClassesFilter::CppClassesFilter(CppLocatorData *locatorData)

View File

@@ -179,8 +179,11 @@ void SymbolsFindFilter::openEditor(const SearchResultItem &item)
if (!item.userData().canConvert<IndexItem::Ptr>()) if (!item.userData().canConvert<IndexItem::Ptr>())
return; return;
IndexItem::Ptr info = item.userData().value<IndexItem::Ptr>(); IndexItem::Ptr info = item.userData().value<IndexItem::Ptr>();
EditorManager::openEditorAt( EditorManager::openEditorAt({FilePath::fromString(info->fileName()),
{FilePath::fromString(info->fileName()), info->line(), info->column()}); info->line(),
info->column()},
{},
Core::EditorManager::AllowExternalEditor);
} }
QWidget *SymbolsFindFilter::createConfigWidget() QWidget *SymbolsFindFilter::createConfigWidget()

View File

@@ -226,9 +226,11 @@ void DocumentLocatorFilter::accept(Core::LocatorFilterEntry selection,
if (selection.internalData.canConvert<Utils::LineColumn>()) { if (selection.internalData.canConvert<Utils::LineColumn>()) {
auto lineColumn = qvariant_cast<Utils::LineColumn>(selection.internalData); auto lineColumn = qvariant_cast<Utils::LineColumn>(selection.internalData);
const Utils::Link link(m_currentUri.toFilePath(), lineColumn.line + 1, lineColumn.column); 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<Utils::Link>()) { } else if (selection.internalData.canConvert<Utils::Link>()) {
Core::EditorManager::openEditorAt(qvariant_cast<Utils::Link>(selection.internalData)); Core::EditorManager::openEditorAt(qvariant_cast<Utils::Link>(selection.internalData),
{},
Core::EditorManager::AllowExternalEditor);
} }
} }
@@ -329,7 +331,9 @@ void WorkspaceLocatorFilter::accept(Core::LocatorFilterEntry selection,
int * /*selectionLength*/) const int * /*selectionLength*/) const
{ {
if (selection.internalData.canConvert<Utils::Link>()) if (selection.internalData.canConvert<Utils::Link>())
Core::EditorManager::openEditorAt(qvariant_cast<Utils::Link>(selection.internalData)); Core::EditorManager::openEditorAt(qvariant_cast<Utils::Link>(selection.internalData),
{},
Core::EditorManager::AllowExternalEditor);
} }
void WorkspaceLocatorFilter::handleResponse(Client *client, void WorkspaceLocatorFilter::handleResponse(Client *client,

View File

@@ -3643,7 +3643,9 @@ void ProjectExplorerPluginPrivate::updateLocationSubMenus()
: tr("%1 in %2").arg(li.displayName).arg(li.path.toUserOutput()); : tr("%1 in %2").arg(li.displayName).arg(li.path.toUserOutput());
auto *action = new QAction(displayName, nullptr); auto *action = new QAction(displayName, nullptr);
connect(action, &QAction::triggered, this, [line, path]() { 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); projectMenu->addAction(action);

View File

@@ -563,7 +563,9 @@ void ProjectTreeWidget::openItem(const QModelIndex &mainIndex)
Node *node = m_model->nodeForIndex(mainIndex); Node *node = m_model->nodeForIndex(mainIndex);
if (!node || !node->asFileNode()) if (!node || !node->asFileNode())
return; return;
IEditor *editor = EditorManager::openEditor(node->filePath()); IEditor *editor = EditorManager::openEditor(node->filePath(),
{},
Core::EditorManager::AllowExternalEditor);
if (editor && node->line() >= 0) if (editor && node->line() >= 0)
editor->gotoLine(node->line()); editor->gotoLine(node->line());
} }