server.cpp 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. // Part of the Carbon Language project, under the Apache License v2.0 with LLVM
  2. // Exceptions. See /LICENSE for license information.
  3. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  4. #include "toolchain/language_server/server.h"
  5. #include "toolchain/base/shared_value_stores.h"
  6. #include "toolchain/diagnostics/null_diagnostics.h"
  7. #include "toolchain/lex/lex.h"
  8. #include "toolchain/parse/node_kind.h"
  9. #include "toolchain/parse/parse.h"
  10. #include "toolchain/parse/tree_and_subtrees.h"
  11. #include "toolchain/source/source_buffer.h"
  12. namespace Carbon::LanguageServer {
  13. Server::Server(std::FILE* input_stream, llvm::raw_ostream& output_stream)
  14. : transport_(clang::clangd::newJSONTransport(input_stream, output_stream,
  15. /*InMirror=*/nullptr,
  16. /*Pretty=*/true)),
  17. binder_(handlers_, *this) {
  18. binder_.notification("textDocument/didOpen", this,
  19. &Server::OnDidOpenTextDocument);
  20. binder_.notification("textDocument/didChange", this,
  21. &Server::OnDidChangeTextDocument);
  22. binder_.method("initialize", this, &Server::OnInitialize);
  23. binder_.method("textDocument/documentSymbol", this,
  24. &Server::OnDocumentSymbol);
  25. }
  26. auto Server::Run() -> ErrorOr<Success> {
  27. llvm::Error err = transport_->loop(*this);
  28. if (err.success()) {
  29. return Success();
  30. } else {
  31. std::string str;
  32. llvm::raw_string_ostream out(str);
  33. out << err;
  34. return Error(str);
  35. }
  36. }
  37. void Server::OnDidOpenTextDocument(
  38. clang::clangd::DidOpenTextDocumentParams const& params) {
  39. files_.emplace(params.textDocument.uri.file(), params.textDocument.text);
  40. }
  41. void Server::OnDidChangeTextDocument(
  42. clang::clangd::DidChangeTextDocumentParams const& params) {
  43. // Full text is sent if full sync is specified in capabilities.
  44. CARBON_CHECK(params.contentChanges.size() == 1);
  45. std::string file = params.textDocument.uri.file().str();
  46. files_[file] = params.contentChanges[0].text;
  47. }
  48. void Server::OnInitialize(
  49. clang::clangd::NoParams const& /*client_capabilities*/,
  50. clang::clangd::Callback<llvm::json::Object> cb) {
  51. llvm::json::Object capabilities{{"documentSymbolProvider", true},
  52. {"textDocumentSync", /*Full=*/1}};
  53. llvm::json::Object reply{{"capabilities", std::move(capabilities)}};
  54. cb(reply);
  55. }
  56. auto Server::onNotify(llvm::StringRef method, llvm::json::Value value) -> bool {
  57. if (method == "exit") {
  58. return false;
  59. }
  60. if (auto handler = handlers_.NotificationHandlers.find(method);
  61. handler != handlers_.NotificationHandlers.end()) {
  62. handler->second(std::move(value));
  63. } else {
  64. clang::clangd::log("unhandled notification {0}", method);
  65. }
  66. return true;
  67. }
  68. auto Server::onCall(llvm::StringRef method, llvm::json::Value params,
  69. llvm::json::Value id) -> bool {
  70. if (auto handler = handlers_.MethodHandlers.find(method);
  71. handler != handlers_.MethodHandlers.end()) {
  72. // TODO: improve this if add threads
  73. handler->second(std::move(params),
  74. [&](llvm::Expected<llvm::json::Value> reply) {
  75. transport_->reply(id, std::move(reply));
  76. });
  77. } else {
  78. transport_->reply(
  79. id, llvm::make_error<clang::clangd::LSPError>(
  80. "method not found", clang::clangd::ErrorCode::MethodNotFound));
  81. }
  82. return true;
  83. }
  84. auto Server::onReply(llvm::json::Value /*id*/,
  85. llvm::Expected<llvm::json::Value> /*result*/) -> bool {
  86. return true;
  87. }
  88. // Returns the text of first child of kind Parse::NodeKind::IdentifierName.
  89. static auto GetIdentifierName(const SharedValueStores& value_stores,
  90. const Lex::TokenizedBuffer& tokens,
  91. const Parse::TreeAndSubtrees& p,
  92. Parse::NodeId node)
  93. -> std::optional<llvm::StringRef> {
  94. for (auto ch : p.children(node)) {
  95. if (p.tree().node_kind(ch) == Parse::NodeKind::IdentifierName) {
  96. auto token = p.tree().node_token(ch);
  97. if (tokens.GetKind(token) == Lex::TokenKind::Identifier) {
  98. return value_stores.identifiers().Get(tokens.GetIdentifier(token));
  99. }
  100. }
  101. }
  102. return std::nullopt;
  103. }
  104. void Server::OnDocumentSymbol(
  105. clang::clangd::DocumentSymbolParams const& params,
  106. clang::clangd::Callback<std::vector<clang::clangd::DocumentSymbol>> cb) {
  107. SharedValueStores value_stores;
  108. llvm::vfs::InMemoryFileSystem vfs;
  109. auto file = params.textDocument.uri.file().str();
  110. vfs.addFile(file, /*mtime=*/0,
  111. llvm::MemoryBuffer::getMemBufferCopy(files_.at(file)));
  112. auto buf = SourceBuffer::MakeFromFile(vfs, file, NullDiagnosticConsumer());
  113. auto lexed = Lex::Lex(value_stores, *buf, NullDiagnosticConsumer());
  114. auto parsed = Parse::Parse(lexed, NullDiagnosticConsumer(), nullptr);
  115. Parse::TreeAndSubtrees tree_and_subtrees(lexed, parsed);
  116. std::vector<clang::clangd::DocumentSymbol> result;
  117. for (const auto& node : parsed.postorder()) {
  118. clang::clangd::SymbolKind symbol_kind;
  119. switch (parsed.node_kind(node)) {
  120. case Parse::NodeKind::FunctionDecl:
  121. case Parse::NodeKind::FunctionDefinitionStart:
  122. symbol_kind = clang::clangd::SymbolKind::Function;
  123. break;
  124. case Parse::NodeKind::Namespace:
  125. symbol_kind = clang::clangd::SymbolKind::Namespace;
  126. break;
  127. case Parse::NodeKind::InterfaceDefinitionStart:
  128. case Parse::NodeKind::NamedConstraintDefinitionStart:
  129. symbol_kind = clang::clangd::SymbolKind::Interface;
  130. break;
  131. case Parse::NodeKind::ClassDefinitionStart:
  132. symbol_kind = clang::clangd::SymbolKind::Class;
  133. break;
  134. default:
  135. continue;
  136. }
  137. if (auto name =
  138. GetIdentifierName(value_stores, lexed, tree_and_subtrees, node)) {
  139. auto tok = parsed.node_token(node);
  140. clang::clangd::Position pos{lexed.GetLineNumber(tok) - 1,
  141. lexed.GetColumnNumber(tok) - 1};
  142. clang::clangd::DocumentSymbol symbol{
  143. .name = std::string(*name),
  144. .kind = symbol_kind,
  145. .range = {.start = pos, .end = pos},
  146. .selectionRange = {.start = pos, .end = pos},
  147. };
  148. result.push_back(symbol);
  149. }
  150. }
  151. cb(result);
  152. }
  153. } // namespace Carbon::LanguageServer