diff --git a/src/plugins/coreplugin/locator/ilocatorfilter.h b/src/plugins/coreplugin/locator/ilocatorfilter.h index 848321daaf1..a6571d186e2 100644 --- a/src/plugins/coreplugin/locator/ilocatorfilter.h +++ b/src/plugins/coreplugin/locator/ilocatorfilter.h @@ -148,7 +148,8 @@ class LocatorMatcherPrivate; enum class MatcherType { AllSymbols, Classes, - Functions + Functions, + CurrentDocumentSymbols }; class CORE_EXPORT LocatorMatcher final : public QObject diff --git a/src/plugins/cppeditor/cppcurrentdocumentfilter.cpp b/src/plugins/cppeditor/cppcurrentdocumentfilter.cpp index 6777ca1c11d..e3f4bbb777b 100644 --- a/src/plugins/cppeditor/cppcurrentdocumentfilter.cpp +++ b/src/plugins/cppeditor/cppcurrentdocumentfilter.cpp @@ -52,7 +52,7 @@ void CppCurrentDocumentFilter::makeAuxiliary() } QList CppCurrentDocumentFilter::matchesFor( - QFutureInterface &future, const QString & entry) + QFutureInterface &future, const QString &entry) { const QRegularExpression regexp = createRegExp(entry); if (!regexp.isValid()) @@ -180,7 +180,7 @@ QList CppCurrentDocumentFilter::itemsOfCurrentDocument() const Snapshot snapshot = m_modelManager->snapshot(); if (const Document::Ptr thisDocument = snapshot.document(m_currentFileName)) { IndexItem::Ptr rootNode = search(thisDocument); - rootNode->visitAllChildren([&](const IndexItem::Ptr &info) -> IndexItem::VisitorResult { + rootNode->visitAllChildren([&](const IndexItem::Ptr &info) { m_itemsOfCurrentDoc.append(info); return IndexItem::Recurse; }); diff --git a/src/plugins/cppeditor/cpplocatorfilter.cpp b/src/plugins/cppeditor/cpplocatorfilter.cpp index 9895e23a26d..d23d81e2d51 100644 --- a/src/plugins/cppeditor/cpplocatorfilter.cpp +++ b/src/plugins/cppeditor/cpplocatorfilter.cpp @@ -7,13 +7,19 @@ #include "cppeditorplugin.h" #include "cppeditortr.h" #include "cpplocatordata.h" +#include "cppmodelmanager.h" + +#include +#include #include #include +#include #include using namespace Core; +using namespace CPlusPlus; using namespace Utils; namespace CppEditor { @@ -163,6 +169,152 @@ LocatorMatcherTask cppFunctionMatcher() return locatorMatcher(IndexItem::Function, converter); } +QList itemsOfCurrentDocument(const FilePath ¤tFileName) +{ + if (currentFileName.isEmpty()) + return {}; + + QList results; + const Snapshot snapshot = CppModelManager::instance()->snapshot(); + if (const Document::Ptr thisDocument = snapshot.document(currentFileName)) { + SearchSymbols search; + search.setSymbolsToSearchFor(SymbolSearcher::Declarations | + SymbolSearcher::Enums | + SymbolSearcher::Functions | + SymbolSearcher::Classes); + IndexItem::Ptr rootNode = search(thisDocument); + rootNode->visitAllChildren([&](const IndexItem::Ptr &info) { + results.append(info); + return IndexItem::Recurse; + }); + } + return results; +} + +LocatorFilterEntry::HighlightInfo highlightInfo(const QRegularExpressionMatch &match, + LocatorFilterEntry::HighlightInfo::DataType dataType) +{ + const FuzzyMatcher::HighlightingPositions positions = + FuzzyMatcher::highlightingPositions(match); + + return LocatorFilterEntry::HighlightInfo(positions.starts, positions.lengths, dataType); +} + +void matchesForCurrentDocument(QPromise &promise, + const QString &entry, const FilePath ¤tFileName) +{ + const QRegularExpression regexp = FuzzyMatcher::createRegExp(entry, Qt::CaseInsensitive, false); + if (!regexp.isValid()) + return; + + struct Entry + { + LocatorFilterEntry entry; + IndexItem::Ptr info; + }; + QList goodEntries; + QList betterEntries; + const QList items = itemsOfCurrentDocument(currentFileName); + for (const IndexItem::Ptr &info : items) { + if (promise.isCanceled()) + break; + + QString matchString = info->symbolName(); + if (info->type() == IndexItem::Declaration) + matchString = info->representDeclaration(); + else if (info->type() == IndexItem::Function) + matchString += info->symbolType(); + + QRegularExpressionMatch match = regexp.match(matchString); + if (match.hasMatch()) { + const bool betterMatch = match.capturedStart() == 0; + QString name = matchString; + QString extraInfo = info->symbolScope(); + if (info->type() == IndexItem::Function) { + if (info->unqualifiedNameAndScope(matchString, &name, &extraInfo)) { + name += info->symbolType(); + match = regexp.match(name); + } + } + + // TODO: Passing nullptr for filter -> accept won't work now. Replace with accept function. + LocatorFilterEntry filterEntry(nullptr, name); + filterEntry.displayIcon = info->icon(); + filterEntry.linkForEditor = {info->filePath(), info->line(), info->column()}; + filterEntry.extraInfo = extraInfo; + if (match.hasMatch()) { + filterEntry.highlightInfo = highlightInfo(match, + LocatorFilterEntry::HighlightInfo::DisplayName); + } else { + match = regexp.match(extraInfo); + filterEntry.highlightInfo = + highlightInfo(match, LocatorFilterEntry::HighlightInfo::ExtraInfo); + } + + if (betterMatch) + betterEntries.append({filterEntry, info}); + else + goodEntries.append({filterEntry, info}); + } + } + + // entries are unsorted by design! + betterEntries += goodEntries; + + QHash> possibleDuplicates; + for (const Entry &e : std::as_const(betterEntries)) + possibleDuplicates[e.info->scopedSymbolName() + e.info->symbolType()] << e; + for (auto it = possibleDuplicates.cbegin(); it != possibleDuplicates.cend(); ++it) { + const QList &duplicates = it.value(); + if (duplicates.size() == 1) + continue; + QList declarations; + QList definitions; + for (const Entry &candidate : duplicates) { + const IndexItem::Ptr info = candidate.info; + if (info->type() != IndexItem::Function) + break; + if (info->isFunctionDefinition()) + definitions << candidate; + else + declarations << candidate; + } + if (definitions.size() == 1 + && declarations.size() + definitions.size() == duplicates.size()) { + for (const Entry &decl : std::as_const(declarations)) { + Utils::erase(betterEntries, [&decl](const Entry &e) { + return e.info == decl.info; + }); + } + } + } + promise.addResult(Utils::transform(betterEntries, + [](const Entry &entry) { return entry.entry; })); +} + +FilePath currentFileName() +{ + IEditor *currentEditor = EditorManager::currentEditor(); + return currentEditor ? currentEditor->document()->filePath() : FilePath(); +} + +LocatorMatcherTask cppCurrentDocumentMatcher() +{ + using namespace Tasking; + + TreeStorage storage; + + const auto onSetup = [=](AsyncTask &async) { + async.setFutureSynchronizer(Internal::CppEditorPlugin::futureSynchronizer()); + async.setConcurrentCallData(matchesForCurrentDocument, storage->input, currentFileName()); + }; + const auto onDone = [storage](const AsyncTask &async) { + if (async.isResultAvailable()) + storage->output = async.result(); + }; + return {Async(onSetup, onDone, onDone), storage}; +} + CppLocatorFilter::CppLocatorFilter() { setId(Constants::LOCATOR_FILTER_ID); diff --git a/src/plugins/cppeditor/cpplocatorfilter.h b/src/plugins/cppeditor/cpplocatorfilter.h index 799bafc553a..33ebce05f41 100644 --- a/src/plugins/cppeditor/cpplocatorfilter.h +++ b/src/plugins/cppeditor/cpplocatorfilter.h @@ -13,6 +13,7 @@ namespace CppEditor { Core::LocatorMatcherTask CPPEDITOR_EXPORT cppAllSymbolsMatcher(); Core::LocatorMatcherTask CPPEDITOR_EXPORT cppClassMatcher(); Core::LocatorMatcherTask CPPEDITOR_EXPORT cppFunctionMatcher(); +Core::LocatorMatcherTask CPPEDITOR_EXPORT cppCurrentDocumentMatcher(); class CPPEDITOR_EXPORT CppLocatorFilter : public Core::ILocatorFilter { diff --git a/src/plugins/cppeditor/cpplocatorfilter_test.cpp b/src/plugins/cppeditor/cpplocatorfilter_test.cpp index fd68b4c1cfd..f81ab6c40f6 100644 --- a/src/plugins/cppeditor/cpplocatorfilter_test.cpp +++ b/src/plugins/cppeditor/cpplocatorfilter_test.cpp @@ -77,6 +77,7 @@ class CppCurrentDocumentFilterTestCase { public: CppCurrentDocumentFilterTestCase(const FilePath &filePath, + const QList &matchers, const ResultDataList &expectedResults, const QString &searchText = QString()) : BasicLocatorFilterTest(CppModelManager::instance()->currentDocumentFilter()) @@ -85,7 +86,16 @@ public: QVERIFY(succeededSoFar()); QVERIFY(!m_filePath.isEmpty()); - ResultDataList results = ResultData::fromFilterEntryList(matchesFor(searchText)); + const auto runMatcher = [this, matchers, searchText] { + CppCurrentDocumentFilterTestCase::doBeforeLocatorRun(); + const auto result = LocatorMatcher::runBlocking(matchers, searchText); + CppCurrentDocumentFilterTestCase::doAfterLocatorRun(); + return result; + }; + + const QList entries = matchers.isEmpty() ? matchesFor(searchText) + : runMatcher(); + ResultDataList results = ResultData::fromFilterEntryList(entries); if (debug) { ResultData::printFilterEntries(expectedResults, "Expected:"); ResultData::printFilterEntries(results, "Results:"); @@ -371,7 +381,10 @@ void LocatorFilterTest::testCurrentDocumentFilter() ResultData("main()", ""), }; - CppCurrentDocumentFilterTestCase(testFile, expectedResults); + CppCurrentDocumentFilterTestCase(testFile, {}, expectedResults); + CppCurrentDocumentFilterTestCase(testFile, + LocatorMatcher::matchers(MatcherType::CurrentDocumentSymbols), + expectedResults); } void LocatorFilterTest::testCurrentDocumentHighlighting() @@ -395,7 +408,10 @@ void LocatorFilterTest::testCurrentDocumentHighlighting() Tests::VerifyCleanCppModelManager verify; - CppCurrentDocumentFilterTestCase(testFile, expectedResults, searchText); + CppCurrentDocumentFilterTestCase(testFile, {}, expectedResults, searchText); + CppCurrentDocumentFilterTestCase(testFile, + LocatorMatcher::matchers(MatcherType::CurrentDocumentSymbols), + expectedResults, searchText); } void LocatorFilterTest::testFunctionsFilterHighlighting() diff --git a/src/plugins/cppeditor/cppmodelmanager.cpp b/src/plugins/cppeditor/cppmodelmanager.cpp index f4aacf74da1..17a702170e8 100644 --- a/src/plugins/cppeditor/cppmodelmanager.cpp +++ b/src/plugins/cppeditor/cppmodelmanager.cpp @@ -903,6 +903,8 @@ void CppModelManager::initCppTools() [] { return QList{CppEditor::cppClassMatcher()}; }); LocatorMatcher::addMatcherCreator(MatcherType::Functions, [] { return QList{CppEditor::cppFunctionMatcher()}; }); + LocatorMatcher::addMatcherCreator(MatcherType::CurrentDocumentSymbols, + [] { return QList{CppEditor::cppCurrentDocumentMatcher()}; }); } CppModelManager::CppModelManager()