#pragma once #include #include #include #include #include class ClientPreferences { public: std::shared_ptr workspace; lsTextDocumentClientCapabilities textDocument; ClientPreferences(lsClientCapabilities const& capabilities) { v3supported = capabilities.textDocument.has_value(); if (v3supported) { textDocument = capabilities.textDocument.value(); } if (capabilities.workspace) { workspace = std::make_shared(capabilities.workspace.value()); } } bool v3supported = false; bool isSignatureHelpSupported() { return v3supported && (textDocument.signatureHelp); } bool isWorkspaceDidChangeConfigurationSupported() const { return workspace && isDynamicRegistrationSupported(workspace->didChangeConfiguration); } bool isWorkspaceFoldersSupported() { return workspace != nullptr && isTrue(workspace->workspaceFolders); } bool isCompletionDynamicRegistered() { return v3supported && isDynamicRegistrationSupported(textDocument.completion); } bool isCompletionSnippetsSupported() { //@formatter:off if (!v3supported || !textDocument.completion) { return false; } auto const& completion = textDocument.completion.value(); if (completion.completionItem) { return isTrue(completion.completionItem.value().snippetSupport); } return false; } bool isV3Supported() { return v3supported; } bool isFormattingDynamicRegistrationSupported() { return v3supported && isDynamicRegistrationSupported(textDocument.formatting); } bool isRangeFormattingDynamicRegistrationSupported() { return v3supported && isDynamicRegistrationSupported(textDocument.rangeFormatting); } bool isOnTypeFormattingDynamicRegistrationSupported() { return v3supported && isDynamicRegistrationSupported(textDocument.onTypeFormatting); } bool isCodeLensDynamicRegistrationSupported() { return v3supported && isDynamicRegistrationSupported(textDocument.codeLens); } bool isSignatureHelpDynamicRegistrationSupported() { return v3supported && isDynamicRegistrationSupported(textDocument.signatureHelp); } template static bool isDynamicRegistrationSupported(optional& capability) { if (capability) { return (capability.value().dynamicRegistration.value()); } return false; } bool isTrue(optional const& value) { if (value) { return *value; } else { return false; } } bool isRenameDynamicRegistrationSupported() { return v3supported && isDynamicRegistrationSupported(textDocument.rename); } bool isExecuteCommandDynamicRegistrationSupported() { return v3supported && workspace != nullptr && isDynamicRegistrationSupported(workspace->executeCommand); } bool isWorkspaceSymbolDynamicRegistered() { return v3supported && workspace != nullptr && isDynamicRegistrationSupported(workspace->symbol); } bool isWorkspaceChangeWatchedFilesDynamicRegistered() { return v3supported && workspace != nullptr && isDynamicRegistrationSupported(workspace->didChangeWatchedFiles); } bool isDocumentSymbolDynamicRegistered() { return v3supported && isDynamicRegistrationSupported(textDocument.documentSymbol); } bool isCodeActionDynamicRegistered() { return v3supported && isDynamicRegistrationSupported(textDocument.codeAction); } bool isDefinitionDynamicRegistered() { return v3supported && isDynamicRegistrationSupported(textDocument.definition); } bool isTypeDefinitionDynamicRegistered() { return v3supported && isDynamicRegistrationSupported(textDocument.typeDefinition); } bool isHoverDynamicRegistered() { return v3supported && isDynamicRegistrationSupported(textDocument.hover); } bool isReferencesDynamicRegistered() { return v3supported && isDynamicRegistrationSupported(textDocument.references); } bool isDocumentHighlightDynamicRegistered() { return v3supported && isDynamicRegistrationSupported(textDocument.documentHighlight); } bool isDocumentLinkDynamicRegistered() { return v3supported && isDynamicRegistrationSupported(textDocument.documentLink); } bool isFoldgingRangeDynamicRegistered() { return v3supported && isDynamicRegistrationSupported(textDocument.foldingRange); } bool isInlayHintDynamicRegistered() { return v3supported && isDynamicRegistrationSupported(textDocument.inlayHint); } bool isImplementationDynamicRegistered() { return v3supported && isDynamicRegistrationSupported(textDocument.implementation); } bool isSelectionRangeDynamicRegistered() { return v3supported && isDynamicRegistrationSupported(textDocument.selectionRange); } bool isWillSaveRegistered() { return v3supported && isTrue(textDocument.synchronization.willSave); } bool isWillSaveWaitUntilRegistered() { return v3supported && isTrue(textDocument.synchronization.willSaveWaitUntil); } bool isWorkspaceApplyEditSupported() { return workspace != nullptr && isTrue(workspace->applyEdit); } bool isSupportsCompletionDocumentationMarkdown() { if (!v3supported || !textDocument.completion) { return false; } auto const& completion = textDocument.completion.value(); if (completion.completionItem) { auto& documentationFormat = completion.completionItem.value().documentationFormat; if (documentationFormat) { auto& data = documentationFormat.value(); for (auto& it : data) { if (it == "markdown") { return true; } } } } return false; } bool isWorkspaceEditResourceChangesSupported() { if (!workspace) { return false; } if (workspace->workspaceEdit) { return isTrue(workspace->workspaceEdit.value().resourceChanges); } return false; } static bool contains(std::vector const& v, std::string const& target) { for (auto& it : v) { if (it == target) { return true; } } return false; } bool isResourceOperationSupported() const { if (!workspace) { return false; } if (!workspace->workspaceEdit) { return false; } auto& it = (workspace->workspaceEdit.value()); if (!it.resourceOperations) { return false; } auto const& resourceOperations = it.resourceOperations.value(); return contains(resourceOperations, "create") && contains(resourceOperations, "rename") && contains(resourceOperations, "delete"); } /** * {@code true} if the client has explicitly set the * {@code textDocument.documentSymbol.hierarchicalDocumentSymbolSupport} to * {@code true} when initializing the LS. Otherwise, {@code false}. */ bool isHierarchicalDocumentSymbolSupported() { if (!v3supported || !textDocument.documentSymbol) { return false; } return isTrue(textDocument.documentSymbol.value().hierarchicalDocumentSymbolSupport); } bool isSemanticHighlightingSupported() { //@formatter:off if (!v3supported || !textDocument.semanticHighlightingCapabilities) { return false; } return isTrue(textDocument.semanticHighlightingCapabilities.value().semanticHighlighting); //@formatter:on } /** * {@code true} if the client has explicitly set the * {@code textDocument.codeAction.codeActionLiteralSupport.codeActionKind.valueSet} * value. Otherwise, {@code false}. */ bool isSupportedCodeActionKind(std::string const& kind) { if (!v3supported || !textDocument.codeAction) { return false; } //@formatter:off auto const& codeAction = textDocument.codeAction.value(); if (codeAction.codeActionLiteralSupport) { auto const& codeActionKind = codeAction.codeActionLiteralSupport.value().codeActionKind; if (codeActionKind) { auto const& valueSet = codeActionKind.value().valueSet; if (valueSet) { for (auto& k : valueSet.value()) { if (lsp::StartsWith(kind, k)) { return true; } } } } } return false; //@formatter:on } /** * {@code true} if the client has explicitly set the * {@code textDocument.publishDiagnostics.tagSupport} to * {@code true} when initializing the LS. Otherwise, {@code false}. */ bool isDiagnosticTagSupported() { if (!v3supported || !textDocument.publishDiagnostics) { return false; } auto const& publishDiagnostics = textDocument.publishDiagnostics.value(); if (publishDiagnostics.tagSupport) { isTagSupported(publishDiagnostics.tagSupport); } return false; } bool isTagSupported(optional, optional>> const& tagSupport) { if (tagSupport) { auto& v = tagSupport.value(); if (v.first) { return v.first.value(); } if (v.second) { return !v.second.value().valueSet.empty(); } } return false; } bool isCallHierarchyDynamicRegistered() { return v3supported && isDynamicRegistrationSupported(textDocument.callHierarchy); } };