LanguageClient: support semanticTokens/refresh

Fixes: QTCREATORBUG-26499
Change-Id: Icd5879609bb856797fa223394357a1f15554d2cf
Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
This commit is contained in:
David Schulz
2021-11-02 14:21:50 +01:00
parent d3345320c7
commit 426fde79d6
9 changed files with 74 additions and 3 deletions

View File

@@ -529,6 +529,24 @@ public:
void clearSemanticTokens() { remove(semanticTokensKey); } 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<bool> refreshSupport() const { return optionalValue<bool>(refreshSupportKey); }
void setRefreshSupport(bool refreshSupport) { insert(refreshSupportKey, refreshSupport); }
void clearRefreshSupport() { remove(refreshSupportKey); }
};
class LANGUAGESERVERPROTOCOL_EXPORT WorkspaceClientCapabilities : public JsonObject class LANGUAGESERVERPROTOCOL_EXPORT WorkspaceClientCapabilities : public JsonObject
{ {
public: public:
@@ -601,6 +619,12 @@ public:
Utils::optional<bool> configuration() const { return optionalValue<bool>(configurationKey); } Utils::optional<bool> configuration() const { return optionalValue<bool>(configurationKey); }
void setConfiguration(bool configuration) { insert(configurationKey, configuration); } void setConfiguration(bool configuration) { insert(configurationKey, configuration); }
void clearConfiguration() { remove(configurationKey); } void clearConfiguration() { remove(configurationKey); }
Utils::optional<SemanticTokensWorkspaceClientCapabilities> semanticTokens() const
{ return optionalValue<SemanticTokensWorkspaceClientCapabilities>(semanticTokensKey); }
void setSemanticTokens(const SemanticTokensWorkspaceClientCapabilities &semanticTokens)
{ insert(semanticTokensKey, semanticTokens); }
void clearSemanticTokens() { remove(semanticTokensKey); }
}; };
class WindowClientClientCapabilities : public JsonObject class WindowClientClientCapabilities : public JsonObject

View File

@@ -171,6 +171,7 @@ constexpr char reasonKey[] = "reason";
constexpr char redKey[] = "red"; constexpr char redKey[] = "red";
constexpr char referencesKey[] = "references"; constexpr char referencesKey[] = "references";
constexpr char referencesProviderKey[] = "referencesProvider"; constexpr char referencesProviderKey[] = "referencesProvider";
constexpr char refreshSupportKey[] = "refreshSupport";
constexpr char registerOptionsKey[] = "registerOptions"; constexpr char registerOptionsKey[] = "registerOptions";
constexpr char registrationsKey[] = "registrations"; constexpr char registrationsKey[] = "registrations";
constexpr char removedKey[] = "removed"; constexpr char removedKey[] = "removed";

View File

@@ -156,4 +156,8 @@ SemanticTokensDeltaResult::SemanticTokensDeltaResult(const QJsonValue &value)
} }
} }
SemanticTokensRefreshRequest::SemanticTokensRefreshRequest()
: Request(methodName, nullptr)
{}
} // namespace LanguageServerProtocol } // namespace LanguageServerProtocol

View File

@@ -240,4 +240,13 @@ public:
constexpr static const char methodName[] = "textDocument/semanticTokens/range"; constexpr static const char methodName[] = "textDocument/semanticTokens/range";
}; };
class LANGUAGESERVERPROTOCOL_EXPORT SemanticTokensRefreshRequest
: public Request<std::nullptr_t, std::nullptr_t, std::nullptr_t>
{
public:
explicit SemanticTokensRefreshRequest();
using Request::Request;
constexpr static const char methodName[] = "workspace/semanticTokens/refresh";
};
} // namespace LanguageServerProtocol } // namespace LanguageServerProtocol

View File

@@ -674,7 +674,7 @@ void ClangdTestHighlighting::initTestCase()
m_results = results; m_results = results;
loop.quit(); loop.quit();
}; };
connect(client(), &ClangdClient::highlightingResultsReady, handler); connect(client(), &ClangdClient::highlightingResultsReady, &loop, handler);
timer.start(10000); timer.start(10000);
loop.exec(); loop.exec();
QVERIFY(timer.isActive()); QVERIFY(timer.isActive());

View File

