From f0a1591035214bbcec41157e2e57071eeb019fe7 Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Wed, 7 Jun 2023 15:45:56 +0200 Subject: [PATCH] ClangCodeModel: Adapt to new "inactiveRegions" notification in clangd See https://reviews.llvm.org/D143974. Change-Id: Iff6cc39f7c567feee1953fde1ca96a9aefec75d4 Reviewed-by: David Schulz --- src/plugins/clangcodemodel/clangdclient.cpp | 16 +++++- .../clangdsemantichighlighting.cpp | 56 +++++++++++++++++-- .../clangdsemantichighlighting.h | 11 +++- .../clangcodemodel/test/clangdtests.cpp | 6 +- .../builtineditordocumentprocessor.cpp | 6 +- src/plugins/languageclient/client.cpp | 12 ++++ src/plugins/languageclient/client.h | 5 ++ 7 files changed, 101 insertions(+), 11 deletions(-) diff --git a/src/plugins/clangcodemodel/clangdclient.cpp b/src/plugins/clangcodemodel/clangdclient.cpp index b081c926bb8..87a059c15cd 100644 --- a/src/plugins/clangcodemodel/clangdclient.cpp +++ b/src/plugins/clangcodemodel/clangdclient.cpp @@ -230,14 +230,22 @@ public: void enableCodeActionsInline() {insert(u"codeActionsInline", true);} }; +class InactiveRegionsCapabilities : public JsonObject +{ +public: + using JsonObject::JsonObject; + void enableInactiveRegionsSupport() { insert(u"inactiveRegions", true); } +}; + class ClangdTextDocumentClientCapabilities : public TextDocumentClientCapabilities { public: using TextDocumentClientCapabilities::TextDocumentClientCapabilities; - void setPublishDiagnostics(const DiagnosticsCapabilities &caps) { insert(u"publishDiagnostics", caps); } + void setInactiveRegionsCapabilities(const InactiveRegionsCapabilities &caps) + { insert(u"inactiveRegionsCapabilities", caps); } }; static qint64 getRevision(const TextDocument *doc) @@ -428,6 +436,9 @@ ClangdClient::ClangdClient(Project *project, const Utils::FilePath &jsonDbDir, c diagnostics.enableCategorySupport(); diagnostics.enableCodeActionsInline(); clangdTextCaps.setPublishDiagnostics(diagnostics); + InactiveRegionsCapabilities inactiveRegions; + inactiveRegions.enableInactiveRegionsSupport(); + clangdTextCaps.setInactiveRegionsCapabilities(inactiveRegions); std::optional completionCaps = textCaps->completion(); if (completionCaps) @@ -456,6 +467,9 @@ ClangdClient::ClangdClient(Project *project, const Utils::FilePath &jsonDbDir, c const Utils::FilePath &filePath) { gatherHelpItemForTooltip(response, filePath); }); + registerCustomMethod(inactiveRegionsMethodName(), [this](const JsonRpcMessage &msg) { + handleInactiveRegions(this, msg); + }); connect(this, &Client::workDone, this, [this, p = QPointer(project)](const ProgressToken &token) { diff --git a/src/plugins/clangcodemodel/clangdsemantichighlighting.cpp b/src/plugins/clangcodemodel/clangdsemantichighlighting.cpp index 8f61703da16..f93d8302bd3 100644 --- a/src/plugins/clangcodemodel/clangdsemantichighlighting.cpp +++ b/src/plugins/clangcodemodel/clangdsemantichighlighting.cpp @@ -398,7 +398,10 @@ void doSemanticHighlighting( } }; auto results = QtConcurrent::blockingMapped(tokens, safeToResult); - const QList ifdefedOutBlocks = cleanupDisabledCode(results, &doc, docContents); + const bool handleInactiveCode = clangdMajorVersion < 17; + QList ifdefedOutBlocks; + if (handleInactiveCode) + ifdefedOutBlocks = cleanupDisabledCode(results, &doc, docContents); ExtraHighlightingResultsCollector(promise, results, filePath, ast, &doc, docContents, clangdVersion).collect(); Utils::erase(results, [](const HighlightingResult &res) { @@ -407,10 +410,12 @@ void doSemanticHighlighting( }); if (!promise.isCanceled()) { qCInfo(clangdLogHighlight) << "reporting" << results.size() << "highlighting results"; - QMetaObject::invokeMethod(textDocument, [textDocument, ifdefedOutBlocks, docRevision] { - if (textDocument && textDocument->document()->revision() == docRevision) - textDocument->setIfdefedOutBlocks(ifdefedOutBlocks); - }, Qt::QueuedConnection); + if (handleInactiveCode) { + QMetaObject::invokeMethod(textDocument, [textDocument, ifdefedOutBlocks, docRevision] { + if (textDocument && textDocument->document()->revision() == docRevision) + textDocument->setIfdefedOutBlocks(ifdefedOutBlocks); + }, Qt::QueuedConnection); + } QList virtualRanges; for (const HighlightingResult &r : results) { if (r.textStyles.mainStyle != C_VIRTUAL_METHOD) @@ -955,4 +960,45 @@ void ExtraHighlightingResultsCollector::visitNode(const ClangdAstNode &node) m_currentFileStatus = prevFileStatus; } +class InactiveRegionsParams : public JsonObject +{ +public: + using JsonObject::JsonObject; + + DocumentUri uri() const { return TextDocumentIdentifier(value(u"textDocument")).uri(); } + QList inactiveRegions() const { return array(u"regions"); } +}; + +class InactiveRegionsNotification : public Notification +{ +public: + explicit InactiveRegionsNotification(const InactiveRegionsParams ¶ms) + : Notification(inactiveRegionsMethodName(), params) {} + using Notification::Notification; +}; + +void handleInactiveRegions(LanguageClient::Client *client, const JsonRpcMessage &msg) +{ + const auto params = InactiveRegionsNotification(msg.toJsonObject()).params(); + if (!params) + return; + TextDocument * const doc = client->documentForFilePath( + params->uri().toFilePath(client->hostPathMapper())); + if (!doc) + return; + const QList inactiveRegions = params->inactiveRegions(); + QList ifdefedOutBlocks; + for (const Range &r : inactiveRegions) { + const int startPos = r.start().toPositionInDocument(doc->document()); + const int endPos = r.end().toPositionInDocument(doc->document()) + 1; + ifdefedOutBlocks.emplaceBack(startPos, endPos); + } + doc->setIfdefedOutBlocks(ifdefedOutBlocks); +} + +QString inactiveRegionsMethodName() +{ + return "textDocument/inactiveRegions"; +} + } // namespace ClangCodeModel::Internal diff --git a/src/plugins/clangcodemodel/clangdsemantichighlighting.h b/src/plugins/clangcodemodel/clangdsemantichighlighting.h index a7f667d459f..285ba2323e7 100644 --- a/src/plugins/clangcodemodel/clangdsemantichighlighting.h +++ b/src/plugins/clangcodemodel/clangdsemantichighlighting.h @@ -12,7 +12,11 @@ template class QPromise; QT_END_NAMESPACE -namespace LanguageClient { class ExpandedSemanticToken; } +namespace LanguageClient { +class Client; +class ExpandedSemanticToken; +} +namespace LanguageServerProtocol { class JsonRpcMessage; } namespace TextEditor { class HighlightingResult; class TextDocument; @@ -36,4 +40,9 @@ void doSemanticHighlighting( const TaskTimer &taskTimer ); + +QString inactiveRegionsMethodName(); +void handleInactiveRegions(LanguageClient::Client *client, + const LanguageServerProtocol::JsonRpcMessage &msg); + } // namespace ClangCodeModel::Internal diff --git a/src/plugins/clangcodemodel/test/clangdtests.cpp b/src/plugins/clangcodemodel/test/clangdtests.cpp index 3389baf051f..a102f1d3527 100644 --- a/src/plugins/clangcodemodel/test/clangdtests.cpp +++ b/src/plugins/clangcodemodel/test/clangdtests.cpp @@ -1225,8 +1225,10 @@ void ClangdTestHighlighting::test_data() << QList{C_PUNCTUATION} << int(CppEditor::SemanticHighlighter::AngleBracketClose); QTest::newRow("macro in struct") << 795 << 9 << 795 << 14 << QList{C_MACRO, C_DECLARATION} << 0; - QTest::newRow("#ifdef'ed out code") << 800 << 1 << 800 << 17 - << QList{C_DISABLED_CODE} << 0; + if (client()->versionNumber() < QVersionNumber(17)) { + QTest::newRow("#ifdef'ed out code") << 800 << 1 << 800 << 17 + << QList{C_DISABLED_CODE} << 0; + } QTest::newRow("static function call (object)") << 819 << 5 << 819 << 6 << QList{C_LOCAL} << 0; QTest::newRow("static function call (argument)") << 819 << 18 << 819 << 19 diff --git a/src/plugins/cppeditor/builtineditordocumentprocessor.cpp b/src/plugins/cppeditor/builtineditordocumentprocessor.cpp index 8e551fd15be..f4df1fcd0cd 100644 --- a/src/plugins/cppeditor/builtineditordocumentprocessor.cpp +++ b/src/plugins/cppeditor/builtineditordocumentprocessor.cpp @@ -254,8 +254,10 @@ void BuiltinEditorDocumentProcessor::onParserFinished(CPlusPlus::Document::Ptr d qCDebug(log) << "document parsed" << document->filePath() << document->editorRevision(); // Emit ifdefed out blocks - const auto ifdefoutBlocks = toTextEditorBlocks(document->skippedBlocks()); - emit ifdefedOutBlocksUpdated(revision(), ifdefoutBlocks); + if (!m_semanticHighlightingChecker || m_semanticHighlightingChecker()) { + const auto ifdefoutBlocks = toTextEditorBlocks(document->skippedBlocks()); + emit ifdefedOutBlocksUpdated(revision(), ifdefoutBlocks); + } // Store parser warnings m_codeWarnings = toTextEditorSelections(document->diagnosticMessages(), textDocument()); diff --git a/src/plugins/languageclient/client.cpp b/src/plugins/languageclient/client.cpp index 130961ebd1b..bb5a4594dc5 100644 --- a/src/plugins/languageclient/client.cpp +++ b/src/plugins/languageclient/client.cpp @@ -313,6 +313,7 @@ public: AssistProviders m_clientProviders; QMap m_resetAssistProvider; QHash m_highlightRequests; + QHash m_customHandlers; static const int MaxRestarts = 5; int m_restartsLeft = MaxRestarts; QTimer m_restartCountResetTimer; @@ -1921,6 +1922,12 @@ void ClientPrivate::handleMethod(const QString &method, const MessageId &id, con error.setMessage(QString("The client cannot handle the method '%1'.").arg(method)); response.setError(error); sendResponse(response); + } else { + const auto customHandler = m_customHandlers.constFind(method); + if (customHandler != m_customHandlers.constEnd()) { + (*customHandler)(message); + return; + } } // we got a request and handled it somewhere above but we missed to generate a response for it @@ -2136,6 +2143,11 @@ DocumentUri Client::hostPathToServerUri(const Utils::FilePath &path) const }); } +void Client::registerCustomMethod(const QString &method, const CustomMethodHandler &handler) +{ + d->m_customHandlers.insert(method, handler); +} + } // namespace LanguageClient #include diff --git a/src/plugins/languageclient/client.h b/src/plugins/languageclient/client.h index 2ddd155a9f1..9c934ea1611 100644 --- a/src/plugins/languageclient/client.h +++ b/src/plugins/languageclient/client.h @@ -169,6 +169,11 @@ public: Utils::FilePath serverUriToHostPath(const LanguageServerProtocol::DocumentUri &uri) const; LanguageServerProtocol::DocumentUri hostPathToServerUri(const Utils::FilePath &path) const; + // custom methods + using CustomMethodHandler = std::function; + void registerCustomMethod(const QString &method, const CustomMethodHandler &handler); + // logging enum class LogTarget { Console, Ui }; void setLogTarget(LogTarget target);