[clang-tools-extra] 0b55ecc - [clangd] Allow modules to bind LSP methods/notifications/commands
Sam McCall via cfe-commits
cfe-commits at lists.llvm.org
Mon Feb 15 02:00:29 PST 2021
Author: Sam McCall
Date: 2021-02-15T11:00:14+01:00
New Revision: 0b55ecce45d7cc79b614bcb91cd070ab257227fc
URL: https://github.com/llvm/llvm-project/commit/0b55ecce45d7cc79b614bcb91cd070ab257227fc
DIFF: https://github.com/llvm/llvm-project/commit/0b55ecce45d7cc79b614bcb91cd070ab257227fc.diff
LOG: [clangd] Allow modules to bind LSP methods/notifications/commands
Differential Revision: https://reviews.llvm.org/D96625
Added:
Modified:
clang-tools-extra/clangd/ClangdLSPServer.cpp
clang-tools-extra/clangd/Module.h
clang-tools-extra/clangd/unittests/ClangdLSPServerTests.cpp
Removed:
################################################################################
diff --git a/clang-tools-extra/clangd/ClangdLSPServer.cpp b/clang-tools-extra/clangd/ClangdLSPServer.cpp
index 52939c0dc035..0f5fe7c3528c 100644
--- a/clang-tools-extra/clangd/ClangdLSPServer.cpp
+++ b/clang-tools-extra/clangd/ClangdLSPServer.cpp
@@ -524,98 +524,106 @@ void ClangdLSPServer::onInitialize(const InitializeParams &Params,
BackgroundIndexProgressState = BackgroundIndexProgress::Empty;
BackgroundIndexSkipCreate = Params.capabilities.ImplicitProgressCreation;
+ llvm::json::Object ServerCaps{
+ {"textDocumentSync",
+ llvm::json::Object{
+ {"openClose", true},
+ {"change", (int)TextDocumentSyncKind::Incremental},
+ {"save", true},
+ }},
+ {"documentFormattingProvider", true},
+ {"documentRangeFormattingProvider", true},
+ {"documentOnTypeFormattingProvider",
+ llvm::json::Object{
+ {"firstTriggerCharacter", "\n"},
+ {"moreTriggerCharacter", {}},
+ }},
+ {"completionProvider",
+ llvm::json::Object{
+ {"allCommitCharacters",
+ {" ", "\t", "(", ")", "[", "]", "{", "}", "<",
+ ">", ":", ";", ",", "+", "-", "/", "*", "%",
+ "^", "&", "#", "?", ".", "=", "\"", "'", "|"}},
+ {"resolveProvider", false},
+ // We do extra checks, e.g. that > is part of ->.
+ {"triggerCharacters", {".", "<", ">", ":", "\"", "/"}},
+ }},
+ {"semanticTokensProvider",
+ llvm::json::Object{
+ {"full", llvm::json::Object{{"delta", true}}},
+ {"range", false},
+ {"legend",
+ llvm::json::Object{{"tokenTypes", semanticTokenTypes()},
+ {"tokenModifiers", semanticTokenModifiers()}}},
+ }},
+ {"signatureHelpProvider",
+ llvm::json::Object{
+ {"triggerCharacters", {"(", ","}},
+ }},
+ {"declarationProvider", true},
+ {"definitionProvider", true},
+ {"implementationProvider", true},
+ {"documentHighlightProvider", true},
+ {"documentLinkProvider",
+ llvm::json::Object{
+ {"resolveProvider", false},
+ }},
+ {"hoverProvider", true},
+ {"selectionRangeProvider", true},
+ {"documentSymbolProvider", true},
+ {"workspaceSymbolProvider", true},
+ {"referencesProvider", true},
+ {"astProvider", true}, // clangd extension
+ {"typeHierarchyProvider", true},
+ {"memoryUsageProvider", true}, // clangd extension
+ {"compilationDatabase", // clangd extension
+ llvm::json::Object{{"automaticReload", true}}},
+ {"callHierarchyProvider", true},
+ };
+
+ {
+ LSPBinder Binder(Handlers);
+ if (Opts.Modules)
+ for (auto &Mod : *Opts.Modules)
+ Mod.initializeLSP(Binder, Params.capabilities, ServerCaps);
+ }
+
// Per LSP, renameProvider can be either boolean or RenameOptions.
// RenameOptions will be specified if the client states it supports prepare.
- llvm::json::Value RenameProvider =
- llvm::json::Object{{"prepareProvider", true}};
- if (!Params.capabilities.RenamePrepareSupport) // Only boolean allowed per LSP
- RenameProvider = true;
+ ServerCaps["renameProvider"] =
+ Params.capabilities.RenamePrepareSupport
+ ? llvm::json::Object{{"prepareProvider", true}}
+ : llvm::json::Value(true);
- // Per LSP, codeActionProvide can be either boolean or CodeActionOptions.
+ // Per LSP, codeActionProvider can be either boolean or CodeActionOptions.
// CodeActionOptions is only valid if the client supports action literal
// via textDocument.codeAction.codeActionLiteralSupport.
llvm::json::Value CodeActionProvider = true;
- if (Params.capabilities.CodeActionStructure)
- CodeActionProvider = llvm::json::Object{
- {"codeActionKinds",
- {CodeAction::QUICKFIX_KIND, CodeAction::REFACTOR_KIND,
- CodeAction::INFO_KIND}}};
+ ServerCaps["codeActionProvider"] =
+ Params.capabilities.CodeActionStructure
+ ? llvm::json::Object{{"codeActionKinds",
+ {CodeAction::QUICKFIX_KIND,
+ CodeAction::REFACTOR_KIND,
+ CodeAction::INFO_KIND}}}
+ : llvm::json::Value(true);
+
+ if (Opts.FoldingRanges)
+ ServerCaps["foldingRangeProvider"] = true;
std::vector<llvm::StringRef> Commands;
for (llvm::StringRef Command : Handlers.CommandHandlers.keys())
Commands.push_back(Command);
llvm::sort(Commands);
+ ServerCaps["executeCommandProvider"] =
+ llvm::json::Object{{"commands", Commands}};
llvm::json::Object Result{
{{"serverInfo",
llvm::json::Object{{"name", "clangd"},
{"version", getClangToolFullVersion("clangd")}}},
- {"capabilities",
- llvm::json::Object{
- {"textDocumentSync",
- llvm::json::Object{
- {"openClose", true},
- {"change", (int)TextDocumentSyncKind::Incremental},
- {"save", true},
- }},
- {"documentFormattingProvider", true},
- {"documentRangeFormattingProvider", true},
- {"documentOnTypeFormattingProvider",
- llvm::json::Object{
- {"firstTriggerCharacter", "\n"},
- {"moreTriggerCharacter", {}},
- }},
- {"codeActionProvider", std::move(CodeActionProvider)},
- {"completionProvider",
- llvm::json::Object{
- {"allCommitCharacters",
- {" ", "\t", "(", ")", "[", "]", "{", "}", "<",
- ">", ":", ";", ",", "+", "-", "/", "*", "%",
- "^", "&", "#", "?", ".", "=", "\"", "'", "|"}},
- {"resolveProvider", false},
- // We do extra checks, e.g. that > is part of ->.
- {"triggerCharacters", {".", "<", ">", ":", "\"", "/"}},
- }},
- {"semanticTokensProvider",
- llvm::json::Object{
- {"full", llvm::json::Object{{"delta", true}}},
- {"range", false},
- {"legend",
- llvm::json::Object{
- {"tokenTypes", semanticTokenTypes()},
- {"tokenModifiers", semanticTokenModifiers()}}},
- }},
- {"signatureHelpProvider",
- llvm::json::Object{
- {"triggerCharacters", {"(", ","}},
- }},
- {"declarationProvider", true},
- {"definitionProvider", true},
- {"implementationProvider", true},
- {"documentHighlightProvider", true},
- {"documentLinkProvider",
- llvm::json::Object{
- {"resolveProvider", false},
- }},
- {"hoverProvider", true},
- {"renameProvider", std::move(RenameProvider)},
- {"selectionRangeProvider", true},
- {"documentSymbolProvider", true},
- {"workspaceSymbolProvider", true},
- {"referencesProvider", true},
- {"astProvider", true}, // clangd extension
- {"executeCommandProvider",
- llvm::json::Object{{"commands", Commands}}},
- {"typeHierarchyProvider", true},
- {"memoryUsageProvider", true}, // clangd extension
- {"compilationDatabase", // clangd extension
- llvm::json::Object{{"automaticReload", true}}},
- {"callHierarchyProvider", true},
- }}}};
+ {"capabilities", std::move(ServerCaps)}}};
if (Opts.Encoding)
Result["offsetEncoding"] = *Opts.Encoding;
- if (Opts.FoldingRanges)
- Result.getObject("capabilities")->insert({"foldingRangeProvider", true});
Reply(std::move(Result));
}
diff --git a/clang-tools-extra/clangd/Module.h b/clang-tools-extra/clangd/Module.h
index d32618238867..3f5d5f0b29b3 100644
--- a/clang-tools-extra/clangd/Module.h
+++ b/clang-tools-extra/clangd/Module.h
@@ -1,7 +1,10 @@
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_MODULE_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_MODULE_H
+#include "LSPBinder.h"
+#include "Protocol.h"
#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/JSON.h"
#include <memory>
#include <vector>
@@ -10,14 +13,11 @@ namespace clangd {
/// A Module contributes a vertical feature to clangd.
///
-/// FIXME: Extend this with LSP bindings to support reading/updating
-/// capabilities and implementing LSP endpoints.
+/// FIXME: Extend this to support outgoing LSP calls.
///
/// The lifetime of a module is roughly:
/// - modules are created before the LSP server, in ClangdMain.cpp
/// - these modules are then passed to ClangdLSPServer and ClangdServer
-/// FIXME: LSP bindings should be registered at ClangdLSPServer
-/// initialization.
/// - module hooks can be called at this point.
/// FIXME: We should make some server facilities like TUScheduler and index
/// available to those modules after ClangdServer is initalized.
@@ -30,6 +30,17 @@ namespace clangd {
class Module {
public:
virtual ~Module() = default;
+
+ /// Called by the server to connect this module to LSP.
+ /// The module should register the methods/notifications/commands it handles,
+ /// and update the server capabilities to advertise them.
+ ///
+ /// This is only called if the module is running in ClangdLSPServer!
+ /// Modules with a public interface should satisfy it without LSP bindings.
+ // FIXME: ClientCaps should be a raw json::Object here.
+ virtual void initializeLSP(LSPBinder &Bind,
+ const ClientCapabilities &ClientCaps,
+ llvm::json::Object &ServerCaps) {}
};
class ModuleSet {
diff --git a/clang-tools-extra/clangd/unittests/ClangdLSPServerTests.cpp b/clang-tools-extra/clangd/unittests/ClangdLSPServerTests.cpp
index 561ef71ca26d..240188de0da0 100644
--- a/clang-tools-extra/clangd/unittests/ClangdLSPServerTests.cpp
+++ b/clang-tools-extra/clangd/unittests/ClangdLSPServerTests.cpp
@@ -221,6 +221,29 @@ TEST_F(LSPTest, CDBConfigIntegration) {
DiagMessage("Use of undeclared identifier 'BAR'"))));
}
+TEST_F(LSPTest, ModulesTest) {
+ class MathModule : public Module {
+ void initializeLSP(LSPBinder &Bind, const ClientCapabilities &ClientCaps,
+ llvm::json::Object &ServerCaps) override {
+ Bind.notification("add", this, &MathModule::add);
+ Bind.method("get", this, &MathModule::get);
+ }
+
+ void add(const int &X) { Value += X; }
+ void get(const std::nullptr_t &, Callback<int> Reply) { Reply(Value); }
+ int Value = 0;
+ };
+ std::vector<std::unique_ptr<Module>> Mods;
+ Mods.push_back(std::make_unique<MathModule>());
+ ModuleSet ModSet(std::move(Mods));
+ Opts.Modules = &ModSet;
+
+ auto &Client = start();
+ Client.notify("add", 2);
+ Client.notify("add", 8);
+ EXPECT_EQ(10, Client.call("get", nullptr).takeValue());
+}
+
} // namespace
} // namespace clangd
} // namespace clang
More information about the cfe-commits
mailing list