diff options
Diffstat (limited to 'src/plugins/texteditor')
44 files changed, 1549 insertions, 1824 deletions
diff --git a/src/plugins/texteditor/CMakeLists.txt b/src/plugins/texteditor/CMakeLists.txt index 886b98390ce..22abf9d8a1e 100644 --- a/src/plugins/texteditor/CMakeLists.txt +++ b/src/plugins/texteditor/CMakeLists.txt @@ -92,11 +92,9 @@ add_qtc_plugin(TextEditor snippets/snippetparser.cpp snippets/snippetparser.h snippets/snippetprovider.cpp snippets/snippetprovider.h snippets/snippetscollection.cpp snippets/snippetscollection.h - snippets/snippetssettings.cpp snippets/snippetssettings.h snippets/snippetssettingspage.cpp snippets/snippetssettingspage.h storagesettings.cpp storagesettings.h syntaxhighlighter.cpp syntaxhighlighter.h - syntaxhighlighterrunner.cpp syntaxhighlighterrunner.h tabsettings.cpp tabsettings.h tabsettingswidget.cpp tabsettingswidget.h textdocument.cpp textdocument.h @@ -105,7 +103,6 @@ add_qtc_plugin(TextEditor texteditor.qrc texteditor_global.h texteditortr.h - texteditoractionhandler.cpp texteditoractionhandler.h texteditorconstants.cpp texteditorconstants.h texteditoroverlay.cpp texteditoroverlay.h texteditorplugin.cpp @@ -113,6 +110,7 @@ add_qtc_plugin(TextEditor textindenter.cpp textindenter.h textmark.cpp textmark.h textstyles.h + typehierarchy.cpp typehierarchy.h typingsettings.cpp typingsettings.h ) diff --git a/src/plugins/texteditor/TextEditor.json.in b/src/plugins/texteditor/TextEditor.json.in index f2b6fbbcf58..42e32325edd 100644 --- a/src/plugins/texteditor/TextEditor.json.in +++ b/src/plugins/texteditor/TextEditor.json.in @@ -14,7 +14,7 @@ ], "Category" : "Core", "Description" : "Text editor framework and the implementation of the basic text editor.", - "Url" : "http://www.qt.io", + "Url" : "https://www.qt.io", ${IDE_PLUGIN_DEPENDENCIES}, "Mimetypes" : [ "<?xml version='1.0'?>", diff --git a/src/plugins/texteditor/basefilefind.cpp b/src/plugins/texteditor/basefilefind.cpp index 399e275fe4f..d18a75531b3 100644 --- a/src/plugins/texteditor/basefilefind.cpp +++ b/src/plugins/texteditor/basefilefind.cpp @@ -21,7 +21,7 @@ #include <utils/algorithm.h> #include <utils/fadingindicator.h> #include <utils/futuresynchronizer.h> -#include <utils/process.h> +#include <utils/qtcprocess.h> #include <utils/qtcassert.h> #include <QComboBox> @@ -139,8 +139,8 @@ public: QString title() const override { return Tr::tr("Internal"); } QString toolTip() const override { return {}; } QWidget *widget() const override { return m_widget; } - void readSettings(QtcSettings * /*settings*/) override {} - void writeSettings(QtcSettings * /*settings*/) const override {} + void readSettings(const Store &) override {} + void writeSettings(Store &) const override {} SearchExecutor searchExecutor() const override { return [](const FileFindParameters ¶meters) { @@ -442,47 +442,57 @@ FilePath BaseFileFind::searchDir() const return d->m_searchDir; } -void BaseFileFind::writeCommonSettings(QtcSettings *settings) +void BaseFileFind::writeCommonSettings( + Store &s, const QString &defaultFilter, const QString &defaultExclusionFilter) const { const auto fromNativeSeparators = [](const QStringList &files) -> QStringList { return Utils::transform(files, &QDir::fromNativeSeparators); }; - settings->setValue("filters", fromNativeSeparators(d->m_filterStrings.stringList())); - if (d->m_filterCombo) - settings->setValue("currentFilter", - QDir::fromNativeSeparators(d->m_filterCombo->currentText())); - settings->setValue("exclusionFilters", fromNativeSeparators(d->m_exclusionStrings.stringList())); - if (d->m_exclusionCombo) - settings->setValue("currentExclusionFilter", - QDir::fromNativeSeparators(d->m_exclusionCombo->currentText())); + const QStringList filterStrings = fromNativeSeparators(d->m_filterStrings.stringList()); + if (filterStrings.size() != 1 || filterStrings.first() != defaultFilter) + s.insert("filters", filterStrings); + const QString currentFilter = d->m_filterCombo + ? QDir::fromNativeSeparators(d->m_filterCombo->currentText()) + : d->m_filterSetting; + if (currentFilter != defaultFilter) + s.insert("currentFilter", currentFilter); + const QStringList exclusionFilters = fromNativeSeparators(d->m_exclusionStrings.stringList()); + if (exclusionFilters.size() != 1 || exclusionFilters.first() != defaultExclusionFilter) + s.insert("exclusionFilters", exclusionFilters); + const QString currentExclusionFilter = d->m_exclusionCombo ? QDir::fromNativeSeparators( + d->m_exclusionCombo->currentText()) + : d->m_exclusionSetting; + if (currentExclusionFilter != defaultExclusionFilter) + s.insert("currentExclusionFilter", currentExclusionFilter); for (const SearchEngine *searchEngine : std::as_const(d->m_searchEngines)) - searchEngine->writeSettings(settings); - settings->setValue("currentSearchEngineIndex", d->m_currentSearchEngineIndex); + searchEngine->writeSettings(s); + if (d->m_currentSearchEngineIndex != 0) + s.insert("currentSearchEngineIndex", d->m_currentSearchEngineIndex); } -void BaseFileFind::readCommonSettings(QtcSettings *settings, const QString &defaultFilter, - const QString &defaultExclusionFilter) +void BaseFileFind::readCommonSettings( + const Store &s, const QString &defaultFilter, const QString &defaultExclusionFilter) { const auto toNativeSeparators = [](const QStringList &files) -> QStringList { return Utils::transform(files, &QDir::toNativeSeparators); }; - const QStringList filterSetting = settings->value("filters").toStringList(); + const QStringList filterSetting = s.value("filters").toStringList(); const QStringList filters = filterSetting.isEmpty() ? QStringList(defaultFilter) : filterSetting; - const QVariant currentFilter = settings->value("currentFilter"); + const QVariant currentFilter = s.value("currentFilter"); d->m_filterSetting = currentFilter.isValid() ? currentFilter.toString() : filters.first(); d->m_filterStrings.setStringList(toNativeSeparators(filters)); if (d->m_filterCombo) syncComboWithSettings(d->m_filterCombo, d->m_filterSetting); - QStringList exclusionFilters = settings->value("exclusionFilters").toStringList(); + QStringList exclusionFilters = s.value("exclusionFilters").toStringList(); if (!exclusionFilters.contains(defaultExclusionFilter)) exclusionFilters << defaultExclusionFilter; - const QVariant currentExclusionFilter = settings->value("currentExclusionFilter"); + const QVariant currentExclusionFilter = s.value("currentExclusionFilter"); d->m_exclusionSetting = currentExclusionFilter.isValid() ? currentExclusionFilter.toString() : exclusionFilters.first(); d->m_exclusionStrings.setStringList(toNativeSeparators(exclusionFilters)); @@ -490,8 +500,8 @@ void BaseFileFind::readCommonSettings(QtcSettings *settings, const QString &defa syncComboWithSettings(d->m_exclusionCombo, d->m_exclusionSetting); for (SearchEngine* searchEngine : std::as_const(d->m_searchEngines)) - searchEngine->readSettings(settings); - const int currentSearchEngineIndex = settings->value("currentSearchEngineIndex", 0).toInt(); + searchEngine->readSettings(s); + const int currentSearchEngineIndex = s.value("currentSearchEngineIndex", 0).toInt(); syncSearchEngineCombo(currentSearchEngineIndex); } @@ -594,8 +604,7 @@ FilePaths BaseFileFind::replaceAll(const QString &text, const SearchResultItems item.mainRange().end.column + 1); changeSet.replace(start, end, replacement); } - file->setChangeSet(changeSet); - file->apply(); + file->apply(changeSet); } return changes.keys(); diff --git a/src/plugins/texteditor/basefilefind.h b/src/plugins/texteditor/basefilefind.h index 47adae4b866..de4286c31b6 100644 --- a/src/plugins/texteditor/basefilefind.h +++ b/src/plugins/texteditor/basefilefind.h @@ -69,8 +69,8 @@ public: virtual QString title() const = 0; virtual QString toolTip() const = 0; // add %1 placeholder where the find flags should be put virtual QWidget *widget() const = 0; - virtual void readSettings(Utils::QtcSettings *settings) = 0; - virtual void writeSettings(Utils::QtcSettings *settings) const = 0; + virtual void readSettings(const Utils::Store &s) = 0; + virtual void writeSettings(Utils::Store &settings) const = 0; virtual SearchExecutor searchExecutor() const = 0; virtual EditorOpener editorOpener() const { return {}; } bool isEnabled() const; @@ -108,8 +108,10 @@ protected: virtual QString toolTip() const = 0; // see Core::SearchResultWindow::startNewSearch, // add %1 placeholder where the find flags should be put - void writeCommonSettings(Utils::QtcSettings *settings); - void readCommonSettings(Utils::QtcSettings *settings, const QString &defaultFilter, const QString &defaultExclusionFilter); + void writeCommonSettings( + Utils::Store &s, const QString &defaultFilter, const QString &defaultExclusionFilter) const; + void readCommonSettings( + const Utils::Store &s, const QString &defaultFilter, const QString &defaultExclusionFilter); QList<QPair<QWidget *, QWidget *>> createPatternWidgets(); QStringList fileNameFilters() const; QStringList fileExclusionFilters() const; diff --git a/src/plugins/texteditor/bookmarkmanager.cpp b/src/plugins/texteditor/bookmarkmanager.cpp index 6dcb4de6cc5..a61cc587363 100644 --- a/src/plugins/texteditor/bookmarkmanager.cpp +++ b/src/plugins/texteditor/bookmarkmanager.cpp @@ -876,6 +876,7 @@ void BookmarkManager::edit() auto layout = new QFormLayout(&dlg); auto noteEdit = new QLineEdit(b->note()); noteEdit->setMinimumWidth(300); + noteEdit->setFocus(); auto lineNumberSpinbox = new QSpinBox; lineNumberSpinbox->setRange(1, INT_MAX); lineNumberSpinbox->setValue(b->lineNumber()); diff --git a/src/plugins/texteditor/commentssettings.cpp b/src/plugins/texteditor/commentssettings.cpp index bc6f76cb4bd..d3171a94d68 100644 --- a/src/plugins/texteditor/commentssettings.cpp +++ b/src/plugins/texteditor/commentssettings.cpp @@ -106,7 +106,7 @@ CommentsSettingsWidget::CommentsSettingsWidget(const CommentsSettings::Data &set const auto commandPrefixLabel = new QLabel(Tr::tr("Doxygen command prefix:")); const QString commandPrefixToolTip = Tr::tr(R"(Doxygen allows "@" and "\" to start commands. By default, "@" is used if the surrounding comment starts with "/**" or "///", and "\" is used -if the comment starts with "/*!" or "//!)"); +if the comment starts with "/*!" or "//!".)"); commandPrefixLabel->setToolTip(commandPrefixToolTip); d->m_commandPrefixComboBox.setToolTip(commandPrefixToolTip); d->m_commandPrefixComboBox.addItem(Tr::tr("Automatic")); diff --git a/src/plugins/texteditor/completionsettingspage.cpp b/src/plugins/texteditor/completionsettingspage.cpp index aba86467143..03f45404c6b 100644 --- a/src/plugins/texteditor/completionsettingspage.cpp +++ b/src/plugins/texteditor/completionsettingspage.cpp @@ -214,7 +214,7 @@ CompletionSettingsPageWidget::CompletionSettingsPageWidget(CompletionSettingsPag } }, Group { - title(Tr::tr("&Automatically insert matching characters")), + title(Tr::tr("&Automatically Insert Matching Characters")), Row { Column { m_insertBrackets, diff --git a/src/plugins/texteditor/displaysettingspage.cpp b/src/plugins/texteditor/displaysettingspage.cpp index b5435ed5e91..fe914b27ce9 100644 --- a/src/plugins/texteditor/displaysettingspage.cpp +++ b/src/plugins/texteditor/displaysettingspage.cpp @@ -108,7 +108,7 @@ public: rightAligned->setChecked(true); betweenLines = new QRadioButton(Tr::tr("Between lines")); - displayAnnotations = new QGroupBox(Tr::tr("Line annotations")), + displayAnnotations = new QGroupBox(Tr::tr("Line Annotations")), displayAnnotations->setCheckable(true); using namespace Layouting; diff --git a/src/plugins/texteditor/findincurrentfile.cpp b/src/plugins/texteditor/findincurrentfile.cpp index 7d3ad849972..45121f72d84 100644 --- a/src/plugins/texteditor/findincurrentfile.cpp +++ b/src/plugins/texteditor/findincurrentfile.cpp @@ -27,8 +27,8 @@ private: QString id() const final; QString displayName() const final; bool isEnabled() const final; - void writeSettings(Utils::QtcSettings *settings) final; - void readSettings(Utils::QtcSettings *settings) final; + Utils::Store save() const final; + void restore(const Utils::Store &s) final; QString label() const final; QString toolTip() const final; @@ -37,6 +37,9 @@ private: void handleFileChange(Core::IEditor *editor); QPointer<Core::IDocument> m_currentDocument; + + // deprecated + QByteArray settingsKey() const final; }; FindInCurrentFile::FindInCurrentFile() @@ -97,18 +100,24 @@ void FindInCurrentFile::handleFileChange(Core::IEditor *editor) } } -void FindInCurrentFile::writeSettings(QtcSettings *settings) +const char kDefaultInclusion[] = "*"; +const char kDefaultExclusion[] = ""; + +Store FindInCurrentFile::save() const +{ + Store s; + writeCommonSettings(s, kDefaultInclusion, kDefaultExclusion); + return s; +} + +void FindInCurrentFile::restore(const Store &s) { - settings->beginGroup("FindInCurrentFile"); - writeCommonSettings(settings); - settings->endGroup(); + readCommonSettings(s, kDefaultInclusion, kDefaultExclusion); } -void FindInCurrentFile::readSettings(QtcSettings *settings) +QByteArray FindInCurrentFile::settingsKey() const { - settings->beginGroup("FindInCurrentFile"); - readCommonSettings(settings, "*", ""); - settings->endGroup(); + return "FindInCurrentFile"; } void setupFindInCurrentFile() diff --git a/src/plugins/texteditor/findinfiles.cpp b/src/plugins/texteditor/findinfiles.cpp index 955316954b5..9dc68cd6532 100644 --- a/src/plugins/texteditor/findinfiles.cpp +++ b/src/plugins/texteditor/findinfiles.cpp @@ -188,18 +188,24 @@ QWidget *FindInFiles::createConfigWidget() return m_configWidget; } -void FindInFiles::writeSettings(QtcSettings *settings) +const char kDefaultInclusion[] = "*.cpp,*.h"; +const char kDefaultExclusion[] = "*/.git/*,*/.cvs/*,*/.svn/*,*.autosave,*/build/*"; + +Store FindInFiles::save() const +{ + Store s; + writeCommonSettings(s, kDefaultInclusion, kDefaultExclusion); + return s; +} + +void FindInFiles::restore(const Utils::Store &s) { - settings->beginGroup("FindInFiles"); - writeCommonSettings(settings); - settings->endGroup(); + readCommonSettings(s, kDefaultInclusion, kDefaultExclusion); } -void FindInFiles::readSettings(QtcSettings *settings) +QByteArray FindInFiles::settingsKey() const { - settings->beginGroup("FindInFiles"); - readCommonSettings(settings, "*.cpp,*.h", "*/.git/*,*/.cvs/*,*/.svn/*,*.autosave"); - settings->endGroup(); + return "FindInFiles"; } void FindInFiles::setBaseDirectory(const FilePath &directory) diff --git a/src/plugins/texteditor/findinfiles.h b/src/plugins/texteditor/findinfiles.h index c7803a16ed4..b3bd7b7095b 100644 --- a/src/plugins/texteditor/findinfiles.h +++ b/src/plugins/texteditor/findinfiles.h @@ -30,8 +30,8 @@ public: QString id() const override; QString displayName() const override; QWidget *createConfigWidget() override; - void writeSettings(Utils::QtcSettings *settings) override; - void readSettings(Utils::QtcSettings *settings) override; + Utils::Store save() const override; + void restore(const Utils::Store &s) override; bool isValid() const override; void setDirectory(const Utils::FilePath &directory); @@ -39,6 +39,9 @@ public: static void findOnFileSystem(const QString &path); static FindInFiles *instance(); + // deprecated + QByteArray settingsKey() const override; + protected: QString label() const override; QString toolTip() const override; diff --git a/src/plugins/texteditor/findinopenfiles.cpp b/src/plugins/texteditor/findinopenfiles.cpp index dcd505cbe1f..deeb66e9f5f 100644 --- a/src/plugins/texteditor/findinopenfiles.cpp +++ b/src/plugins/texteditor/findinopenfiles.cpp @@ -25,14 +25,17 @@ private: QString id() const final; QString displayName() const final; bool isEnabled() const final; - void writeSettings(Utils::QtcSettings *settings) final; - void readSettings(Utils::QtcSettings *settings) final; + Utils::Store save() const final; + void restore(const Utils::Store &s) final; QString label() const final; QString toolTip() const final; FileContainerProvider fileContainerProvider() const final; void updateEnabledState() { emit enabledChanged(isEnabled()); } + + // deprecated + QByteArray settingsKey() const final; }; FindInOpenFiles::FindInOpenFiles() @@ -90,21 +93,25 @@ bool FindInOpenFiles::isEnabled() const return Core::DocumentModel::entryCount() > 0; } -void FindInOpenFiles::writeSettings(QtcSettings *settings) +const char kDefaultInclusion[] = "*"; +const char kDefaultExclusion[] = ""; + +Store FindInOpenFiles::save() const { - settings->beginGroup("FindInOpenFiles"); - writeCommonSettings(settings); - settings->endGroup(); + Store s; + writeCommonSettings(s, kDefaultInclusion, kDefaultExclusion); + return s; } -void FindInOpenFiles::readSettings(QtcSettings *settings) +void FindInOpenFiles::restore(const Store &s) { - settings->beginGroup("FindInOpenFiles"); - readCommonSettings(settings, "*", ""); - settings->endGroup(); + readCommonSettings(s, kDefaultInclusion, kDefaultExclusion); } - +QByteArray FindInOpenFiles::settingsKey() const +{ + return "FindInOpenFiles"; +} void setupFindInOpenFiles() { diff --git a/src/plugins/texteditor/fontsettings.cpp b/src/plugins/texteditor/fontsettings.cpp index e74a37c0d37..44b179d01c8 100644 --- a/src/plugins/texteditor/fontsettings.cpp +++ b/src/plugins/texteditor/fontsettings.cpp @@ -491,8 +491,7 @@ static QString defaultFontFamily() return QLatin1String("Menlo"); const QString sourceCodePro(g_sourceCodePro); - const QFontDatabase dataBase; - if (dataBase.hasFamily(sourceCodePro)) + if (QFontDatabase::hasFamily(sourceCodePro)) return sourceCodePro; if (Utils::HostOsInfo::isAnyUnixHost()) diff --git a/src/plugins/texteditor/fontsettingspage.cpp b/src/plugins/texteditor/fontsettingspage.cpp index bdd899b7e04..f528496dcf0 100644 --- a/src/plugins/texteditor/fontsettingspage.cpp +++ b/src/plugins/texteditor/fontsettingspage.cpp @@ -458,15 +458,14 @@ void FontSettingsPageWidget::updateFontZoom(const FontSettings &fontSettings) QList<int> FontSettingsPageWidget::pointSizesForSelectedFont() const { - QFontDatabase db; const QString familyName = m_fontComboBox->currentFont().family(); - QList<int> sizeLst = db.pointSizes(familyName); + QList<int> sizeLst = QFontDatabase::pointSizes(familyName); if (!sizeLst.isEmpty()) return sizeLst; - QStringList styles = db.styles(familyName); + QStringList styles = QFontDatabase::styles(familyName); if (!styles.isEmpty()) - sizeLst = db.pointSizes(familyName, styles.first()); + sizeLst = QFontDatabase::pointSizes(familyName, styles.first()); if (sizeLst.isEmpty()) sizeLst = QFontDatabase::standardSizes(); diff --git a/src/plugins/texteditor/formattexteditor.cpp b/src/plugins/texteditor/formattexteditor.cpp index 14f3d4e4e78..4bd358e3e39 100644 --- a/src/plugins/texteditor/formattexteditor.cpp +++ b/src/plugins/texteditor/formattexteditor.cpp @@ -12,8 +12,10 @@ #include <utils/async.h> #include <utils/differ.h> -#include <utils/process.h> +#include <utils/expected.h> +#include <utils/qtcprocess.h> #include <utils/qtcassert.h> +#include <utils/qtcprocess.h> #include <utils/temporarydirectory.h> #include <utils/textutils.h> @@ -27,6 +29,17 @@ using namespace std::chrono_literals; namespace TextEditor { +struct FormatInput +{ + Utils::FilePath filePath; + QString sourceData; + TextEditor::Command command; + int startPos = -1; + int endPos = 0; +}; + +using FormatOutput = expected_str<QString>; + void formatCurrentFile(const Command &command, int startPos, int endPos) { if (TextEditorWidget *editor = TextEditorWidget::currentTextEditorWidget()) @@ -40,89 +53,80 @@ static QString sourceData(TextEditorWidget *editor, int startPos, int endPos) : Utils::Text::textAt(editor->textCursor(), startPos, (endPos - startPos)); } -static FormatTask format(FormatTask task) +static FormatOutput format(const FormatInput &input) { - task.error.clear(); - task.formattedData.clear(); - - const FilePath executable = task.command.executable(); + const FilePath executable = input.command.executable(); if (executable.isEmpty()) - return task; + return {}; - switch (task.command.processing()) { + switch (input.command.processing()) { case Command::FileProcessing: { // Save text to temporary file Utils::TempFileSaver sourceFile(Utils::TemporaryDirectory::masterDirectoryPath() - + "/qtc_beautifier_XXXXXXXX." - + task.filePath.suffix()); + + "/qtc_beautifier_XXXXXXXX." + input.filePath.suffix()); sourceFile.setAutoRemove(true); - sourceFile.write(task.sourceData.toUtf8()); + sourceFile.write(input.sourceData.toUtf8()); if (!sourceFile.finalize()) { - task.error = Tr::tr("Cannot create temporary file \"%1\": %2.") - .arg(sourceFile.filePath().toUserOutput(), sourceFile.errorString()); - return task; + return Utils::make_unexpected(Tr::tr("Cannot create temporary file \"%1\": %2.") + .arg(sourceFile.filePath().toUserOutput(), sourceFile.errorString())); } // Format temporary file - QStringList options = task.command.options(); + QStringList options = input.command.options(); options.replaceInStrings(QLatin1String("%file"), sourceFile.filePath().toString()); Process process; process.setCommand({executable, options}); process.runBlocking(5s); if (process.result() != ProcessResult::FinishedWithSuccess) { - task.error = Tr::tr("Failed to format: %1.").arg(process.exitMessage()); - return task; + return Utils::make_unexpected(Tr::tr("Failed to format: %1.") + .arg(process.exitMessage())); } const QString output = process.cleanedStdErr(); if (!output.isEmpty()) - task.error = executable.toUserOutput() + ": " + output; + return Utils::make_unexpected(executable.toUserOutput() + ": " + output); // Read text back Utils::FileReader reader; if (!reader.fetch(sourceFile.filePath(), QIODevice::Text)) { - task.error = Tr::tr("Cannot read file \"%1\": %2.") - .arg(sourceFile.filePath().toUserOutput(), reader.errorString()); - return task; + return Utils::make_unexpected(Tr::tr("Cannot read file \"%1\": %2.") + .arg(sourceFile.filePath().toUserOutput(), reader.errorString())); } - task.formattedData = QString::fromUtf8(reader.data()); + return QString::fromUtf8(reader.data()); } - return task; case Command::PipeProcessing: { Process process; - QStringList options = task.command.options(); - options.replaceInStrings("%filename", task.filePath.fileName()); - options.replaceInStrings("%file", task.filePath.toString()); + QStringList options = input.command.options(); + options.replaceInStrings("%filename", input.filePath.fileName()); + options.replaceInStrings("%file", input.filePath.toString()); process.setCommand({executable, options}); - process.setWriteData(task.sourceData.toUtf8()); + process.setWriteData(input.sourceData.toUtf8()); process.start(); if (!process.waitForFinished(5s)) { - task.error = Tr::tr("Cannot call %1 or some other error occurred. Timeout " - "reached while formatting file %2.") - .arg(executable.toUserOutput(), task.filePath.displayName()); - return task; + return Utils::make_unexpected(Tr::tr("Cannot call %1 or some other error occurred. " + "Timeout reached while formatting file %2.") + .arg(executable.toUserOutput(), input.filePath.displayName())); } const QString errorText = process.readAllStandardError(); if (!errorText.isEmpty()) { - task.error = QString("%1: %2").arg(executable.toUserOutput(), errorText); - return task; + return Utils::make_unexpected(QString("%1: %2").arg(executable.toUserOutput(), + errorText)); } - task.formattedData = process.readAllStandardOutput(); + QString formattedData = process.readAllStandardOutput(); - if (task.command.pipeAddsNewline() && task.formattedData.endsWith('\n')) { - task.formattedData.chop(1); - if (task.formattedData.endsWith('\r')) - task.formattedData.chop(1); + if (input.command.pipeAddsNewline() && formattedData.endsWith('\n')) { + formattedData.chop(1); + if (formattedData.endsWith('\r')) + formattedData.chop(1); } - if (task.command.returnsCRLF()) - task.formattedData.replace("\r\n", "\n"); + if (input.command.returnsCRLF()) + formattedData.replace("\r\n", "\n"); - return task; + return formattedData; } } - - return task; + return {}; } /** @@ -256,31 +260,26 @@ static void showError(const QString &error) * Checks the state of @a task and if the formatting was successful calls updateEditorText() with * the respective members of @a task. */ -static void checkAndApplyTask(const FormatTask &task) +static void checkAndApplyTask(const QPointer<QPlainTextEdit> &textEditor, const FormatInput &input, + const FormatOutput &output) { - if (!task.error.isEmpty()) { - showError(task.error); + if (!output.has_value()) { + showError(output.error()); return; } - if (task.formattedData.isEmpty()) { - showError(Tr::tr("Could not format file %1.").arg( - task.filePath.displayName())); + if (output->isEmpty()) { + showError(Tr::tr("Could not format file %1.").arg(input.filePath.displayName())); return; } - QPlainTextEdit *textEditor = task.editor; if (!textEditor) { - showError(Tr::tr("File %1 was closed.").arg( - task.filePath.displayName())); + showError(Tr::tr("File %1 was closed.").arg(input.filePath.displayName())); return; } - const QString formattedData = (task.startPos < 0) - ? task.formattedData - : QString(textEditor->toPlainText()).replace( - task.startPos, (task.endPos - task.startPos), task.formattedData); - + const QString formattedData = (input.startPos < 0) ? *output + : textEditor->toPlainText().replace(input.startPos, (input.endPos - input.startPos), *output); updateEditorText(textEditor, formattedData); } @@ -298,8 +297,8 @@ void formatEditor(TextEditorWidget *editor, const Command &command, int startPos const QString sd = sourceData(editor, startPos, endPos); if (sd.isEmpty()) return; - checkAndApplyTask(format(FormatTask(editor, editor->textDocument()->filePath(), sd, - command, startPos, endPos))); + const FormatInput input{editor->textDocument()->filePath(), sd, command, startPos, endPos}; + checkAndApplyTask(editor, input, format(input)); } /** @@ -313,18 +312,20 @@ void formatEditorAsync(TextEditorWidget *editor, const Command &command, int sta if (sd.isEmpty()) return; - auto watcher = new QFutureWatcher<FormatTask>; + auto watcher = new QFutureWatcher<FormatOutput>; const TextDocument *doc = editor->textDocument(); - QObject::connect(doc, &TextDocument::contentsChanged, watcher, &QFutureWatcher<FormatTask>::cancel); - QObject::connect(watcher, &QFutureWatcherBase::finished, [watcher] { + const FormatInput input{doc->filePath(), sd, command, startPos, endPos}; + QObject::connect(doc, &TextDocument::contentsChanged, watcher, + &QFutureWatcher<FormatOutput>::cancel); + QObject::connect(watcher, &QFutureWatcherBase::finished, watcher, + [watcher, editor = QPointer<QPlainTextEdit>(editor), input] { if (watcher->isCanceled()) showError(Tr::tr("File was modified.")); else - checkAndApplyTask(watcher->result()); + checkAndApplyTask(editor, input, watcher->result()); watcher->deleteLater(); }); - watcher->setFuture(Utils::asyncRun(&format, FormatTask(editor, doc->filePath(), sd, - command, startPos, endPos))); + watcher->setFuture(Utils::asyncRun(&format, input)); } } // namespace TextEditor diff --git a/src/plugins/texteditor/formattexteditor.h b/src/plugins/texteditor/formattexteditor.h index e1c58cdf94f..929763ecba8 100644 --- a/src/plugins/texteditor/formattexteditor.h +++ b/src/plugins/texteditor/formattexteditor.h @@ -10,35 +10,11 @@ #include <utils/filepath.h> #include <QPlainTextEdit> -#include <QPointer> namespace TextEditor { class TextEditorWidget; -class TEXTEDITOR_EXPORT FormatTask -{ -public: - FormatTask(QPlainTextEdit *_editor, const Utils::FilePath &_filePath, const QString &_sourceData, - const Command &_command, int _startPos = -1, int _endPos = 0) : - editor(_editor), - filePath(_filePath), - sourceData(_sourceData), - command(_command), - startPos(_startPos), - endPos(_endPos) - {} - - QPointer<QPlainTextEdit> editor; - Utils::FilePath filePath; - QString sourceData; - TextEditor::Command command; - int startPos = -1; - int endPos = 0; - QString formattedData; - QString error; -}; - TEXTEDITOR_EXPORT void formatCurrentFile(const TextEditor::Command &command, int startPos = -1, int endPos = 0); TEXTEDITOR_EXPORT void formatEditor(TextEditorWidget *editor, const TextEditor::Command &command, int startPos = -1, int endPos = 0); diff --git a/src/plugins/texteditor/highlighter_test.cpp b/src/plugins/texteditor/highlighter_test.cpp index d50c560e23a..2cbd2b75d57 100644 --- a/src/plugins/texteditor/highlighter_test.cpp +++ b/src/plugins/texteditor/highlighter_test.cpp @@ -1,11 +1,10 @@ // Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -#include "syntaxhighlighterrunner.h" - #include "highlighter_test.h" #include "fontsettings.h" +#include "syntaxhighlighter.h" #include "textdocument.h" #include "texteditor.h" #include "texteditorsettings.h" @@ -61,7 +60,7 @@ void GenerigHighlighterTests::initTestCase() m_editor->editorWidget()->configureGenericHighlighter( Utils::mimeTypeForName(Utils::Constants::JSON_MIMETYPE)); QVERIFY(m_editor); - m_editor->textDocument()->syntaxHighlighterRunner()->rehighlight(); + m_editor->textDocument()->syntaxHighlighter()->rehighlight(); } using FormatRanges = QList<QTextLayout::FormatRange>; @@ -214,7 +213,7 @@ void GenerigHighlighterTests::testPreeditText() block.layout()->setPreeditArea(7, "uaf"); c.endEditBlock(); - m_editor->textDocument()->syntaxHighlighterRunner()->rehighlight(); + m_editor->textDocument()->syntaxHighlighter()->rehighlight(); const FormatRanges formatRanges = {{0, 4, toFormat(C_VISUAL_WHITESPACE)}, {4, 3, toFormat(C_TYPE)}, diff --git a/src/plugins/texteditor/highlighterhelper.cpp b/src/plugins/texteditor/highlighterhelper.cpp index 6aa98439c05..e25b935f8ac 100644 --- a/src/plugins/texteditor/highlighterhelper.cpp +++ b/src/plugins/texteditor/highlighterhelper.cpp @@ -9,7 +9,6 @@ #include "texteditor.h" #include "texteditorsettings.h" #include "texteditortr.h" -#include "syntaxhighlighterrunner.h" #include <coreplugin/editormanager/documentmodel.h> #include <coreplugin/icore.h> @@ -210,8 +209,8 @@ void reload() highlightRepository()->reload(); for (auto editor : Core::DocumentModel::editorsForOpenedDocuments()) { if (auto textEditor = qobject_cast<BaseTextEditor *>(editor)) { - if (auto highlighterRunner = textEditor->textDocument()->syntaxHighlighterRunner(); - highlighterRunner && highlighterRunner->useGenericHighlighter()) { + if (auto highlighter = textEditor->textDocument()->syntaxHighlighter(); + highlighter && qobject_cast<SyntaxHighlighter*>(highlighter)) { textEditor->editorWidget()->configureGenericHighlighter(); } } diff --git a/src/plugins/texteditor/jsoneditor.cpp b/src/plugins/texteditor/jsoneditor.cpp index d06f215c688..7081394f9be 100644 --- a/src/plugins/texteditor/jsoneditor.cpp +++ b/src/plugins/texteditor/jsoneditor.cpp @@ -6,7 +6,6 @@ #include "autocompleter.h" #include "textdocument.h" #include "texteditor.h" -#include "texteditoractionhandler.h" #include "texteditortr.h" #include "textindenter.h" @@ -164,7 +163,7 @@ public: setDocumentCreator([] { return new TextDocument(JSON_EDITOR_ID); }); setAutoCompleterCreator([] { return new JsonAutoCompleter; }); setIndenterCreator([](QTextDocument *doc) { return new JsonIndenter(doc); }); - setEditorActionHandlers(TextEditorActionHandler::Format); + setOptionalActionMask(OptionalActions::Format); setUseGenericHighlighter(true); } }; diff --git a/src/plugins/texteditor/markdowneditor.cpp b/src/plugins/texteditor/markdowneditor.cpp index dbfdcda2b42..93b580a935e 100644 --- a/src/plugins/texteditor/markdowneditor.cpp +++ b/src/plugins/texteditor/markdowneditor.cpp @@ -16,8 +16,6 @@ #include <coreplugin/icore.h> #include <coreplugin/minisplitter.h> -#include <texteditor/texteditoractionhandler.h> - #include <utils/action.h> #include <utils/ranges.h> #include <utils/qtcsettings.h> @@ -73,7 +71,7 @@ class MarkdownEditor : public IEditor { Q_OBJECT public: - MarkdownEditor(const TextEditor::TextEditorActionHandler *actionHandler) + MarkdownEditor() : m_document(new TextDocument(MARKDOWNVIEWER_ID)) { m_document->setMimeType(MARKDOWNVIEWER_MIME_TYPE); @@ -108,10 +106,25 @@ public: // editor m_textEditorWidget = new MarkdownEditorWidget; - m_textEditorWidget->setOptionalActions(actionHandler->optionalActions()); + m_textEditorWidget->setOptionalActions(OptionalActions::FollowSymbolUnderCursor); m_textEditorWidget->setTextDocument(m_document); m_textEditorWidget->setupGenericHighlighter(); m_textEditorWidget->setMarksVisible(false); + QObject::connect( + m_textEditorWidget, + &TextEditorWidget::saveCurrentStateForNavigationHistory, + this, + &MarkdownEditor::saveCurrentStateForNavigationHistory); + QObject::connect( + m_textEditorWidget, + &TextEditorWidget::addSavedStateToNavigationHistory, + this, + &MarkdownEditor::addSavedStateToNavigationHistory); + QObject::connect( + m_textEditorWidget, + &TextEditorWidget::addCurrentStateToNavigationHistory, + this, + &MarkdownEditor::addCurrentStateToNavigationHistory); auto context = new IContext(this); context->setWidget(m_textEditorWidget); context->setContext(Context(MARKDOWNVIEWER_TEXT_CONTEXT)); @@ -478,6 +491,18 @@ private: } } + void saveCurrentStateForNavigationHistory() { m_savedNavigationState = saveState(); } + + void addSavedStateToNavigationHistory() + { + EditorManager::addCurrentPositionToNavigationHistory(m_savedNavigationState); + } + + void addCurrentStateToNavigationHistory() + { + EditorManager::addCurrentPositionToNavigationHistory(); + } + private: QTimer m_previewTimer; bool m_performDelayedUpdate = false; @@ -493,6 +518,7 @@ private: QAction *m_togglePreviewVisibleAction; QAction *m_swapViewsAction; std::optional<QPoint> m_previewRestoreScrollPosition; + QByteArray m_savedNavigationState; }; class MarkdownEditorFactory final : public IEditorFactory @@ -501,7 +527,6 @@ public: MarkdownEditorFactory(); private: - TextEditorActionHandler m_actionHandler; Action m_emphasisAction; Action m_strongAction; Action m_inlineCodeAction; @@ -512,17 +537,11 @@ private: }; MarkdownEditorFactory::MarkdownEditorFactory() - : m_actionHandler(MARKDOWNVIEWER_ID, - MARKDOWNVIEWER_TEXT_CONTEXT, - TextEditor::TextEditorActionHandler::FollowSymbolUnderCursor, - [](IEditor *editor) { - return static_cast<MarkdownEditor *>(editor)->textEditorWidget(); - }) { setId(MARKDOWNVIEWER_ID); setDisplayName(::Core::Tr::tr("Markdown Editor")); addMimeType(MARKDOWNVIEWER_MIME_TYPE); - setEditorCreator([this] { return new MarkdownEditor{&m_actionHandler}; }); + setEditorCreator([] { return new MarkdownEditor; }); const auto textContext = Context(MARKDOWNVIEWER_TEXT_CONTEXT); const auto context = Context(MARKDOWNVIEWER_ID); diff --git a/src/plugins/texteditor/plaintexteditorfactory.cpp b/src/plugins/texteditor/plaintexteditorfactory.cpp index fbaf430284d..ef989ee6216 100644 --- a/src/plugins/texteditor/plaintexteditorfactory.cpp +++ b/src/plugins/texteditor/plaintexteditorfactory.cpp @@ -5,7 +5,6 @@ #include "basehoverhandler.h" #include "textdocument.h" #include "texteditor.h" -#include "texteditoractionhandler.h" #include "texteditorconstants.h" #include <coreplugin/coreconstants.h> @@ -40,9 +39,9 @@ public: setEditorWidgetCreator([]() { return new PlainTextEditorWidget; }); setUseGenericHighlighter(true); - setEditorActionHandlers(TextEditorActionHandler::Format | - TextEditorActionHandler::UnCommentSelection | - TextEditorActionHandler::UnCollapseAll); + setOptionalActionMask( + OptionalActions::Format | OptionalActions::UnCommentSelection + | OptionalActions::UnCollapseAll); } }; diff --git a/src/plugins/texteditor/refactoringchanges.cpp b/src/plugins/texteditor/refactoringchanges.cpp index ebd487cc370..c8688f2fba5 100644 --- a/src/plugins/texteditor/refactoringchanges.cpp +++ b/src/plugins/texteditor/refactoringchanges.cpp @@ -214,6 +214,9 @@ void RefactoringFile::setOpenEditor(bool activate, int pos) bool RefactoringFile::apply() { + if (m_changes.isEmpty()) + return true; + // test file permissions if (!m_filePath.isWritableFile()) { ReadOnlyFilesDialog roDialog(m_filePath, ICore::dialogParent()); @@ -287,6 +290,12 @@ bool RefactoringFile::apply() return result; } +bool RefactoringFile::apply(const Utils::ChangeSet &changeSet) +{ + setChangeSet(changeSet); + return apply(); +} + void RefactoringFile::setupFormattingRanges(const QList<ChangeSet::EditOp> &replaceList) { QTextDocument * const doc = m_editor ? m_editor->document() : m_document; @@ -359,7 +368,7 @@ void RefactoringFile::doFormatting() Utils::sort(m_formattingCursors, [](const auto &tc1, const auto &tc2) { return tc1.first.selectionStart() < tc2.first.selectionStart(); }); - static const QString clangFormatLineRemovalBlocker("// QTC_TEMP"); + static const QString clangFormatLineRemovalBlocker(""); for (auto &[formattingCursor, _] : m_formattingCursors) { const QTextBlock firstBlock = document->findBlock(formattingCursor.selectionStart()); const QTextBlock lastBlock = document->findBlock(formattingCursor.selectionEnd()); diff --git a/src/plugins/texteditor/refactoringchanges.h b/src/plugins/texteditor/refactoringchanges.h index 913c731df27..3e043c4f6be 100644 --- a/src/plugins/texteditor/refactoringchanges.h +++ b/src/plugins/texteditor/refactoringchanges.h @@ -60,6 +60,7 @@ public: void setChangeSet(const Utils::ChangeSet &changeSet); void setOpenEditor(bool activate = false, int pos = -1); bool apply(); + bool apply(const Utils::ChangeSet &changeSet); bool create(const QString &contents, bool reindent, bool openInEditor); protected: diff --git a/src/plugins/texteditor/semantichighlighter.cpp b/src/plugins/texteditor/semantichighlighter.cpp index 9cee346d8b0..0c385d2e4b5 100644 --- a/src/plugins/texteditor/semantichighlighter.cpp +++ b/src/plugins/texteditor/semantichighlighter.cpp @@ -5,7 +5,6 @@ #include "syntaxhighlighter.h" #include "texteditorsettings.h" -#include "syntaxhighlighterrunner.h" #include <utils/qtcassert.h> @@ -75,7 +74,7 @@ const Ranges rangesForResult( } void SemanticHighlighter::incrementalApplyExtraAdditionalFormats( - SyntaxHighlighterRunner *highlighter, + SyntaxHighlighter *highlighter, const QFuture<HighlightingResult> &future, int from, int to, @@ -114,21 +113,17 @@ void SemanticHighlighter::incrementalApplyExtraAdditionalFormats( formatRanges[range.block].append(range.formatRange); } - QList<int> clearBlockNumberVector; - QMap<int, QList<QTextLayout::FormatRange>> blockNumberMap; for (auto &[block, ranges] : formatRanges) { while (currentBlock < block) { - clearBlockNumberVector.append(currentBlock.blockNumber()); + highlighter->clearExtraFormats(currentBlock); currentBlock = currentBlock.next(); } - blockNumberMap[block.blockNumber()] = ranges; + highlighter->setExtraFormats(block, std::move(ranges)); currentBlock = block.next(); } - highlighter->clearExtraFormats(clearBlockNumberVector); - highlighter->setExtraFormats(blockNumberMap); } -void SemanticHighlighter::setExtraAdditionalFormats(SyntaxHighlighterRunner *highlighter, +void SemanticHighlighter::setExtraAdditionalFormats(SyntaxHighlighter *highlighter, const QList<HighlightingResult> &results, const QHash<int, QTextCharFormat> &kindToFormat) { @@ -139,19 +134,19 @@ void SemanticHighlighter::setExtraAdditionalFormats(SyntaxHighlighterRunner *hig QTextDocument *doc = highlighter->document(); QTC_ASSERT(doc, return ); - QMap<int, QList<QTextLayout::FormatRange>> blockNumberMap; + std::map<QTextBlock, QVector<QTextLayout::FormatRange>> formatRanges; - for (const HighlightingResult &result : results) { - const Ranges ranges = rangesForResult(result, doc, kindToFormat); - for (const Range &range : ranges) - blockNumberMap[range.block.blockNumber()].append(range.formatRange); + for (auto result : results) { + for (const Range &range : rangesForResult(result, doc, kindToFormat)) + formatRanges[range.block].append(range.formatRange); } - highlighter->setExtraFormats(blockNumberMap); + for (auto &[block, ranges] : formatRanges) + highlighter->setExtraFormats(block, std::move(ranges)); } void SemanticHighlighter::clearExtraAdditionalFormatsUntilEnd( - SyntaxHighlighterRunner *highlighter, const QFuture<HighlightingResult> &future) + SyntaxHighlighter *highlighter, const QFuture<HighlightingResult> &future) { const QTextDocument * const doc = highlighter->document(); QTextBlock firstBlockToClear = doc->begin(); @@ -168,7 +163,6 @@ void SemanticHighlighter::clearExtraAdditionalFormatsUntilEnd( QList<int> clearBlockNumberVector; for (QTextBlock b = firstBlockToClear; b.isValid(); b = b.next()) - clearBlockNumberVector.append(b.blockNumber()); + highlighter->clearExtraFormats(b); - highlighter->clearExtraFormats(clearBlockNumberVector); } diff --git a/src/plugins/texteditor/semantichighlighter.h b/src/plugins/texteditor/semantichighlighter.h index ad1d854b834..f8ae30a0eda 100644 --- a/src/plugins/texteditor/semantichighlighter.h +++ b/src/plugins/texteditor/semantichighlighter.h @@ -21,7 +21,6 @@ QT_END_NAMESPACE namespace TextEditor { class SyntaxHighlighter; -class SyntaxHighlighterRunner; class TEXTEDITOR_EXPORT HighlightingResult { @@ -74,7 +73,7 @@ using Splitter = std::function<const QList<std::pair<HighlightingResult, QTextBl // the (to-1).line result. // Requires that results of the Future are ordered by line. void TEXTEDITOR_EXPORT -incrementalApplyExtraAdditionalFormats(SyntaxHighlighterRunner *highlighter, +incrementalApplyExtraAdditionalFormats(SyntaxHighlighter *highlighter, const QFuture<HighlightingResult> &future, int from, int to, @@ -85,7 +84,7 @@ incrementalApplyExtraAdditionalFormats(SyntaxHighlighterRunner *highlighter, // indicated by Result::kind and kindToFormat to the correct location using // SyntaxHighlighter::setExtraFormats. In contrast to // incrementalApplyExtraAdditionalFormats the results do not have to be ordered by line. -void TEXTEDITOR_EXPORT setExtraAdditionalFormats(SyntaxHighlighterRunner *highlighter, +void TEXTEDITOR_EXPORT setExtraAdditionalFormats(SyntaxHighlighter *highlighter, const HighlightingResults &results, const QHash<int, QTextCharFormat> &kindToFormat); @@ -93,7 +92,7 @@ void TEXTEDITOR_EXPORT setExtraAdditionalFormats(SyntaxHighlighterRunner *highli // until the end of the document. // Requires that results of the Future are ordered by line. void TEXTEDITOR_EXPORT clearExtraAdditionalFormatsUntilEnd( - SyntaxHighlighterRunner *highlighter, const QFuture<HighlightingResult> &future); + SyntaxHighlighter *highlighter, const QFuture<HighlightingResult> &future); } // namespace SemanticHighlighter } // namespace TextEditor diff --git a/src/plugins/texteditor/snippets/snippetssettings.cpp b/src/plugins/texteditor/snippets/snippetssettings.cpp deleted file mode 100644 index cce25bc7109..00000000000 --- a/src/plugins/texteditor/snippets/snippetssettings.cpp +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "snippetssettings.h" - -#include <utils/qtcsettings.h> - -using namespace Utils; - -namespace TextEditor { - -const char kGroupPostfix[] = "SnippetsSettings"; -const char kLastUsedSnippetGroup[] = "LastUsedSnippetGroup"; - -void SnippetsSettings::toSettings(const Key &category, QtcSettings *s) const -{ - const Key group = category + kGroupPostfix; - s->beginGroup(group); - s->setValue(kLastUsedSnippetGroup, m_lastUsedSnippetGroup); - s->endGroup(); -} - -void SnippetsSettings::fromSettings(const Key &category, QtcSettings *s) -{ - const Key group = category + kGroupPostfix; - s->beginGroup(group); - m_lastUsedSnippetGroup = s->value(kLastUsedSnippetGroup, QString()).toString(); - s->endGroup(); -} - -void SnippetsSettings::setLastUsedSnippetGroup(const QString &lastUsed) -{ - m_lastUsedSnippetGroup = lastUsed; -} - -const QString &SnippetsSettings::lastUsedSnippetGroup() const -{ - return m_lastUsedSnippetGroup; -} - -bool SnippetsSettings::equals(const SnippetsSettings &snippetsSettings) const -{ - return m_lastUsedSnippetGroup == snippetsSettings.m_lastUsedSnippetGroup; -} - -} // TextEditor diff --git a/src/plugins/texteditor/snippets/snippetssettings.h b/src/plugins/texteditor/snippets/snippetssettings.h deleted file mode 100644 index 732d51f6716..00000000000 --- a/src/plugins/texteditor/snippets/snippetssettings.h +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include <QString> - -namespace Utils { -class Key; -class QtcSettings; -} // Utils - -namespace TextEditor { - -class SnippetsSettings -{ -public: - SnippetsSettings() = default; - - void toSettings(const Utils::Key &category, Utils::QtcSettings *s) const; - void fromSettings(const Utils::Key &category, Utils::QtcSettings *s); - - void setLastUsedSnippetGroup(const QString &lastUsed); - const QString &lastUsedSnippetGroup() const; - - bool equals(const SnippetsSettings &snippetsSettings) const; - - friend bool operator==(const SnippetsSettings &a, const SnippetsSettings &b) - { return a.equals(b); } - friend bool operator!=(const SnippetsSettings &a, const SnippetsSettings &b) - { return !a.equals(b); } - -private: - QString m_lastUsedSnippetGroup; -}; - -} // TextEditor diff --git a/src/plugins/texteditor/snippets/snippetssettingspage.cpp b/src/plugins/texteditor/snippets/snippetssettingspage.cpp index 51deff5ad1f..546d532bbf3 100644 --- a/src/plugins/texteditor/snippets/snippetssettingspage.cpp +++ b/src/plugins/texteditor/snippets/snippetssettingspage.cpp @@ -7,7 +7,6 @@ #include "snippetprovider.h" #include "snippet.h" #include "snippetscollection.h" -#include "snippetssettings.h" #include "../fontsettings.h" #include "../textdocument.h" #include "../texteditorconstants.h" @@ -39,6 +38,8 @@ using namespace Utils; namespace TextEditor::Internal { +const char kLastUsedSnippetGroup[] = "TextSnippetsSettings/LastUsedSnippetGroup"; + // SnippetsTableModel class SnippetsTableModel : public QAbstractTableModel @@ -244,8 +245,6 @@ void SnippetsTableModel::replaceSnippet(const Snippet &snippet, const QModelInde } } -// SnippetsSettingsWidget - class SnippetsSettingsWidget : public Core::IOptionsPageWidget { public: @@ -275,10 +274,9 @@ private: bool settingsChanged() const; void writeSettings(); - const Key m_settingsPrefix{"Text"}; SnippetsTableModel m_model; bool m_snippetsCollectionChanged = false; - SnippetsSettings m_settings; + QString m_lastUsedSnippetGroup; QStackedWidget *m_snippetsEditorStack; QComboBox *m_groupCombo; @@ -407,9 +405,8 @@ void SnippetsSettingsWidget::loadSettings() if (m_groupCombo->count() == 0) return; - m_settings.fromSettings(m_settingsPrefix, Core::ICore::settings()); - const QString &lastGroupName = m_settings.lastUsedSnippetGroup(); - const int index = m_groupCombo->findText(lastGroupName); + m_lastUsedSnippetGroup = Core::ICore::settings()->value(kLastUsedSnippetGroup, QString()).toString(); + const int index = m_groupCombo->findText(m_lastUsedSnippetGroup); if (index != -1) m_groupCombo->setCurrentIndex(index); else @@ -421,15 +418,13 @@ void SnippetsSettingsWidget::writeSettings() if (m_groupCombo->count() == 0) return; - m_settings.setLastUsedSnippetGroup(m_groupCombo->currentText()); - m_settings.toSettings(m_settingsPrefix, Core::ICore::settings()); + m_lastUsedSnippetGroup = m_groupCombo->currentText(); + Core::ICore::settings()->setValue(kLastUsedSnippetGroup, m_lastUsedSnippetGroup); } bool SnippetsSettingsWidget::settingsChanged() const { - if (m_settings.lastUsedSnippetGroup() != m_groupCombo->currentText()) - return true; - return false; + return m_lastUsedSnippetGroup != m_groupCombo->currentText(); } void SnippetsSettingsWidget::loadSnippetGroup(int index) diff --git a/src/plugins/texteditor/syntaxhighlighter.cpp b/src/plugins/texteditor/syntaxhighlighter.cpp index f7db190852d..0162cdeeff0 100644 --- a/src/plugins/texteditor/syntaxhighlighter.cpp +++ b/src/plugins/texteditor/syntaxhighlighter.cpp @@ -25,8 +25,6 @@ enum HighlighterTypeProperty class SyntaxHighlighterPrivate { - SyntaxHighlighter *q_ptr = nullptr; - Q_DECLARE_PUBLIC(SyntaxHighlighter) public: SyntaxHighlighterPrivate() = default; @@ -37,8 +35,10 @@ public: QPointer<QTextDocument> doc; + void updateFormats(int from, int charsRemoved, int charsAdded); void reformatBlocks(int from, int charsRemoved, int charsAdded); - void reformatBlock(const QTextBlock &block, int from, int charsRemoved, int charsAdded); + void reformatBlocks(); + void reformatBlock(const QTextBlock &block); inline void rehighlight(QTextCursor &cursor, QTextCursor::MoveOperation operation) { inReformatBlocks = true; @@ -48,7 +48,7 @@ public: inReformatBlocks = false; } - void applyFormatChanges(int from, int charsRemoved, int charsAdded); + void applyFormatChanges(); void updateFormats(const FontSettings &fontSettings); FontSettings fontSettings; @@ -61,6 +61,11 @@ public: QList<std::pair<int,TextStyle>> formatCategories; QTextCharFormat whitespaceFormat; QString mimeType; + bool syntaxInfoUpToDate = false; + int highlightStartBlock = 0; + int highlightEndBlock = 0; + QSet<int> forceRehighlightBlocks; + SyntaxHighlighter *q; }; static bool adjustRange(QTextLayout::FormatRange &range, int from, int charsDelta) @@ -77,31 +82,30 @@ static bool adjustRange(QTextLayout::FormatRange &range, int from, int charsDelt void SyntaxHighlighter::delayedRehighlight() { - Q_D(SyntaxHighlighter); if (!d->rehighlightPending) return; d->rehighlightPending = false; - if (document()->isEmpty()) - return; - rehighlight(); } +void SyntaxHighlighter::continueRehighlight() +{ + d->reformatBlocks(); +} + #ifdef WITH_TESTS SyntaxHighlighter::SyntaxHighlighter(QTextDocument *parent, const FontSettings &fontsettings) - : QObject(parent), d_ptr(new SyntaxHighlighterPrivate(fontsettings)) + : QObject(parent), d(new SyntaxHighlighterPrivate(fontsettings)) { - d_ptr->q_ptr = this; + d->q = this; if (parent) setDocument(parent); } #endif -void SyntaxHighlighterPrivate::applyFormatChanges(int from, int charsRemoved, int charsAdded) +void SyntaxHighlighterPrivate::applyFormatChanges() { - bool formatsChanged = false; - QTextLayout *layout = currentBlock.layout(); QList<QTextLayout::FormatRange> ranges; @@ -111,12 +115,6 @@ void SyntaxHighlighterPrivate::applyFormatChanges(int from, int charsRemoved, in return range.format.property(SyntaxHighlight).toBool(); }); - if (currentBlock.contains(from)) { - const int charsDelta = charsAdded - charsRemoved; - for (QTextLayout::FormatRange &range : ranges) - formatsChanged |= adjustRange(range, from - currentBlock.position(), charsDelta); - } - QTextCharFormat emptyFormat; QTextLayout::FormatRange r; @@ -157,7 +155,7 @@ void SyntaxHighlighterPrivate::applyFormatChanges(int from, int charsRemoved, in newRanges << r; } - formatsChanged = formatsChanged || (newRanges.size() != oldRanges.size()); + bool formatsChanged = (newRanges.size() != oldRanges.size()); for (int i = 0; !formatsChanged && i < newRanges.size(); ++i) { const QTextLayout::FormatRange &o = oldRanges.at(i); @@ -174,85 +172,103 @@ void SyntaxHighlighterPrivate::applyFormatChanges(int from, int charsRemoved, in void SyntaxHighlighter::reformatBlocks(int from, int charsRemoved, int charsAdded) { - Q_D(SyntaxHighlighter); if (!d->inReformatBlocks) d->reformatBlocks(from, charsRemoved, charsAdded); } -void SyntaxHighlighterPrivate::reformatBlocks(int from, int charsRemoved, int charsAdded) +void SyntaxHighlighterPrivate::updateFormats(int from, int charsRemoved, int charsAdded) { - Q_Q(SyntaxHighlighter); - foldValidator.reset(); + bool formatsChanged = false; - rehighlightPending = false; + const QTextBlock block = doc->findBlock(from); + QTextLayout *layout = block.layout(); - QTextBlock block = doc->findBlock(from); - if (!block.isValid()) - return; + QList<QTextLayout::FormatRange> ranges = layout->formats(); - int endPosition; - QTextBlock lastBlock = doc->findBlock(from + charsAdded + (charsRemoved > 0 ? 1 : 0)); - if (lastBlock.isValid()) - endPosition = lastBlock.position() + lastBlock.length(); - else - endPosition = doc->lastBlock().position() + doc->lastBlock().length(); + const int charsDelta = charsAdded - charsRemoved; + for (QTextLayout::FormatRange &range : ranges) + formatsChanged |= adjustRange(range, from - block.position(), charsDelta); - bool forceHighlightOfNextBlock = false; + if (formatsChanged) { + layout->setFormats(ranges); + doc->markContentsDirty(block.position(), block.length()); + } +} - QList<SyntaxHighlighter::Result> vecRes; +void SyntaxHighlighterPrivate::reformatBlocks(int from, int charsRemoved, int charsAdded) +{ + updateFormats(from, charsRemoved, charsAdded); - SyntaxHighlighter::Result resStart; - resStart.m_state = SyntaxHighlighter::State::Start; - vecRes << resStart; + QTextBlock block = doc->findBlock(from); + if (block.isValid() && block.blockNumber() < highlightStartBlock) + highlightStartBlock = block.blockNumber(); + block = doc->findBlock(from + charsAdded + (charsRemoved > 0 ? 1 : 0)); + if (!block.isValid()) + highlightEndBlock = doc->blockCount() - 1; + else if (block.blockNumber() > highlightEndBlock) + highlightEndBlock = block.blockNumber(); - while (block.isValid() && (block.position() < endPosition || forceHighlightOfNextBlock)) { - if (QThread::currentThread()->isInterruptionRequested() || q->isInterrupted()) { - inReformatBlocks = false; - emit q->resultsReady({}); - return; - } + reformatBlocks(); +} - const int stateBeforeHighlight = block.userState(); +void SyntaxHighlighterPrivate::reformatBlocks() +{ + QElapsedTimer et; + et.start(); - reformatBlock(block, from, charsRemoved, charsAdded); + syntaxInfoUpToDate = false; + rehighlightPending = false; - forceHighlightOfNextBlock = (block.userState() != stateBeforeHighlight); + foldValidator.reset(); - SyntaxHighlighter::Result res; - res.m_formatRanges = block.layout()->formats(); - res.fillByBlock(block); - vecRes << res; + bool forceHighlightOfNextBlock = false; + QTextBlock block = doc->findBlockByNumber(highlightStartBlock); + QTC_ASSERT(block.isValid(), block = doc->firstBlock()); + QTextBlock endBlock = doc->findBlockByNumber(highlightEndBlock); + QTC_ASSERT(endBlock.isValid(), endBlock = doc->lastBlock()); - // Sending data to the text editor in chunks of 200 blocks is a sensible approach. - // This helps avoid UI slowdowns when applying formatting to the text. - if (vecRes.size() >= 200) { - emit q->resultsReady(vecRes); - vecRes.clear(); + while (block.isValid()) { + if (et.elapsed() > 20) + break; + + const int stateBeforeHighlight = block.userState(); + + if (forceHighlightOfNextBlock || forceRehighlightBlocks.contains(block.blockNumber()) + || block.blockNumber() <= highlightEndBlock) { + reformatBlock(block); + forceRehighlightBlocks.remove(block.blockNumber()); + forceHighlightOfNextBlock = (block.userState() != stateBeforeHighlight); } + highlightStartBlock = block.blockNumber(); + if (block == endBlock && !forceHighlightOfNextBlock) + break; block = block.next(); } formatChanges.clear(); - foldValidator.finalize(); - SyntaxHighlighter::Result res; - res.m_state = SyntaxHighlighter::State::Done; - vecRes << res; - emit q->resultsReady(vecRes); + + if (endBlock.isValid() && block.isValid() && block.blockNumber() < endBlock.blockNumber()) { + QMetaObject::invokeMethod(q, &SyntaxHighlighter::continueRehighlight, Qt::QueuedConnection); + if (forceHighlightOfNextBlock) + forceRehighlightBlocks << block.blockNumber(); + } else { + highlightEndBlock = 0; + syntaxInfoUpToDate = true; + emit q->finished(); + } } -void SyntaxHighlighterPrivate::reformatBlock(const QTextBlock &block, int from, int charsRemoved, int charsAdded) +void SyntaxHighlighterPrivate::reformatBlock(const QTextBlock &block) { - Q_Q(SyntaxHighlighter); - - Q_ASSERT_X(!currentBlock.isValid(), "SyntaxHighlighter::reformatBlock()", "reFormatBlock() called recursively"); + QTC_ASSERT(!currentBlock.isValid(), return); currentBlock = block; formatChanges.fill(QTextCharFormat(), block.length() - 1); q->highlightBlock(block.text()); - applyFormatChanges(from, charsRemoved, charsAdded); + applyFormatChanges(); foldValidator.process(currentBlock); @@ -293,9 +309,9 @@ void SyntaxHighlighterPrivate::reformatBlock(const QTextBlock &block, int from, Constructs a SyntaxHighlighter with the given \a parent. */ SyntaxHighlighter::SyntaxHighlighter(QObject *parent) - : QObject(parent), d_ptr(new SyntaxHighlighterPrivate) + : QObject(parent), d(new SyntaxHighlighterPrivate) { - d_ptr->q_ptr = this; + d->q = this; } /*! @@ -304,9 +320,9 @@ SyntaxHighlighter::SyntaxHighlighter(QObject *parent) SyntaxHighlighter. */ SyntaxHighlighter::SyntaxHighlighter(QTextDocument *parent) - : QObject(parent), d_ptr(new SyntaxHighlighterPrivate) + : QObject(parent), d(new SyntaxHighlighterPrivate) { - d_ptr->q_ptr = this; + d->q = this; if (parent) setDocument(parent); } @@ -317,9 +333,9 @@ SyntaxHighlighter::SyntaxHighlighter(QTextDocument *parent) the SyntaxHighlighter. */ SyntaxHighlighter::SyntaxHighlighter(QTextEdit *parent) - : QObject(parent), d_ptr(new SyntaxHighlighterPrivate) + : QObject(parent), d(new SyntaxHighlighterPrivate) { - d_ptr->q_ptr = this; + d->q = this; if (parent) setDocument(parent->document()); } @@ -338,7 +354,6 @@ SyntaxHighlighter::~SyntaxHighlighter() */ void SyntaxHighlighter::setDocument(QTextDocument *doc) { - Q_D(SyntaxHighlighter); if (d->doc == doc) return; @@ -356,10 +371,7 @@ void SyntaxHighlighter::setDocument(QTextDocument *doc) documentChanged(oldDoc, d->doc); if (d->doc) { connect(d->doc, &QTextDocument::contentsChange, this, &SyntaxHighlighter::reformatBlocks); - d->rehighlightPending = true; - QMetaObject::invokeMethod(this, - &SyntaxHighlighter::delayedRehighlight, - Qt::QueuedConnection); + scheduleRehighlight(); d->foldValidator.setup(qobject_cast<TextDocumentLayout *>(doc->documentLayout())); } } @@ -370,19 +382,16 @@ void SyntaxHighlighter::setDocument(QTextDocument *doc) */ QTextDocument *SyntaxHighlighter::document() const { - Q_D(const SyntaxHighlighter); return d->doc; } void SyntaxHighlighter::setMimeType(const QString &mimeType) { - Q_D(SyntaxHighlighter); d->mimeType = mimeType; } QString SyntaxHighlighter::mimeType() const { - Q_D(const SyntaxHighlighter); return d->mimeType; } @@ -395,7 +404,6 @@ QString SyntaxHighlighter::mimeType() const */ void SyntaxHighlighter::rehighlight() { - Q_D(SyntaxHighlighter); if (!d->doc) return; @@ -403,6 +411,17 @@ void SyntaxHighlighter::rehighlight() d->rehighlight(cursor, QTextCursor::End); } +void SyntaxHighlighter::scheduleRehighlight() +{ + if (d->rehighlightPending) + return; + d->rehighlightPending = true; + d->syntaxInfoUpToDate = false; + QMetaObject::invokeMethod(this, + &SyntaxHighlighter::delayedRehighlight, + Qt::QueuedConnection); +} + /*! \since 4.6 @@ -412,7 +431,6 @@ void SyntaxHighlighter::rehighlight() */ void SyntaxHighlighter::rehighlightBlock(const QTextBlock &block) { - Q_D(SyntaxHighlighter); if (!d->doc || !block.isValid() || block.document() != d->doc) return; @@ -486,7 +504,6 @@ void SyntaxHighlighter::rehighlightBlock(const QTextBlock &block) */ void SyntaxHighlighter::setFormat(int start, int count, const QTextCharFormat &format) { - Q_D(SyntaxHighlighter); if (start < 0 || start >= d->formatChanges.count()) return; @@ -533,7 +550,6 @@ void SyntaxHighlighter::setFormat(int start, int count, const QFont &font) void SyntaxHighlighter::formatSpaces(const QString &text, int start, int count) { - Q_D(const SyntaxHighlighter); int offset = start; const int end = std::min(start + count, int(text.length())); while (offset < end) { @@ -585,10 +601,7 @@ void SyntaxHighlighter::setFormatWithSpaces(const QString &text, int start, int */ QTextCharFormat SyntaxHighlighter::format(int pos) const { - Q_D(const SyntaxHighlighter); - if (pos < 0 || pos >= d->formatChanges.count()) - return QTextCharFormat(); - return d->formatChanges.at(pos); + return d->formatChanges.value(pos); } /*! @@ -600,7 +613,6 @@ QTextCharFormat SyntaxHighlighter::format(int pos) const */ int SyntaxHighlighter::previousBlockState() const { - Q_D(const SyntaxHighlighter); if (!d->currentBlock.isValid()) return -1; @@ -617,7 +629,6 @@ int SyntaxHighlighter::previousBlockState() const */ int SyntaxHighlighter::currentBlockState() const { - Q_D(const SyntaxHighlighter); if (!d->currentBlock.isValid()) return -1; @@ -631,7 +642,6 @@ int SyntaxHighlighter::currentBlockState() const */ void SyntaxHighlighter::setCurrentBlockState(int newState) { - Q_D(SyntaxHighlighter); if (!d->currentBlock.isValid()) return; @@ -674,7 +684,6 @@ void SyntaxHighlighter::setCurrentBlockState(int newState) */ void SyntaxHighlighter::setCurrentBlockUserData(QTextBlockUserData *data) { - Q_D(SyntaxHighlighter); if (!d->currentBlock.isValid()) return; @@ -689,7 +698,6 @@ void SyntaxHighlighter::setCurrentBlockUserData(QTextBlockUserData *data) */ QTextBlockUserData *SyntaxHighlighter::currentBlockUserData() const { - Q_D(const SyntaxHighlighter); if (!d->currentBlock.isValid()) return nullptr; @@ -703,7 +711,6 @@ QTextBlockUserData *SyntaxHighlighter::currentBlockUserData() const */ QTextBlock SyntaxHighlighter::currentBlock() const { - Q_D(const SyntaxHighlighter); return d->currentBlock; } @@ -715,8 +722,6 @@ static bool byStartOfRange(const QTextLayout::FormatRange &range, const QTextLay void SyntaxHighlighter::setExtraFormats(const QTextBlock &block, const QList<QTextLayout::FormatRange> &formats) { - Q_D(SyntaxHighlighter); - QList<QTextLayout::FormatRange> formatsCopy = formats; const int blockLength = block.length(); @@ -766,20 +771,17 @@ void SyntaxHighlighter::setExtraFormats(const QTextBlock &block, d->inReformatBlocks = true; block.layout()->setFormats(formatsToApply); - SyntaxHighlighter::Result res; - res.m_formatRanges = block.layout()->formats(); - res.fillByBlock(block); - res.m_state = SyntaxHighlighter::State::Extras; - emit resultsReady({std::move(res)}); - document()->markContentsDirty(block.position(), blockLength - 1); d->inReformatBlocks = wasInReformatBlocks; } -void SyntaxHighlighter::clearExtraFormats(const QTextBlock &block) +bool SyntaxHighlighter::syntaxHighlighterUpToDate() const { - Q_D(SyntaxHighlighter); + return d->syntaxInfoUpToDate; +} +void SyntaxHighlighter::clearExtraFormats(const QTextBlock &block) +{ const int blockLength = block.length(); if (block.layout() == nullptr || blockLength == 0) return; @@ -793,12 +795,6 @@ void SyntaxHighlighter::clearExtraFormats(const QTextBlock &block) d->inReformatBlocks = true; block.layout()->setFormats(formatsToApply); - SyntaxHighlighter::Result res; - res.m_formatRanges = block.layout()->formats(); - res.fillByBlock(block); - res.m_state = SyntaxHighlighter::State::Extras; - emit resultsReady({std::move(res)}); - document()->markContentsDirty(block.position(), blockLength - 1); d->inReformatBlocks = wasInReformatBlocks; } @@ -848,13 +844,11 @@ QList<QColor> SyntaxHighlighter::generateColors(int n, const QColor &background) void SyntaxHighlighter::setFontSettings(const FontSettings &fontSettings) { - Q_D(SyntaxHighlighter); d->updateFormats(fontSettings); } FontSettings SyntaxHighlighter::fontSettings() const { - Q_D(const SyntaxHighlighter); return d->fontSettings; } @@ -897,7 +891,6 @@ void SyntaxHighlighter::setTextFormatCategories(int count, */ void SyntaxHighlighter::setTextFormatCategories(const QList<std::pair<int, TextStyle>> &categories) { - Q_D(SyntaxHighlighter); d->formatCategories = categories; const int maxCategory = Utils::maxElementOr(categories, {-1, C_TEXT}).first; d->formats = QList<QTextCharFormat>(maxCategory + 1); @@ -906,7 +899,6 @@ void SyntaxHighlighter::setTextFormatCategories(const QList<std::pair<int, TextS QTextCharFormat SyntaxHighlighter::formatForCategory(int category) const { - Q_D(const SyntaxHighlighter); QTC_ASSERT(d->formats.size() > category, return QTextCharFormat()); return d->formats.at(category); @@ -914,7 +906,6 @@ QTextCharFormat SyntaxHighlighter::formatForCategory(int category) const QTextCharFormat SyntaxHighlighter::whitespacified(const QTextCharFormat &fmt) { - Q_D(SyntaxHighlighter); QTextCharFormat format = d->whitespaceFormat; format.setBackground(fmt.background()); return format; diff --git a/src/plugins/texteditor/syntaxhighlighter.h b/src/plugins/texteditor/syntaxhighlighter.h index 13d595d82f4..39bb084465c 100644 --- a/src/plugins/texteditor/syntaxhighlighter.h +++ b/src/plugins/texteditor/syntaxhighlighter.h @@ -32,7 +32,6 @@ class SyntaxHighlighterPrivate; class TEXTEDITOR_EXPORT SyntaxHighlighter : public QObject { Q_OBJECT - Q_DECLARE_PRIVATE(SyntaxHighlighter) public: SyntaxHighlighter(QObject *parent = nullptr); SyntaxHighlighter(QTextDocument *parent); @@ -51,79 +50,16 @@ public: virtual void setFontSettings(const TextEditor::FontSettings &fontSettings); TextEditor::FontSettings fontSettings() const; - enum State { - Start, - InProgress, - Done, - Extras - }; - - struct Result - { - void fillByBlock(const QTextBlock &block) - { - m_blockNumber = block.blockNumber(); - m_userState = block.userState(); - - TextBlockUserData *userDate = TextDocumentLayout::textUserData(block); - if (!userDate) - return; - - m_hasBlockUserData = true; - m_foldingIndent = userDate->foldingIndent(); - m_ifdefedOut = userDate->ifdefedOut(); - m_foldingStartIncluded = userDate->foldingStartIncluded(); - m_foldingEndIncluded = userDate->foldingEndIncluded(); - m_parentheses = userDate->parentheses(); - m_expectedRawStringSuffix = userDate->expectedRawStringSuffix(); - } - - void copyToBlock(QTextBlock &block) const - { - block.setUserState(m_userState); - - if (!m_hasBlockUserData) - return; - - TextBlockUserData *data = TextDocumentLayout::userData(block); - data->setExpectedRawStringSuffix(m_expectedRawStringSuffix); - data->setFoldingIndent(m_foldingIndent); - data->setFoldingStartIncluded(m_foldingStartIncluded); - data->setFoldingEndIncluded(m_foldingEndIncluded); - - if (m_ifdefedOut) - data->setIfdefedOut(); - else - data->clearIfdefedOut(); - - data->setParentheses(m_parentheses); - } - - int m_blockNumber; - bool m_hasBlockUserData = false; - - int m_foldingIndent : 16; - uint m_ifdefedOut : 1; - uint m_foldingStartIncluded : 1; - uint m_foldingEndIncluded : 1; - - Parentheses m_parentheses; - QByteArray m_expectedRawStringSuffix; - int m_userState = -1; - QList<QTextLayout::FormatRange> m_formatRanges; - - State m_state = InProgress; - }; - - void setInterrupted(bool interrupted) { m_interrupted = interrupted; } - bool isInterrupted() { return m_interrupted; } void setExtraFormats(const QTextBlock &block, const QList<QTextLayout::FormatRange> &formats); virtual void setLanguageFeaturesFlags(unsigned int /*flags*/) {}; // needed for CppHighlighting virtual void setEnabled(bool /*enabled*/) {}; // needed for DiffAndLogHighlighter virtual void setDefinitionName(const QString & /*definitionName*/) {} // needed for Highlighter + bool syntaxHighlighterUpToDate() const; + public slots: virtual void rehighlight(); + virtual void scheduleRehighlight(); void rehighlightBlock(const QTextBlock &block); void clearExtraFormats(const QTextBlock &block); void reformatBlocks(int from, int charsRemoved, int charsAdded); @@ -158,18 +94,18 @@ protected: QTextBlock currentBlock() const; -protected: virtual void documentChanged(QTextDocument * /*oldDoc*/, QTextDocument * /*newDoc*/) {}; signals: - void resultsReady(const QList<Result> &result); + void finished(); private: void setTextFormatCategories(const QList<std::pair<int, TextStyle>> &categories); void delayedRehighlight(); + void continueRehighlight(); - QScopedPointer<SyntaxHighlighterPrivate> d_ptr; - std::atomic<bool> m_interrupted = false; + friend class SyntaxHighlighterPrivate; + std::unique_ptr<SyntaxHighlighterPrivate> d; #ifdef WITH_TESTS friend class tst_highlighter; diff --git a/src/plugins/texteditor/syntaxhighlighterrunner.cpp b/src/plugins/texteditor/syntaxhighlighterrunner.cpp deleted file mode 100644 index 874f8254abf..00000000000 --- a/src/plugins/texteditor/syntaxhighlighterrunner.cpp +++ /dev/null @@ -1,385 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "syntaxhighlighterrunner.h" - -#include "fontsettings.h" -#include "textdocumentlayout.h" -#include "texteditorsettings.h" -#include "highlighter.h" - -#include <utils/algorithm.h> -#include <utils/textutils.h> - -#include <QMetaObject> -#include <QTextCursor> -#include <QTextDocument> -#include <QThread> - -namespace TextEditor { - -struct BlockPreeditData { - int position; - QString text; -}; - -class SyntaxHighlighterRunnerPrivate : public QObject -{ - Q_OBJECT -public: - SyntaxHighlighterRunnerPrivate(SyntaxHighlighter *highlighter, - QTextDocument *document, - bool async) - : m_highlighter(highlighter) - { - if (async) { - m_document = new QTextDocument(this); - m_document->setDocumentLayout(new TextDocumentLayout(m_document)); - } else { - m_document = document; - } - - m_highlighter->setParent(m_document); - m_highlighter->setDocument(m_document); - - connect(m_highlighter, - &SyntaxHighlighter::resultsReady, - this, - &SyntaxHighlighterRunnerPrivate::resultsReady); - } - - void changeDocument(int from, - int charsRemoved, - const QString textAdded, - const QMap<int, BlockPreeditData> &blocksPreedit) - { - QTextCursor cursor(m_document); - cursor.setPosition(qMin(m_document->characterCount() - 1, from + charsRemoved)); - cursor.setPosition(from, QTextCursor::KeepAnchor); - cursor.insertText(textAdded); - - for (auto it = blocksPreedit.cbegin(); it != blocksPreedit.cend(); ++it) { - const QTextBlock block = m_document->findBlockByNumber(it.key()); - block.layout()->setPreeditArea(it.value().position, it.value().text); - } - } - - void setExtraFormats(const QMap<int, QList<QTextLayout::FormatRange>> &formatMap) - { - QTC_ASSERT(m_highlighter, return); - for (auto it = formatMap.cbegin(); it != formatMap.cend(); ++it) - m_highlighter->setExtraFormats(m_document->findBlockByNumber(it.key()), it.value()); - } - - void clearExtraFormats(const QList<int> &blockNumbers) - { - QTC_ASSERT(m_highlighter, return); - for (auto it = blockNumbers.cbegin(); it != blockNumbers.cend(); ++it) - m_highlighter->clearExtraFormats(m_document->findBlockByNumber(*it)); - } - - void clearAllExtraFormats() - { - QTC_ASSERT(m_highlighter, return); - m_highlighter->clearAllExtraFormats(); - } - - void setFontSettings(const TextEditor::FontSettings &fontSettings) - { - QTC_ASSERT(m_highlighter, return); - m_highlighter->setFontSettings(fontSettings); - } - - void setDefinitionName(const QString &name) - { - QTC_ASSERT(m_highlighter, return); - m_highlighter->setDefinitionName(name); - } - - void setLanguageFeaturesFlags(unsigned int flags) - { - QTC_ASSERT(m_highlighter, return); - m_highlighter->setLanguageFeaturesFlags(flags); - } - - void setEnabled(bool enabled) - { - QTC_ASSERT(m_highlighter, return); - m_highlighter->setEnabled(enabled); - } - - void rehighlight() - { - QTC_ASSERT(m_highlighter, return); - m_highlighter->rehighlight(); - } - - void reformatBlocks(int from, int charsRemoved, int charsAdded) - { - QTC_ASSERT(m_highlighter, return); - m_highlighter->reformatBlocks(from, charsRemoved, charsAdded); - } - - void setInterrupted(bool interrupted) - { - QTC_ASSERT(m_highlighter, return); - m_highlighter->setInterrupted(interrupted); - } - - QPointer<SyntaxHighlighter> m_highlighter = nullptr; - QTextDocument *m_document = nullptr; - -signals: - void resultsReady(const QList<SyntaxHighlighter::Result> &result); - -}; - -void SyntaxHighlighterRunner::HighlightingStatus::notInterrupted(int from, - int charsRemoved, - int charsAdded) -{ - m_from = from; - m_addedChars = charsAdded; - m_removedChars = charsRemoved; - m_current = from; - m_newFrom = from + m_addedChars; - m_interruptionRequested = false; -} - -void SyntaxHighlighterRunner::HighlightingStatus::interrupted(int from, - int charsRemoved, - int charsAdded) -{ - m_newFrom = std::min(m_newFrom, from); - m_newFrom = std::min(m_current, m_newFrom); - m_removedChars += charsRemoved; - m_addedChars += charsAdded; - m_interruptionRequested = true; -} - -void SyntaxHighlighterRunner::HighlightingStatus::applyNewFrom() -{ - m_from = m_newFrom; - m_current = m_newFrom; - m_interruptionRequested = false; -} - -SyntaxHighlighterRunner::SyntaxHighlighterRunner(SyntaxHighlighter *highlighter, - QTextDocument *document, - bool async) - : d(new SyntaxHighlighterRunnerPrivate(highlighter, document, async)) - , m_document(document) -{ - m_useGenericHighlighter = qobject_cast<Highlighter *>(d->m_highlighter); - - if (async) { - m_thread.emplace(); - d->moveToThread(&*m_thread); - connect(&*m_thread, &QThread::finished, d, &QObject::deleteLater); - m_thread->start(); - - connect(d, - &SyntaxHighlighterRunnerPrivate::resultsReady, - this, - &SyntaxHighlighterRunner::applyFormatRanges); - - changeDocument(0, 0, m_document->characterCount()); - connect(m_document, - &QTextDocument::contentsChange, - this, - &SyntaxHighlighterRunner::changeDocument); - - m_foldValidator.setup(qobject_cast<TextDocumentLayout *>(document->documentLayout())); - } else { - connect(d, - &SyntaxHighlighterRunnerPrivate::resultsReady, - this, - [this](const QList<SyntaxHighlighter::Result> &result) { - if (result.size() == 1 - && result.at(0).m_state == SyntaxHighlighter::State::Extras) - return; - - auto done = std::find_if(result.cbegin(), - result.cend(), - [](const SyntaxHighlighter::Result &res) { - return res.m_state - == SyntaxHighlighter::State::Done; - }); - if (done != result.cend()) { - m_syntaxInfoUpdated = SyntaxHighlighter::State::Done; - emit highlightingFinished(); - return; - } - m_syntaxInfoUpdated = SyntaxHighlighter::State::InProgress; - }); - } -} - -SyntaxHighlighterRunner::~SyntaxHighlighterRunner() -{ - if (m_thread) { - m_thread->requestInterruption(); - m_thread->quit(); - m_thread->wait(); - } else { - delete d; - } -} - -void SyntaxHighlighterRunner::applyFormatRanges(const QList<SyntaxHighlighter::Result> &results) -{ - if (m_document == nullptr) - return; - - if (m_highlightingStatus.m_interruptionRequested) { - d->setInterrupted(false); - m_highlightingStatus.applyNewFrom(); - reformatBlocks(m_highlightingStatus.m_newFrom, - m_highlightingStatus.m_removedChars, - m_highlightingStatus.m_addedChars); - return; - } - - auto processResult = [this](SyntaxHighlighter::Result result, QTextBlock docBlock) { - if (!docBlock.isValid()) - return; - - result.copyToBlock(docBlock); - m_highlightingStatus.m_current = docBlock.position() + docBlock.length() - 1; - - if (result.m_formatRanges != docBlock.layout()->formats()) { - docBlock.layout()->setFormats(result.m_formatRanges); - m_document->markContentsDirty(docBlock.position(), docBlock.length()); - } - }; - - if (results.size() == 1 && results.at(0).m_state == SyntaxHighlighter::State::Extras) { - QTextBlock docBlock = m_document->findBlockByNumber(results.at(0).m_blockNumber); - processResult(results.at(0), docBlock); - return; - } - - for (const SyntaxHighlighter::Result &result : results) { - m_syntaxInfoUpdated = result.m_state; - if (m_syntaxInfoUpdated == SyntaxHighlighter::State::Start) { - m_foldValidator.reset(); - continue; - } - if (m_syntaxInfoUpdated == SyntaxHighlighter::State::Done) { - m_foldValidator.finalize(); - emit highlightingFinished(); - return; - } - - QTextBlock docBlock = m_document->findBlockByNumber(result.m_blockNumber); - processResult(result, docBlock); - m_foldValidator.process(docBlock); - } -} - -void SyntaxHighlighterRunner::changeDocument(int from, int charsRemoved, int charsAdded) -{ - QTC_ASSERT(m_document, return); - SyntaxHighlighter::State prevSyntaxInfoUpdated = m_syntaxInfoUpdated; - m_syntaxInfoUpdated = SyntaxHighlighter::State::InProgress; - - QMap<int, BlockPreeditData> blocksPreedit; - QTextBlock block = m_document->findBlock(from); - const QTextBlock endBlock = m_document->findBlock(from + charsAdded); - while (block.isValid() && block != endBlock) { - if (QTextLayout *layout = block.layout()) { - if (const int pos = layout->preeditAreaPosition(); pos != -1) - blocksPreedit[block.blockNumber()] = {pos, layout->preeditAreaText()}; - } - block = block.next(); - } - const QString text = Utils::Text::textAt(QTextCursor(m_document), from, charsAdded); - QMetaObject::invokeMethod(d, [this, from, charsRemoved, text, blocksPreedit] { - d->changeDocument(from, charsRemoved, text, blocksPreedit); - }); - - if (prevSyntaxInfoUpdated == SyntaxHighlighter::State::InProgress) { - m_highlightingStatus.interrupted(from, charsRemoved, charsAdded); - d->setInterrupted(true); - } else { - m_highlightingStatus.notInterrupted(from, charsRemoved, charsAdded); - d->setInterrupted(false); - } -} - -bool SyntaxHighlighterRunner::useGenericHighlighter() const -{ - return m_useGenericHighlighter; -} - -void SyntaxHighlighterRunner::setExtraFormats( - const QMap<int, QList<QTextLayout::FormatRange>> &formatMap) -{ - QMetaObject::invokeMethod(d, [this, formatMap] { d->setExtraFormats(formatMap); }); -} - -void SyntaxHighlighterRunner::clearExtraFormats(const QList<int> &blockNumbers) -{ - QMetaObject::invokeMethod(d, [this, blockNumbers] { d->clearExtraFormats(blockNumbers); }); -} - -void SyntaxHighlighterRunner::clearAllExtraFormats() -{ - QMetaObject::invokeMethod(d, [this] { d->clearAllExtraFormats(); }); -} - -void SyntaxHighlighterRunner::setFontSettings(const TextEditor::FontSettings &fontSettings) -{ - QMetaObject::invokeMethod(d, [this, fontSettings] { d->setFontSettings(fontSettings); }); - rehighlight(); -} - -void SyntaxHighlighterRunner::setLanguageFeaturesFlags(unsigned int flags) -{ - QMetaObject::invokeMethod(d, [this, flags] { d->setLanguageFeaturesFlags(flags); }); -} - -void SyntaxHighlighterRunner::setEnabled(bool enabled) -{ - QMetaObject::invokeMethod(d, [this, enabled] { d->setEnabled(enabled); }); -} - -void SyntaxHighlighterRunner::rehighlight() -{ - if (m_syntaxInfoUpdated == SyntaxHighlighter::State::InProgress) { - m_highlightingStatus.interrupted(0, 0, m_document->characterCount()); - d->setInterrupted(true); - } else { - m_highlightingStatus.notInterrupted(0, 0, m_document->characterCount()); - d->setInterrupted(false); - QMetaObject::invokeMethod(d, [this] { d->rehighlight(); }); - } -} - - -void SyntaxHighlighterRunner::reformatBlocks(int from, int charsRemoved, int charsAdded) -{ - QMetaObject::invokeMethod( - d, - [this, from, charsRemoved, charsAdded] { - d->reformatBlocks(from, charsRemoved, charsAdded); - }); -} - -QString SyntaxHighlighterRunner::definitionName() -{ - return m_definitionName; -} - -void SyntaxHighlighterRunner::setDefinitionName(const QString &name) -{ - if (name.isEmpty()) - return; - - m_definitionName = name; - QMetaObject::invokeMethod(d, [this, name] { d->setDefinitionName(name); }); -} - -} // namespace TextEditor - -#include "syntaxhighlighterrunner.moc" diff --git a/src/plugins/texteditor/syntaxhighlighterrunner.h b/src/plugins/texteditor/syntaxhighlighterrunner.h deleted file mode 100644 index 16b7c1535c8..00000000000 --- a/src/plugins/texteditor/syntaxhighlighterrunner.h +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include <texteditor/fontsettings.h> -#include <texteditor/syntaxhighlighter.h> -#include <texteditor/texteditorsettings.h> - -#include <QObject> -#include <QPointer> - -QT_BEGIN_NAMESPACE -class QTextDocument; -QT_END_NAMESPACE - -namespace TextEditor { - -class SyntaxHighlighterRunnerPrivate; - -class TEXTEDITOR_EXPORT SyntaxHighlighterRunner : public QObject -{ - Q_OBJECT -public: - using SyntaxHighlighterCreator = std::function<SyntaxHighlighter *()>; - - SyntaxHighlighterRunner(SyntaxHighlighter *highlighter, QTextDocument *document, bool async); - virtual ~SyntaxHighlighterRunner(); - - void setExtraFormats(const QMap<int, QList<QTextLayout::FormatRange>> &formats); - void clearExtraFormats(const QList<int> &blockNumbers); - void clearAllExtraFormats(); - void setFontSettings(const TextEditor::FontSettings &fontSettings); - void setLanguageFeaturesFlags(unsigned int flags); - void setEnabled(bool enabled); - void rehighlight(); - void reformatBlocks(int from, int charsRemoved, int charsAdded); - - QString definitionName(); - void setDefinitionName(const QString &name); - - QTextDocument *document() const { return m_document; } - bool useGenericHighlighter() const; - - bool syntaxInfoUpdated() const { return m_syntaxInfoUpdated == SyntaxHighlighter::State::Done; } - -signals: - void highlightingFinished(); - -private: - void applyFormatRanges(const QList<SyntaxHighlighter::Result> &results); - void changeDocument(int from, int charsRemoved, int charsAdded); - - SyntaxHighlighterRunnerPrivate *d; - QPointer<QTextDocument> m_document = nullptr; - SyntaxHighlighter::State m_syntaxInfoUpdated = SyntaxHighlighter::State::Done; - - struct HighlightingStatus - { - int m_from = 0; - int m_addedChars = 0; - int m_current = 0; - int m_removedChars = 0; - int m_newFrom = 0; - bool m_interruptionRequested = false; - - void notInterrupted(int from, int charsRemoved, int charsAdded); - void interrupted(int from, int charsRemoved, int charsAdded); - void applyNewFrom(); - } m_highlightingStatus; - - bool m_useGenericHighlighter = false; - QString m_definitionName; - std::optional<QThread> m_thread; - TextDocumentLayout::FoldValidator m_foldValidator; -}; - -} // namespace TextEditor - diff --git a/src/plugins/texteditor/textdocument.cpp b/src/plugins/texteditor/textdocument.cpp index 2636ad46e23..7fcfe5c86c2 100644 --- a/src/plugins/texteditor/textdocument.cpp +++ b/src/plugins/texteditor/textdocument.cpp @@ -10,10 +10,10 @@ #include "tabsettings.h" #include "textdocumentlayout.h" #include "texteditor.h" +#include "texteditorsettings.h" #include "texteditortr.h" #include "textindenter.h" #include "typingsettings.h" -#include "syntaxhighlighterrunner.h" #include <coreplugin/coreconstants.h> #include <coreplugin/diffservice.h> @@ -64,8 +64,6 @@ public: ~TextDocumentPrivate() { - if (m_highlighterRunner) - m_highlighterRunner->deleteLater(); } MultiTextCursor indentOrUnindent(const MultiTextCursor &cursor, bool doIndent, const TabSettings &tabSettings); @@ -101,7 +99,7 @@ public: TextMarks m_marksCache; // Marks not owned Utils::Guard m_modificationChangedGuard; - SyntaxHighlighterRunner *m_highlighterRunner = nullptr; + SyntaxHighlighter *m_highlighter = nullptr; }; MultiTextCursor TextDocumentPrivate::indentOrUnindent(const MultiTextCursor &cursors, @@ -462,8 +460,8 @@ void TextDocument::applyFontSettings() block = block.next(); } updateLayout(); - if (d->m_highlighterRunner) - d->m_highlighterRunner->setFontSettings(d->m_fontSettings); + if (d->m_highlighter) + d->m_highlighter->setFontSettings(d->m_fontSettings); } const FontSettings &TextDocument::fontSettings() const @@ -524,15 +522,21 @@ bool TextDocument::applyChangeSet(const ChangeSet &changeSet) { if (changeSet.isEmpty()) return true; - PlainRefactoringFileFactory changes; - const RefactoringFilePtr file = changes.file(filePath()); - file->setChangeSet(changeSet); - return file->apply(); + return PlainRefactoringFileFactory().file(filePath())->apply(changeSet); } // the blocks list must be sorted void TextDocument::setIfdefedOutBlocks(const QList<BlockRange> &blocks) { + if (syntaxHighlighter() && !syntaxHighlighter()->syntaxHighlighterUpToDate()) { + connect(syntaxHighlighter(), + &SyntaxHighlighter::finished, + this, + [this, blocks] { setIfdefedOutBlocks(blocks); }, + Qt::SingleShotConnection); + return; + } + QTextDocument *doc = document(); auto documentLayout = qobject_cast<TextDocumentLayout*>(doc->documentLayout()); QTC_ASSERT(documentLayout, return); @@ -635,11 +639,6 @@ QTextDocument *TextDocument::document() const return &d->m_document; } -SyntaxHighlighterRunner *TextDocument::syntaxHighlighterRunner() const -{ - return d->m_highlighterRunner; -} - /*! * Saves the document to the file specified by \a fileName. If errors occur, * \a errorString contains their cause. @@ -911,26 +910,19 @@ bool TextDocument::reload(QString *errorString, ReloadFlag flag, ChangeType type return reload(errorString); } -void TextDocument::resetSyntaxHighlighter(const std::function<SyntaxHighlighter *()> &creator, - bool threaded) +void TextDocument::resetSyntaxHighlighter(const std::function<SyntaxHighlighter *()> &creator) { - delete d->m_highlighterRunner; - - static const std::optional<bool> envValue = []() -> std::optional<bool> { - const QString key("QTC_USE_THREADED_HIGHLIGHTER"); - if (qtcEnvironmentVariableIsSet(key)) { - const QString value = qtcEnvironmentVariable(key).toUpper(); - return value != "FALSE" && value != "0"; - } - return {}; - }(); - SyntaxHighlighter *highlighter = creator(); + highlighter->setParent(this); + highlighter->setDocument(this->document()); highlighter->setFontSettings(TextEditorSettings::fontSettings()); highlighter->setMimeType(mimeType()); - d->m_highlighterRunner = new SyntaxHighlighterRunner(highlighter, - document(), - envValue.value_or(threaded)); + d->m_highlighter = highlighter; +} + +SyntaxHighlighter *TextDocument::syntaxHighlighter() const +{ + return d->m_highlighter; } void TextDocument::cleanWhitespace(const QTextCursor &cursor) diff --git a/src/plugins/texteditor/textdocument.h b/src/plugins/texteditor/textdocument.h index 5879d9e9ec2..b3bf77e5442 100644 --- a/src/plugins/texteditor/textdocument.h +++ b/src/plugins/texteditor/textdocument.h @@ -34,7 +34,6 @@ class FontSettings; class IAssistProvider; class StorageSettings; class SyntaxHighlighter; -class SyntaxHighlighterRunner; class TabSettings; class TextDocumentPrivate; class TextMark; @@ -127,8 +126,8 @@ public: QTextDocument *document() const; using SyntaxHighLighterCreator = std::function<SyntaxHighlighter *()>; - void resetSyntaxHighlighter(const SyntaxHighLighterCreator &creator, bool threaded = false); - SyntaxHighlighterRunner *syntaxHighlighterRunner() const; + void resetSyntaxHighlighter(const SyntaxHighLighterCreator &creator); + SyntaxHighlighter *syntaxHighlighter() const; bool reload(QString *errorString, QTextCodec *codec); void cleanWhitespace(const QTextCursor &cursor); diff --git a/src/plugins/texteditor/texteditor.cpp b/src/plugins/texteditor/texteditor.cpp index bea58df3c88..d75db87a166 100644 --- a/src/plugins/texteditor/texteditor.cpp +++ b/src/plugins/texteditor/texteditor.cpp @@ -21,6 +21,7 @@ #include "highlighterhelper.h" #include "highlightersettings.h" #include "icodestylepreferences.h" +#include "linenumberfilter.h" #include "marginsettings.h" #include "refactoroverlay.h" #include "snippets/snippetoverlay.h" @@ -28,13 +29,12 @@ #include "tabsettings.h" #include "textdocument.h" #include "textdocumentlayout.h" -#include "texteditoractionhandler.h" #include "texteditorconstants.h" #include "texteditoroverlay.h" #include "texteditorsettings.h" #include "texteditortr.h" +#include "typehierarchy.h" #include "typingsettings.h" -#include "syntaxhighlighterrunner.h" #include <aggregation/aggregate.h> @@ -46,7 +46,9 @@ #include <coreplugin/find/basetextfind.h> #include <coreplugin/find/highlightscrollbarcontroller.h> #include <coreplugin/icore.h> +#include <coreplugin/locator/locatormanager.h> #include <coreplugin/manhattanstyle.h> +#include <coreplugin/navigationwidget.h> #include <utils/algorithm.h> #include <utils/async.h> @@ -352,6 +354,7 @@ public: BaseTextEditorPrivate() = default; TextEditorFactoryPrivate *m_origin = nullptr; + QByteArray m_savedNavigationState; }; class HoverHandlerRunner @@ -739,6 +742,14 @@ public: void openTypeUnderCursor(bool openInNextSplit); qreal charWidth() const; + // actions + void registerActions(); + void updateActions(); + void updateOptionalActions(); + void updateRedoAction(); + void updateUndoAction(); + void updateCopyAction(bool on); + public: TextEditorWidget *q; QWidget *m_toolBarWidget = nullptr; @@ -754,7 +765,7 @@ public: QToolButton *m_fileLineEnding = nullptr; QAction *m_fileLineEndingAction = nullptr; - uint m_optionalActionMask = TextEditorActionHandler::None; + uint m_optionalActionMask = OptionalActions::None; bool m_contentsChanged = false; bool m_lastCursorChangeWasInteresting = false; std::shared_ptr<void> m_suggestionBlocker; @@ -762,7 +773,6 @@ public: QSharedPointer<TextDocument> m_document; QList<QMetaObject::Connection> m_documentConnections; QByteArray m_tempState; - QByteArray m_tempNavigationState; bool m_parenthesesMatchingEnabled = false; QTimer m_parenthesesMatchingTimer; @@ -928,6 +938,30 @@ public: void updateSuggestion(); void clearCurrentSuggestion(); QTextBlock m_suggestionBlock; + + Context m_editorContext; + QAction *m_undoAction = nullptr; + QAction *m_redoAction = nullptr; + QAction *m_copyAction = nullptr; + QAction *m_copyHtmlAction = nullptr; + QAction *m_cutAction = nullptr; + QAction *m_autoIndentAction = nullptr; + QAction *m_autoFormatAction = nullptr; + QAction *m_visualizeWhitespaceAction = nullptr; + QAction *m_textWrappingAction = nullptr; + QAction *m_unCommentSelectionAction = nullptr; + QAction *m_unfoldAllAction = nullptr; + QAction *m_followSymbolAction = nullptr; + QAction *m_followSymbolInNextSplitAction = nullptr; + QAction *m_followToTypeAction = nullptr; + QAction *m_followToTypeInNextSplitAction = nullptr; + QAction *m_findUsageAction = nullptr; + QAction *m_openCallHierarchyAction = nullptr; + QAction *m_openTypeHierarchyAction = nullptr; + QAction *m_renameSymbolAction = nullptr; + QAction *m_jumpToFileAction = nullptr; + QAction *m_jumpToFileInNextSplitAction = nullptr; + QList<QAction *> m_modifyingActions; }; class TextEditorWidgetFind : public BaseTextFind @@ -986,6 +1020,8 @@ void TextEditorWidgetFind::selectAll(const QString &txt, FindFlags findFlags) if (future.resultCount() <= 0) return; const SearchResultItems &results = future.result(); + if (results.isEmpty()) + return; const auto cursorForResult = [this](const SearchResultItem &item) { return selectRange(m_editor->document(), item.mainRange()); }; @@ -1032,6 +1068,7 @@ TextEditorWidgetPrivate::TextEditorWidgetPrivate(TextEditorWidget *parent) , m_codeAssistant(parent) , m_hoverHandlerRunner(parent, m_hoverHandlers) , m_autoCompleter(new AutoCompleter) + , m_editorContext(Id::fromString(QUuid::createUuid().toString())) { m_selectionHighlightOverlay->show(); auto aggregate = new Aggregation::Aggregate; @@ -1096,6 +1133,15 @@ TextEditorWidgetPrivate::TextEditorWidgetPrivate(TextEditorWidget *parent) connect(q, &QPlainTextEdit::selectionChanged, this, &TextEditorWidgetPrivate::slotSelectionChanged); + connect(q, &QPlainTextEdit::undoAvailable, + this, &TextEditorWidgetPrivate::updateUndoAction); + + connect(q, &QPlainTextEdit::redoAvailable, + this, &TextEditorWidgetPrivate::updateRedoAction); + + connect(q, &QPlainTextEdit::copyAvailable, + this, &TextEditorWidgetPrivate::updateCopyAction); + m_parenthesesMatchingTimer.setSingleShot(true); m_parenthesesMatchingTimer.setInterval(50); connect(&m_parenthesesMatchingTimer, &QTimer::timeout, @@ -1142,6 +1188,14 @@ TextEditorWidgetPrivate::TextEditorWidgetPrivate(TextEditorWidget *parent) q, &TextEditorWidget::setCompletionSettings); connect(settings, &TextEditorSettings::extraEncodingSettingsChanged, q, &TextEditorWidget::setExtraEncodingSettings); + + auto context = new Core::IContext(this); + context->setWidget(q); + context->setContext(m_editorContext); + Core::ICore::addContextObject(context); + + registerActions(); + updateActions(); } TextEditorWidgetPrivate::~TextEditorWidgetPrivate() @@ -1262,15 +1316,15 @@ TextEditorWidget::TextEditorWidget(QWidget *parent) { // "Needed", as the creation below triggers ChildEvents that are // passed to this object's event() which uses 'd'. - d = nullptr; - d = new TextEditorWidgetPrivate(this); - + d = std::make_unique<Internal::TextEditorWidgetPrivate>(this); setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); setLayoutDirection(Qt::LeftToRight); viewport()->setMouseTracking(true); setFrameStyle(QFrame::NoFrame); } +TextEditorWidget::~TextEditorWidget() = default; + void TextEditorWidget::setTextDocument(const QSharedPointer<TextDocument> &doc) { d->setDocument(doc); @@ -1449,12 +1503,6 @@ void TextEditorWidgetPrivate::setDocument(const QSharedPointer<TextDocument> &do setupFromDefinition(currentDefinition()); } -TextEditorWidget::~TextEditorWidget() -{ - delete d; - d = nullptr; -} - void TextEditorWidget::print(QPrinter *printer) { const bool oldFullPage = printer->fullPage(); @@ -1884,6 +1932,12 @@ void TextEditorWidget::setVisualIndentOffset(int offset) d->m_visualIndentOffset = qMax(0, offset); } +void TextEditorWidget::updateUndoRedoActions() +{ + d->updateUndoAction(); + d->updateRedoAction(); +} + void TextEditorWidgetPrivate::updateCannotDecodeInfo() { q->setReadOnly(m_document->hasDecodingError()); @@ -1935,8 +1989,8 @@ void TextEditorWidgetPrivate::foldLicenseHeader() QStringList commentMarker; QStringList docMarker; HighlighterHelper::Definition def; - if (SyntaxHighlighterRunner *highlighter = q->textDocument()->syntaxHighlighterRunner()) - def = HighlighterHelper::definitionForName(highlighter->definitionName()); + if (auto highlighter = qobject_cast<Highlighter *>(q->textDocument()->syntaxHighlighter())) + def = highlighter->definition(); if (def.isValid()) { for (const QString &marker : @@ -2535,6 +2589,16 @@ void TextEditorWidget::redo() QPlainTextEdit::redo(); } +bool TextEditorWidget::isUndoAvailable() const +{ + return document()->isUndoAvailable(); +} + +bool TextEditorWidget::isRedoAvailable() const +{ + return document()->isRedoAvailable(); +} + void TextEditorWidget::openLinkUnderCursor() { d->openLinkUnderCursor(alwaysOpenLinksInNextSplit()); @@ -3494,10 +3558,10 @@ QByteArray TextEditorWidget::saveState() const bool TextEditorWidget::singleShotAfterHighlightingDone(std::function<void()> &&f) { - if (d->m_document->syntaxHighlighterRunner() - && !d->m_document->syntaxHighlighterRunner()->syntaxInfoUpdated()) { - connect(d->m_document->syntaxHighlighterRunner(), - &SyntaxHighlighterRunner::highlightingFinished, + if (d->m_document->syntaxHighlighter() + && !d->m_document->syntaxHighlighter()->syntaxHighlighterUpToDate()) { + connect(d->m_document->syntaxHighlighter(), + &SyntaxHighlighter::finished, this, [f = std::move(f)] { f(); }, Qt::SingleShotConnection); return true; @@ -3741,10 +3805,11 @@ void TextEditorWidgetPrivate::configureGenericHighlighter( const QString definitionFilesPath = TextEditorSettings::highlighterSettings().definitionFilesPath().toString(); - m_document->resetSyntaxHighlighter([definitionFilesPath] { - return new Highlighter(definitionFilesPath); + m_document->resetSyntaxHighlighter([definitionFilesPath, definition] { + auto highlighter = new Highlighter(definitionFilesPath); + highlighter->setDefinition(definition); + return highlighter; }); - m_document->syntaxHighlighterRunner()->setDefinitionName(definition.name()); m_document->setFontSettings(TextEditorSettings::fontSettings()); } @@ -3769,8 +3834,8 @@ void TextEditorWidgetPrivate::setupFromDefinition(const KSyntaxHighlighting::Def KSyntaxHighlighting::Definition TextEditorWidgetPrivate::currentDefinition() { - if (SyntaxHighlighterRunner *highlighter = m_document->syntaxHighlighterRunner()) - return HighlighterHelper::definitionForName(highlighter->definitionName()); + if (auto *highlighter = qobject_cast<Highlighter *>(m_document->syntaxHighlighter())) + return highlighter->definition(); return {}; } @@ -3810,6 +3875,531 @@ qreal TextEditorWidgetPrivate::charWidth() const return QFontMetricsF(q->font()).horizontalAdvance(QLatin1Char('x')); } +void TextEditorWidgetPrivate::registerActions() +{ + using namespace Core::Constants; + using namespace TextEditor::Constants; + + ActionBuilder(this, Constants::COMPLETE_THIS) + .setContext(m_editorContext) + .addOnTriggered(this, [this] { q->invokeAssist(Completion); }); + + ActionBuilder(this, Constants::FUNCTION_HINT) + .setContext(m_editorContext) + .addOnTriggered(this, [this] { q->invokeAssist(FunctionHint); }); + + ActionBuilder(this, Constants::QUICKFIX_THIS) + .setContext(m_editorContext) + .addOnTriggered(this, [this] { q->invokeAssist(QuickFix); }); + + ActionBuilder(this, Constants::SHOWCONTEXTMENU) + .setContext(m_editorContext) + .addOnTriggered(this, [this] { q->showContextMenu(); }); + + m_undoAction = ActionBuilder(this, UNDO) + .setContext(m_editorContext) + .addOnTriggered([this] { q->undo(); }) + .setScriptable(true) + .contextAction(); + m_redoAction = ActionBuilder(this, REDO) + .setContext(m_editorContext) + .addOnTriggered([this] { q->redo(); }) + .setScriptable(true) + .contextAction(); + m_copyAction = ActionBuilder(this, COPY) + .setContext(m_editorContext) + .addOnTriggered([this] { q->copy(); }) + .setScriptable(true) + .contextAction(); + m_cutAction = ActionBuilder(this, CUT) + .setContext(m_editorContext) + .addOnTriggered([this] { q->cut(); }) + .setScriptable(true) + .contextAction(); + m_modifyingActions << ActionBuilder(this, PASTE) + .setContext(m_editorContext) + .addOnTriggered([this] { q->paste(); }) + .setScriptable(true) + .contextAction(); + ActionBuilder(this, SELECTALL) + .setContext(m_editorContext) + .setScriptable(true) + .addOnTriggered([this] { q->selectAll(); }); + ActionBuilder(this, GOTO).setContext(m_editorContext).addOnTriggered([] { + LocatorManager::showFilter(lineNumberFilter()); + }); + ActionBuilder(this, PRINT) + .setContext(m_editorContext) + .addOnTriggered([this] { q->print(ICore::printer()); }) + .contextAction(); + m_modifyingActions << ActionBuilder(this, DELETE_LINE) + .setContext(m_editorContext) + .addOnTriggered([this] { q->deleteLine(); }) + .setScriptable(true) + .contextAction(); + m_modifyingActions << ActionBuilder(this, DELETE_END_OF_LINE) + .setContext(m_editorContext) + .addOnTriggered([this] { q->deleteEndOfLine(); }) + .setScriptable(true) + .contextAction(); + m_modifyingActions << ActionBuilder(this, DELETE_END_OF_WORD) + .setContext(m_editorContext) + .addOnTriggered([this] { q->deleteEndOfWord(); }) + .setScriptable(true) + .contextAction(); + m_modifyingActions << ActionBuilder(this, DELETE_END_OF_WORD_CAMEL_CASE) + .setContext(m_editorContext) + .addOnTriggered([this] { q->deleteEndOfWordCamelCase(); }) + .setScriptable(true) + .contextAction(); + m_modifyingActions << ActionBuilder(this, DELETE_START_OF_LINE) + .setContext(m_editorContext) + .addOnTriggered([this] { q->deleteStartOfLine(); }) + .setScriptable(true) + .contextAction(); + m_modifyingActions << ActionBuilder(this, DELETE_START_OF_WORD) + .setContext(m_editorContext) + .addOnTriggered([this] { q->deleteStartOfWord(); }) + .setScriptable(true) + .contextAction(); + m_modifyingActions << ActionBuilder(this, DELETE_START_OF_WORD_CAMEL_CASE) + .setContext(m_editorContext) + .addOnTriggered([this] { q->deleteStartOfWordCamelCase(); }) + .setScriptable(true) + .contextAction(); + ActionBuilder(this, GOTO_BLOCK_START_WITH_SELECTION) + .setContext(m_editorContext) + .addOnTriggered([this] { q->gotoBlockStartWithSelection(); }) + .setScriptable(true); + ActionBuilder(this, GOTO_BLOCK_END_WITH_SELECTION) + .setContext(m_editorContext) + .addOnTriggered([this] { q->gotoBlockEndWithSelection(); }) + .setScriptable(true); + m_modifyingActions << ActionBuilder(this, MOVE_LINE_UP) + .setContext(m_editorContext) + .addOnTriggered([this] { q->moveLineUp(); }) + .setScriptable(true) + .contextAction(); + m_modifyingActions << ActionBuilder(this, MOVE_LINE_DOWN) + .setContext(m_editorContext) + .addOnTriggered([this] { q->moveLineDown(); }) + .setScriptable(true) + .contextAction(); + m_modifyingActions << ActionBuilder(this, COPY_LINE_UP) + .setContext(m_editorContext) + .addOnTriggered([this] { q->copyLineUp(); }) + .setScriptable(true) + .contextAction(); + m_modifyingActions << ActionBuilder(this, COPY_LINE_DOWN) + .setContext(m_editorContext) + .addOnTriggered([this] { q->copyLineDown(); }) + .setScriptable(true) + .contextAction(); + m_modifyingActions << ActionBuilder(this, JOIN_LINES) + .setContext(m_editorContext) + .addOnTriggered([this] { q->joinLines(); }) + .setScriptable(true) + .contextAction(); + m_modifyingActions << ActionBuilder(this, INSERT_LINE_ABOVE) + .setContext(m_editorContext) + .addOnTriggered([this] { q->insertLineAbove(); }) + .setScriptable(true) + .contextAction(); + m_modifyingActions << ActionBuilder(this, INSERT_LINE_BELOW) + .setContext(m_editorContext) + .addOnTriggered([this] { q->insertLineBelow(); }) + .setScriptable(true) + .contextAction(); + m_modifyingActions << ActionBuilder(this, SWITCH_UTF8BOM) + .setContext(m_editorContext) + .addOnTriggered([this] { q->switchUtf8bom(); }) + .contextAction(); + m_modifyingActions << ActionBuilder(this, INDENT) + .setContext(m_editorContext) + .addOnTriggered([this] { q->indent(); }) + .setScriptable(true) + .contextAction(); + m_modifyingActions << ActionBuilder(this, UNINDENT) + .setContext(m_editorContext) + .addOnTriggered([this] { q->unindent(); }) + .setScriptable(true) + .contextAction(); + m_followSymbolAction = ActionBuilder(this, FOLLOW_SYMBOL_UNDER_CURSOR) + .setContext(m_editorContext) + .addOnTriggered([this] { q->openLinkUnderCursor(); }) + .contextAction(); + m_followSymbolInNextSplitAction = ActionBuilder(this, FOLLOW_SYMBOL_UNDER_CURSOR_IN_NEXT_SPLIT) + .setContext(m_editorContext) + .addOnTriggered( + [this] { q->openLinkUnderCursorInNextSplit(); }) + .contextAction(); + m_followToTypeAction = ActionBuilder(this, FOLLOW_SYMBOL_TO_TYPE) + .setContext(m_editorContext) + .addOnTriggered([this] { q->openTypeUnderCursor(); }) + .contextAction(); + m_followToTypeInNextSplitAction = ActionBuilder(this, FOLLOW_SYMBOL_TO_TYPE_IN_NEXT_SPLIT) + .setContext(m_editorContext) + .addOnTriggered( + [this] { q->openTypeUnderCursorInNextSplit(); }) + .contextAction(); + m_findUsageAction = ActionBuilder(this, FIND_USAGES) + .setContext(m_editorContext) + .addOnTriggered([this] { q->findUsages(); }) + .contextAction(); + m_renameSymbolAction = ActionBuilder(this, RENAME_SYMBOL) + .setContext(m_editorContext) + .addOnTriggered([this] { q->renameSymbolUnderCursor(); }) + .contextAction(); + m_jumpToFileAction = ActionBuilder(this, JUMP_TO_FILE_UNDER_CURSOR) + .setContext(m_editorContext) + .addOnTriggered([this] { q->openLinkUnderCursor(); }) + .contextAction(); + m_jumpToFileInNextSplitAction = ActionBuilder(this, JUMP_TO_FILE_UNDER_CURSOR_IN_NEXT_SPLIT) + .setContext(m_editorContext) + .addOnTriggered( + [this] { q->openLinkUnderCursorInNextSplit(); }) + .contextAction(); + m_openCallHierarchyAction = ActionBuilder(this, OPEN_CALL_HIERARCHY) + .setContext(m_editorContext) + .addOnTriggered([this] { q->openCallHierarchy(); }) + .setScriptable(true) + .contextAction(); + m_openTypeHierarchyAction = ActionBuilder(this, OPEN_TYPE_HIERARCHY) + .setContext(m_editorContext) + .addOnTriggered([] { + updateTypeHierarchy(NavigationWidget::activateSubWidget( + Constants::TYPE_HIERARCHY_FACTORY_ID, Side::Left)); + }) + .setScriptable(true) + .contextAction(); + ActionBuilder(this, VIEW_PAGE_UP) + .setContext(m_editorContext) + .addOnTriggered([this] { q->viewPageUp(); }) + .setScriptable(true); + ActionBuilder(this, VIEW_PAGE_DOWN) + .setContext(m_editorContext) + .addOnTriggered([this] { q->viewPageDown(); }) + .setScriptable(true); + ActionBuilder(this, VIEW_LINE_UP) + .setContext(m_editorContext) + .addOnTriggered([this] { q->viewLineUp(); }) + .setScriptable(true); + ActionBuilder(this, VIEW_LINE_DOWN) + .setContext(m_editorContext) + .addOnTriggered([this] { q->viewLineDown(); }) + .setScriptable(true); + + ActionBuilder(this, SELECT_ENCODING).setContext(m_editorContext).addOnTriggered([this] { + q->selectEncoding(); + }); + m_modifyingActions << ActionBuilder(this, CIRCULAR_PASTE) + .setContext(m_editorContext) + .addOnTriggered([this] { q->circularPaste(); }) + .contextAction(); + m_modifyingActions << ActionBuilder(this, NO_FORMAT_PASTE) + .setContext(m_editorContext) + .addOnTriggered([this] { q->pasteWithoutFormat(); }) + .setScriptable(true) + .contextAction(); + + m_autoIndentAction = ActionBuilder(this, AUTO_INDENT_SELECTION) + .setContext(m_editorContext) + .addOnTriggered([this] { q->autoIndent(); }) + .setScriptable(true) + .contextAction(); + m_autoFormatAction = ActionBuilder(this, AUTO_FORMAT_SELECTION) + .setContext(m_editorContext) + .addOnTriggered([this] { q->autoFormat(); }) + .setScriptable(true) + .contextAction(); + m_modifyingActions << ActionBuilder(this, REWRAP_PARAGRAPH) + .setContext(m_editorContext) + .addOnTriggered([this] { q->rewrapParagraph(); }) + .setScriptable(true) + .contextAction(); + m_visualizeWhitespaceAction = ActionBuilder(this, VISUALIZE_WHITESPACE) + .setContext(m_editorContext) + .addOnToggled( + this, + [this](bool checked) { + DisplaySettings ds = q->displaySettings(); + ds.m_visualizeWhitespace = checked; + q->setDisplaySettings(ds); + }) + .contextAction(); + m_modifyingActions << ActionBuilder(this, CLEAN_WHITESPACE) + .setContext(m_editorContext) + .addOnTriggered([this] { q->cleanWhitespace(); }) + .setScriptable(true) + .contextAction(); + m_textWrappingAction = ActionBuilder(this, TEXT_WRAPPING) + .setContext(m_editorContext) + .addOnToggled( + this, + [this](bool checked) { + DisplaySettings ds = q->displaySettings(); + ds.m_textWrapping = checked; + q->setDisplaySettings(ds); + }) + .contextAction(); + m_unCommentSelectionAction = ActionBuilder(this, UN_COMMENT_SELECTION) + .setContext(m_editorContext) + .addOnTriggered([this] { q->unCommentSelection(); }) + .setScriptable(true) + .contextAction(); + m_modifyingActions << ActionBuilder(this, CUT_LINE) + .setContext(m_editorContext) + .addOnTriggered([this] { q->cutLine(); }) + .setScriptable(true) + .contextAction(); + ActionBuilder(this, COPY_LINE) + .setContext(m_editorContext) + .addOnTriggered([this] { q->copyLine(); }) + .setScriptable(true); + m_copyHtmlAction = ActionBuilder(this, COPY_WITH_HTML) + .setContext(m_editorContext) + .addOnTriggered([this] { q->copyWithHtml(); }) + .setScriptable(true) + .contextAction(); + ActionBuilder(this, ADD_CURSORS_TO_LINE_ENDS) + .setContext(m_editorContext) + .addOnTriggered([this] { q->addCursorsToLineEnds(); }) + .setScriptable(true); + ActionBuilder(this, ADD_SELECT_NEXT_FIND_MATCH) + .setContext(m_editorContext) + .addOnTriggered([this] { q->addSelectionNextFindMatch(); }) + .setScriptable(true); + m_modifyingActions << ActionBuilder(this, DUPLICATE_SELECTION) + .setContext(m_editorContext) + .addOnTriggered([this] { q->duplicateSelection(); }) + .setScriptable(true) + .contextAction(); + m_modifyingActions << ActionBuilder(this, DUPLICATE_SELECTION_AND_COMMENT) + .setContext(m_editorContext) + .addOnTriggered([this] { q->duplicateSelectionAndComment(); }) + .setScriptable(true) + .contextAction(); + m_modifyingActions << ActionBuilder(this, UPPERCASE_SELECTION) + .setContext(m_editorContext) + .addOnTriggered([this] { q->uppercaseSelection(); }) + .setScriptable(true) + .contextAction(); + m_modifyingActions << ActionBuilder(this, LOWERCASE_SELECTION) + .setContext(m_editorContext) + .addOnTriggered([this] { q->lowercaseSelection(); }) + .setScriptable(true) + .contextAction(); + m_modifyingActions << ActionBuilder(this, SORT_LINES) + .setContext(m_editorContext) + .addOnTriggered([this] { q->sortLines(); }) + .setScriptable(true) + .contextAction(); + ActionBuilder(this, FOLD) + .setContext(m_editorContext) + .addOnTriggered([this] { q->foldCurrentBlock(); }) + .setScriptable(true); + ActionBuilder(this, UNFOLD) + .setContext(m_editorContext) + .addOnTriggered([this] { q->unfoldCurrentBlock(); }) + .setScriptable(true); + m_unfoldAllAction = ActionBuilder(this, UNFOLD_ALL) + .setContext(m_editorContext) + .addOnTriggered([this] { q->unfoldAll(); }) + .setScriptable(true) + .contextAction(); + ActionBuilder(this, INCREASE_FONT_SIZE).setContext(m_editorContext).addOnTriggered([this] { + q->increaseFontZoom(); + }); + ActionBuilder(this, DECREASE_FONT_SIZE).setContext(m_editorContext).addOnTriggered([this] { + q->decreaseFontZoom(); + }); + ActionBuilder(this, RESET_FONT_SIZE).setContext(m_editorContext).addOnTriggered([this] { + q->zoomReset(); + }); + ActionBuilder(this, GOTO_BLOCK_START) + .setContext(m_editorContext) + .addOnTriggered([this] { q->gotoBlockStart(); }) + .setScriptable(true); + ActionBuilder(this, GOTO_BLOCK_END) + .setContext(m_editorContext) + .addOnTriggered([this] { q->gotoBlockEnd(); }) + .setScriptable(true); + ActionBuilder(this, SELECT_BLOCK_UP) + .setContext(m_editorContext) + .addOnTriggered([this] { q->selectBlockUp(); }) + .setScriptable(true); + ActionBuilder(this, SELECT_BLOCK_DOWN) + .setContext(m_editorContext) + .addOnTriggered([this] { q->selectBlockDown(); }) + .setScriptable(true); + ActionBuilder(this, SELECT_WORD_UNDER_CURSOR) + .setContext(m_editorContext) + .addOnTriggered([this] { q->selectWordUnderCursor(); }) + .setScriptable(true); + + ActionBuilder(this, GOTO_DOCUMENT_START) + .setContext(m_editorContext) + .addOnTriggered([this] { q->gotoDocumentStart(); }) + .setScriptable(true); + ActionBuilder(this, GOTO_DOCUMENT_END) + .setContext(m_editorContext) + .addOnTriggered([this] { q->gotoDocumentEnd(); }) + .setScriptable(true); + ActionBuilder(this, GOTO_LINE_START) + .setContext(m_editorContext) + .addOnTriggered([this] { q->gotoLineStart(); }) + .setScriptable(true); + ActionBuilder(this, GOTO_LINE_END) + .setContext(m_editorContext) + .addOnTriggered([this] { q->gotoLineEnd(); }) + .setScriptable(true); + ActionBuilder(this, GOTO_NEXT_LINE) + .setContext(m_editorContext) + .addOnTriggered([this] { q->gotoNextLine(); }) + .setScriptable(true); + ActionBuilder(this, GOTO_PREVIOUS_LINE) + .setContext(m_editorContext) + .addOnTriggered([this] { q->gotoPreviousLine(); }) + .setScriptable(true); + ActionBuilder(this, GOTO_PREVIOUS_CHARACTER) + .setContext(m_editorContext) + .addOnTriggered([this] { q->gotoPreviousCharacter(); }) + .setScriptable(true); + ActionBuilder(this, GOTO_NEXT_CHARACTER) + .setContext(m_editorContext) + .addOnTriggered([this] { q->gotoNextCharacter(); }) + .setScriptable(true); + ActionBuilder(this, GOTO_PREVIOUS_WORD) + .setContext(m_editorContext) + .addOnTriggered([this] { q->gotoPreviousWord(); }) + .setScriptable(true); + ActionBuilder(this, GOTO_NEXT_WORD) + .setContext(m_editorContext) + .addOnTriggered([this] { q->gotoNextWord(); }) + .setScriptable(true); + ActionBuilder(this, GOTO_PREVIOUS_WORD_CAMEL_CASE) + .setContext(m_editorContext) + .addOnTriggered([this] { q->gotoPreviousWordCamelCase(); }) + .setScriptable(true); + ActionBuilder(this, GOTO_NEXT_WORD_CAMEL_CASE) + .setContext(m_editorContext) + .addOnTriggered([this] { q->gotoNextWordCamelCase(); }) + .setScriptable(true); + + ActionBuilder(this, GOTO_LINE_START_WITH_SELECTION) + .setContext(m_editorContext) + .addOnTriggered([this] { q->gotoLineStartWithSelection(); }) + .setScriptable(true); + ActionBuilder(this, GOTO_LINE_END_WITH_SELECTION) + .setContext(m_editorContext) + .addOnTriggered([this] { q->gotoLineEndWithSelection(); }) + .setScriptable(true); + ActionBuilder(this, GOTO_NEXT_LINE_WITH_SELECTION) + .setContext(m_editorContext) + .addOnTriggered([this] { q->gotoNextLineWithSelection(); }) + .setScriptable(true); + ActionBuilder(this, GOTO_PREVIOUS_LINE_WITH_SELECTION) + .setContext(m_editorContext) + .addOnTriggered([this] { q->gotoPreviousLineWithSelection(); }) + .setScriptable(true); + ActionBuilder(this, GOTO_PREVIOUS_CHARACTER_WITH_SELECTION) + .setContext(m_editorContext) + .addOnTriggered([this] { q->gotoPreviousCharacterWithSelection(); }) + .setScriptable(true); + ActionBuilder(this, GOTO_NEXT_CHARACTER_WITH_SELECTION) + .setContext(m_editorContext) + .addOnTriggered([this] { q->gotoNextCharacterWithSelection(); }) + .setScriptable(true); + ActionBuilder(this, GOTO_PREVIOUS_WORD_WITH_SELECTION) + .setContext(m_editorContext) + .addOnTriggered([this] { q->gotoPreviousWordWithSelection(); }) + .setScriptable(true); + ActionBuilder(this, GOTO_NEXT_WORD_WITH_SELECTION) + .setContext(m_editorContext) + .addOnTriggered([this] { q->gotoNextWordWithSelection(); }) + .setScriptable(true); + ActionBuilder(this, GOTO_PREVIOUS_WORD_CAMEL_CASE_WITH_SELECTION) + .setContext(m_editorContext) + .addOnTriggered([this] { q->gotoPreviousWordCamelCaseWithSelection(); }) + .setScriptable(true); + ActionBuilder(this, GOTO_NEXT_WORD_CAMEL_CASE_WITH_SELECTION) + .setContext(m_editorContext) + .addOnTriggered([this] { q->gotoNextWordCamelCaseWithSelection(); }) + .setScriptable(true); + + // Collect additional modifying actions so we can check for them inside a readonly file + // and disable them + m_modifyingActions << m_autoIndentAction; + m_modifyingActions << m_autoFormatAction; + m_modifyingActions << m_unCommentSelectionAction; + + updateOptionalActions(); +} + +void TextEditorWidgetPrivate::updateActions() +{ + bool isWritable = !q->isReadOnly(); + for (QAction *a : std::as_const(m_modifyingActions)) + a->setEnabled(isWritable); + m_unCommentSelectionAction->setEnabled((m_optionalActionMask & OptionalActions::UnCommentSelection) && isWritable); + m_visualizeWhitespaceAction->setEnabled(q); + if (TextEditorSettings::fontSettings().relativeLineSpacing() == 100) { + m_textWrappingAction->setEnabled(q); + } else { + m_textWrappingAction->setEnabled(false); + m_textWrappingAction->setChecked(false); + } + m_visualizeWhitespaceAction->setChecked(m_displaySettings.m_visualizeWhitespace); + m_textWrappingAction->setChecked(m_displaySettings.m_textWrapping); + + updateRedoAction(); + updateUndoAction(); + updateCopyAction(q->textCursor().hasSelection()); + + updateOptionalActions(); +} + +void TextEditorWidgetPrivate::updateOptionalActions() +{ + using namespace OptionalActions; + m_followSymbolAction->setEnabled(m_optionalActionMask & FollowSymbolUnderCursor); + m_followSymbolInNextSplitAction->setEnabled(m_optionalActionMask & FollowSymbolUnderCursor); + m_followToTypeAction->setEnabled(m_optionalActionMask & FollowTypeUnderCursor); + m_followToTypeInNextSplitAction->setEnabled(m_optionalActionMask & FollowTypeUnderCursor); + m_findUsageAction->setEnabled(m_optionalActionMask & FindUsage); + m_jumpToFileAction->setEnabled(m_optionalActionMask & JumpToFileUnderCursor); + m_jumpToFileInNextSplitAction->setEnabled(m_optionalActionMask & JumpToFileUnderCursor); + m_unfoldAllAction->setEnabled(m_optionalActionMask & UnCollapseAll); + m_renameSymbolAction->setEnabled(m_optionalActionMask & RenameSymbol); + m_openCallHierarchyAction->setEnabled(m_optionalActionMask & CallHierarchy); + m_openTypeHierarchyAction->setEnabled(m_optionalActionMask & TypeHierarchy); + + bool formatEnabled = (m_optionalActionMask & OptionalActions::Format) + && !q->isReadOnly(); + m_autoIndentAction->setEnabled(formatEnabled); + m_autoFormatAction->setEnabled(formatEnabled); +} + +void TextEditorWidgetPrivate::updateRedoAction() +{ + m_redoAction->setEnabled(q->isRedoAvailable()); +} + +void TextEditorWidgetPrivate::updateUndoAction() +{ + m_undoAction->setEnabled(q->isUndoAvailable()); +} + +void TextEditorWidgetPrivate::updateCopyAction(bool hasCopyableText) +{ + if (m_cutAction) + m_cutAction->setEnabled(hasCopyableText && !q->isReadOnly()); + if (m_copyAction) + m_copyAction->setEnabled(hasCopyableText); + if (m_copyHtmlAction) + m_copyHtmlAction->setEnabled(hasCopyableText); +} + bool TextEditorWidget::codeFoldingVisible() const { return d->m_codeFoldingVisible; @@ -5127,7 +5717,8 @@ void TextEditorWidgetPrivate::setupBlockLayout(const PaintEventData &data, blockData.layout = data.block.layout(); QTextOption option = blockData.layout->textOption(); - if (data.suppressSyntaxInIfdefedOutBlock && TextDocumentLayout::ifdefedOut(data.block)) { + if (data.suppressSyntaxInIfdefedOutBlock + && TextDocumentLayout::ifdefedOut(data.block)) { option.setFlags(option.flags() | QTextOption::SuppressColors); painter.setPen(data.ifdefedOutFormat.foreground().color()); } else { @@ -5746,8 +6337,8 @@ void TextEditorWidgetPrivate::paintCodeFolding(QPainter &painter, TextBlockUserData *nextBlockUserData = TextDocumentLayout::textUserData(nextBlock); bool drawBox = nextBlockUserData - && TextDocumentLayout::foldingIndent(data.block) < nextBlockUserData->foldingIndent(); - + && TextDocumentLayout::foldingIndent(data.block) + < nextBlockUserData->foldingIndent(); const int blockNumber = data.block.blockNumber(); bool active = blockNumber == extraAreaHighlightFoldBlockNumber; @@ -5796,7 +6387,7 @@ void TextEditorWidgetPrivate::paintRevisionMarker(QPainter &painter, void TextEditorWidget::extraAreaPaintEvent(QPaintEvent *e) { - ExtraAreaPaintEventData data(this, d); + ExtraAreaPaintEventData data(this, d.get()); QTC_ASSERT(data.documentLayout, return); QPainter painter(d->m_extraArea); @@ -5903,7 +6494,7 @@ void TextEditorWidgetPrivate::slotUpdateRequest(const QRect &r, int dy) void TextEditorWidgetPrivate::saveCurrentCursorPositionForNavigation() { m_lastCursorChangeWasInteresting = true; - m_tempNavigationState = q->saveState(); + emit q->saveCurrentStateForNavigationHistory(); } void TextEditorWidgetPrivate::updateCurrentLineHighlight() @@ -5959,8 +6550,7 @@ void TextEditorWidget::slotCursorPositionChanged() << "indent:" << BaseTextDocumentLayout::userData(textCursor().block())->foldingIndent(); #endif if (!d->m_contentsChanged && d->m_lastCursorChangeWasInteresting) { - if (EditorManager::currentEditor() && EditorManager::currentEditor()->widget() == this) - EditorManager::addCurrentPositionToNavigationHistory(d->m_tempNavigationState); + emit addSavedStateToNavigationHistory(); d->m_lastCursorChangeWasInteresting = false; } else if (d->m_contentsChanged) { d->saveCurrentCursorPositionForNavigation(); @@ -6266,7 +6856,6 @@ void TextEditorWidget::mouseReleaseEvent(QMouseEvent *e) { const Qt::MouseButton button = e->button(); if (d->m_linkPressed && d->isMouseNavigationEvent(e) && button == Qt::LeftButton) { - EditorManager::addCurrentPositionToNavigationHistory(); bool inNextSplit = ((e->modifiers() & Qt::AltModifier) && !alwaysOpenLinksInNextSplit()) || (alwaysOpenLinksInNextSplit() && !(e->modifiers() & Qt::AltModifier)); @@ -6988,13 +7577,13 @@ bool TextEditorWidget::openLink(const Utils::Link &link, bool inNextSplit) #endif QString url = link.targetFilePath.toString(); - if (url.startsWith(u"https://"_qs) || url.startsWith(u"http://"_qs)) { + if (url.startsWith(u"https://") || url.startsWith(u"http://")) { QDesktopServices::openUrl(url); return true; } if (!inNextSplit && textDocument()->filePath() == link.targetFilePath) { - EditorManager::addCurrentPositionToNavigationHistory(); + emit addCurrentStateToNavigationHistory(); gotoLine(link.targetLine, link.targetColumn, true, true); setFocus(); return true; @@ -7027,11 +7616,12 @@ void TextEditorWidgetPrivate::requestUpdateLink(QMouseEvent *e) return; // Check that the mouse was actually on the text somewhere - bool onText = q->cursorRect(cursor).right() >= e->x(); + const int posX = e->position().x(); + bool onText = q->cursorRect(cursor).right() >= posX; if (!onText) { QTextCursor nextPos = cursor; nextPos.movePosition(QTextCursor::Right); - onText = q->cursorRect(nextPos).right() >= e->x(); + onText = q->cursorRect(nextPos).right() >= posX; } if (onText) { @@ -8102,6 +8692,7 @@ void TextEditorWidgetPrivate::applyFontSettingsDelayed() m_fontSettingsNeedsApply = true; if (q->isVisible()) q->triggerPendingUpdates(); + updateActions(); } void TextEditorWidgetPrivate::markRemoved(TextMark *mark) @@ -8183,7 +8774,7 @@ void TextEditorWidget::setDisplaySettings(const DisplaySettings &ds) optionFlags.setFlag(QTextOption::AddSpaceForLineAndParagraphSeparators); optionFlags.setFlag(QTextOption::ShowTabsAndSpaces, ds.m_visualizeWhitespace); if (optionFlags != currentOptionFlags) { - if (SyntaxHighlighterRunner *highlighter = textDocument()->syntaxHighlighterRunner()) + if (SyntaxHighlighter *highlighter = textDocument()->syntaxHighlighter()) highlighter->rehighlight(); QTextOption option = document()->defaultTextOption(); option.setFlags(optionFlags); @@ -8337,6 +8928,7 @@ void TextEditorWidget::setReadOnly(bool b) emit readOnlyChanged(); if (b) setTextInteractionFlags(textInteractionFlags() | Qt::TextSelectableByKeyboard); + d->updateActions(); } void TextEditorWidget::cut() @@ -8462,7 +9054,7 @@ QMimeData *TextEditorWidget::createMimeDataFromSelection(bool withHtml) const } else { const int startPosition = current.position() - selectionStart - removedCount; - int endPosition = startPosition + current.text().count(); + int endPosition = startPosition + current.text().size(); if (current != last) endPosition++; removedCount += endPosition - startPosition; @@ -8641,7 +9233,7 @@ void TextEditorWidget::dragLeaveEvent(QDragLeaveEvent *) void TextEditorWidget::dragMoveEvent(QDragMoveEvent *e) { const QRect rect = cursorRect(d->m_dndCursor); - d->m_dndCursor = cursorForPosition(e->pos()); + d->m_dndCursor = cursorForPosition(e->position().toPoint()); if (!rect.isNull()) viewport()->update(rect); viewport()->update(cursorRect(d->m_dndCursor)); @@ -8659,7 +9251,7 @@ void TextEditorWidget::dropEvent(QDropEvent *e) // Update multi text cursor before inserting data MultiTextCursor cursor = multiTextCursor(); cursor.beginEditBlock(); - const QTextCursor eventCursor = cursorForPosition(e->pos()); + const QTextCursor eventCursor = cursorForPosition(e->position().toPoint()); if (e->dropAction() == Qt::MoveAction && e->source() == viewport()) cursor.removeSelectedText(); cursor.setCursors({eventCursor}); @@ -8734,31 +9326,36 @@ void TextEditorWidget::setupFallBackEditor(Id id) void TextEditorWidget::appendStandardContextMenuActions(QMenu *menu) { - if (optionalActions() & TextEditorActionHandler::FollowSymbolUnderCursor) { + if (optionalActions() & OptionalActions::FollowSymbolUnderCursor) { const auto action = ActionManager::command(Constants::FOLLOW_SYMBOL_UNDER_CURSOR)->action(); if (!menu->actions().contains(action)) menu->addAction(action); } - if (optionalActions() & TextEditorActionHandler::FollowTypeUnderCursor) { + if (optionalActions() & OptionalActions::FollowTypeUnderCursor) { const auto action = ActionManager::command(Constants::FOLLOW_SYMBOL_TO_TYPE)->action(); if (!menu->actions().contains(action)) menu->addAction(action); } - if (optionalActions() & TextEditorActionHandler::FindUsage) { + if (optionalActions() & OptionalActions::FindUsage) { const auto action = ActionManager::command(Constants::FIND_USAGES)->action(); if (!menu->actions().contains(action)) menu->addAction(action); } - if (optionalActions() & TextEditorActionHandler::RenameSymbol) { + if (optionalActions() & OptionalActions::RenameSymbol) { const auto action = ActionManager::command(Constants::RENAME_SYMBOL)->action(); if (!menu->actions().contains(action)) menu->addAction(action); } - if (optionalActions() & TextEditorActionHandler::CallHierarchy) { + if (optionalActions() & OptionalActions::CallHierarchy) { const auto action = ActionManager::command(Constants::OPEN_CALL_HIERARCHY)->action(); if (!menu->actions().contains(action)) menu->addAction(action); } + if (optionalActions() & OptionalActions::TypeHierarchy) { + const auto action = ActionManager::command(Constants::OPEN_TYPE_HIERARCHY)->action(); + if (!menu->actions().contains(action)) + menu->addAction(action); + } menu->addSeparator(); appendMenuActionsFromContext(menu, Constants::M_STANDARDCONTEXTMENU); @@ -8786,7 +9383,7 @@ void TextEditorWidget::setOptionalActions(uint optionalActionMask) if (d->m_optionalActionMask == optionalActionMask) return; d->m_optionalActionMask = optionalActionMask; - emit optionalActionMaskChanged(); + d->updateOptionalActions(); } void TextEditorWidget::addOptionalActions( uint optionalActionMask) @@ -9003,6 +9600,23 @@ void BaseTextEditor::select(int toPos) editorWidget()->setTextCursor(tc); } +void BaseTextEditor::saveCurrentStateForNavigationHistory() +{ + d->m_savedNavigationState = saveState(); +} + +void BaseTextEditor::addSavedStateToNavigationHistory() +{ + if (EditorManager::currentEditor() == this) + EditorManager::addCurrentPositionToNavigationHistory(d->m_savedNavigationState); +} + +void BaseTextEditor::addCurrentStateToNavigationHistory() +{ + if (EditorManager::currentEditor() == this) + EditorManager::addCurrentPositionToNavigationHistory(); +} + void TextEditorWidgetPrivate::updateCursorPosition() { m_contextHelpItem = HelpItem(); @@ -9402,7 +10016,7 @@ void TextEditorWidget::setupGenericHighlighter() setLineSeparatorsAllowed(true); connect(textDocument(), &IDocument::filePathChanged, - d, &TextEditorWidgetPrivate::reconfigure); + d.get(), &TextEditorWidgetPrivate::reconfigure); } // @@ -9486,7 +10100,7 @@ public: CommentDefinition m_commentDefinition; QList<BaseHoverHandler *> m_hoverHandlers; // owned std::unique_ptr<CompletionAssistProvider> m_completionAssistProvider; // owned - std::unique_ptr<TextEditorActionHandler> m_textEditorActionHandler; + int m_optionalActionMask = 0; bool m_useGenericHighlighter = false; bool m_duplicatedSupported = true; bool m_codeFoldingSupported = false; @@ -9559,9 +10173,9 @@ void TextEditorFactory::setAutoCompleterCreator(const AutoCompleterCreator &crea d->m_autoCompleterCreator = creator; } -void TextEditorFactory::setEditorActionHandlers(uint optionalActions) +void TextEditorFactory::setOptionalActionMask(int optionalActions) { - d->m_textEditorActionHandler.reset(new TextEditorActionHandler(id(), id(), optionalActions)); + d->m_optionalActionMask = optionalActions; } void TextEditorFactory::addHoverHandler(BaseHoverHandler *handler) @@ -9607,8 +10221,7 @@ BaseTextEditor *TextEditorFactoryPrivate::createEditorHelper(const TextDocumentP textEditorWidget->setMarksVisible(m_marksVisible); textEditorWidget->setParenthesesMatchingEnabled(m_paranthesesMatchinEnabled); textEditorWidget->setCodeFoldingSupported(m_codeFoldingSupported); - if (m_textEditorActionHandler) - textEditorWidget->setOptionalActions(m_textEditorActionHandler->optionalActions()); + textEditorWidget->setOptionalActions(m_optionalActionMask); BaseTextEditor *editor = m_editorCreator(); editor->setDuplicateSupported(m_duplicatedSupported); @@ -9635,6 +10248,21 @@ BaseTextEditor *TextEditorFactoryPrivate::createEditorHelper(const TextDocumentP [editor](EditorManager::OpenEditorFlags flags) { EditorManager::activateEditor(editor, flags); }); + QObject::connect( + textEditorWidget, + &TextEditorWidget::saveCurrentStateForNavigationHistory, + editor, + &BaseTextEditor::saveCurrentStateForNavigationHistory); + QObject::connect( + textEditorWidget, + &TextEditorWidget::addSavedStateToNavigationHistory, + editor, + &BaseTextEditor::addSavedStateToNavigationHistory); + QObject::connect( + textEditorWidget, + &TextEditorWidget::addCurrentStateToNavigationHistory, + editor, + &BaseTextEditor::addCurrentStateToNavigationHistory); if (m_useGenericHighlighter) textEditorWidget->setupGenericHighlighter(); diff --git a/src/plugins/texteditor/texteditor.h b/src/plugins/texteditor/texteditor.h index 85344a8dbd0..7fc56e87337 100644 --- a/src/plugins/texteditor/texteditor.h +++ b/src/plugins/texteditor/texteditor.h @@ -85,6 +85,22 @@ enum TextMarkRequestKind TaskMarkRequest }; +namespace OptionalActions { +enum Mask { + None = 0, + Format = 1, + UnCommentSelection = 2, + UnCollapseAll = 4, + FollowSymbolUnderCursor = 8, + FollowTypeUnderCursor = 16, + JumpToFileUnderCursor = 32, + RenameSymbol = 64, + FindUsage = 128, + CallHierarchy = 256, + TypeHierarchy = 512, +}; +} // namespace OptionalActions + class TEXTEDITOR_EXPORT BaseTextEditor : public Core::IEditor { Q_OBJECT @@ -153,6 +169,11 @@ public: private: friend class TextEditorFactory; friend class Internal::TextEditorFactoryPrivate; + + void saveCurrentStateForNavigationHistory(); + void addSavedStateToNavigationHistory(); + void addCurrentStateToNavigationHistory(); + Internal::BaseTextEditorPrivate *d; }; @@ -441,6 +462,9 @@ public: virtual void undo(); virtual void redo(); + virtual bool isUndoAvailable() const; + virtual bool isRedoAvailable() const; + void openLinkUnderCursor(); void openLinkUnderCursorInNextSplit(); void openTypeUnderCursor(); @@ -511,9 +535,13 @@ signals: void requestUsages(const QTextCursor &cursor); void requestRename(const QTextCursor &cursor); void requestCallHierarchy(const QTextCursor &cursor); - void optionalActionMaskChanged(); void toolbarOutlineChanged(QWidget *newOutline); + // used by the IEditor + void saveCurrentStateForNavigationHistory(); + void addSavedStateToNavigationHistory(); + void addCurrentStateToNavigationHistory(); + protected: QTextBlock blockForVisibleRow(int row) const; QTextBlock blockForVerticalOffset(int offset) const; @@ -569,6 +597,8 @@ protected: void setVisualIndentOffset(int offset); + void updateUndoRedoActions(); + public: QString selectedText() const; @@ -637,7 +667,7 @@ protected: virtual void slotCodeStyleSettingsChanged(const QVariant &); // Used in CppEditor private: - Internal::TextEditorWidgetPrivate *d; + std::unique_ptr<Internal::TextEditorWidgetPrivate> d; friend class BaseTextEditor; friend class TextEditorFactory; friend class Internal::TextEditorFactoryPrivate; @@ -689,7 +719,7 @@ public: void setSyntaxHighlighterCreator(const SyntaxHighLighterCreator &creator); void setUseGenericHighlighter(bool enabled); void setAutoCompleterCreator(const AutoCompleterCreator &creator); - void setEditorActionHandlers(uint optionalActions); + void setOptionalActionMask(int optionalActions); void addHoverHandler(BaseHoverHandler *handler); void setCompletionAssistProvider(CompletionAssistProvider *provider); diff --git a/src/plugins/texteditor/texteditor.qbs b/src/plugins/texteditor/texteditor.qbs index 821d9a4a479..ccb298a5aa8 100644 --- a/src/plugins/texteditor/texteditor.qbs +++ b/src/plugins/texteditor/texteditor.qbs @@ -124,8 +124,6 @@ QtcPlugin { "storagesettings.h", "syntaxhighlighter.cpp", "syntaxhighlighter.h", - "syntaxhighlighterrunner.cpp", - "syntaxhighlighterrunner.h", "tabsettings.cpp", "tabsettings.h", "tabsettingswidget.cpp", @@ -137,9 +135,8 @@ QtcPlugin { "texteditor.cpp", "texteditor.h", "texteditor.qrc", - "texteditor_global.h", "texteditortr.h", - "texteditoractionhandler.cpp", - "texteditoractionhandler.h", + "texteditor_global.h", + "texteditortr.h", "texteditorconstants.cpp", "texteditorconstants.h", "texteditoroverlay.cpp", @@ -152,6 +149,8 @@ QtcPlugin { "textmark.cpp", "textmark.h", "textstyles.h", + "typehierarchy.cpp", + "typehierarchy.h", "typingsettings.cpp", "typingsettings.h", ] @@ -223,8 +222,6 @@ QtcPlugin { "snippetprovider.h", "snippetscollection.cpp", "snippetscollection.h", - "snippetssettings.cpp", - "snippetssettings.h", "snippetssettingspage.cpp", "snippetssettingspage.h", ] diff --git a/src/plugins/texteditor/texteditoractionhandler.cpp b/src/plugins/texteditor/texteditoractionhandler.cpp deleted file mode 100644 index 240e8498ed1..00000000000 --- a/src/plugins/texteditor/texteditoractionhandler.cpp +++ /dev/null @@ -1,656 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "texteditoractionhandler.h" - -#include "texteditor.h" -#include "displaysettings.h" -#include "fontsettings.h" -#include "linenumberfilter.h" -#include "texteditortr.h" -#include "texteditorsettings.h" - -#include <aggregation/aggregate.h> - -#include <coreplugin/locator/locatormanager.h> -#include <coreplugin/icore.h> -#include <coreplugin/editormanager/editormanager.h> -#include <coreplugin/coreconstants.h> -#include <coreplugin/actionmanager/actionmanager.h> -#include <coreplugin/actionmanager/actioncontainer.h> -#include <coreplugin/actionmanager/command.h> - -#include <utils/hostosinfo.h> -#include <utils/qtcassert.h> - -#include <QDebug> -#include <QAction> - -#include <functional> - -using namespace Core; - -namespace TextEditor { -namespace Internal { - -class TextEditorActionHandlerPrivate : public QObject -{ -public: - TextEditorActionHandlerPrivate(Utils::Id editorId, Utils::Id contextId, uint optionalActions); - - QAction *registerActionHelper(Utils::Id id, - bool scriptable, - const QString &title, - const QKeySequence &keySequence, - Utils::Id menueGroup, - ActionContainer *container, - std::function<void(bool)> slot) - { - auto result = new QAction(title, this); - Command *command - = ActionManager::registerAction(result, id, Context(m_contextId), scriptable); - if (!keySequence.isEmpty()) - command->setDefaultKeySequence(keySequence); - - if (container && menueGroup.isValid()) - container->addAction(command, menueGroup); - - connect(result, &QAction::triggered, slot); - return result; - } - - QAction *registerAction(Utils::Id id, - std::function<void(TextEditorWidget *)> slot, - bool scriptable = false, - const QString &title = QString(), - const QKeySequence &keySequence = QKeySequence(), - Utils::Id menueGroup = Utils::Id(), - ActionContainer *container = nullptr) - { - return registerActionHelper(id, - scriptable, - title, - keySequence, - menueGroup, - container, - [this, slot, id](bool) { - if (m_currentEditorWidget) - slot(m_currentEditorWidget); - else if (m_unhandledCallback) - m_unhandledCallback(id, m_currentEditor); - }); - } - - QAction *registerBoolAction(Utils::Id id, - std::function<void(TextEditorWidget *, bool)> slot, - bool scriptable = false, - const QString &title = QString(), - const QKeySequence &keySequence = QKeySequence(), - Utils::Id menueGroup = Utils::Id(), - ActionContainer *container = nullptr) - { - return registerActionHelper(id, scriptable, title, keySequence, menueGroup, container, - [this, slot](bool on) { if (m_currentEditorWidget) slot(m_currentEditorWidget, on); }); - } - - QAction *registerIntAction(Utils::Id id, - std::function<void(TextEditorWidget *, int)> slot, - bool scriptable = false, - const QString &title = QString(), - const QKeySequence &keySequence = QKeySequence(), - Utils::Id menueGroup = Utils::Id(), - ActionContainer *container = nullptr) - { - return registerActionHelper(id, scriptable, title, keySequence, menueGroup, container, - [this, slot](bool on) { if (m_currentEditorWidget) slot(m_currentEditorWidget, on); }); - } - - void createActions(); - - void updateActions(); - void updateOptionalActions(); - void updateRedoAction(bool on); - void updateUndoAction(bool on); - void updateCopyAction(bool on); - - void updateCurrentEditor(IEditor *editor); - - void setCanUndoCallback(const TextEditorActionHandler::Predicate &callback); - void setCanRedoCallback(const TextEditorActionHandler::Predicate &callback); - -public: - TextEditorActionHandler::TextEditorWidgetResolver m_findTextWidget; - QAction *m_undoAction = nullptr; - QAction *m_redoAction = nullptr; - QAction *m_copyAction = nullptr; - QAction *m_copyHtmlAction = nullptr; - QAction *m_cutAction = nullptr; - QAction *m_autoIndentAction = nullptr; - QAction *m_autoFormatAction = nullptr; - QAction *m_visualizeWhitespaceAction = nullptr; - QAction *m_textWrappingAction = nullptr; - QAction *m_unCommentSelectionAction = nullptr; - QAction *m_unfoldAllAction = nullptr; - QAction *m_followSymbolAction = nullptr; - QAction *m_followSymbolInNextSplitAction = nullptr; - QAction *m_followToTypeAction = nullptr; - QAction *m_followToTypeInNextSplitAction = nullptr; - QAction *m_findUsageAction = nullptr; - QAction *m_openCallHierarchyAction = nullptr; - QAction *m_renameSymbolAction = nullptr; - QAction *m_jumpToFileAction = nullptr; - QAction *m_jumpToFileInNextSplitAction = nullptr; - QList<QAction *> m_modifyingActions; - - uint m_optionalActions = TextEditorActionHandler::None; - QPointer<TextEditorWidget> m_currentEditorWidget; - QPointer<IEditor> m_currentEditor; - Utils::Id m_editorId; - Utils::Id m_contextId; - - TextEditorActionHandler::Predicate m_canUndoCallback; - TextEditorActionHandler::Predicate m_canRedoCallback; - - TextEditorActionHandler::UnhandledCallback m_unhandledCallback; -}; - -TextEditorActionHandlerPrivate::TextEditorActionHandlerPrivate - (Utils::Id editorId, Utils::Id contextId, uint optionalActions) - : m_optionalActions(optionalActions) - , m_editorId(editorId) - , m_contextId(contextId) -{ - createActions(); - connect(EditorManager::instance(), - &EditorManager::currentEditorChanged, - this, - &TextEditorActionHandlerPrivate::updateCurrentEditor); - connect(TextEditorSettings::instance(), &TextEditorSettings::fontSettingsChanged, - this, &TextEditorActionHandlerPrivate::updateActions); -} - -void TextEditorActionHandlerPrivate::createActions() -{ - using namespace Core::Constants; - using namespace TextEditor::Constants; - - m_undoAction = registerAction(UNDO, - [] (TextEditorWidget *w) { w->undo(); }, true, Tr::tr("&Undo")); - m_redoAction = registerAction(REDO, - [] (TextEditorWidget *w) { w->redo(); }, true, Tr::tr("&Redo")); - m_copyAction = registerAction(COPY, - [] (TextEditorWidget *w) { w->copy(); }, true); - m_cutAction = registerAction(CUT, - [] (TextEditorWidget *w) { w->cut(); }, true); - m_modifyingActions << registerAction(PASTE, - [] (TextEditorWidget *w) { w->paste(); }, true); - registerAction(SELECTALL, - [] (TextEditorWidget *w) { w->selectAll(); }, true); - registerAction(GOTO, [](TextEditorWidget *) { - LocatorManager::showFilter(lineNumberFilter()); - }); - m_modifyingActions << registerAction(PRINT, [](TextEditorWidget *widget) { - widget->print(ICore::printer()); - }); - m_modifyingActions << registerAction(DELETE_LINE, - [] (TextEditorWidget *w) { w->deleteLine(); }, true, Tr::tr("Delete &Line")); - m_modifyingActions << registerAction(DELETE_END_OF_LINE, - [] (TextEditorWidget *w) { w->deleteEndOfLine(); }, true, Tr::tr("Delete Line from Cursor On")); - m_modifyingActions << registerAction(DELETE_END_OF_WORD, - [] (TextEditorWidget *w) { w->deleteEndOfWord(); }, true, Tr::tr("Delete Word from Cursor On")); - m_modifyingActions << registerAction(DELETE_END_OF_WORD_CAMEL_CASE, - [] (TextEditorWidget *w) { w->deleteEndOfWordCamelCase(); }, true, Tr::tr("Delete Word Camel Case from Cursor On")); - m_modifyingActions << registerAction( - DELETE_START_OF_LINE, - [](TextEditorWidget *w) { w->deleteStartOfLine(); }, - true, - Tr::tr("Delete Line up to Cursor"), - Core::useMacShortcuts ? QKeySequence(Tr::tr("Ctrl+Backspace")) : QKeySequence()); - m_modifyingActions << registerAction(DELETE_START_OF_WORD, - [] (TextEditorWidget *w) { w->deleteStartOfWord(); }, true, Tr::tr("Delete Word up to Cursor")); - m_modifyingActions << registerAction(DELETE_START_OF_WORD_CAMEL_CASE, - [] (TextEditorWidget *w) { w->deleteStartOfWordCamelCase(); }, true, Tr::tr("Delete Word Camel Case up to Cursor")); - registerAction(GOTO_BLOCK_START_WITH_SELECTION, - [] (TextEditorWidget *w) { w->gotoBlockStartWithSelection(); }, true, Tr::tr("Go to Block Start with Selection"), - QKeySequence(Tr::tr("Ctrl+{"))); - registerAction(GOTO_BLOCK_END_WITH_SELECTION, - [] (TextEditorWidget *w) { w->gotoBlockEndWithSelection(); }, true, Tr::tr("Go to Block End with Selection"), - QKeySequence(Tr::tr("Ctrl+}"))); - m_modifyingActions << registerAction(MOVE_LINE_UP, - [] (TextEditorWidget *w) { w->moveLineUp(); }, true, Tr::tr("Move Line Up"), - QKeySequence(Tr::tr("Ctrl+Shift+Up"))); - m_modifyingActions << registerAction(MOVE_LINE_DOWN, - [] (TextEditorWidget *w) { w->moveLineDown(); }, true, Tr::tr("Move Line Down"), - QKeySequence(Tr::tr("Ctrl+Shift+Down"))); - m_modifyingActions << registerAction(COPY_LINE_UP, - [] (TextEditorWidget *w) { w->copyLineUp(); }, true, Tr::tr("Copy Line Up"), - QKeySequence(Tr::tr("Ctrl+Alt+Up"))); - m_modifyingActions << registerAction(COPY_LINE_DOWN, - [] (TextEditorWidget *w) { w->copyLineDown(); }, true, Tr::tr("Copy Line Down"), - QKeySequence(Tr::tr("Ctrl+Alt+Down"))); - m_modifyingActions << registerAction(JOIN_LINES, - [] (TextEditorWidget *w) { w->joinLines(); }, true, Tr::tr("Join Lines"), - QKeySequence(Tr::tr("Ctrl+J"))); - m_modifyingActions << registerAction(INSERT_LINE_ABOVE, - [] (TextEditorWidget *w) { w->insertLineAbove(); }, true, Tr::tr("Insert Line Above Current Line"), - QKeySequence(Tr::tr("Ctrl+Shift+Return"))); - m_modifyingActions << registerAction(INSERT_LINE_BELOW, - [] (TextEditorWidget *w) { w->insertLineBelow(); }, true, Tr::tr("Insert Line Below Current Line"), - QKeySequence(Tr::tr("Ctrl+Return"))); - m_modifyingActions << registerAction(SWITCH_UTF8BOM, - [] (TextEditorWidget *w) { w->switchUtf8bom(); }, true, Tr::tr("Toggle UTF-8 BOM")); - m_modifyingActions << registerAction(INDENT, - [] (TextEditorWidget *w) { w->indent(); }, true, Tr::tr("Indent")); - m_modifyingActions << registerAction(UNINDENT, - [] (TextEditorWidget *w) { w->unindent(); }, true, Tr::tr("Unindent")); - m_followSymbolAction = registerAction(FOLLOW_SYMBOL_UNDER_CURSOR, - [] (TextEditorWidget *w) { w->openLinkUnderCursor(); }, true, Tr::tr("Follow Symbol Under Cursor"), - QKeySequence(Qt::Key_F2)); - m_followSymbolInNextSplitAction = registerAction(FOLLOW_SYMBOL_UNDER_CURSOR_IN_NEXT_SPLIT, - [] (TextEditorWidget *w) { w->openLinkUnderCursorInNextSplit(); }, true, Tr::tr("Follow Symbol Under Cursor in Next Split"), - QKeySequence(Utils::HostOsInfo::isMacHost() ? Tr::tr("Meta+E, F2") : Tr::tr("Ctrl+E, F2"))); - m_followToTypeAction = registerAction(FOLLOW_SYMBOL_TO_TYPE, - [] (TextEditorWidget *w) { w->openTypeUnderCursor(); }, true, Tr::tr("Follow Type Under Cursor"), - QKeySequence(Tr::tr("Ctrl+Shift+F2"))); - m_followToTypeInNextSplitAction = registerAction(FOLLOW_SYMBOL_TO_TYPE_IN_NEXT_SPLIT, - [] (TextEditorWidget *w) { w->openTypeUnderCursorInNextSplit(); }, true, Tr::tr("Follow Type Under Cursor in Next Split"), - QKeySequence(Utils::HostOsInfo::isMacHost() ? Tr::tr("Meta+E, Shift+F2") : Tr::tr("Ctrl+E, Ctrl+Shift+F2"))); - m_findUsageAction = registerAction(FIND_USAGES, - [] (TextEditorWidget *w) { w->findUsages(); }, true, Tr::tr("Find References to Symbol Under Cursor"), - QKeySequence(Tr::tr("Ctrl+Shift+U"))); - m_renameSymbolAction = registerAction(RENAME_SYMBOL, - [] (TextEditorWidget *w) { w->renameSymbolUnderCursor(); }, true, Tr::tr("Rename Symbol Under Cursor"), - QKeySequence(Tr::tr("Ctrl+Shift+R"))); - m_jumpToFileAction = registerAction(JUMP_TO_FILE_UNDER_CURSOR, - [] (TextEditorWidget *w) { w->openLinkUnderCursor(); }, true, Tr::tr("Jump to File Under Cursor"), - QKeySequence(Qt::Key_F2)); - m_jumpToFileInNextSplitAction = registerAction(JUMP_TO_FILE_UNDER_CURSOR_IN_NEXT_SPLIT, - [] (TextEditorWidget *w) { w->openLinkUnderCursorInNextSplit(); }, true, Tr::tr("Jump to File Under Cursor in Next Split"), - QKeySequence(Utils::HostOsInfo::isMacHost() ? Tr::tr("Meta+E, F2") : Tr::tr("Ctrl+E, F2")).toString()); - m_openCallHierarchyAction = registerAction(OPEN_CALL_HIERARCHY, - [] (TextEditorWidget *w) { w->openCallHierarchy(); }, true, Tr::tr("Open Call Hierarchy")); - - registerAction(VIEW_PAGE_UP, - [] (TextEditorWidget *w) { w->viewPageUp(); }, true, Tr::tr("Move the View a Page Up and Keep the Cursor Position"), - QKeySequence(Tr::tr("Ctrl+PgUp"))); - registerAction(VIEW_PAGE_DOWN, - [] (TextEditorWidget *w) { w->viewPageDown(); }, true, Tr::tr("Move the View a Page Down and Keep the Cursor Position"), - QKeySequence(Tr::tr("Ctrl+PgDown"))); - registerAction(VIEW_LINE_UP, - [] (TextEditorWidget *w) { w->viewLineUp(); }, true, Tr::tr("Move the View a Line Up and Keep the Cursor Position"), - QKeySequence(Tr::tr("Ctrl+Up"))); - registerAction(VIEW_LINE_DOWN, - [] (TextEditorWidget *w) { w->viewLineDown(); }, true, Tr::tr("Move the View a Line Down and Keep the Cursor Position"), - QKeySequence(Tr::tr("Ctrl+Down"))); - - // register "Edit" Menu Actions - ActionContainer *editMenu = ActionManager::actionContainer(M_EDIT); - registerAction(SELECT_ENCODING, - [] (TextEditorWidget *w) { w->selectEncoding(); }, false, Tr::tr("Select Encoding..."), - QKeySequence(), G_EDIT_OTHER, editMenu); - m_modifyingActions << registerAction(CIRCULAR_PASTE, - [] (TextEditorWidget *w) { w->circularPaste(); }, false, Tr::tr("Paste from Clipboard History"), - QKeySequence(Tr::tr("Ctrl+Shift+V")), G_EDIT_COPYPASTE, editMenu); - m_modifyingActions << registerAction(NO_FORMAT_PASTE, - [] (TextEditorWidget *w) { w->pasteWithoutFormat(); }, false, Tr::tr("Paste Without Formatting"), - QKeySequence(Core::useMacShortcuts ? Tr::tr("Ctrl+Alt+Shift+V") : QString()), G_EDIT_COPYPASTE, editMenu); - - // register "Edit -> Advanced" Menu Actions - ActionContainer *advancedEditMenu = ActionManager::actionContainer(M_EDIT_ADVANCED); - m_autoIndentAction = registerAction(AUTO_INDENT_SELECTION, - [] (TextEditorWidget *w) { w->autoIndent(); }, true, Tr::tr("Auto-&indent Selection"), - QKeySequence(Tr::tr("Ctrl+I")), - G_EDIT_FORMAT, advancedEditMenu); - m_autoFormatAction = registerAction(AUTO_FORMAT_SELECTION, - [] (TextEditorWidget *w) { w->autoFormat(); }, true, Tr::tr("Auto-&format Selection"), - QKeySequence(Tr::tr("Ctrl+;")), - G_EDIT_FORMAT, advancedEditMenu); - m_modifyingActions << registerAction(REWRAP_PARAGRAPH, - [] (TextEditorWidget *w) { w->rewrapParagraph(); }, true, Tr::tr("&Rewrap Paragraph"), - QKeySequence(Core::useMacShortcuts ? Tr::tr("Meta+E, R") : Tr::tr("Ctrl+E, R")), - G_EDIT_FORMAT, advancedEditMenu); - m_visualizeWhitespaceAction = registerBoolAction(VISUALIZE_WHITESPACE, - [] (TextEditorWidget *widget, bool checked) { - if (widget) { - DisplaySettings ds = widget->displaySettings(); - ds.m_visualizeWhitespace = checked; - widget->setDisplaySettings(ds); - } - }, - false, Tr::tr("&Visualize Whitespace"), - QKeySequence(Core::useMacShortcuts ? Tr::tr("Meta+E, Meta+V") : Tr::tr("Ctrl+E, Ctrl+V")), - G_EDIT_FORMAT, advancedEditMenu); - m_visualizeWhitespaceAction->setCheckable(true); - m_modifyingActions << registerAction(CLEAN_WHITESPACE, - [] (TextEditorWidget *w) { w->cleanWhitespace(); }, true, Tr::tr("Clean Whitespace"), - QKeySequence(), - G_EDIT_FORMAT, advancedEditMenu); - m_textWrappingAction = registerBoolAction(TEXT_WRAPPING, - [] (TextEditorWidget *widget, bool checked) { - if (widget) { - DisplaySettings ds = widget->displaySettings(); - ds.m_textWrapping = checked; - widget->setDisplaySettings(ds); - } - }, - false, Tr::tr("Enable Text &Wrapping"), - QKeySequence(Core::useMacShortcuts ? Tr::tr("Meta+E, Meta+W") : Tr::tr("Ctrl+E, Ctrl+W")), - G_EDIT_FORMAT, advancedEditMenu); - m_textWrappingAction->setCheckable(true); - m_unCommentSelectionAction = registerAction(UN_COMMENT_SELECTION, - [] (TextEditorWidget *w) { w->unCommentSelection(); }, true, Tr::tr("Toggle Comment &Selection"), - QKeySequence(Tr::tr("Ctrl+/")), - G_EDIT_FORMAT, advancedEditMenu); - m_modifyingActions << registerAction(CUT_LINE, - [] (TextEditorWidget *w) { w->cutLine(); }, true, Tr::tr("Cut &Line"), - QKeySequence(Tr::tr("Shift+Del")), - G_EDIT_TEXT, advancedEditMenu); - registerAction(COPY_LINE, - [] (TextEditorWidget *w) { w->copyLine(); }, false, Tr::tr("Copy &Line"), - QKeySequence(Tr::tr("Ctrl+Ins")), - G_EDIT_TEXT, advancedEditMenu); - m_copyHtmlAction = registerAction(COPY_WITH_HTML, - [] (TextEditorWidget *w) { w->copyWithHtml(); }, true, Tr::tr("Copy With Highlighting"), - QKeySequence(), G_EDIT_TEXT, advancedEditMenu); - - registerAction(ADD_CURSORS_TO_LINE_ENDS, - [] (TextEditorWidget *w) { w->addCursorsToLineEnds(); }, false, Tr::tr("Create Cursors at Selected Line Ends"), - QKeySequence(Tr::tr("Alt+Shift+I")), - G_EDIT_TEXT, advancedEditMenu); - registerAction(ADD_SELECT_NEXT_FIND_MATCH, - [] (TextEditorWidget *w) { w->addSelectionNextFindMatch(); }, false, Tr::tr("Add Next Occurrence to Selection"), - QKeySequence(Tr::tr("Ctrl+D")), - G_EDIT_TEXT, advancedEditMenu); - m_modifyingActions << registerAction(DUPLICATE_SELECTION, - [] (TextEditorWidget *w) { w->duplicateSelection(); }, false, Tr::tr("&Duplicate Selection"), - QKeySequence(), - G_EDIT_TEXT, advancedEditMenu); - m_modifyingActions << registerAction(DUPLICATE_SELECTION_AND_COMMENT, - [] (TextEditorWidget *w) { w->duplicateSelectionAndComment(); }, false, Tr::tr("&Duplicate Selection and Comment"), - QKeySequence(), - G_EDIT_TEXT, advancedEditMenu); - m_modifyingActions << registerAction(UPPERCASE_SELECTION, - [] (TextEditorWidget *w) { w->uppercaseSelection(); }, true, Tr::tr("Uppercase Selection"), - QKeySequence(Core::useMacShortcuts ? Tr::tr("Meta+Shift+U") : Tr::tr("Alt+Shift+U")), - G_EDIT_TEXT, advancedEditMenu); - m_modifyingActions << registerAction(LOWERCASE_SELECTION, - [] (TextEditorWidget *w) { w->lowercaseSelection(); }, true, Tr::tr("Lowercase Selection"), - QKeySequence(Core::useMacShortcuts ? Tr::tr("Meta+U") : Tr::tr("Alt+U")), - G_EDIT_TEXT, advancedEditMenu); - m_modifyingActions << registerAction(SORT_LINES, - [] (TextEditorWidget *w) { w->sortLines(); }, false, Tr::tr("&Sort Lines"), - QKeySequence(Core::useMacShortcuts ? Tr::tr("Meta+Shift+S") : Tr::tr("Alt+Shift+S")), - G_EDIT_TEXT, advancedEditMenu); - registerAction(FOLD, - [] (TextEditorWidget *w) { w->foldCurrentBlock(); }, true, Tr::tr("Fold"), - QKeySequence(Tr::tr("Ctrl+<")), - G_EDIT_COLLAPSING, advancedEditMenu); - registerAction(UNFOLD, - [] (TextEditorWidget *w) { w->unfoldCurrentBlock(); }, true, Tr::tr("Unfold"), - QKeySequence(Tr::tr("Ctrl+>")), - G_EDIT_COLLAPSING, advancedEditMenu); - m_unfoldAllAction = registerAction(UNFOLD_ALL, - [] (TextEditorWidget *w) { w->unfoldAll(); }, true, Tr::tr("Toggle &Fold All"), - QKeySequence(), - G_EDIT_COLLAPSING, advancedEditMenu); - registerAction(INCREASE_FONT_SIZE, - [] (TextEditorWidget *w) { w->increaseFontZoom(); }, false, Tr::tr("Increase Font Size"), - QKeySequence(Tr::tr("Ctrl++")), - G_EDIT_FONT, advancedEditMenu); - registerAction(DECREASE_FONT_SIZE, - [] (TextEditorWidget *w) { w->decreaseFontZoom(); }, false, Tr::tr("Decrease Font Size"), - QKeySequence(Tr::tr("Ctrl+-")), - G_EDIT_FONT, advancedEditMenu); - registerAction(RESET_FONT_SIZE, - [] (TextEditorWidget *w) { w->zoomReset(); }, false, Tr::tr("Reset Font Size"), - QKeySequence(Core::useMacShortcuts ? Tr::tr("Meta+0") : Tr::tr("Ctrl+0")), - G_EDIT_FONT, advancedEditMenu); - registerAction(GOTO_BLOCK_START, - [] (TextEditorWidget *w) { w->gotoBlockStart(); }, true, Tr::tr("Go to Block Start"), - QKeySequence(Tr::tr("Ctrl+[")), - G_EDIT_BLOCKS, advancedEditMenu); - registerAction(GOTO_BLOCK_END, - [] (TextEditorWidget *w) { w->gotoBlockEnd(); }, true, Tr::tr("Go to Block End"), - QKeySequence(Tr::tr("Ctrl+]")), - G_EDIT_BLOCKS, advancedEditMenu); - registerAction(SELECT_BLOCK_UP, - [] (TextEditorWidget *w) { w->selectBlockUp(); }, true, Tr::tr("Select Block Up"), - QKeySequence(Tr::tr("Ctrl+U")), - G_EDIT_BLOCKS, advancedEditMenu); - registerAction(SELECT_BLOCK_DOWN, - [] (TextEditorWidget *w) { w->selectBlockDown(); }, true, Tr::tr("Select Block Down"), - QKeySequence(Tr::tr("Ctrl+Shift+Alt+U")), - G_EDIT_BLOCKS, advancedEditMenu); - registerAction(SELECT_WORD_UNDER_CURSOR, - [] (TextEditorWidget *w) { w->selectWordUnderCursor(); }, true, - Tr::tr("Select Word Under Cursor")); - - // register GOTO Actions - registerAction(GOTO_DOCUMENT_START, - [] (TextEditorWidget *w) { w->gotoDocumentStart(); }, true, Tr::tr("Go to Document Start")); - registerAction(GOTO_DOCUMENT_END, - [] (TextEditorWidget *w) { w->gotoDocumentEnd(); }, true, Tr::tr("Go to Document End")); - registerAction(GOTO_LINE_START, - [] (TextEditorWidget *w) { w->gotoLineStart(); }, true, Tr::tr("Go to Line Start")); - registerAction(GOTO_LINE_END, - [] (TextEditorWidget *w) { w->gotoLineEnd(); }, true, Tr::tr("Go to Line End")); - registerAction(GOTO_NEXT_LINE, - [] (TextEditorWidget *w) { w->gotoNextLine(); }, true, Tr::tr("Go to Next Line")); - registerAction(GOTO_PREVIOUS_LINE, - [] (TextEditorWidget *w) { w->gotoPreviousLine(); }, true, Tr::tr("Go to Previous Line")); - registerAction(GOTO_PREVIOUS_CHARACTER, - [] (TextEditorWidget *w) { w->gotoPreviousCharacter(); }, true, Tr::tr("Go to Previous Character")); - registerAction(GOTO_NEXT_CHARACTER, - [] (TextEditorWidget *w) { w->gotoNextCharacter(); }, true, Tr::tr("Go to Next Character")); - registerAction(GOTO_PREVIOUS_WORD, - [] (TextEditorWidget *w) { w->gotoPreviousWord(); }, true, Tr::tr("Go to Previous Word")); - registerAction(GOTO_NEXT_WORD, - [] (TextEditorWidget *w) { w->gotoNextWord(); }, true, Tr::tr("Go to Next Word")); - registerAction(GOTO_PREVIOUS_WORD_CAMEL_CASE, - [] (TextEditorWidget *w) { w->gotoPreviousWordCamelCase(); }, false, Tr::tr("Go to Previous Word Camel Case")); - registerAction(GOTO_NEXT_WORD_CAMEL_CASE, - [] (TextEditorWidget *w) { w->gotoNextWordCamelCase(); }, false, Tr::tr("Go to Next Word Camel Case")); - - // register GOTO actions with selection - registerAction(GOTO_LINE_START_WITH_SELECTION, - [] (TextEditorWidget *w) { w->gotoLineStartWithSelection(); }, true, Tr::tr("Go to Line Start with Selection")); - registerAction(GOTO_LINE_END_WITH_SELECTION, - [] (TextEditorWidget *w) { w->gotoLineEndWithSelection(); }, true, Tr::tr("Go to Line End with Selection")); - registerAction(GOTO_NEXT_LINE_WITH_SELECTION, - [] (TextEditorWidget *w) { w->gotoNextLineWithSelection(); }, true, Tr::tr("Go to Next Line with Selection")); - registerAction(GOTO_PREVIOUS_LINE_WITH_SELECTION, - [] (TextEditorWidget *w) { w->gotoPreviousLineWithSelection(); }, true, Tr::tr("Go to Previous Line with Selection")); - registerAction(GOTO_PREVIOUS_CHARACTER_WITH_SELECTION, - [] (TextEditorWidget *w) { w->gotoPreviousCharacterWithSelection(); }, true, Tr::tr("Go to Previous Character with Selection")); - registerAction(GOTO_NEXT_CHARACTER_WITH_SELECTION, - [] (TextEditorWidget *w) { w->gotoNextCharacterWithSelection(); }, true, Tr::tr("Go to Next Character with Selection")); - registerAction(GOTO_PREVIOUS_WORD_WITH_SELECTION, - [] (TextEditorWidget *w) { w->gotoPreviousWordWithSelection(); }, true, Tr::tr("Go to Previous Word with Selection")); - registerAction(GOTO_NEXT_WORD_WITH_SELECTION, - [] (TextEditorWidget *w) { w->gotoNextWordWithSelection(); }, true, Tr::tr("Go to Next Word with Selection")); - registerAction(GOTO_PREVIOUS_WORD_CAMEL_CASE_WITH_SELECTION, - [] (TextEditorWidget *w) { w->gotoPreviousWordCamelCaseWithSelection(); }, false, Tr::tr("Go to Previous Word Camel Case with Selection")); - registerAction(GOTO_NEXT_WORD_CAMEL_CASE_WITH_SELECTION, - [] (TextEditorWidget *w) { w->gotoNextWordCamelCaseWithSelection(); }, false, Tr::tr("Go to Next Word Camel Case with Selection")); - - // Collect additional modifying actions so we can check for them inside a readonly file - // and disable them - m_modifyingActions << m_autoIndentAction; - m_modifyingActions << m_autoFormatAction; - m_modifyingActions << m_unCommentSelectionAction; - - updateOptionalActions(); -} - -void TextEditorActionHandlerPrivate::updateActions() -{ - bool isWritable = m_currentEditorWidget && !m_currentEditorWidget->isReadOnly(); - for (QAction *a : std::as_const(m_modifyingActions)) - a->setEnabled(isWritable); - m_unCommentSelectionAction->setEnabled((m_optionalActions & TextEditorActionHandler::UnCommentSelection) && isWritable); - m_visualizeWhitespaceAction->setEnabled(m_currentEditorWidget); - if (TextEditorSettings::fontSettings().relativeLineSpacing() == 100) { - m_textWrappingAction->setEnabled(m_currentEditorWidget); - } else { - m_textWrappingAction->setEnabled(false); - m_textWrappingAction->setChecked(false); - } - if (m_currentEditorWidget) { - m_visualizeWhitespaceAction->setChecked( - m_currentEditorWidget->displaySettings().m_visualizeWhitespace); - m_textWrappingAction->setChecked(m_currentEditorWidget->displaySettings().m_textWrapping); - } - - bool canRedo = false; - bool canUndo = false; - bool canCopy = false; - - if (m_currentEditor && m_currentEditor->document() - && m_currentEditor->document()->id() == m_editorId) { - canRedo = m_canRedoCallback ? m_canRedoCallback(m_currentEditor) : false; - canUndo = m_canUndoCallback ? m_canUndoCallback(m_currentEditor) : false; - - if (m_currentEditorWidget) { - canRedo = m_canRedoCallback ? canRedo - : m_currentEditorWidget->document()->isRedoAvailable(); - canUndo = m_canUndoCallback ? canUndo - : m_currentEditorWidget->document()->isUndoAvailable(); - canCopy = m_currentEditorWidget->textCursor().hasSelection(); - } - } - - updateRedoAction(canRedo); - updateUndoAction(canUndo); - updateCopyAction(canCopy); - - updateOptionalActions(); -} - -void TextEditorActionHandlerPrivate::updateOptionalActions() -{ - const uint optionalActions = m_currentEditorWidget ? m_currentEditorWidget->optionalActions() - : m_optionalActions; - m_followSymbolAction->setEnabled( - optionalActions & TextEditorActionHandler::FollowSymbolUnderCursor); - m_followSymbolInNextSplitAction->setEnabled( - optionalActions & TextEditorActionHandler::FollowSymbolUnderCursor); - m_followToTypeAction->setEnabled( - optionalActions & TextEditorActionHandler::FollowTypeUnderCursor); - m_followToTypeInNextSplitAction->setEnabled( - optionalActions & TextEditorActionHandler::FollowTypeUnderCursor); - m_findUsageAction->setEnabled( - optionalActions & TextEditorActionHandler::FindUsage); - m_jumpToFileAction->setEnabled( - optionalActions & TextEditorActionHandler::JumpToFileUnderCursor); - m_jumpToFileInNextSplitAction->setEnabled( - optionalActions & TextEditorActionHandler::JumpToFileUnderCursor); - m_unfoldAllAction->setEnabled( - optionalActions & TextEditorActionHandler::UnCollapseAll); - m_renameSymbolAction->setEnabled( - optionalActions & TextEditorActionHandler::RenameSymbol); - m_openCallHierarchyAction->setEnabled( - optionalActions & TextEditorActionHandler::CallHierarchy); - - bool formatEnabled = (optionalActions & TextEditorActionHandler::Format) - && m_currentEditorWidget && !m_currentEditorWidget->isReadOnly(); - m_autoIndentAction->setEnabled(formatEnabled); - m_autoFormatAction->setEnabled(formatEnabled); -} - -void TextEditorActionHandlerPrivate::updateRedoAction(bool on) -{ - m_redoAction->setEnabled(on); -} - -void TextEditorActionHandlerPrivate::updateUndoAction(bool on) -{ - m_undoAction->setEnabled(on); -} - -void TextEditorActionHandlerPrivate::updateCopyAction(bool hasCopyableText) -{ - if (m_cutAction) - m_cutAction->setEnabled(hasCopyableText && m_currentEditorWidget - && !m_currentEditorWidget->isReadOnly()); - if (m_copyAction) - m_copyAction->setEnabled(hasCopyableText); - if (m_copyHtmlAction) - m_copyHtmlAction->setEnabled(hasCopyableText); -} - -void TextEditorActionHandlerPrivate::updateCurrentEditor(IEditor *editor) -{ - if (m_currentEditorWidget) - m_currentEditorWidget->disconnect(this); - m_currentEditorWidget = nullptr; - - m_currentEditor = editor; - - if (editor && editor->document()->id() == m_editorId) { - m_currentEditorWidget = m_findTextWidget(editor); - if (m_currentEditorWidget) { - connect(m_currentEditorWidget, &QPlainTextEdit::undoAvailable, - this, &TextEditorActionHandlerPrivate::updateUndoAction); - connect(m_currentEditorWidget, &QPlainTextEdit::redoAvailable, - this, &TextEditorActionHandlerPrivate::updateRedoAction); - connect(m_currentEditorWidget, &QPlainTextEdit::copyAvailable, - this, &TextEditorActionHandlerPrivate::updateCopyAction); - connect(m_currentEditorWidget, &TextEditorWidget::readOnlyChanged, - this, &TextEditorActionHandlerPrivate::updateActions); - connect(m_currentEditorWidget, &TextEditorWidget::optionalActionMaskChanged, - this, &TextEditorActionHandlerPrivate::updateOptionalActions); - } - } - updateActions(); -} - -} // namespace Internal - -TextEditorActionHandler::TextEditorActionHandler(Utils::Id editorId, - Utils::Id contextId, - uint optionalActions, - const TextEditorWidgetResolver &resolver) - : d(new Internal::TextEditorActionHandlerPrivate(editorId, contextId, optionalActions)) -{ - if (resolver) - d->m_findTextWidget = resolver; - else - d->m_findTextWidget = TextEditorWidget::fromEditor; -} - -uint TextEditorActionHandler::optionalActions() const -{ - return d->m_optionalActions; -} - -TextEditorActionHandler::~TextEditorActionHandler() -{ - delete d; -} - -void TextEditorActionHandler::updateCurrentEditor() -{ - d->updateCurrentEditor(EditorManager::currentEditor()); -} - -void TextEditorActionHandler::updateActions() -{ - d->updateActions(); -} - -void TextEditorActionHandler::setCanUndoCallback(const Predicate &callback) -{ - d->m_canUndoCallback = callback; -} - -void TextEditorActionHandler::setCanRedoCallback(const Predicate &callback) -{ - d->m_canRedoCallback = callback; -} - -void TextEditorActionHandler::setUnhandledCallback(const UnhandledCallback &callback) -{ - d->m_unhandledCallback = callback; -} - -} // namespace TextEditor diff --git a/src/plugins/texteditor/texteditoractionhandler.h b/src/plugins/texteditor/texteditoractionhandler.h deleted file mode 100644 index 344a4a553e1..00000000000 --- a/src/plugins/texteditor/texteditoractionhandler.h +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include "texteditor_global.h" - -#include <utils/id.h> - -#include <QObject> - -#include <functional> - -namespace Core { -class IEditor; -} - -namespace TextEditor { -class TextEditorWidget; - -namespace Internal { class TextEditorActionHandlerPrivate; } - -// Redirects slots from global actions to the respective editor. - -class TEXTEDITOR_EXPORT TextEditorActionHandler final -{ - TextEditorActionHandler(const TextEditorActionHandler &) = delete; - TextEditorActionHandler &operator=(const TextEditorActionHandler &) = delete; - -public: - enum OptionalActionsMask { - None = 0, - Format = 1, - UnCommentSelection = 2, - UnCollapseAll = 4, - FollowSymbolUnderCursor = 8, - FollowTypeUnderCursor = 16, - JumpToFileUnderCursor = 32, - RenameSymbol = 64, - FindUsage = 128, - CallHierarchy = 256 - }; - using TextEditorWidgetResolver = std::function<TextEditorWidget *(Core::IEditor *)>; - - TextEditorActionHandler(Utils::Id editorId, - Utils::Id contextId, - uint optionalActions = None, - const TextEditorWidgetResolver &resolver = {}); - - uint optionalActions() const; - ~TextEditorActionHandler(); - - void updateCurrentEditor(); - void updateActions(); - - using Predicate = std::function<bool(Core::IEditor *editor)>; - - void setCanUndoCallback(const Predicate &callback); - void setCanRedoCallback(const Predicate &callback); - - using UnhandledCallback = std::function<void(Utils::Id commandId, Core::IEditor *editor)>; - void setUnhandledCallback(const UnhandledCallback &callback); - -private: - Internal::TextEditorActionHandlerPrivate *d; -}; - -} // namespace TextEditor diff --git a/src/plugins/texteditor/texteditorconstants.h b/src/plugins/texteditor/texteditorconstants.h index e2115261882..2597deb6bba 100644 --- a/src/plugins/texteditor/texteditorconstants.h +++ b/src/plugins/texteditor/texteditorconstants.h @@ -212,6 +212,8 @@ const char FIND_USAGES[] = "TextEditor.FindUsages"; // moved from CppEditor to TextEditor avoid breaking the setting by using the old key const char RENAME_SYMBOL[] = "CppEditor.RenameSymbolUnderCursor"; const char OPEN_CALL_HIERARCHY[] = "TextEditor.OpenCallHierarchy"; +const char OPEN_TYPE_HIERARCHY[] = "TextEditor.OpenTypeHierarchy"; +const char TYPE_HIERARCHY_FACTORY_ID[] = "TextEditor.TypeHierarchy"; const char JUMP_TO_FILE_UNDER_CURSOR[] = "TextEditor.JumpToFileUnderCursor"; const char JUMP_TO_FILE_UNDER_CURSOR_IN_NEXT_SPLIT[] = "TextEditor.JumpToFileUnderCursorInNextSplit"; diff --git a/src/plugins/texteditor/texteditorplugin.cpp b/src/plugins/texteditor/texteditorplugin.cpp index 04f11bcbb0b..5db6a17e0d5 100644 --- a/src/plugins/texteditor/texteditorplugin.cpp +++ b/src/plugins/texteditor/texteditorplugin.cpp @@ -26,6 +26,7 @@ #include "texteditorsettings.h" #include "texteditortr.h" #include "textmark.h" +#include "typehierarchy.h" #include "typingsettings.h" #ifdef WITH_TESTS @@ -84,6 +85,7 @@ public: void updateCurrentSelection(const QString &text); void createStandardContextMenu(); + void createEditorCommands(); }; void TextEditorPlugin::initialize() @@ -105,6 +107,7 @@ void TextEditorPlugin::initialize() setupTextMarkRegistry(this); setupOutlineFactory(); + setupTypeHierarchyFactory(); setupLineNumberFilter(); // Goto line functionality for quick open setupPlainTextEditor(); @@ -120,58 +123,12 @@ void TextEditorPlugin::initialize() setupMarkdownEditor(); setupJsonEditor(); - Context context(TextEditor::Constants::C_TEXTEDITOR); - - // Add shortcut for invoking automatic completion - Command *command = nullptr; - ActionBuilder(this, Constants::COMPLETE_THIS) - .setText(Tr::tr("Trigger Completion")) - .setContext(context) - .bindCommand(&command) - .setDefaultKeySequence(Tr::tr("Meta+Space"), Tr::tr("Ctrl+Space")) - .addOnTriggered(this, [] { - if (BaseTextEditor *editor = BaseTextEditor::currentTextEditor()) - editor->editorWidget()->invokeAssist(Completion); - }); - - connect(command, &Command::keySequenceChanged, [command] { - FancyLineEdit::setCompletionShortcut(command->keySequence()); - }); - FancyLineEdit::setCompletionShortcut(command->keySequence()); - - // Add shortcut for invoking function hint completion - ActionBuilder(this, Constants::FUNCTION_HINT) - .setText(Tr::tr("Display Function Hint")) - .setContext(context) - .setDefaultKeySequence(Tr::tr("Meta+Shift+D"), Tr::tr("Ctrl+Shift+D")) - .addOnTriggered(this, [] { - if (BaseTextEditor *editor = BaseTextEditor::currentTextEditor()) - editor->editorWidget()->invokeAssist(FunctionHint); - }); - - // Add shortcut for invoking quick fix options - ActionBuilder(this, Constants::QUICKFIX_THIS) - .setText(Tr::tr("Trigger Refactoring Action")) - .setContext(context) - .setDefaultKeySequence(Tr::tr("Alt+Return")) - .addOnTriggered(this, [] { - if (BaseTextEditor *editor = BaseTextEditor::currentTextEditor()) - editor->editorWidget()->invokeAssist(QuickFix); - }); - - ActionBuilder(this, Constants::SHOWCONTEXTMENU) - .setText(Tr::tr("Show Context Menu")) - .setContext(context) - .addOnTriggered(this, [] { - if (BaseTextEditor *editor = BaseTextEditor::currentTextEditor()) - editor->editorWidget()->showContextMenu(); - }); - // Add text snippet provider. SnippetProvider::registerGroup(Constants::TEXT_SNIPPET_GROUP_ID, Tr::tr("Text", "SnippetProvider")); createStandardContextMenu(); + createEditorCommands(); #ifdef WITH_TESTS addTestCreator(createCodeAssistTests); @@ -347,6 +304,272 @@ void TextEditorPlugin::createStandardContextMenu() add(Constants::SWITCH_UTF8BOM, Constants::G_BOM); } +void TextEditorPlugin::createEditorCommands() +{ + using namespace Core::Constants; + // Add shortcut for invoking automatic completion + Command *command = nullptr; + ActionBuilder(this, Constants::COMPLETE_THIS) + .setText(Tr::tr("Trigger Completion")) + .bindCommand(&command) + .setDefaultKeySequence(Tr::tr("Meta+Space"), Tr::tr("Ctrl+Space")); + + connect(command, &Command::keySequenceChanged, [command] { + FancyLineEdit::setCompletionShortcut(command->keySequence()); + }); + FancyLineEdit::setCompletionShortcut(command->keySequence()); + + // Add shortcut for invoking function hint completion + ActionBuilder(this, Constants::FUNCTION_HINT) + .setText(Tr::tr("Display Function Hint")) + .setDefaultKeySequence(Tr::tr("Meta+Shift+D"), Tr::tr("Ctrl+Shift+D")); + + // Add shortcut for invoking quick fix options + ActionBuilder(this, Constants::QUICKFIX_THIS) + .setText(Tr::tr("Trigger Refactoring Action")) + .setDefaultKeySequence(Tr::tr("Alt+Return")); + + ActionBuilder(this, Constants::SHOWCONTEXTMENU) + .setText(Tr::tr("Show Context Menu")); + + ActionBuilder(this, DELETE_LINE).setText(Tr::tr("Delete &Line")); + ActionBuilder(this, DELETE_END_OF_LINE).setText(Tr::tr("Delete Line from Cursor On")); + ActionBuilder(this, DELETE_END_OF_WORD).setText(Tr::tr("Delete Word from Cursor On")); + ActionBuilder(this, DELETE_END_OF_WORD_CAMEL_CASE) + .setText(Tr::tr("Delete Word Camel Case from Cursor On")); + ActionBuilder(this, DELETE_START_OF_LINE) + .setText(Tr::tr("Delete Line up to Cursor")) + .setDefaultKeySequence(Tr::tr("Ctrl+Backspace"), {}); + ActionBuilder(this, DELETE_START_OF_WORD).setText(Tr::tr("Delete Word up to Cursor")); + ActionBuilder(this, DELETE_START_OF_WORD_CAMEL_CASE) + .setText(Tr::tr("Delete Word Camel Case up to Cursor")); + ActionBuilder(this, GOTO_BLOCK_START_WITH_SELECTION) + .setText(Tr::tr("Go to Block Start with Selection")) + .setDefaultKeySequence(QKeySequence(Tr::tr("Ctrl+{"))); + ActionBuilder(this, GOTO_BLOCK_END_WITH_SELECTION) + .setText(Tr::tr("Go to Block End with Selection")) + .setDefaultKeySequence(QKeySequence(Tr::tr("Ctrl+}"))); + ActionBuilder(this, MOVE_LINE_UP) + .setText(Tr::tr("Move Line Up")) + .setDefaultKeySequence(QKeySequence(Tr::tr("Ctrl+Shift+Up"))); + ActionBuilder(this, MOVE_LINE_DOWN) + .setText(Tr::tr("Move Line Down")) + .setDefaultKeySequence(QKeySequence(Tr::tr("Ctrl+Shift+Down"))); + ActionBuilder(this, COPY_LINE_UP) + .setText(Tr::tr("Copy Line Up")) + .setDefaultKeySequence(QKeySequence(Tr::tr("Ctrl+Alt+Up"))); + ActionBuilder(this, COPY_LINE_DOWN) + .setText(Tr::tr("Copy Line Down")) + .setDefaultKeySequence(QKeySequence(Tr::tr("Ctrl+Alt+Down"))); + ActionBuilder(this, JOIN_LINES) + .setText(Tr::tr("Join Lines")) + .setDefaultKeySequence(QKeySequence(Tr::tr("Ctrl+J"))); + ActionBuilder(this, INSERT_LINE_ABOVE) + .setText(Tr::tr("Insert Line Above Current Line")) + .setDefaultKeySequence(QKeySequence(Tr::tr("Ctrl+Shift+Return"))); + ActionBuilder(this, INSERT_LINE_BELOW) + .setText(Tr::tr("Insert Line Below Current Line")) + .setDefaultKeySequence(QKeySequence(Tr::tr("Ctrl+Return"))); + ActionBuilder(this, SWITCH_UTF8BOM).setText(Tr::tr("Toggle UTF-8 BOM")); + ActionBuilder(this, INDENT).setText(Tr::tr("Indent")); + ActionBuilder(this, UNINDENT).setText(Tr::tr("Unindent")); + ActionBuilder(this, FOLLOW_SYMBOL_UNDER_CURSOR) + .setText(Tr::tr("Follow Symbol Under Cursor")) + .setDefaultKeySequence(QKeySequence(Qt::Key_F2)); + ActionBuilder(this, FOLLOW_SYMBOL_UNDER_CURSOR_IN_NEXT_SPLIT) + .setText(Tr::tr("Follow Symbol Under Cursor in Next Split")) + .setDefaultKeySequence(Tr::tr("Meta+E, F2"), Tr::tr("Ctrl+E, F2")); + ActionBuilder(this, FOLLOW_SYMBOL_TO_TYPE) + .setText(Tr::tr("Follow Type Under Cursor")) + .setDefaultKeySequence(QKeySequence(Tr::tr("Ctrl+Shift+F2"))); + ActionBuilder(this, FOLLOW_SYMBOL_TO_TYPE_IN_NEXT_SPLIT) + .setText(Tr::tr("Follow Type Under Cursor in Next Split")) + .setDefaultKeySequence(Tr::tr("Meta+E, Shift+F2"), Tr::tr("Ctrl+E, Ctrl+Shift+F2")); + ActionBuilder(this, FIND_USAGES) + .setText(Tr::tr("Find References to Symbol Under Cursor")) + .setDefaultKeySequence(QKeySequence(Tr::tr("Ctrl+Shift+U"))); + ActionBuilder(this, RENAME_SYMBOL) + .setText(Tr::tr("Rename Symbol Under Cursor")) + .setDefaultKeySequence(QKeySequence(Tr::tr("Ctrl+Shift+R"))); + ActionBuilder(this, JUMP_TO_FILE_UNDER_CURSOR) + .setText(Tr::tr("Jump to File Under Cursor")) + .setDefaultKeySequence(QKeySequence(Qt::Key_F2)); + ActionBuilder(this, JUMP_TO_FILE_UNDER_CURSOR_IN_NEXT_SPLIT) + .setText(Tr::tr("Jump to File Under Cursor in Next Split")) + .setDefaultKeySequence(Tr::tr("Meta+E, F2"), Tr::tr("Ctrl+E, F2")); + ActionBuilder(this, OPEN_CALL_HIERARCHY).setText(Tr::tr("Open Call Hierarchy")); + ActionBuilder(this, OPEN_TYPE_HIERARCHY) + .setText(Tr::tr("Open Type Hierarchy")) + .setDefaultKeySequence(Tr::tr("Meta+Shift+T"), Tr::tr("Ctrl+Shift+T")); + ActionBuilder(this, VIEW_PAGE_UP) + .setText(Tr::tr("Move the View a Page Up and Keep the Cursor Position")) + .setDefaultKeySequence(QKeySequence(Tr::tr("Ctrl+PgUp"))); + ActionBuilder(this, VIEW_PAGE_DOWN) + .setText(Tr::tr("Move the View a Page Down and Keep the Cursor Position")) + .setDefaultKeySequence(QKeySequence(Tr::tr("Ctrl+PgDown"))); + ActionBuilder(this, VIEW_LINE_UP) + .setText(Tr::tr("Move the View a Line Up and Keep the Cursor Position")) + .setDefaultKeySequence(QKeySequence(Tr::tr("Ctrl+Up"))); + ActionBuilder(this, VIEW_LINE_DOWN) + .setText(Tr::tr("Move the View a Line Down and Keep the Cursor Position")) + .setDefaultKeySequence(QKeySequence(Tr::tr("Ctrl+Down"))); + + ActionManager::actionContainer(M_EDIT); + ActionBuilder(this, SELECT_ENCODING) + .setText(Tr::tr("Select Encoding...")) + .addToContainer(M_EDIT, G_EDIT_OTHER); + ActionBuilder(this, CIRCULAR_PASTE) + .setText(Tr::tr("Paste from Clipboard History")) + .setDefaultKeySequence(QKeySequence(Tr::tr("Ctrl+Shift+V"))) + .addToContainer(M_EDIT, G_EDIT_COPYPASTE); + ActionBuilder(this, NO_FORMAT_PASTE) + .setText(Tr::tr("Paste Without Formatting")) + .setDefaultKeySequence(Tr::tr("Ctrl+Alt+Shift+V"), QString()) + .addToContainer(M_EDIT, G_EDIT_COPYPASTE); + + ActionManager::actionContainer(M_EDIT_ADVANCED); + ActionBuilder(this, AUTO_INDENT_SELECTION) + .setText(Tr::tr("Auto-&indent Selection")) + .setDefaultKeySequence(QKeySequence(Tr::tr("Ctrl+I"))) + .addToContainer(M_EDIT_ADVANCED, G_EDIT_FORMAT); + ActionBuilder(this, AUTO_FORMAT_SELECTION) + .setText(Tr::tr("Auto-&format Selection")) + .setDefaultKeySequence(QKeySequence(Tr::tr("Ctrl+;"))) + .addToContainer(M_EDIT_ADVANCED, G_EDIT_FORMAT); + ActionBuilder(this, REWRAP_PARAGRAPH) + .setText(Tr::tr("&Rewrap Paragraph")) + .setDefaultKeySequence(Tr::tr("Meta+E, R"), Tr::tr("Ctrl+E, R")) + .addToContainer(M_EDIT_ADVANCED, G_EDIT_FORMAT); + ActionBuilder(this, VISUALIZE_WHITESPACE) + .setText(Tr::tr("&Visualize Whitespace")) + .setDefaultKeySequence(Tr::tr("Meta+E, Meta+V"), Tr::tr("Ctrl+E, Ctrl+V")) + .addToContainer(M_EDIT_ADVANCED, G_EDIT_FORMAT); + ActionBuilder(this, CLEAN_WHITESPACE) + .setText(Tr::tr("Clean Whitespace")) + .addToContainer(M_EDIT_ADVANCED, G_EDIT_FORMAT); + ActionBuilder(this, TEXT_WRAPPING) + .setText(Tr::tr("Enable Text &Wrapping")) + .setDefaultKeySequence(Tr::tr("Meta+E, Meta+W"), Tr::tr("Ctrl+E, Ctrl+W")) + .addToContainer(M_EDIT_ADVANCED, G_EDIT_FORMAT) + .setCheckable(true); + ActionBuilder(this, UN_COMMENT_SELECTION) + .setText(Tr::tr("Toggle Comment &Selection")) + .setDefaultKeySequence(QKeySequence(Tr::tr("Ctrl+/"))) + .addToContainer(M_EDIT_ADVANCED, G_EDIT_FORMAT); + ActionBuilder(this, CUT_LINE) + .setText(Tr::tr("Cut &Line")) + .setDefaultKeySequence(QKeySequence(Tr::tr("Shift+Del"))) + .addToContainer(M_EDIT_ADVANCED, G_EDIT_TEXT); + ActionBuilder(this, COPY_LINE) + .setText(Tr::tr("Copy &Line")) + .setDefaultKeySequence(QKeySequence(Tr::tr("Ctrl+Ins"))) + .addToContainer(M_EDIT_ADVANCED, G_EDIT_TEXT); + ActionBuilder(this, COPY_WITH_HTML) + .setText(Tr::tr("Copy With Highlighting")) + .addToContainer(M_EDIT_ADVANCED, G_EDIT_TEXT); + ActionBuilder(this, ADD_CURSORS_TO_LINE_ENDS) + .setText(Tr::tr("Create Cursors at Selected Line Ends")) + .setDefaultKeySequence(QKeySequence(Tr::tr("Alt+Shift+I"))) + .addToContainer(M_EDIT_ADVANCED, G_EDIT_TEXT); + ActionBuilder(this, ADD_SELECT_NEXT_FIND_MATCH) + .setText(Tr::tr("Add Next Occurrence to Selection")) + .setDefaultKeySequence(QKeySequence(Tr::tr("Ctrl+D"))) + .addToContainer(M_EDIT_ADVANCED, G_EDIT_TEXT); + ActionBuilder(this, DUPLICATE_SELECTION) + .setText(Tr::tr("&Duplicate Selection")) + .addToContainer(M_EDIT_ADVANCED, G_EDIT_TEXT); + ActionBuilder(this, DUPLICATE_SELECTION_AND_COMMENT) + .setText(Tr::tr("&Duplicate Selection and Comment")) + .addToContainer(M_EDIT_ADVANCED, G_EDIT_TEXT); + ActionBuilder(this, UPPERCASE_SELECTION) + .setText(Tr::tr("Uppercase Selection")) + .setDefaultKeySequence(Tr::tr("Meta+Shift+U"), Tr::tr("Alt+Shift+U")) + .addToContainer(M_EDIT_ADVANCED, G_EDIT_TEXT); + ActionBuilder(this, LOWERCASE_SELECTION) + .setText(Tr::tr("Lowercase Selection")) + .setDefaultKeySequence(Tr::tr("Meta+U"), Tr::tr("Alt+U")) + .addToContainer(M_EDIT_ADVANCED, G_EDIT_TEXT); + ActionBuilder(this, SORT_LINES) + .setText(Tr::tr("Sort Lines")) + .setDefaultKeySequence(Tr::tr("Meta+Shift+S"), Tr::tr("Alt+Shift+S")) + .addToContainer(M_EDIT_ADVANCED, G_EDIT_TEXT); + ActionBuilder(this, FOLD) + .setText(Tr::tr("Fold")) + .setDefaultKeySequence(QKeySequence(Tr::tr("Ctrl+<"))) + .addToContainer(M_EDIT_ADVANCED, G_EDIT_COLLAPSING); + ActionBuilder(this, UNFOLD) + .setText(Tr::tr("Unfold")) + .setDefaultKeySequence(QKeySequence(Tr::tr("Ctrl+>"))) + .addToContainer(M_EDIT_ADVANCED, G_EDIT_COLLAPSING); + ActionBuilder(this, UNFOLD_ALL) + .setText(Tr::tr("Toggle &Fold All")) + .addToContainer(M_EDIT_ADVANCED, G_EDIT_COLLAPSING); + ActionBuilder(this, INCREASE_FONT_SIZE) + .setText(Tr::tr("Increase Font Size")) + .setDefaultKeySequence(QKeySequence(Tr::tr("Ctrl++"))) + .addToContainer(M_EDIT_ADVANCED, G_EDIT_FONT); + ActionBuilder(this, DECREASE_FONT_SIZE) + .setText(Tr::tr("Decrease Font Size")) + .setDefaultKeySequence(QKeySequence(Tr::tr("Ctrl+-"))) + .addToContainer(M_EDIT_ADVANCED, G_EDIT_FONT); + ActionBuilder(this, RESET_FONT_SIZE) + .setText(Tr::tr("Reset Font Size")) + .setDefaultKeySequence(QKeySequence(Tr::tr("Ctrl+0"))) + .addToContainer(M_EDIT_ADVANCED, G_EDIT_FONT); + ActionBuilder(this, GOTO_BLOCK_START) + .setText(Tr::tr("Go to Block Start")) + .setDefaultKeySequence(QKeySequence(Tr::tr("Ctrl+["))) + .addToContainer(M_EDIT_ADVANCED, G_EDIT_BLOCKS); + ActionBuilder(this, GOTO_BLOCK_END) + .setText(Tr::tr("Go to Block End")) + .setDefaultKeySequence(QKeySequence(Tr::tr("Ctrl+]"))) + .addToContainer(M_EDIT_ADVANCED, G_EDIT_BLOCKS); + ActionBuilder(this, SELECT_BLOCK_UP) + .setText(Tr::tr("Select Block Up")) + .setDefaultKeySequence(QKeySequence(Tr::tr("Ctrl+U"))) + .addToContainer(M_EDIT_ADVANCED, G_EDIT_BLOCKS); + ActionBuilder(this, SELECT_BLOCK_DOWN) + .setText(Tr::tr("Select Block Down")) + .setDefaultKeySequence(QKeySequence(Tr::tr("Ctrl+Shift+Alt+U"))) + .addToContainer(M_EDIT_ADVANCED, G_EDIT_BLOCKS); + ActionBuilder(this, SELECT_WORD_UNDER_CURSOR).setText(Tr::tr("Select Word Under Cursor")); + + ActionBuilder(this, GOTO_DOCUMENT_START).setText(Tr::tr("Go to Document Start")); + ActionBuilder(this, GOTO_DOCUMENT_END).setText(Tr::tr("Go to Document End")); + ActionBuilder(this, GOTO_LINE_START).setText(Tr::tr("Go to Line Start")); + ActionBuilder(this, GOTO_LINE_END).setText(Tr::tr("Go to Line End")); + ActionBuilder(this, GOTO_NEXT_LINE).setText(Tr::tr("Go to Next Line")); + ActionBuilder(this, GOTO_PREVIOUS_LINE).setText(Tr::tr("Go to Previous Line")); + ActionBuilder(this, GOTO_PREVIOUS_CHARACTER).setText(Tr::tr("Go to Previous Character")); + ActionBuilder(this, GOTO_NEXT_CHARACTER).setText(Tr::tr("Go to Next Character")); + ActionBuilder(this, GOTO_PREVIOUS_WORD).setText(Tr::tr("Go to Previous Word")); + ActionBuilder(this, GOTO_NEXT_WORD).setText(Tr::tr("Go to Next Word")); + ActionBuilder(this, GOTO_PREVIOUS_WORD_CAMEL_CASE) + .setText(Tr::tr("Go to Previous Word (Camel Case)")); + ActionBuilder(this, GOTO_NEXT_WORD_CAMEL_CASE).setText(Tr::tr("Go to Next Word (Camel Case)")); + + ActionBuilder(this, GOTO_LINE_START_WITH_SELECTION) + .setText(Tr::tr("Go to Line Start with Selection")); + ActionBuilder(this, GOTO_LINE_END_WITH_SELECTION) + .setText(Tr::tr("Go to Line End with Selection")); + ActionBuilder(this, GOTO_NEXT_LINE_WITH_SELECTION) + .setText(Tr::tr("Go to Next Line with Selection")); + ActionBuilder(this, GOTO_PREVIOUS_LINE_WITH_SELECTION) + .setText(Tr::tr("Go to Previous Line with Selection")); + ActionBuilder(this, GOTO_PREVIOUS_CHARACTER_WITH_SELECTION) + .setText(Tr::tr("Go to Previous Character with Selection")); + ActionBuilder(this, GOTO_NEXT_CHARACTER_WITH_SELECTION) + .setText(Tr::tr("Go to Next Character with Selection")); + ActionBuilder(this, GOTO_PREVIOUS_WORD_WITH_SELECTION) + .setText(Tr::tr("Go to Previous Word with Selection")); + ActionBuilder(this, GOTO_NEXT_WORD_WITH_SELECTION) + .setText(Tr::tr("Go to Next Word with Selection")); + ActionBuilder(this, GOTO_PREVIOUS_WORD_CAMEL_CASE_WITH_SELECTION) + .setText(Tr::tr("Go to Previous Word (Camel Case) with Selection")); + ActionBuilder(this, GOTO_NEXT_WORD_CAMEL_CASE_WITH_SELECTION) + .setText(Tr::tr("Go to Next Word (Camel Case) with Selection")); +} + } // namespace TextEditor::Internal #include "texteditorplugin.moc" diff --git a/src/plugins/texteditor/textmark.cpp b/src/plugins/texteditor/textmark.cpp index 1739cf61b9e..459d0ca27c1 100644 --- a/src/plugins/texteditor/textmark.cpp +++ b/src/plugins/texteditor/textmark.cpp @@ -371,7 +371,7 @@ bool TextMark::addToolTipContent(QLayout *target) const QColor TextMark::annotationColor() const { if (m_color.has_value()) - return Utils::creatorTheme()->color(*m_color).toHsl(); + return Utils::creatorColor(*m_color).toHsl(); return {}; } diff --git a/src/plugins/texteditor/typehierarchy.cpp b/src/plugins/texteditor/typehierarchy.cpp new file mode 100644 index 00000000000..53924a2b9d6 --- /dev/null +++ b/src/plugins/texteditor/typehierarchy.cpp @@ -0,0 +1,136 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "typehierarchy.h" + +#include "texteditorconstants.h" +#include "texteditortr.h" + +#include <coreplugin/actionmanager/actionmanager.h> +#include <coreplugin/editormanager/editormanager.h> +#include <coreplugin/editormanager/ieditor.h> +#include <coreplugin/inavigationwidgetfactory.h> + +#include <utils/utilsicons.h> + +#include <QLabel> +#include <QPalette> +#include <QStackedWidget> +#include <QToolButton> + +using namespace Utils; + +namespace TextEditor { +namespace Internal { + +static QList<TypeHierarchyWidgetFactory *> g_widgetFactories; + +class TypeHierarchyFactory : public Core::INavigationWidgetFactory +{ +public: + TypeHierarchyFactory() + { + setDisplayName(Tr::tr("Type Hierarchy")); + setPriority(649); + setId(Constants::TYPE_HIERARCHY_FACTORY_ID); + } + +private: + Core::NavigationView createWidget() override; +}; + +class TypeHierarchyWidgetStack : public QStackedWidget +{ + Q_OBJECT + +public: + TypeHierarchyWidgetStack(); + + void reload(); +}; + +static TypeHierarchyFactory &typeHierarchyFactory() +{ + static TypeHierarchyFactory theFactory; + return theFactory; +} + +void setupTypeHierarchyFactory() +{ + (void) typeHierarchyFactory(); +} + +TypeHierarchyWidgetStack::TypeHierarchyWidgetStack() +{ + QLabel *label = new QLabel(Tr::tr("No type hierarchy available"), this); + label->setAlignment(Qt::AlignCenter); + + label->setAutoFillBackground(true); + label->setBackgroundRole(QPalette::Base); + + addWidget(label); + reload(); +} + +void TypeHierarchyWidgetStack::reload() +{ + const auto editor = Core::EditorManager::currentEditor(); + TypeHierarchyWidget *newWidget = nullptr; + + if (editor) { + for (TypeHierarchyWidgetFactory * const widgetFactory : std::as_const(g_widgetFactories)) { + if ((newWidget = widgetFactory->createWidget(editor))) + break; + } + } + + QWidget * const current = currentWidget(); + if (current) { + removeWidget(current); + current->deleteLater(); + } + if (newWidget) { + addWidget(newWidget); + setCurrentWidget(newWidget); + setFocusProxy(newWidget); + newWidget->reload(); + } +} + +Core::NavigationView TypeHierarchyFactory::createWidget() +{ + const auto placeholder = new TypeHierarchyWidgetStack; + const auto reloadButton = new QToolButton; + reloadButton->setIcon(Icons::RELOAD_TOOLBAR.icon()); + reloadButton->setToolTip(Tr::tr("Reloads the type hierarchy for the symbol under the cursor.")); + connect(reloadButton, &QToolButton::clicked, placeholder, &TypeHierarchyWidgetStack::reload); + return {placeholder, {reloadButton}}; +} + +void updateTypeHierarchy(QWidget *widget) +{ + if (const auto w = qobject_cast<TypeHierarchyWidgetStack *>(widget)) + w->reload(); +} + +} // namespace Internal + +TypeHierarchyWidgetFactory::TypeHierarchyWidgetFactory() +{ + Internal::g_widgetFactories.append(this); +} + +TypeHierarchyWidgetFactory::~TypeHierarchyWidgetFactory() +{ + Internal::g_widgetFactories.removeOne(this); +} + +void openTypeHierarchy() +{ + if (const auto action = Core::ActionManager::command(Constants::OPEN_TYPE_HIERARCHY)->action()) + action->trigger(); +} + +} // namespace TextEditor + +#include <typehierarchy.moc> diff --git a/src/plugins/texteditor/typehierarchy.h b/src/plugins/texteditor/typehierarchy.h new file mode 100644 index 00000000000..ee7c1fa1a7b --- /dev/null +++ b/src/plugins/texteditor/typehierarchy.h @@ -0,0 +1,39 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include <texteditor/texteditor_global.h> + +#include <QWidget> + +namespace Core { class IEditor; } + +namespace TextEditor { +namespace Internal { +void setupTypeHierarchyFactory(); +void updateTypeHierarchy(QWidget *widget); +} + +class TEXTEDITOR_EXPORT TypeHierarchyWidget : public QWidget +{ + Q_OBJECT +public: + virtual void reload() = 0; +}; + +class TEXTEDITOR_EXPORT TypeHierarchyWidgetFactory : public QObject +{ + Q_OBJECT + +public: + virtual TypeHierarchyWidget *createWidget(Core::IEditor *editor) = 0; + +protected: + TypeHierarchyWidgetFactory(); + ~TypeHierarchyWidgetFactory() override; +}; + +TEXTEDITOR_EXPORT void openTypeHierarchy(); + +} // namespace TextEditor |