diff --git a/src/libs/languageserverprotocol/clientcapabilities.h b/src/libs/languageserverprotocol/clientcapabilities.h index c0bdae40319..693fc231f26 100644 --- a/src/libs/languageserverprotocol/clientcapabilities.h +++ b/src/libs/languageserverprotocol/clientcapabilities.h @@ -529,6 +529,24 @@ public: void clearSemanticTokens() { remove(semanticTokensKey); } }; +class LANGUAGESERVERPROTOCOL_EXPORT SemanticTokensWorkspaceClientCapabilities : public JsonObject +{ +public: + using JsonObject::JsonObject; + /** + * Whether the client implementation supports a refresh request sent from + * the server to the client. + * + * Note that this event is global and will force the client to refresh all + * semantic tokens currently shown. It should be used with absolute care + * and is useful for situation where a server for example detect a project + * wide change that requires such a calculation. + */ + Utils::optional refreshSupport() const { return optionalValue(refreshSupportKey); } + void setRefreshSupport(bool refreshSupport) { insert(refreshSupportKey, refreshSupport); } + void clearRefreshSupport() { remove(refreshSupportKey); } +}; + class LANGUAGESERVERPROTOCOL_EXPORT WorkspaceClientCapabilities : public JsonObject { public: @@ -601,6 +619,12 @@ public: Utils::optional configuration() const { return optionalValue(configurationKey); } void setConfiguration(bool configuration) { insert(configurationKey, configuration); } void clearConfiguration() { remove(configurationKey); } + + Utils::optional semanticTokens() const + { return optionalValue(semanticTokensKey); } + void setSemanticTokens(const SemanticTokensWorkspaceClientCapabilities &semanticTokens) + { insert(semanticTokensKey, semanticTokens); } + void clearSemanticTokens() { remove(semanticTokensKey); } }; class WindowClientClientCapabilities : public JsonObject diff --git a/src/libs/languageserverprotocol/jsonkeys.h b/src/libs/languageserverprotocol/jsonkeys.h index fa0e5d63e01..332571fb2cf 100644 --- a/src/libs/languageserverprotocol/jsonkeys.h +++ b/src/libs/languageserverprotocol/jsonkeys.h @@ -171,6 +171,7 @@ constexpr char reasonKey[] = "reason"; constexpr char redKey[] = "red"; constexpr char referencesKey[] = "references"; constexpr char referencesProviderKey[] = "referencesProvider"; +constexpr char refreshSupportKey[] = "refreshSupport"; constexpr char registerOptionsKey[] = "registerOptions"; constexpr char registrationsKey[] = "registrations"; constexpr char removedKey[] = "removed"; diff --git a/src/libs/languageserverprotocol/semantictokens.cpp b/src/libs/languageserverprotocol/semantictokens.cpp index d827136a414..fb8cc4247be 100644 --- a/src/libs/languageserverprotocol/semantictokens.cpp +++ b/src/libs/languageserverprotocol/semantictokens.cpp @@ -156,4 +156,8 @@ SemanticTokensDeltaResult::SemanticTokensDeltaResult(const QJsonValue &value) } } +SemanticTokensRefreshRequest::SemanticTokensRefreshRequest() + : Request(methodName, nullptr) +{} + } // namespace LanguageServerProtocol diff --git a/src/libs/languageserverprotocol/semantictokens.h b/src/libs/languageserverprotocol/semantictokens.h index fccfd29dcb4..cc561ec670b 100644 --- a/src/libs/languageserverprotocol/semantictokens.h +++ b/src/libs/languageserverprotocol/semantictokens.h @@ -240,4 +240,13 @@ public: constexpr static const char methodName[] = "textDocument/semanticTokens/range"; }; +class LANGUAGESERVERPROTOCOL_EXPORT SemanticTokensRefreshRequest + : public Request +{ +public: + explicit SemanticTokensRefreshRequest(); + using Request::Request; + constexpr static const char methodName[] = "workspace/semanticTokens/refresh"; +}; + } // namespace LanguageServerProtocol diff --git a/src/plugins/clangcodemodel/test/clangdtests.cpp b/src/plugins/clangcodemodel/test/clangdtests.cpp index ff69d650f39..22fd7a20c66 100644 --- a/src/plugins/clangcodemodel/test/clangdtests.cpp +++ b/src/plugins/clangcodemodel/test/clangdtests.cpp @@ -674,7 +674,7 @@ void ClangdTestHighlighting::initTestCase() m_results = results; loop.quit(); }; - connect(client(), &ClangdClient::highlightingResultsReady, handler); + connect(client(), &ClangdClient::highlightingResultsReady, &loop, handler); timer.start(10000); loop.exec(); QVERIFY(timer.isActive()); diff --git a/src/plugins/languageclient/client.cpp b/src/plugins/languageclient/client.cpp index 29afb0e85a0..03b75680abd 100644 --- a/src/plugins/languageclient/client.cpp +++ b/src/plugins/languageclient/client.cpp @@ -170,6 +170,9 @@ static ClientCapabilities generateClientCapabilities() workspaceCapabilities.setDidChangeConfiguration(allowDynamicRegistration); workspaceCapabilities.setExecuteCommand(allowDynamicRegistration); workspaceCapabilities.setConfiguration(true); + SemanticTokensWorkspaceClientCapabilities semanticTokensWorkspaceClientCapabilities; + semanticTokensWorkspaceClientCapabilities.setRefreshSupport(true); + workspaceCapabilities.setSemanticTokens(semanticTokensWorkspaceClientCapabilities); capabilities.setWorkspace(workspaceCapabilities); TextDocumentClientCapabilities documentCapabilities; @@ -1363,6 +1366,11 @@ void Client::handleMethod(const QString &method, const MessageId &id, const ICon dynamic_cast(content)->id()); response.setResult(nullptr); sendContent(response); + } else if (method == SemanticTokensRefreshRequest::methodName) { + m_tokenSupport.refresh(); + Response response(id); + response.setResult(nullptr); + sendContent(response); } else if (method == ProgressNotification::methodName) { if (Utils::optional params = dynamic_cast(content)->params()) { diff --git a/src/plugins/languageclient/languageclientmanager.cpp b/src/plugins/languageclient/languageclientmanager.cpp index c05f8df459f..63a4f1c9da5 100644 --- a/src/plugins/languageclient/languageclientmanager.cpp +++ b/src/plugins/languageclient/languageclientmanager.cpp @@ -71,6 +71,7 @@ LanguageClientManager::LanguageClientManager(QObject *parent) JsonRpcMessageHandler::registerMessageProvider(); JsonRpcMessageHandler::registerMessageProvider(); JsonRpcMessageHandler::registerMessageProvider(); + JsonRpcMessageHandler::registerMessageProvider(); connect(EditorManager::instance(), &EditorManager::editorOpened, this, &LanguageClientManager::editorOpened); connect(EditorManager::instance(), &EditorManager::documentOpened, diff --git a/src/plugins/languageclient/semantichighlightsupport.cpp b/src/plugins/languageclient/semantichighlightsupport.cpp index 11365566bac..5132cd9326a 100644 --- a/src/plugins/languageclient/semantichighlightsupport.cpp +++ b/src/plugins/languageclient/semantichighlightsupport.cpp @@ -183,6 +183,17 @@ SemanticTokenSupport::SemanticTokenSupport(Client *client) &TextEditorSettings::fontSettingsChanged, client, [this]() { updateFormatHash(); }); + QObject::connect(Core::EditorManager::instance(), + &Core::EditorManager::currentEditorChanged, + this, + &SemanticTokenSupport::onCurrentEditorChanged); +} + +void SemanticTokenSupport::refresh() +{ + m_tokens.clear(); + for (Core::IEditor *editor : Core::EditorManager::visibleEditors()) + onCurrentEditorChanged(editor); } void SemanticTokenSupport::reloadSemanticTokens(TextDocument *textDocument) @@ -224,8 +235,11 @@ void SemanticTokenSupport::updateSemanticTokens(TextDocument *textDocument) const SemanticRequestTypes supportedRequests = supportedSemanticRequests(textDocument); if (supportedRequests.testFlag(SemanticRequestType::FullDelta)) { const Utils::FilePath filePath = textDocument->filePath(); - const QString &previousResultId = m_tokens.value(filePath).tokens.resultId().value_or(QString()); + const VersionedTokens versionedToken = m_tokens.value(filePath); + const QString &previousResultId = versionedToken.tokens.resultId().value_or(QString()); if (!previousResultId.isEmpty()) { + if (m_client->documentVersion(filePath) == versionedToken.version) + return; SemanticTokensDeltaParams params; params.setTextDocument(TextDocumentIdentifier(DocumentUri::fromFilePath(filePath))); params.setPreviousResultId(previousResultId); @@ -325,6 +339,12 @@ void SemanticTokenSupport::updateFormatHash() rehighlight(); } +void SemanticTokenSupport::onCurrentEditorChanged(Core::IEditor *editor) +{ + if (auto textEditor = qobject_cast(editor)) + updateSemanticTokens(textEditor->textDocument()); +} + void SemanticTokenSupport::setTokenTypesMap(const QMap &tokenTypesMap) { m_tokenTypesMap = tokenTypesMap; diff --git a/src/plugins/languageclient/semantichighlightsupport.h b/src/plugins/languageclient/semantichighlightsupport.h index 1a90101b3e8..084b3c1058a 100644 --- a/src/plugins/languageclient/semantichighlightsupport.h +++ b/src/plugins/languageclient/semantichighlightsupport.h @@ -36,6 +36,8 @@ #include +namespace Core { class IEditor; } + namespace LanguageClient { class Client; @@ -62,11 +64,12 @@ void applyHighlight(TextEditor::TextDocument *doc, } // namespace SemanticHighligtingSupport -class SemanticTokenSupport +class SemanticTokenSupport : public QObject { public: explicit SemanticTokenSupport(Client *client); + void refresh(); void reloadSemanticTokens(TextEditor::TextDocument *doc); void updateSemanticTokens(TextEditor::TextDocument *doc); void rehighlight(); @@ -94,6 +97,7 @@ private: void highlight(const Utils::FilePath &filePath); void updateFormatHash(); void currentEditorChanged(); + void onCurrentEditorChanged(Core::IEditor *editor); Client *m_client = nullptr;