Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor the language server structure. #4721

Open
wants to merge 8 commits into
base: trunk
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 60 additions & 9 deletions toolchain/language_server/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -8,26 +8,77 @@ package(default_visibility = ["//visibility:public"])

cc_library(
name = "language_server",
srcs = [
"language_server.cpp",
"server.cpp",
"server.h",
],
srcs = ["language_server.cpp"],
hdrs = ["language_server.h"],
# Some parameters are unused in clangd headers.
copts = ["-Wno-unused-parameter"],
deps = [
":context",
":handler_registry",
":handlers",
":incoming_messages",
":outgoing_messages",
"//common:error",
"//common:ostream",
"@llvm-project//clang-tools-extra/clangd:ClangDaemon",
],
)

cc_library(
name = "context",
hdrs = ["context.h"],
deps = [
"//common:map",
"@llvm-project//clang-tools-extra/clangd:ClangDaemon",
],
)

cc_library(
name = "handler_registry",
srcs = [
"handler_registry_for_call.cpp",
"handler_registry_for_notification.cpp",
],
hdrs = ["handler_registry.h"],
deps = [
"@llvm-project//clang-tools-extra/clangd:ClangDaemon",
"@llvm-project//llvm:Support",
],
alwayslink = 1,
)

cc_library(
name = "handlers",
srcs = glob(["handle_*"]),
deps = [
":context",
":handler_registry",
"//toolchain/base:shared_value_stores",
"//toolchain/diagnostics:null_diagnostics",
"//toolchain/lex",
"//toolchain/lex:tokenized_buffer",
"//toolchain/parse",
"//toolchain/parse:node_kind",
"//toolchain/parse:tree",
"//toolchain/source:source_buffer",
],
alwayslink = 1,
)

cc_library(
name = "incoming_messages",
srcs = ["incoming_messages.cpp"],
hdrs = ["incoming_messages.h"],
deps = [
":context",
":handler_registry",
"//common:check",
"//common:map",
"@llvm-project//clang-tools-extra/clangd:ClangDaemon",
],
)

cc_library(
name = "outgoing_messages",
hdrs = ["outgoing_messages.h"],
deps = [
"@llvm-project//clang-tools-extra/clangd:ClangDaemon",
"@llvm-project//llvm:Support",
],
)
26 changes: 26 additions & 0 deletions toolchain/language_server/context.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#ifndef CARBON_TOOLCHAIN_LANGUAGE_SERVER_CONTEXT_H_
#define CARBON_TOOLCHAIN_LANGUAGE_SERVER_CONTEXT_H_

#include "clang-tools-extra/clangd/Protocol.h"
#include "clang-tools-extra/clangd/support/Function.h"
#include "common/map.h"

namespace Carbon::LanguageServer {

// Context for LSP call handling.
class Context {
public:
auto files() -> Map<std::string, std::string>& { return files_; }

private:
// Content of files managed by the language client.
Map<std::string, std::string> files_;
};

} // namespace Carbon::LanguageServer

#endif // CARBON_TOOLCHAIN_LANGUAGE_SERVER_CONTEXT_H_
96 changes: 96 additions & 0 deletions toolchain/language_server/handle_document_symbol.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#include "toolchain/base/shared_value_stores.h"
#include "toolchain/diagnostics/null_diagnostics.h"
#include "toolchain/language_server/context.h"
#include "toolchain/language_server/handler_registry.h"
#include "toolchain/lex/lex.h"
#include "toolchain/parse/node_kind.h"
#include "toolchain/parse/parse.h"
#include "toolchain/parse/tree_and_subtrees.h"
#include "toolchain/source/source_buffer.h"

