clang-tools  14.0.0git
LSPBinder.h
Go to the documentation of this file.
1 //===--- LSPBinder.h - Tables of LSP handlers --------------------*- C++-*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_LSPBINDER_H
10 #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_LSPBINDER_H
11 
12 #include "Protocol.h"
13 #include "support/Function.h"
14 #include "support/Logger.h"
15 #include "llvm/ADT/FunctionExtras.h"
16 #include "llvm/ADT/StringMap.h"
17 #include "llvm/Support/JSON.h"
18 
19 namespace clang {
20 namespace clangd {
21 
22 /// LSPBinder collects a table of functions that handle LSP calls.
23 ///
24 /// It translates a handler method's signature, e.g.
25 /// void codeComplete(CompletionParams, Callback<CompletionList>)
26 /// into a wrapper with a generic signature:
27 /// void(json::Value, Callback<json::Value>)
28 /// The wrapper takes care of parsing/serializing responses.
29 ///
30 /// Incoming calls can be methods, notifications, or commands - all are similar.
31 ///
32 /// FIXME: this should also take responsibility for wrapping *outgoing* calls,
33 /// replacing the typed ClangdLSPServer::call<> etc.
34 class LSPBinder {
35 public:
37 
38  struct RawHandlers {
39  template <typename HandlerT>
40  using HandlerMap = llvm::StringMap<llvm::unique_function<HandlerT>>;
41 
45  };
46  class RawOutgoing {
47  public:
48  virtual ~RawOutgoing() = default;
49  virtual void callMethod(llvm::StringRef Method, JSON Params,
50  Callback<JSON> Reply) = 0;
51  virtual void notify(llvm::StringRef Method, JSON Params) = 0;
52  };
53 
54  LSPBinder(RawHandlers &Raw, RawOutgoing &Out) : Raw(Raw), Out(Out) {}
55 
56  /// Bind a handler for an LSP method.
57  /// e.g. Bind.method("peek", this, &ThisModule::peek);
58  /// Handler should be e.g. void peek(const PeekParams&, Callback<PeekResult>);
59  /// PeekParams must be JSON-parseable and PeekResult must be serializable.
60  template <typename Param, typename Result, typename ThisT>
61  void method(llvm::StringLiteral Method, ThisT *This,
62  void (ThisT::*Handler)(const Param &, Callback<Result>));
63 
64  /// Bind a handler for an LSP notification.
65  /// e.g. Bind.notification("poke", this, &ThisModule::poke);
66  /// Handler should be e.g. void poke(const PokeParams&);
67  /// PokeParams must be JSON-parseable.
68  template <typename Param, typename ThisT>
69  void notification(llvm::StringLiteral Method, ThisT *This,
70  void (ThisT::*Handler)(const Param &));
71 
72  /// Bind a handler for an LSP command.
73  /// e.g. Bind.command("load", this, &ThisModule::load);
74  /// Handler should be e.g. void load(const LoadParams&, Callback<LoadResult>);
75  /// LoadParams must be JSON-parseable and LoadResult must be serializable.
76  template <typename Param, typename Result, typename ThisT>
77  void command(llvm::StringLiteral Command, ThisT *This,
78  void (ThisT::*Handler)(const Param &, Callback<Result>));
79 
80  template <typename P, typename R>
81  using OutgoingMethod = llvm::unique_function<void(const P &, Callback<R>)>;
82  /// UntypedOutgoingMethod is convertible to OutgoingMethod<P, R>.
84  /// Bind a function object to be used for outgoing method calls.
85  /// e.g. OutgoingMethod<EParams, EResult> Edit = Bind.outgoingMethod("edit");
86  /// EParams must be JSON-serializable, EResult must be parseable.
87  UntypedOutgoingMethod outgoingMethod(llvm::StringLiteral Method);
88 
89  template <typename P>
90  using OutgoingNotification = llvm::unique_function<void(const P &)>;
91  /// UntypedOutgoingNotification is convertible to OutgoingNotification<T>.
93  /// Bind a function object to be used for outgoing notifications.
94  /// e.g. OutgoingNotification<LogParams> Log = Bind.outgoingMethod("log");
95  /// LogParams must be JSON-serializable.
96  UntypedOutgoingNotification outgoingNotification(llvm::StringLiteral Method);
97 
98 private:
99  // FIXME: remove usage from ClangdLSPServer and make this private.
100  template <typename T>
101  static llvm::Expected<T> parse(const llvm::json::Value &Raw,
102  llvm::StringRef PayloadName,
103  llvm::StringRef PayloadKind);
104 
105  RawHandlers &Raw;
106  RawOutgoing &Out;
107 };
108 
109 template <typename T>
110 llvm::Expected<T> LSPBinder::parse(const llvm::json::Value &Raw,
111  llvm::StringRef PayloadName,
112  llvm::StringRef PayloadKind) {
113  T Result;
115  if (!fromJSON(Raw, Result, Root)) {
116  elog("Failed to decode {0} {1}: {2}", PayloadName, PayloadKind,
117  Root.getError());
118  // Dump the relevant parts of the broken message.
119  std::string Context;
120  llvm::raw_string_ostream OS(Context);
121  Root.printErrorContext(Raw, OS);
122  vlog("{0}", OS.str());
123  // Report the error (e.g. to the client).
124  return llvm::make_error<LSPError>(
125  llvm::formatv("failed to decode {0} {1}: {2}", PayloadName, PayloadKind,
126  fmt_consume(Root.getError())),
128  }
129  return std::move(Result);
130 }
131 
132 template <typename Param, typename Result, typename ThisT>
133 void LSPBinder::method(llvm::StringLiteral Method, ThisT *This,
134  void (ThisT::*Handler)(const Param &,
135  Callback<Result>)) {
136  Raw.MethodHandlers[Method] = [Method, Handler, This](JSON RawParams,
137  Callback<JSON> Reply) {
138  auto P = LSPBinder::parse<Param>(RawParams, Method, "request");
139  if (!P)
140  return Reply(P.takeError());
141  (This->*Handler)(*P, std::move(Reply));
142  };
143 }
144 
145 template <typename Param, typename ThisT>
146 void LSPBinder::notification(llvm::StringLiteral Method, ThisT *This,
147  void (ThisT::*Handler)(const Param &)) {
148  Raw.NotificationHandlers[Method] = [Method, Handler, This](JSON RawParams) {
149  llvm::Expected<Param> P =
150  LSPBinder::parse<Param>(RawParams, Method, "request");
151  if (!P)
152  return llvm::consumeError(P.takeError());
153  (This->*Handler)(*P);
154  };
155 }
156 
157 template <typename Param, typename Result, typename ThisT>
158 void LSPBinder::command(llvm::StringLiteral Method, ThisT *This,
159  void (ThisT::*Handler)(const Param &,
160  Callback<Result>)) {
161  Raw.CommandHandlers[Method] = [Method, Handler, This](JSON RawParams,
162  Callback<JSON> Reply) {
163  auto P = LSPBinder::parse<Param>(RawParams, Method, "command");
164  if (!P)
165  return Reply(P.takeError());
166  (This->*Handler)(*P, std::move(Reply));
167  };
168 }
169 
171  llvm::StringLiteral Method;
172  RawOutgoing *Out;
173  UntypedOutgoingNotification(llvm::StringLiteral Method, RawOutgoing *Out)
174  : Method(Method), Out(Out) {}
176  LSPBinder::outgoingNotification(llvm::StringLiteral);
177 
178 public:
179  template <typename Request> operator OutgoingNotification<Request>() && {
180  return
181  [Method(Method), Out(Out)](Request R) { Out->notify(Method, JSON(R)); };
182  }
183 };
184 
186 LSPBinder::outgoingNotification(llvm::StringLiteral Method) {
187  return UntypedOutgoingNotification(Method, &Out);
188 }
189 
191  llvm::StringLiteral Method;
192  RawOutgoing *Out;
193  UntypedOutgoingMethod(llvm::StringLiteral Method, RawOutgoing *Out)
194  : Method(Method), Out(Out) {}
195  friend UntypedOutgoingMethod LSPBinder::outgoingMethod(llvm::StringLiteral);
196 
197 public:
198  template <typename Request, typename Response>
200  return [Method(Method), Out(Out)](Request R, Callback<Response> Reply) {
201  Out->callMethod(
202  Method, JSON(R),
203  // FIXME: why keep ctx alive but not restore it for the callback?
204  [Reply(std::move(Reply)), Ctx(Context::current().clone()),
205  Method](llvm::Expected<JSON> RawRsp) mutable {
206  if (!RawRsp)
207  return Reply(RawRsp.takeError());
208  Reply(LSPBinder::parse<Response>(std::move(*RawRsp), Method,
209  "reply"));
210  });
211  };
212  }
213 };
214 
216 LSPBinder::outgoingMethod(llvm::StringLiteral Method) {
217  return UntypedOutgoingMethod(Method, &Out);
218 }
219 
220 } // namespace clangd
221 } // namespace clang
222 
223 #endif
clang::clangd::LSPBinder::RawOutgoing::callMethod
virtual void callMethod(llvm::StringRef Method, JSON Params, Callback< JSON > Reply)=0
clang::clangd::LSPBinder::outgoingNotification
UntypedOutgoingNotification outgoingNotification(llvm::StringLiteral Method)
Bind a function object to be used for outgoing notifications.
Definition: LSPBinder.h:186
clang::clangd::LSPBinder::RawOutgoing::notify
virtual void notify(llvm::StringRef Method, JSON Params)=0
clang::clangd::Context::current
static const Context & current()
Returns the context for the current thread, creating it if needed.
Definition: Context.cpp:27
Root
ASTNode Root
Definition: DumpAST.cpp:332
clang::clangd::LSPBinder::notification
void notification(llvm::StringLiteral Method, ThisT *This, void(ThisT::*Handler)(const Param &))
Bind a handler for an LSP notification.
Definition: LSPBinder.h:146
clang::clangd::LSPBinder::OutgoingMethod
llvm::unique_function< void(const P &, Callback< R >)> OutgoingMethod
Definition: LSPBinder.h:81
Ctx
Context Ctx
Definition: TUScheduler.cpp:454
clang::clangd::LSPBinder::method
void method(llvm::StringLiteral Method, ThisT *This, void(ThisT::*Handler)(const Param &, Callback< Result >))
Bind a handler for an LSP method.
Definition: LSPBinder.h:133
clang::clangd::LSPBinder::UntypedOutgoingMethod
Definition: LSPBinder.h:190
clang::clangd::LSPBinder::RawOutgoing
Definition: LSPBinder.h:46
clang::clangd::LSPBinder::JSON
llvm::json::Value JSON
Definition: LSPBinder.h:36
Protocol.h
clang::clangd::LSPBinder::RawHandlers::MethodHandlers
HandlerMap< void(JSON, Callback< JSON >)> MethodHandlers
Definition: LSPBinder.h:43
clang::clangd::ErrorCode::InvalidParams
@ InvalidParams
clang::clangd::LSPBinder::UntypedOutgoingNotification
Definition: LSPBinder.h:170
Logger.h
clang::clangd::LSPBinder::RawHandlers::NotificationHandlers
HandlerMap< void(JSON)> NotificationHandlers
Definition: LSPBinder.h:42
clang::clangd::LSPBinder::RawHandlers::HandlerMap
llvm::StringMap< llvm::unique_function< HandlerT > > HandlerMap
Definition: LSPBinder.h:40
clang::clangd::LSPBinder::RawHandlers
Definition: LSPBinder.h:38
clang::clangd::fromJSON
bool fromJSON(const llvm::json::Value &Parameters, FuzzyFindRequest &Request, llvm::json::Path P)
Definition: Index.cpp:34
clang::clangd::vlog
void vlog(const char *Fmt, Ts &&... Vals)
Definition: Logger.h:73
clang::clangd::CompletionItemKind::Method
@ Method
clang::clangd::LSPBinder::OutgoingNotification
llvm::unique_function< void(const P &)> OutgoingNotification
Definition: LSPBinder.h:90
clang::clangd::LSPBinder
LSPBinder collects a table of functions that handle LSP calls.
Definition: LSPBinder.h:34
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
Function.h
OS
llvm::raw_string_ostream OS
Definition: TraceTests.cpp:163
clang::clangd::LSPBinder::outgoingMethod
UntypedOutgoingMethod outgoingMethod(llvm::StringLiteral Method)
Bind a function object to be used for outgoing method calls.
Definition: LSPBinder.h:216
clang::clangd::LSPBinder::RawHandlers::CommandHandlers
HandlerMap< void(JSON, Callback< JSON >)> CommandHandlers
Definition: LSPBinder.h:44
clang::clangd::LSPBinder::RawOutgoing::~RawOutgoing
virtual ~RawOutgoing()=default
clang::clangd::Callback
llvm::unique_function< void(llvm::Expected< T >)> Callback
A Callback<T> is a void function that accepts Expected<T>.
Definition: Function.h:28
clang::clangd::LSPBinder::command
void command(llvm::StringLiteral Command, ThisT *This, void(ThisT::*Handler)(const Param &, Callback< Result >))
Bind a handler for an LSP command.
Definition: LSPBinder.h:158
Out
CompiledFragmentImpl & Out
Definition: ConfigCompile.cpp:100
clang::clangd::elog
void elog(const char *Fmt, Ts &&... Vals)
Definition: Logger.h:62
clang::clangd::Context
A context is an immutable container for per-request data that must be propagated through layers that ...
Definition: Context.h:69
clang::clangd::LSPBinder::LSPBinder
LSPBinder(RawHandlers &Raw, RawOutgoing &Out)
Definition: LSPBinder.h:54
Value
static constexpr bool Value
Definition: SuspiciousCallArgumentCheck.cpp:72