@@ -170,6 +170,9 @@ static ClientCapabilities generateClientCapabilities()
workspaceCapabilities.setDidChangeConfiguration(allowDynamicRegistration); workspaceCapabilities.setDidChangeConfiguration(allowDynamicRegistration);
workspaceCapabilities.setExecuteCommand(allowDynamicRegistration); workspaceCapabilities.setExecuteCommand(allowDynamicRegistration);
workspaceCapabilities.setConfiguration(true); workspaceCapabilities.setConfiguration(true);
SemanticTokensWorkspaceClientCapabilities semanticTokensWorkspaceClientCapabilities;
semanticTokensWorkspaceClientCapabilities.setRefreshSupport(true);
workspaceCapabilities.setSemanticTokens(semanticTokensWorkspaceClientCapabilities);
capabilities.setWorkspace(workspaceCapabilities); capabilities.setWorkspace(workspaceCapabilities);
TextDocumentClientCapabilities documentCapabilities; TextDocumentClientCapabilities documentCapabilities;
@@ -1363,6 +1366,11 @@ void Client::handleMethod(const QString &method, const MessageId &id, const ICon
dynamic_cast<const WorkDoneProgressCreateRequest *>(content)->id()); dynamic_cast<const WorkDoneProgressCreateRequest *>(content)->id());
response.setResult(nullptr); response.setResult(nullptr);
sendContent(response); sendContent(response);
} else if (method == SemanticTokensRefreshRequest::methodName) {
m_tokenSupport.refresh();
Response<std::nullptr_t, JsonObject> response(id);
response.setResult(nullptr);
sendContent(response);
} else if (method == ProgressNotification::methodName) { } else if (method == ProgressNotification::methodName) {
if (Utils::optional<ProgressParams> params if (Utils::optional<ProgressParams> params
= dynamic_cast<const ProgressNotification *>(content)->params()) { = dynamic_cast<const ProgressNotification *>(content)->params()) {

View File

@@ -71,6 +71,7 @@ LanguageClientManager::LanguageClientManager(QObject *parent)
JsonRpcMessageHandler::registerMessageProvider<UnregisterCapabilityRequest>(); JsonRpcMessageHandler::registerMessageProvider<UnregisterCapabilityRequest>();
JsonRpcMessageHandler::registerMessageProvider<WorkDoneProgressCreateRequest>(); JsonRpcMessageHandler::registerMessageProvider<WorkDoneProgressCreateRequest>();
JsonRpcMessageHandler::registerMessageProvider<ProgressNotification>(); JsonRpcMessageHandler::registerMessageProvider<ProgressNotification>();
JsonRpcMessageHandler::registerMessageProvider<SemanticTokensRefreshRequest>();
connect(EditorManager::instance(), &EditorManager::editorOpened, connect(EditorManager::instance(), &EditorManager::editorOpened,
this, &LanguageClientManager::editorOpened); this, &LanguageClientManager::editorOpened);
connect(EditorManager::instance(), &EditorManager::documentOpened, connect(EditorManager::instance(), &EditorManager::documentOpened,

View File

@@ -183,6 +183,17 @@ SemanticTokenSupport::SemanticTokenSupport(Client *client)
&TextEditorSettings::fontSettingsChanged, &TextEditorSettings::fontSettingsChanged,
client, client,
[this]() { updateFormatHash(); }); [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) void SemanticTokenSupport::reloadSemanticTokens(TextDocument *textDocument)
@@ -224,8 +235,11 @@ void SemanticTokenSupport::updateSemanticTokens(TextDocument *textDocument)
const SemanticRequestTypes supportedRequests = supportedSemanticRequests(textDocument); const SemanticRequestTypes supportedRequests = supportedSemanticRequests(textDocument);
if (supportedRequests.testFlag(SemanticRequestType::FullDelta)) { if (supportedRequests.testFlag(SemanticRequestType::FullDelta)) {
const Utils::FilePath filePath = textDocument->filePath(); 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 (!previousResultId.isEmpty()) {
if (m_client->documentVersion(filePath) == versionedToken.version)
return;
SemanticTokensDeltaParams params; SemanticTokensDeltaParams params;
params.setTextDocument(TextDocumentIdentifier(DocumentUri::fromFilePath(filePath))); params.setTextDocument(TextDocumentIdentifier(DocumentUri::fromFilePath(filePath)));
params.setPreviousResultId(previousResultId); params.setPreviousResultId(previousResultId);
@@ -325,6 +339,12 @@ void SemanticTokenSupport::updateFormatHash()
rehighlight(); rehighlight();
} }
void SemanticTokenSupport::onCurrentEditorChanged(Core::IEditor *editor)
{
if (auto textEditor = qobject_cast<BaseTextEditor *>(editor))
updateSemanticTokens(textEditor->textDocument());
}
void SemanticTokenSupport::setTokenTypesMap(const QMap<QString, int> &tokenTypesMap) void SemanticTokenSupport::setTokenTypesMap(const QMap<QString, int> &tokenTypesMap)
{ {
m_tokenTypesMap = tokenTypesMap; m_tokenTypesMap = tokenTypesMap;

View File

@@ -36,6 +36,8 @@
#include <functional> #include <functional>
namespace Core { class IEditor; }
namespace LanguageClient { namespace LanguageClient {
class Client; class Client;
@@ -62,11 +64,12 @@ void applyHighlight(TextEditor::TextDocument *doc,
} // namespace SemanticHighligtingSupport } // namespace SemanticHighligtingSupport
class SemanticTokenSupport class SemanticTokenSupport : public QObject
{ {
public: public:
explicit SemanticTokenSupport(Client *client); explicit SemanticTokenSupport(Client *client);
void refresh();
void reloadSemanticTokens(TextEditor::TextDocument *doc); void reloadSemanticTokens(TextEditor::TextDocument *doc);
void updateSemanticTokens(TextEditor::TextDocument *doc); void updateSemanticTokens(TextEditor::TextDocument *doc);
void rehighlight(); void rehighlight();
@@ -94,6 +97,7 @@ private:
void highlight(const Utils::FilePath &filePath); void highlight(const Utils::FilePath &filePath);
void updateFormatHash(); void updateFormatHash();
void currentEditorChanged(); void currentEditorChanged();
void onCurrentEditorChanged(Core::IEditor *editor);
Client *m_client = nullptr; Client *m_client = nullptr;