diff --git a/src/plugins/coreplugin/find/currentdocumentfind.cpp b/src/plugins/coreplugin/find/currentdocumentfind.cpp index 7fe84aac158..529bbc4a2ba 100644 --- a/src/plugins/coreplugin/find/currentdocumentfind.cpp +++ b/src/plugins/coreplugin/find/currentdocumentfind.cpp @@ -81,6 +81,13 @@ bool CurrentDocumentFind::supportsReplace() const return m_currentFind->supportsReplace(); } +bool CurrentDocumentFind::supportsSelectAll() const +{ + if (!m_currentFind) + return false; + return m_currentFind->supportsSelectAll(); +} + FindFlags CurrentDocumentFind::supportedFindFlags() const { QTC_ASSERT(m_currentFind, return {}); @@ -119,6 +126,12 @@ IFindSupport::Result CurrentDocumentFind::findStep(const QString &txt, FindFlags return m_currentFind->findStep(txt, findFlags); } +void CurrentDocumentFind::selectAll(const QString &txt, FindFlags findFlags) +{ + QTC_ASSERT(m_currentFind && m_currentFind->supportsSelectAll(), return); + m_currentFind->selectAll(txt, findFlags); +} + void CurrentDocumentFind::replace(const QString &before, const QString &after, FindFlags findFlags) { QTC_ASSERT(m_currentFind, return); diff --git a/src/plugins/coreplugin/find/currentdocumentfind.h b/src/plugins/coreplugin/find/currentdocumentfind.h index 32aa5b97f8e..4a879a134fc 100644 --- a/src/plugins/coreplugin/find/currentdocumentfind.h +++ b/src/plugins/coreplugin/find/currentdocumentfind.h @@ -42,6 +42,7 @@ public: void resetIncrementalSearch(); void clearHighlights(); bool supportsReplace() const; + bool supportsSelectAll() const; FindFlags supportedFindFlags() const; QString currentFindString() const; QString completedFindString() const; @@ -51,6 +52,7 @@ public: void highlightAll(const QString &txt, FindFlags findFlags); IFindSupport::Result findIncremental(const QString &txt, FindFlags findFlags); IFindSupport::Result findStep(const QString &txt, FindFlags findFlags); + void selectAll(const QString &txt, FindFlags findFlags); void replace(const QString &before, const QString &after, FindFlags findFlags); bool replaceStep(const QString &before, const QString &after, FindFlags findFlags); int replaceAll(const QString &before, const QString &after, FindFlags findFlags); diff --git a/src/plugins/coreplugin/find/findtoolbar.cpp b/src/plugins/coreplugin/find/findtoolbar.cpp index 498b290b264..4a374fa742e 100644 --- a/src/plugins/coreplugin/find/findtoolbar.cpp +++ b/src/plugins/coreplugin/find/findtoolbar.cpp @@ -222,6 +222,17 @@ FindToolBar::FindToolBar(CurrentDocumentFind *currentDocumentFind) connect(m_findPreviousSelectedAction, &QAction::triggered, this, &FindToolBar::findPreviousSelected); + m_selectAllAction = new QAction(tr("Select All"), this); + cmd = ActionManager::registerAction(m_selectAllAction, Constants::FIND_SELECT_ALL); + cmd->setDefaultKeySequence(QKeySequence(tr("Alt+Return"))); + mfind->addAction(cmd, Constants::G_FIND_ACTIONS); + connect(m_selectAllAction, &QAction::triggered, this, &FindToolBar::selectAll); + m_localSelectAllAction = new QAction(m_selectAllAction->text(), this); + cmd = ActionManager::registerAction(m_localSelectAllAction, Constants::FIND_SELECT_ALL, findcontext); + cmd->setDefaultKeySequence(QKeySequence(tr("Alt+Return"))); + connect(m_localSelectAllAction, &QAction::triggered, this, &FindToolBar::selectAll); + m_ui.selectAllButton->setDefaultAction(m_localSelectAllAction); + m_replaceAction = new QAction(tr("Replace"), this); cmd = ActionManager::registerAction(m_replaceAction, Constants::REPLACE); cmd->setDefaultKeySequence(QKeySequence()); @@ -400,6 +411,7 @@ void FindToolBar::updateActions() if (m_enterFindStringAction) m_enterFindStringAction->setEnabled(enabled); updateFindReplaceEnabled(); + m_selectAllAction->setEnabled(m_currentDocumentFind->supportsSelectAll()); } void FindToolBar::updateToolBar() @@ -869,6 +881,15 @@ void FindToolBar::findPreviousSelected() invokeFindPrevious(); } +void FindToolBar::selectAll() +{ + if (m_currentDocumentFind->isEnabled()) { + const FindFlags ef = effectiveFindFlags(); + Find::updateFindCompletion(getFindText(), ef); + m_currentDocumentFind->selectAll(getFindText(), ef); + } +} + bool FindToolBar::focusNextPrevChild(bool next) { QAbstractButton *optionsButton = m_ui.findEdit->button(Utils::FancyLineEdit::Left); @@ -1000,6 +1021,7 @@ void FindToolBar::updateFindReplaceEnabled() m_localFindPreviousAction->setEnabled(enabled); m_findEnabled = enabled; } + m_localSelectAllAction->setEnabled(enabled && m_currentDocumentFind->supportsSelectAll()); m_findNextAction->setEnabled(enabled && m_findInDocumentAction->isEnabled()); m_findPreviousAction->setEnabled(enabled && m_findInDocumentAction->isEnabled()); diff --git a/src/plugins/coreplugin/find/findtoolbar.h b/src/plugins/coreplugin/find/findtoolbar.h index 34103b234ac..485e67381df 100644 --- a/src/plugins/coreplugin/find/findtoolbar.h +++ b/src/plugins/coreplugin/find/findtoolbar.h @@ -104,6 +104,7 @@ private: void openFind(bool focus = true); void findNextSelected(); void findPreviousSelected(); + void selectAll(); void updateActions(); void updateToolBar(); void findFlagsChanged(); @@ -149,6 +150,7 @@ private: QAction *m_findInDocumentAction = nullptr; QAction *m_findNextSelectedAction = nullptr; QAction *m_findPreviousSelectedAction = nullptr; + QAction *m_selectAllAction = nullptr; QAction *m_enterFindStringAction = nullptr; QAction *m_findNextAction = nullptr; QAction *m_findPreviousAction = nullptr; @@ -163,6 +165,7 @@ private: QAction *m_localFindNextAction = nullptr; QAction *m_localFindPreviousAction = nullptr; + QAction *m_localSelectAllAction = nullptr; QAction *m_localReplaceAction = nullptr; QAction *m_localReplaceNextAction = nullptr; QAction *m_localReplacePreviousAction = nullptr; diff --git a/src/plugins/coreplugin/find/findwidget.ui b/src/plugins/coreplugin/find/findwidget.ui index 89c847b3203..2e50aaa878b 100644 --- a/src/plugins/coreplugin/find/findwidget.ui +++ b/src/plugins/coreplugin/find/findwidget.ui @@ -73,6 +73,13 @@ + + + + + + + diff --git a/src/plugins/coreplugin/find/ifindsupport.cpp b/src/plugins/coreplugin/find/ifindsupport.cpp index 9dc51794d69..84087553b36 100644 --- a/src/plugins/coreplugin/find/ifindsupport.cpp +++ b/src/plugins/coreplugin/find/ifindsupport.cpp @@ -66,6 +66,15 @@ using namespace Core; Returns whether the find filter supports search and replace. */ +/*! + \fn bool Core::IFindSupport::supportsSelectAll() const + Returns whether the find filter supports selecting all results. +*/ +bool IFindSupport::supportsSelectAll() const +{ + return false; +} + /*! \fn Core::FindFlags Core::IFindSupport::supportedFindFlags() const Returns the find flags, such as whole words or regular expressions, @@ -165,6 +174,15 @@ int IFindSupport::replaceAll(const QString &before, const QString &after, FindFl return 0; } +/*! + Finds and selects all instances of \a txt with specified \a findFlags. +*/ +void IFindSupport::selectAll(const QString &txt, FindFlags findFlags) +{ + Q_UNUSED(txt) + Q_UNUSED(findFlags) +} + /*! Shows \a parent overlayed with the wrap indicator. */ diff --git a/src/plugins/coreplugin/find/ifindsupport.h b/src/plugins/coreplugin/find/ifindsupport.h index f1ec970b924..106e3ea9bc1 100644 --- a/src/plugins/coreplugin/find/ifindsupport.h +++ b/src/plugins/coreplugin/find/ifindsupport.h @@ -43,6 +43,7 @@ public: ~IFindSupport() override = default; virtual bool supportsReplace() const = 0; + virtual bool supportsSelectAll() const; virtual FindFlags supportedFindFlags() const = 0; virtual void resetIncrementalSearch() = 0; virtual void clearHighlights() = 0; @@ -58,6 +59,7 @@ public: FindFlags findFlags); virtual int replaceAll(const QString &before, const QString &after, FindFlags findFlags); + virtual void selectAll(const QString &txt, FindFlags findFlags); virtual void defineFindScope(){} virtual void clearFindScope(){} diff --git a/src/plugins/coreplugin/find/textfindconstants.h b/src/plugins/coreplugin/find/textfindconstants.h index d670abd0f04..120a01fd171 100644 --- a/src/plugins/coreplugin/find/textfindconstants.h +++ b/src/plugins/coreplugin/find/textfindconstants.h @@ -47,6 +47,7 @@ const char ADVANCED_FIND[] = "Find.Dialog"; const char FIND_IN_DOCUMENT[] = "Find.FindInCurrentDocument"; const char FIND_NEXT_SELECTED[]= "Find.FindNextSelected"; const char FIND_PREV_SELECTED[]= "Find.FindPreviousSelected"; +const char FIND_SELECT_ALL[] = "Find.SelectAll"; const char FIND_NEXT[] = "Find.FindNext"; const char FIND_PREVIOUS[] = "Find.FindPrevious"; const char REPLACE[] = "Find.Replace"; diff --git a/src/plugins/texteditor/texteditor.cpp b/src/plugins/texteditor/texteditor.cpp index 58a5913675a..455a0203dbf 100644 --- a/src/plugins/texteditor/texteditor.cpp +++ b/src/plugins/texteditor/texteditor.cpp @@ -825,6 +825,76 @@ public: QStack m_undoCursorStack; }; +class TextEditorWidgetFind : public BaseTextFind +{ +public: + TextEditorWidgetFind(TextEditorWidget *editor) + : BaseTextFind(editor) + , m_editor(editor) + { + setMultiTextCursorProvider([editor]() { return editor->multiTextCursor(); }); + } + + bool supportsSelectAll() const override { return true; } + void selectAll(const QString &txt, FindFlags findFlags) override; + + static void cancelCurrentSelectAll(); + +private: + TextEditorWidget * const m_editor; + static QFutureWatcher *m_selectWatcher; +}; + +QFutureWatcher *TextEditorWidgetFind::m_selectWatcher = nullptr; + +void TextEditorWidgetFind::selectAll(const QString &txt, FindFlags findFlags) +{ + if (txt.isEmpty()) + return; + + cancelCurrentSelectAll(); + + m_selectWatcher = new QFutureWatcher(); + connect(m_selectWatcher, &QFutureWatcher::finished, + this, [this]() { + const FileSearchResultList &results = m_selectWatcher->result(); + const QTextCursor c(m_editor->document()); + auto cursorForResult = [c](const FileSearchResult &r) { + return Utils::Text::selectAt(c, r.lineNumber, r.matchStart + 1, r.matchLength); + }; + QList cursors = Utils::transform(results, cursorForResult); + cursors = Utils::filtered(cursors, [this](const QTextCursor &c) { + return m_editor->inFindScope(c); + }); + m_editor->setMultiTextCursor(MultiTextCursor(cursors)); + m_editor->setFocus(); + }); + + const QString &fileName = m_editor->textDocument()->filePath().toString(); + QMap fileToContentsMap; + fileToContentsMap[fileName] = m_editor->textDocument()->plainText(); + + FileListIterator *it = new FileListIterator({fileName}, + {const_cast( + m_editor->textDocument()->codec())}); + const QTextDocument::FindFlags findFlags2 = textDocumentFlagsForFindFlags(findFlags); + + if (findFlags & FindRegularExpression) + m_selectWatcher->setFuture(findInFilesRegExp(txt, it, findFlags2, fileToContentsMap)); + else + m_selectWatcher->setFuture(findInFiles(txt, it, findFlags2, fileToContentsMap)); +} + +void TextEditorWidgetFind::cancelCurrentSelectAll() +{ + if (m_selectWatcher) { + m_selectWatcher->disconnect(); + m_selectWatcher->cancel(); + m_selectWatcher->deleteLater(); + m_selectWatcher = nullptr; + } +} + TextEditorWidgetPrivate::TextEditorWidgetPrivate(TextEditorWidget *parent) : q(parent), m_marksVisible(false), @@ -841,8 +911,7 @@ TextEditorWidgetPrivate::TextEditorWidgetPrivate(TextEditorWidget *parent) m_autoCompleter(new AutoCompleter) { auto aggregate = new Aggregation::Aggregate; - m_find = new BaseTextFind(q); - m_find->setMultiTextCursorProvider([this]() { return m_cursors; }); + m_find = new TextEditorWidgetFind(q); connect(m_find, &BaseTextFind::highlightAllRequested, this, &TextEditorWidgetPrivate::highlightSearchResultsSlot); connect(m_find, &BaseTextFind::findScopeChanged, @@ -2307,6 +2376,7 @@ void TextEditorWidget::keyPressEvent(QKeyEvent *e) } else { d->m_maybeFakeTooltipEvent = false; if (e->key() == Qt::Key_Escape ) { + TextEditorWidgetFind::cancelCurrentSelectAll(); if (d->m_snippetOverlay->isVisible()) { e->accept(); d->m_snippetOverlay->accept(); @@ -7823,7 +7893,7 @@ void TextEditorWidget::setRefactorMarkers(const RefactorMarkers &markers) emit requestBlockUpdate(marker.cursor.block()); } -bool TextEditorWidget::inFindScope(const QTextCursor &cursor) +bool TextEditorWidget::inFindScope(const QTextCursor &cursor) const { return d->m_find->inScope(cursor); } diff --git a/src/plugins/texteditor/texteditor.h b/src/plugins/texteditor/texteditor.h index 2e702e20d28..7daed6de22c 100644 --- a/src/plugins/texteditor/texteditor.h +++ b/src/plugins/texteditor/texteditor.h @@ -564,6 +564,8 @@ public: void contextHelpItem(const Core::IContext::HelpCallback &callback); void setContextHelpItem(const Core::HelpItem &item); + Q_INVOKABLE bool inFindScope(const QTextCursor &cursor) const; + static TextEditorWidget *currentTextEditorWidget(); static TextEditorWidget *fromEditor(const Core::IEditor *editor); @@ -610,8 +612,6 @@ protected: virtual void slotCursorPositionChanged(); // Used in VcsBase virtual void slotCodeStyleSettingsChanged(const QVariant &); // Used in CppEditor - Q_INVOKABLE bool inFindScope(const QTextCursor &cursor); - private: Internal::TextEditorWidgetPrivate *d; friend class BaseTextEditor;