namespace Carbon::LanguageServer {

// Returns the text of first child of kind Parse::NodeKind::IdentifierName.
static auto GetIdentifierName(const SharedValueStores& value_stores,
const Lex::TokenizedBuffer& tokens,
const Parse::TreeAndSubtrees& p,
Parse::NodeId node)
-> std::optional<llvm::StringRef> {
for (auto ch : p.children(node)) {
if (p.tree().node_kind(ch) == Parse::NodeKind::IdentifierName) {
auto token = p.tree().node_token(ch);
if (tokens.GetKind(token) == Lex::TokenKind::Identifier) {
return value_stores.identifiers().Get(tokens.GetIdentifier(token));
}
}
}
return std::nullopt;
}

// Provides information about document symbols.
static auto HandleDocumentSymbol(
Context& context, const clang::clangd::DocumentSymbolParams& params,
llvm::function_ref<
void(llvm::Expected<std::vector<clang::clangd::DocumentSymbol>>)>
on_done) -> void {
SharedValueStores value_stores;
llvm::vfs::InMemoryFileSystem vfs;
auto lookup = context.files().Lookup(params.textDocument.uri.file());
CARBON_CHECK(lookup);
vfs.addFile(lookup.key(), /*mtime=*/0,
llvm::MemoryBuffer::getMemBufferCopy(lookup.value()));

auto buf =
SourceBuffer::MakeFromFile(vfs, lookup.key(), NullDiagnosticConsumer());
auto lexed = Lex::Lex(value_stores, *buf, NullDiagnosticConsumer());
auto parsed = Parse::Parse(lexed, NullDiagnosticConsumer(), nullptr);
Parse::TreeAndSubtrees tree_and_subtrees(lexed, parsed);
std::vector<clang::clangd::DocumentSymbol> result;
for (const auto& node : parsed.postorder()) {
clang::clangd::SymbolKind symbol_kind;
switch (parsed.node_kind(node)) {
case Parse::NodeKind::FunctionDecl:
case Parse::NodeKind::FunctionDefinitionStart:
symbol_kind = clang::clangd::SymbolKind::Function;
break;
case Parse::NodeKind::Namespace:
symbol_kind = clang::clangd::SymbolKind::Namespace;
break;
case Parse::NodeKind::InterfaceDefinitionStart:
case Parse::NodeKind::NamedConstraintDefinitionStart:
symbol_kind = clang::clangd::SymbolKind::Interface;
break;
case Parse::NodeKind::ClassDefinitionStart:
symbol_kind = clang::clangd::SymbolKind::Class;
break;
default:
continue;
}

if (auto name =
GetIdentifierName(value_stores, lexed, tree_and_subtrees, node)) {
auto tok = parsed.node_token(node);
clang::clangd::Position pos{lexed.GetLineNumber(tok) - 1,
lexed.GetColumnNumber(tok) - 1};

clang::clangd::DocumentSymbol symbol{
.name = std::string(*name),
.kind = symbol_kind,
.range = {.start = pos, .end = pos},
.selectionRange = {.start = pos, .end = pos},
};

result.push_back(symbol);
}
}
on_done(result);
}

static RegisterCallHandler<HandleDocumentSymbol> register_call(
"textDocument/didChange", "");

} // namespace Carbon::LanguageServer
25 changes: 25 additions & 0 deletions toolchain/language_server/handle_initialize.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#include "toolchain/language_server/context.h"
#include "toolchain/language_server/handler_registry.h"

namespace Carbon::LanguageServer {

// Tells the client what features are supported.
static auto HandleInitialize(
Context& /*context*/,
const clang::clangd::NoParams& /*client_capabilities*/,
llvm::function_ref<void(llvm::Expected<llvm::json::Object>)> on_done)
-> void {
llvm::json::Object capabilities{{"documentSymbolProvider", true},
{"textDocumentSync", /*Full=*/1}};

llvm::json::Object reply{{"capabilities", std::move(capabilities)}};
on_done(reply);
}

static RegisterCallHandler<HandleInitialize> register_call("initialize", "");

} // namespace Carbon::LanguageServer
33 changes: 33 additions & 0 deletions toolchain/language_server/handle_text_document.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#include "toolchain/language_server/context.h"
#include "toolchain/language_server/handler_registry.h"

namespace Carbon::LanguageServer {

// Updates the content of already-open documents.
static auto HandleDidOpenTextDocument(
Context& context, const clang::clangd::DidOpenTextDocumentParams& params)
-> void {
context.files().Update(params.textDocument.uri.file(),
params.textDocument.text);
}

// Stores the content of newly-opened documents.
static auto HandleDidChangeTextDocument(
Context& context, const clang::clangd::DidChangeTextDocumentParams& params)
-> void {
// Full text is sent if full sync is specified in capabilities.
CARBON_CHECK(params.contentChanges.size() == 1);
context.files().Update(params.textDocument.uri.file(),
params.contentChanges[0].text);
}

static RegisterNotificationHandler<HandleDidChangeTextDocument>
register_notification1("textDocument/didChange", "");
static RegisterNotificationHandler<HandleDidOpenTextDocument>
register_notification2("textDocument/didOpen", "");

} // namespace Carbon::LanguageServer
Loading
Loading