diff --git a/src/libs/languageserverprotocol/jsonkeys.h b/src/libs/languageserverprotocol/jsonkeys.h index c45ded8c34b..98b0f488d94 100644 --- a/src/libs/languageserverprotocol/jsonkeys.h +++ b/src/libs/languageserverprotocol/jsonkeys.h @@ -215,6 +215,7 @@ constexpr char valueSetKey[] = "valueSet"; constexpr char versionKey[] = "version"; constexpr char willSaveKey[] = "willSave"; constexpr char willSaveWaitUntilKey[] = "willSaveWaitUntil"; +constexpr char workDoneProgressKey[] = "workDoneProgress"; constexpr char workspaceEditKey[] = "workspaceEdit"; constexpr char workspaceFoldersKey[] = "workspaceFolders"; constexpr char workspaceKey[] = "workspace"; diff --git a/src/libs/languageserverprotocol/servercapabilities.cpp b/src/libs/languageserverprotocol/servercapabilities.cpp index 29a95958c36..9c3d076f3c3 100644 --- a/src/libs/languageserverprotocol/servercapabilities.cpp +++ b/src/libs/languageserverprotocol/servercapabilities.cpp @@ -55,11 +55,29 @@ TextDocumentSyncKind ServerCapabilities::textDocumentSyncKindHelper() return TextDocumentSyncKind::None; } +Utils::optional> ServerCapabilities::hoverProvider() + const +{ + using RetType = Utils::variant; + const QJsonValue &provider = value(hoverProviderKey); + if (provider.isBool()) + return Utils::make_optional(RetType(provider.toBool())); + if (provider.isObject()) + return Utils::make_optional(RetType(WorkDoneProgressOptions(provider.toObject()))); + return Utils::nullopt; +} + +void ServerCapabilities::setHoverProvider( + const Utils::variant &hoverProvider) +{ + insertVariant(hoverProviderKey, hoverProvider); +} + Utils::optional> ServerCapabilities::typeDefinitionProvider() const { using RetType = Utils::variant; - QJsonValue provider = value(typeDefinitionProviderKey); + const QJsonValue &provider = value(typeDefinitionProviderKey); if (provider.isUndefined() || !(provider.isBool() || provider.isObject())) return Utils::nullopt; return Utils::make_optional(provider.isBool() ? RetType(provider.toBool()) @@ -77,7 +95,7 @@ Utils::optional> ServerCapabilities::implementationProvider() const { using RetType = Utils::variant; - QJsonValue provider = value(implementationProviderKey); + const QJsonValue &provider = value(implementationProviderKey); if (provider.isUndefined() || !(provider.isBool() || provider.isObject())) return Utils::nullopt; return Utils::make_optional(provider.isBool() ? RetType(provider.toBool()) @@ -90,9 +108,85 @@ void ServerCapabilities::setImplementationProvider( insertVariant(implementationProviderKey, implementationProvider); } +Utils::optional> +ServerCapabilities::referencesProvider() const +{ + using RetType = Utils::variant; + const QJsonValue &provider = value(referencesProviderKey); + if (provider.isBool()) + return Utils::make_optional(RetType(provider.toBool())); + if (provider.isObject()) + return Utils::make_optional(RetType(WorkDoneProgressOptions(provider.toObject()))); + return Utils::nullopt; +} + +void ServerCapabilities::setReferencesProvider( + const Utils::variant &referencesProvider) +{ + insertVariant(referencesProviderKey, + referencesProvider); +} + +Utils::optional> +ServerCapabilities::documentHighlightProvider() const +{ + using RetType = Utils::variant; + const QJsonValue &provider = value(documentHighlightProviderKey); + if (provider.isBool()) + return Utils::make_optional(RetType(provider.toBool())); + if (provider.isObject()) + return Utils::make_optional(RetType(WorkDoneProgressOptions(provider.toObject()))); + return Utils::nullopt; +} + +void ServerCapabilities::setDocumentHighlightProvider( + const Utils::variant &documentHighlightProvider) +{ + insertVariant(documentHighlightProviderKey, + documentHighlightProvider); +} + +Utils::optional> +ServerCapabilities::documentSymbolProvider() const +{ + using RetType = Utils::variant; + const QJsonValue &provider = value(documentSymbolProviderKey); + if (provider.isBool()) + return Utils::make_optional(RetType(provider.toBool())); + if (provider.isObject()) + return Utils::make_optional(RetType(WorkDoneProgressOptions(provider.toObject()))); + return Utils::nullopt; +} + +void ServerCapabilities::setDocumentSymbolProvider( + Utils::variant documentSymbolProvider) +{ + insertVariant(documentSymbolProviderKey, + documentSymbolProvider); +} + +Utils::optional> +ServerCapabilities::workspaceSymbolProvider() const +{ + using RetType = Utils::variant; + const QJsonValue &provider = value(workspaceSymbolProviderKey); + if (provider.isBool()) + return Utils::make_optional(RetType(provider.toBool())); + if (provider.isObject()) + return Utils::make_optional(RetType(WorkDoneProgressOptions(provider.toObject()))); + return Utils::nullopt; +} + +void ServerCapabilities::setWorkspaceSymbolProvider( + Utils::variant workspaceSymbolProvider) +{ + insertVariant(workspaceSymbolProviderKey, + workspaceSymbolProvider); +} + Utils::optional> ServerCapabilities::codeActionProvider() const { - QJsonValue provider = value(codeActionProviderKey); + const QJsonValue &provider = value(codeActionProviderKey); if (provider.isBool()) return Utils::make_optional(Utils::variant(provider.toBool())); if (provider.isObject()) { @@ -103,6 +197,44 @@ Utils::optional> ServerCapabilities::cod return Utils::nullopt; } +Utils::optional> +ServerCapabilities::documentFormattingProvider() const +{ + using RetType = Utils::variant; + const QJsonValue &provider = value(documentFormattingProviderKey); + if (provider.isBool()) + return Utils::make_optional(RetType(provider.toBool())); + if (provider.isObject()) + return Utils::make_optional(RetType(WorkDoneProgressOptions(provider.toObject()))); + return Utils::nullopt; +} + +void ServerCapabilities::setDocumentFormattingProvider( + const Utils::variant &documentFormattingProvider) +{ + insertVariant(documentFormattingProviderKey, + documentFormattingProvider); +} + +Utils::optional> +ServerCapabilities::documentRangeFormattingProvider() const +{ + using RetType = Utils::variant; + const QJsonValue &provider = value(documentRangeFormattingProviderKey); + if (provider.isBool()) + return Utils::make_optional(RetType(provider.toBool())); + if (provider.isObject()) + return Utils::make_optional(RetType(WorkDoneProgressOptions(provider.toObject()))); + return Utils::nullopt; +} + +void ServerCapabilities::setDocumentRangeFormattingProvider( + Utils::variant documentRangeFormattingProvider) +{ + insertVariant(documentRangeFormattingProviderKey, + documentRangeFormattingProvider); +} + Utils::optional> ServerCapabilities::renameProvider() const { using RetType = Utils::variant; @@ -164,7 +296,7 @@ Utils::optional > ServerCapabilities::WorkspaceServerCapabilities::WorkspaceFoldersCapabilities::changeNotifications() const { using RetType = Utils::variant; - QJsonValue provider = value(implementationProviderKey); + const QJsonValue &provider = value(implementationProviderKey); if (provider.isUndefined()) return Utils::nullopt; return Utils::make_optional(provider.isBool() ? RetType(provider.toBool()) @@ -247,4 +379,34 @@ bool ServerCapabilities::SemanticHighlightingServerCapabilities::isValid(ErrorHi }); } +bool ServerCapabilities::ExecuteCommandOptions::isValid(ErrorHierarchy *error) const +{ + return WorkDoneProgressOptions::isValid(error) && checkArray(error, commandsKey); +} + +bool ServerCapabilities::CompletionOptions::isValid(ErrorHierarchy *error) const +{ + return WorkDoneProgressOptions::isValid(error) + && checkOptionalArray(error, triggerCharactersKey) + && checkOptional(error, resolveProviderKey); +} + +bool ServerCapabilities::SignatureHelpOptions::isValid(ErrorHierarchy *error) const +{ + return WorkDoneProgressOptions::isValid(error) + && checkOptionalArray(error, triggerCharactersKey); +} + +bool CodeActionOptions::isValid(ErrorHierarchy *error) const +{ + return WorkDoneProgressOptions::isValid(error) + && checkArray(error, codeActionKindsKey); +} + +bool ServerCapabilities::RenameOptions::isValid(ErrorHierarchy *error) const +{ + return WorkDoneProgressOptions::isValid(error) + && checkOptional(error, prepareProviderKey); +} + } // namespace LanguageServerProtocol diff --git a/src/libs/languageserverprotocol/servercapabilities.h b/src/libs/languageserverprotocol/servercapabilities.h index 5e7d67ea917..f575017ad9b 100644 --- a/src/libs/languageserverprotocol/servercapabilities.h +++ b/src/libs/languageserverprotocol/servercapabilities.h @@ -29,6 +29,19 @@ namespace LanguageServerProtocol { +class LANGUAGESERVERPROTOCOL_EXPORT WorkDoneProgressOptions : public JsonObject +{ +public: + using JsonObject::JsonObject; + + Utils::optional workDoneProgress() const { return optionalValue(workDoneProgressKey); } + void setWorkDoneProgress(bool workDoneProgress) { insert(workDoneProgressKey, workDoneProgress); } + void clearWorkDoneProgress() { remove(workDoneProgressKey); } + + bool isValid(ErrorHierarchy *error) const override + { return checkOptional(error, workDoneProgressKey); } +}; + class LANGUAGESERVERPROTOCOL_EXPORT ResolveProviderOption : public JsonObject { public: @@ -120,17 +133,16 @@ enum class TextDocumentSyncKind Incremental = 2 }; -class LANGUAGESERVERPROTOCOL_EXPORT CodeActionOptions : public JsonObject +class LANGUAGESERVERPROTOCOL_EXPORT CodeActionOptions : public WorkDoneProgressOptions { public: - using JsonObject::JsonObject; + using WorkDoneProgressOptions::WorkDoneProgressOptions; QList codeActionKinds() const { return array(codeActionKindsKey); } void setCodeActionKinds(const QList &codeActionKinds) { insertArray(codeActionKindsKey, codeActionKinds); } - bool isValid(ErrorHierarchy *error) const override - { return checkArray(error, codeActionKindsKey); } + bool isValid(ErrorHierarchy *error) const override; }; class LANGUAGESERVERPROTOCOL_EXPORT ServerCapabilities : public JsonObject @@ -140,10 +152,10 @@ public: // Defines how the host (editor) should sync document changes to the language server. - class LANGUAGESERVERPROTOCOL_EXPORT CompletionOptions : public ResolveProviderOption + class LANGUAGESERVERPROTOCOL_EXPORT CompletionOptions : public WorkDoneProgressOptions { public: - using ResolveProviderOption::ResolveProviderOption; + using WorkDoneProgressOptions::WorkDoneProgressOptions; // The characters that trigger completion automatically. Utils::optional> triggerCharacters() const @@ -152,14 +164,17 @@ public: { insertArray(triggerCharactersKey, triggerCharacters); } void clearTriggerCharacters() { remove(triggerCharactersKey); } - bool isValid(ErrorHierarchy *error) const override - { return checkOptionalArray(error, triggerCharactersKey); } + Utils::optional resolveProvider() const { return optionalValue(resolveProviderKey); } + void setResolveProvider(bool resolveProvider) { insert(resolveProviderKey, resolveProvider); } + void clearResolveProvider() { remove(resolveProviderKey); } + + bool isValid(ErrorHierarchy *error) const override; }; - class LANGUAGESERVERPROTOCOL_EXPORT SignatureHelpOptions : public JsonObject + class LANGUAGESERVERPROTOCOL_EXPORT SignatureHelpOptions : public WorkDoneProgressOptions { public: - using JsonObject::JsonObject; + using WorkDoneProgressOptions::WorkDoneProgressOptions; // The characters that trigger signature help automatically. Utils::optional> triggerCharacters() const @@ -167,6 +182,8 @@ public: void setTriggerCharacters(const QList &triggerCharacters) { insertArray(triggerCharactersKey, triggerCharacters); } void clearTriggerCharacters() { remove(triggerCharactersKey); } + + bool isValid(ErrorHierarchy *error) const override; }; using CodeLensOptions = ResolveProviderOption; @@ -197,16 +214,15 @@ public: using DocumentLinkOptions = ResolveProviderOption; - class LANGUAGESERVERPROTOCOL_EXPORT ExecuteCommandOptions : public JsonObject + class LANGUAGESERVERPROTOCOL_EXPORT ExecuteCommandOptions : public WorkDoneProgressOptions { public: - using JsonObject::JsonObject; + using WorkDoneProgressOptions::WorkDoneProgressOptions; QList commands() const { return array(commandsKey); } void setCommands(const QList &commands) { insertArray(commandsKey, commands); } - bool isValid(ErrorHierarchy *error) const override - { return checkArray(error, commandsKey); } + bool isValid(ErrorHierarchy *error) const override; }; using ColorProviderOptions = JsonObject; @@ -244,8 +260,8 @@ public: TextDocumentSyncKind textDocumentSyncKindHelper(); // The server provides hover support. - Utils::optional hoverProvider() const { return optionalValue(hoverProviderKey); } - void setHoverProvider(bool hoverProvider) { insert(hoverProviderKey, hoverProvider); } + Utils::optional> hoverProvider() const; + void setHoverProvider(const Utils::variant &hoverProvider); void clearHoverProvider() { remove(hoverProviderKey); } // The server provides completion support. @@ -303,29 +319,24 @@ public: void clearImplementationProvider() { remove(implementationProviderKey); } // The server provides find references support. - Utils::optional referencesProvider() const { return optionalValue(referencesProviderKey); } - void setReferencesProvider(bool referenceProvider) { insert(referencesProviderKey, referenceProvider); } + Utils::optional> referencesProvider() const; + void setReferencesProvider(const Utils::variant &referencesProvider); void clearReferencesProvider() { remove(referencesProviderKey); } // The server provides document highlight support. - Utils::optional documentHighlightProvider() const - { return optionalValue(documentHighlightProviderKey); } - void setDocumentHighlightProvider(bool documentHighlightProvider) - { insert(documentHighlightProviderKey, documentHighlightProvider); } + Utils::optional> documentHighlightProvider() const; + void setDocumentHighlightProvider( + const Utils::variant &documentHighlightProvider); void clearDocumentHighlightProvider() { remove(documentHighlightProviderKey); } // The server provides document symbol support. - Utils::optional documentSymbolProvider() const - { return optionalValue(documentSymbolProviderKey); } - void setDocumentSymbolProvider(bool documentSymbolProvider) - { insert(documentSymbolProviderKey, documentSymbolProvider); } + Utils::optional> documentSymbolProvider() const; + void setDocumentSymbolProvider(Utils::variant documentSymbolProvider); void clearDocumentSymbolProvider() { remove(documentSymbolProviderKey); } // The server provides workspace symbol support. - Utils::optional workspaceSymbolProvider() const - { return optionalValue(workspaceSymbolProviderKey); } - void setWorkspaceSymbolProvider(bool workspaceSymbolProvider) - { insert(workspaceSymbolProviderKey, workspaceSymbolProvider); } + Utils::optional> workspaceSymbolProvider() const; + void setWorkspaceSymbolProvider(Utils::variant workspaceSymbolProvider); void clearWorkspaceSymbolProvider() { remove(workspaceSymbolProviderKey); } // The server provides code actions. @@ -344,31 +355,27 @@ public: void clearCodeLensProvider() { remove(codeLensProviderKey); } // The server provides document formatting. - Utils::optional documentFormattingProvider() const - { return optionalValue(documentFormattingProviderKey); } - void setDocumentFormattingProvider(bool documentFormattingProvider) - { insert(documentFormattingProviderKey, documentFormattingProvider); } + Utils::optional> documentFormattingProvider() const; + void setDocumentFormattingProvider( + const Utils::variant &documentFormattingProvider); void clearDocumentFormattingProvider() { remove(documentFormattingProviderKey); } // The server provides document formatting on typing. - Utils::optional documentRangeFormattingProvider() const - { return optionalValue(documentRangeFormattingProviderKey); } - void setDocumentRangeFormattingProvider(bool documentRangeFormattingProvider) - { insert(documentRangeFormattingProviderKey, documentRangeFormattingProvider); } + Utils::optional> documentRangeFormattingProvider() const; + void setDocumentRangeFormattingProvider(Utils::variant documentRangeFormattingProvider); void clearDocumentRangeFormattingProvider() { remove(documentRangeFormattingProviderKey); } - class RenameOptions : public JsonObject + class LANGUAGESERVERPROTOCOL_EXPORT RenameOptions : public WorkDoneProgressOptions { public: - using JsonObject::JsonObject; + using WorkDoneProgressOptions::WorkDoneProgressOptions; // Renames should be checked and tested before being executed. Utils::optional prepareProvider() const { return optionalValue(prepareProviderKey); } void setPrepareProvider(bool prepareProvider) { insert(prepareProviderKey, prepareProvider); } void clearPrepareProvider() { remove(prepareProviderKey); } - bool isValid(ErrorHierarchy * error) const override - { return checkOptional(error, prepareProviderKey); } + bool isValid(ErrorHierarchy * error) const override; }; // The server provides rename support. diff --git a/src/plugins/languageclient/client.cpp b/src/plugins/languageclient/client.cpp index fbabc51a1cf..e4905eb1069 100644 --- a/src/plugins/languageclient/client.cpp +++ b/src/plugins/languageclient/client.cpp @@ -625,8 +625,13 @@ void Client::cursorPositionChanged(TextEditor::TextEditorWidget *widget) m_dynamicCapabilities.option(DocumentHighlightsRequest::methodName)); if (!option.filterApplies(widget->textDocument()->filePath())) return; - } else if (!m_serverCapabilities.documentHighlightProvider().value_or(false)) { - return; + } else { + Utils::optional> provider + = m_serverCapabilities.documentHighlightProvider(); + if (!provider.has_value()) + return; + if (Utils::holds_alternative(*provider) && !Utils::get(*provider)) + return; } auto runningRequest = m_highlightRequests.find(uri); @@ -1233,9 +1238,13 @@ void Client::initializeCallback(const InitializeRequest::Response &initResponse) qCDebug(LOGLSPCLIENT) << "language server " << m_displayName << " initialized"; m_state = Initialized; sendContent(InitializeNotification(InitializedParams())); - if (m_dynamicCapabilities.isRegistered(DocumentSymbolsRequest::methodName) - .value_or(capabilities().documentSymbolProvider().value_or(false))) { - TextEditor::IOutlineWidgetFactory::updateOutline(); + Utils::optional> documentSymbolProvider + = capabilities().documentSymbolProvider(); + if (documentSymbolProvider.has_value()) { + if (!Utils::holds_alternative(*documentSymbolProvider) + || Utils::get(*documentSymbolProvider)) { + TextEditor::IOutlineWidgetFactory::updateOutline(); + } } for (auto it = m_openedDocument.cbegin(); it != m_openedDocument.cend(); ++it) diff --git a/src/plugins/languageclient/languageclientformatter.cpp b/src/plugins/languageclient/languageclientformatter.cpp index fa83f85979c..31883d5fef0 100644 --- a/src/plugins/languageclient/languageclientformatter.cpp +++ b/src/plugins/languageclient/languageclientformatter.cpp @@ -84,8 +84,13 @@ QFutureWatcher *LanguageClientFormatter::format( && !option.filterApplies(filePath, Utils::mimeTypeForName(m_document->mimeType()))) { return nullptr; } - } else if (!m_client->capabilities().documentRangeFormattingProvider().value_or(false)) { - return nullptr; + } else { + const Utils::optional> &provider + = m_client->capabilities().documentRangeFormattingProvider(); + if (!provider.has_value()) + return nullptr; + if (Utils::holds_alternative(*provider) && !Utils::get(*provider)) + return nullptr; } DocumentRangeFormattingParams params; const DocumentUri uri = DocumentUri::fromFilePath(filePath); diff --git a/src/plugins/languageclient/languageclienthoverhandler.cpp b/src/plugins/languageclient/languageclienthoverhandler.cpp index cf0cf596980..3ef437ac793 100644 --- a/src/plugins/languageclient/languageclienthoverhandler.cpp +++ b/src/plugins/languageclient/languageclienthoverhandler.cpp @@ -74,7 +74,11 @@ void HoverHandler::identifyMatch(TextEditor::TextEditorWidget *editorWidget, return; } - bool sendMessage = m_client->capabilities().hoverProvider().value_or(false); + const Utils::optional> &provider + = m_client->capabilities().hoverProvider(); + bool sendMessage = provider.has_value(); + if (sendMessage && Utils::holds_alternative(*provider)) + sendMessage = Utils::get(*provider); if (Utils::optional registered = m_client->dynamicCapabilities().isRegistered( HoverRequest::methodName)) { sendMessage = registered.value(); diff --git a/src/plugins/languageclient/languageclientoutline.cpp b/src/plugins/languageclient/languageclientoutline.cpp index 779867d54aa..622b0100f08 100644 --- a/src/plugins/languageclient/languageclientoutline.cpp +++ b/src/plugins/languageclient/languageclientoutline.cpp @@ -234,7 +234,13 @@ bool LanguageClientOutlineWidgetFactory::clientSupportsDocumentSymbols( return !options.isValid(nullptr) || options.filterApplies(doc->filePath(), Utils::mimeTypeForName(doc->mimeType())); } - return client->capabilities().documentSymbolProvider().value_or(false); + const Utils::optional> &provider + = client->capabilities().documentSymbolProvider(); + if (!provider.has_value()) + return false; + if (Utils::holds_alternative(*provider)) + return Utils::get(*provider); + return true; } bool LanguageClientOutlineWidgetFactory::supportsEditor(Core::IEditor *editor) const diff --git a/src/plugins/languageclient/languageclientsymbolsupport.cpp b/src/plugins/languageclient/languageclientsymbolsupport.cpp index 8dc5f7bd516..97951252580 100644 --- a/src/plugins/languageclient/languageclientsymbolsupport.cpp +++ b/src/plugins/languageclient/languageclientsymbolsupport.cpp @@ -46,7 +46,7 @@ template static void sendTextDocumentPositionParamsRequest(Client *client, const Request &request, const DynamicCapabilities &dynamicCapabilities, - const Utils::optional &serverCapability) + const ServerCapabilities &serverCapability) { if (!request.isValid(nullptr)) return; @@ -62,7 +62,11 @@ static void sendTextDocumentPositionParamsRequest(Client *client, else sendMessage = supportedFile; } else { - sendMessage = serverCapability.value_or(sendMessage) && supportedFile; + const Utils::optional> &provider + = serverCapability.referencesProvider(); + sendMessage = provider.has_value(); + if (sendMessage && Utils::holds_alternative(*provider)) + sendMessage = Utils::get(*provider); } if (sendMessage) client->sendContent(request); @@ -121,7 +125,7 @@ void SymbolSupport::findLinkAt(TextEditor::TextDocument *document, sendTextDocumentPositionParamsRequest(m_client, request, m_client->dynamicCapabilities(), - m_client->capabilities().referencesProvider()); + m_client->capabilities()); } @@ -230,7 +234,7 @@ void SymbolSupport::findUsages(TextEditor::TextDocument *document, const QTextCu sendTextDocumentPositionParamsRequest(m_client, request, m_client->dynamicCapabilities(), - m_client->capabilities().referencesProvider()); + m_client->capabilities()); } static bool supportsRename(Client *client, diff --git a/src/plugins/languageclient/locatorfilter.cpp b/src/plugins/languageclient/locatorfilter.cpp index 7a02abd3ed2..c26faf90af2 100644 --- a/src/plugins/languageclient/locatorfilter.cpp +++ b/src/plugins/languageclient/locatorfilter.cpp @@ -221,15 +221,19 @@ void WorkspaceLocatorFilter::prepareSearch(const QString &entry) QMutexLocker locker(&m_mutex); for (auto client : Utils::filtered(LanguageClientManager::clients(), &Client::reachable)) { - if (client->capabilities().workspaceSymbolProvider().value_or(false)) { - WorkspaceSymbolRequest request(params); - request.setResponseCallback( - [this, client](const WorkspaceSymbolRequest::Response &response) { - handleResponse(client, response); - }); - m_pendingRequests[client] = request.id(); - client->sendContent(request); - } + Utils::optional> capability + = client->capabilities().workspaceSymbolProvider(); + if (!capability.has_value()) + continue; + if (Utils::holds_alternative(*capability) && !Utils::get(*capability)) + continue; + WorkspaceSymbolRequest request(params); + request.setResponseCallback( + [this, client](const WorkspaceSymbolRequest::Response &response) { + handleResponse(client, response); + }); + m_pendingRequests[client] = request.id(); + client->sendContent(request); } }