From eb41e98b00c7bb10e246f343d85b031c6e23c3d1 Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Mon, 17 Apr 2023 21:04:20 +0200 Subject: [PATCH] ClangCodeModel: Reimplement matchers() Change-Id: I2d541e24347aa47ec41f88cd7d18004dee7197cf Reviewed-by: Qt CI Bot Reviewed-by: Christian Kandeler Reviewed-by: --- .../clangcodemodel/clangdlocatorfilters.cpp | 129 +++++++++++++++++- .../clangcodemodel/clangdlocatorfilters.h | 1 + 2 files changed, 129 insertions(+), 1 deletion(-) diff --git a/src/plugins/clangcodemodel/clangdlocatorfilters.cpp b/src/plugins/clangcodemodel/clangdlocatorfilters.cpp index 097b9f495a8..1f4db567def 100644 --- a/src/plugins/clangcodemodel/clangdlocatorfilters.cpp +++ b/src/plugins/clangcodemodel/clangdlocatorfilters.cpp @@ -12,12 +12,15 @@ #include #include #include +#include +#include #include #include #include #include -#include #include +#include +#include #include @@ -25,6 +28,7 @@ using namespace Core; using namespace LanguageClient; using namespace LanguageServerProtocol; using namespace ProjectExplorer; +using namespace TextEditor; using namespace Utils; namespace ClangCodeModel { @@ -212,6 +216,7 @@ LocatorMatcherTasks ClangFunctionsFilter::matchers() ClangModelManagerSupport::clientsForOpenProjects(), MaxResultCount); } +// TODO: Remove this class, it's used only internally by ClangdCurrentDocumentFilter class LspCurrentDocumentFilter : public DocumentLocatorFilter { public: @@ -341,6 +346,128 @@ ClangdCurrentDocumentFilter::ClangdCurrentDocumentFilter() : d(new Private) ClangdCurrentDocumentFilter::~ClangdCurrentDocumentFilter() { delete d; } +static void filterCurrentResults(QPromise &promise, const LocatorStorage &storage, + const CurrentDocumentSymbolsData ¤tSymbolsData, + const QString &contents) +{ + Q_UNUSED(promise) + struct Entry + { + LocatorFilterEntry entry; + DocumentSymbol symbol; + }; + QList docEntries; + + const auto docSymbolModifier = [&docEntries](LocatorFilterEntry &entry, + const DocumentSymbol &info, + const LocatorFilterEntry &parent) { + entry.displayName = ClangdClient::displayNameFromDocumentSymbol( + static_cast(info.kind()), info.name(), + info.detail().value_or(QString())); + entry.extraInfo = parent.extraInfo; + if (!entry.extraInfo.isEmpty()) + entry.extraInfo.append("::"); + entry.extraInfo.append(parent.displayName); + + // TODO: Can we extend clangd to send visibility information? + docEntries.append({entry, info}); + return entry; + }; + // TODO: Pass promise into currentSymbols + const LocatorFilterEntries allMatches = LanguageClient::currentDocumentSymbols(storage.input(), + currentSymbolsData, docSymbolModifier); + if (docEntries.isEmpty()) { + storage.reportOutput(allMatches); + return; // SymbolInformation case + } + + QTC_CHECK(docEntries.size() == allMatches.size()); + QHash> possibleDuplicates; + for (const Entry &e : std::as_const(docEntries)) + possibleDuplicates[e.entry.displayName + e.entry.extraInfo] << e; + const QTextDocument doc(contents); + 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 DocumentSymbol symbol = candidate.symbol; + const SymbolKind kind = static_cast(symbol.kind()); + if (kind != SymbolKind::Class && kind != SymbolKind::Function) + break; + const Range range = symbol.range(); + const Range selectionRange = symbol.selectionRange(); + if (kind == SymbolKind::Class) { + if (range.end() == selectionRange.end()) + declarations << candidate; + else + definitions << candidate; + continue; + } + const int startPos = selectionRange.end().toPositionInDocument(&doc); + const int endPos = range.end().toPositionInDocument(&doc); + const QString functionBody = contents.mid(startPos, endPos - startPos); + + // Hacky, but I don't see anything better. + if (functionBody.contains('{') && functionBody.contains('}')) + 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(docEntries, [&decl](const Entry &e) { + return e.symbol == decl.symbol; + }); + } + } + } + storage.reportOutput(Utils::transform(docEntries, + [](const Entry &entry) { return entry.entry; })); +} + +LocatorMatcherTask currentDocumentMatcher() +{ + using namespace Tasking; + + TreeStorage storage; + TreeStorage resultStorage; + + const auto onQuerySetup = [=](CurrentDocumentSymbolsRequestTask &request) { + Q_UNUSED(request) + }; + const auto onQueryDone = [resultStorage](const CurrentDocumentSymbolsRequestTask &request) { + *resultStorage = request.currentDocumentSymbolsData(); + }; + + const auto onFilterSetup = [=](AsyncTask &async) { + async.setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer()); + async.setConcurrentCallData(filterCurrentResults, *storage, *resultStorage, + TextDocument::currentTextDocument()->plainText()); + }; + + const Group root { + Storage(resultStorage), + CurrentDocumentSymbolsRequest(onQuerySetup, onQueryDone), + Async(onFilterSetup) + }; + return {root, storage}; +} + +LocatorMatcherTasks ClangdCurrentDocumentFilter::matchers() +{ + const auto doc = TextDocument::currentTextDocument(); + QTC_ASSERT(doc, return {}); + if (const ClangdClient *client = ClangModelManagerSupport::clientForFile(doc->filePath()); + client && client->reachable()) { + return {currentDocumentMatcher()}; + } + return CppEditor::cppMatchers(MatcherType::CurrentDocumentSymbols); +} + void ClangdCurrentDocumentFilter::prepareSearch(const QString &entry) { const auto doc = TextEditor::TextDocument::currentTextDocument(); diff --git a/src/plugins/clangcodemodel/clangdlocatorfilters.h b/src/plugins/clangcodemodel/clangdlocatorfilters.h index 384edeab588..8074c95c9ac 100644 --- a/src/plugins/clangcodemodel/clangdlocatorfilters.h +++ b/src/plugins/clangcodemodel/clangdlocatorfilters.h @@ -54,6 +54,7 @@ public: ~ClangdCurrentDocumentFilter() override; private: + Core::LocatorMatcherTasks matchers() final; void prepareSearch(const QString &entry) override; QList matchesFor(QFutureInterface &future, const QString &entry) override;