incoming_messages.cpp 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  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/incoming_messages.h"
  5. #include <utility>
  6. #include "common/ostream.h"
  7. #include "common/raw_string_ostream.h"
  8. #include "toolchain/language_server/handle.h"
  9. namespace Carbon::LanguageServer {
  10. // Parses a JSON value into a specific parameter type. The name of the method is
  11. // used when producing errors.
  12. template <typename ParamsT>
  13. inline auto Parse(llvm::StringRef name, const llvm::json::Value& raw_params)
  14. -> llvm::Expected<ParamsT> {
  15. ParamsT params;
  16. llvm::json::Path::Root root;
  17. if (!clang::clangd::fromJSON(raw_params, params, root)) {
  18. return llvm::make_error<clang::clangd::LSPError>(
  19. llvm::formatv("in call to `{0}`, JSON parse failed: {1}", name,
  20. llvm::fmt_consume(root.getError())),
  21. clang::clangd::ErrorCode::InvalidParams);
  22. }
  23. return std::move(params);
  24. }
  25. template <typename ParamsT, typename ResultT>
  26. auto IncomingMessages::AddCallHandler(
  27. llvm::StringRef name,
  28. auto (*handler)(Context&, const ParamsT&,
  29. llvm::function_ref<auto(llvm::Expected<ResultT>)->void>)
  30. ->void) -> void {
  31. CallHandler parsing_handler =
  32. [name, handler](
  33. Context& context, llvm::json::Value raw_params,
  34. llvm::function_ref<auto(llvm::Expected<llvm::json::Value>)->void>
  35. on_done) -> void {
  36. auto params = Parse<ParamsT>(name, raw_params);
  37. if (!params) {
  38. on_done(params.takeError());
  39. return;
  40. }
  41. handler(context, *params, on_done);
  42. };
  43. auto result = call_handlers_.Insert(name, parsing_handler);
  44. CARBON_CHECK(result.is_inserted(), "Duplicate handler: {0}", name);
  45. }
  46. template <typename ParamsT>
  47. auto IncomingMessages::AddNotificationHandler(
  48. llvm::StringRef name, auto (*handler)(Context&, const ParamsT&)->void)
  49. -> void {
  50. NotificationHandler parsing_handler =
  51. [name, handler](Context& context, llvm::json::Value raw_params) -> void {
  52. auto params = Parse<ParamsT>(name, raw_params);
  53. if (!params) {
  54. CARBON_DIAGNOSTIC(LanguageServerNotificationParseError, Warning, "{0}",
  55. std::string);
  56. context.no_loc_emitter().Emit(LanguageServerNotificationParseError,
  57. llvm::toString(params.takeError()));
  58. return;
  59. }
  60. handler(context, *params);
  61. };
  62. auto result = notification_handlers_.Insert(name, parsing_handler);
  63. CARBON_CHECK(result.is_inserted(), "Duplicate handler: {0}", name);
  64. }
  65. IncomingMessages::IncomingMessages(clang::clangd::Transport* transport,
  66. Context* context)
  67. : transport_(transport), context_(context) {
  68. AddCallHandler("textDocument/documentSymbol", &HandleDocumentSymbol);
  69. AddCallHandler("initialize", &HandleInitialize);
  70. AddCallHandler("shutdown", &HandleShutdown);
  71. AddNotificationHandler("textDocument/didChange",
  72. &HandleDidChangeTextDocument);
  73. AddNotificationHandler("textDocument/didClose", &HandleDidCloseTextDocument);
  74. AddNotificationHandler("textDocument/didOpen", &HandleDidOpenTextDocument);
  75. }
  76. auto IncomingMessages::onCall(llvm::StringRef name, llvm::json::Value params,
  77. llvm::json::Value id) -> bool {
  78. if (auto result = call_handlers_.Lookup(name)) {
  79. (result.value())(*context_, std::move(params),
  80. [&](llvm::Expected<llvm::json::Value> reply) {
  81. transport_->reply(id, std::move(reply));
  82. });
  83. } else {
  84. transport_->reply(id, llvm::make_error<clang::clangd::LSPError>(
  85. llvm::formatv("unsupported call `{0}`", name),
  86. clang::clangd::ErrorCode::MethodNotFound));
  87. }
  88. return true;
  89. }
  90. auto IncomingMessages::onNotify(llvm::StringRef name, llvm::json::Value value)
  91. -> bool {
  92. if (name == "exit") {
  93. return false;
  94. }
  95. if (auto result = notification_handlers_.Lookup(name)) {
  96. (result.value())(*context_, std::move(value));
  97. } else {
  98. CARBON_DIAGNOSTIC(LanguageServerUnsupportedNotification, Warning,
  99. "unsupported notification `{0}`", std::string);
  100. context_->no_loc_emitter().Emit(LanguageServerUnsupportedNotification,
  101. name.str());
  102. }
  103. return true;
  104. }
  105. auto IncomingMessages::onReply(llvm::json::Value id,
  106. llvm::Expected<llvm::json::Value> result)
  107. -> bool {
  108. RawStringOstream id_str;
  109. id_str << id;
  110. RawStringOstream result_str;
  111. if (result) {
  112. result_str << result.get();
  113. } else {
  114. result_str << result.takeError();
  115. }
  116. CARBON_DIAGNOSTIC(LanguageServerUnexpectedReply, Warning,
  117. "unexpected reply to request ID {0}: {1}", std::string,
  118. std::string);
  119. context_->no_loc_emitter().Emit(LanguageServerUnexpectedReply,
  120. id_str.TakeStr(), result_str.TakeStr());
  121. return true;
  122. }
  123. } // namespace Carbon::LanguageServer