clang-tools 20.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
19namespace clang {
20namespace 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.
34class LSPBinder {
35public:
36 using JSON = llvm::json::Value;
37
38 struct RawHandlers {
39 template <typename HandlerT>
40 using HandlerMap = llvm::StringMap<llvm::unique_function<HandlerT>>;
41
45 };
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.
97
98private:
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;
107};
108
109template <typename T>
110llvm::Expected<T> LSPBinder::parse(const llvm::json::Value &Raw,
111 llvm::StringRef PayloadName,
112 llvm::StringRef PayloadKind) {
113 T Result;
114 llvm::json::Path::Root Root;
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
132template <typename Param, typename Result, typename ThisT>
133void LSPBinder::method(llvm::StringLiteral Method, ThisT *This,
134 void (ThisT::*Handler)(const Param &,
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
145template <typename Param, typename ThisT>
146void 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
157template <typename Param, typename Result, typename ThisT>
158void LSPBinder::command(llvm::StringLiteral Method, ThisT *This,
159 void (ThisT::*Handler)(const Param &,
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;
174 : Method(Method), Out(Out) {}
177
178public:
179 template <typename Request> operator OutgoingNotification<Request>() && {
180 return
181 [Method(Method), Out(Out)](Request R) { Out->notify(Method, JSON(R)); };
182 }
183};
184
188}
189
191 llvm::StringLiteral Method;
192 RawOutgoing *Out;
193 UntypedOutgoingMethod(llvm::StringLiteral Method, RawOutgoing *Out)
194 : Method(Method), Out(Out) {}
196
197public:
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
216LSPBinder::outgoingMethod(llvm::StringLiteral Method) {
217 return UntypedOutgoingMethod(Method, &Out);
218}
219
220} // namespace clangd
221} // namespace clang
222
223#endif
CompiledFragmentImpl & Out
ASTNode Root
Definition: DumpAST.cpp:342
llvm::raw_string_ostream OS
Definition: TraceTests.cpp:160
A context is an immutable container for per-request data that must be propagated through layers that ...
Definition: Context.h:69
static const Context & current()
Returns the context for the current thread, creating it if needed.
Definition: Context.cpp:27
virtual void callMethod(llvm::StringRef Method, JSON Params, Callback< JSON > Reply)=0
virtual void notify(llvm::StringRef Method, JSON Params)=0
LSPBinder collects a table of functions that handle LSP calls.
Definition: LSPBinder.h:34
llvm::unique_function< void(const P &)> OutgoingNotification
Definition: LSPBinder.h:90
LSPBinder(RawHandlers &Raw, RawOutgoing &Out)
Definition: LSPBinder.h:54
llvm::json::Value JSON
Definition: LSPBinder.h:36
UntypedOutgoingMethod outgoingMethod(llvm::StringLiteral Method)
Bind a function object to be used for outgoing method calls.
Definition: LSPBinder.h:216
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
void notification(llvm::StringLiteral Method, ThisT *This, void(ThisT::*Handler)(const Param &))
Bind a handler for an LSP notification.
Definition: LSPBinder.h:146
UntypedOutgoingNotification outgoingNotification(llvm::StringLiteral Method)
Bind a function object to be used for outgoing notifications.
Definition: LSPBinder.h:186
llvm::unique_function< void(const P &, Callback< R >)> OutgoingMethod
Definition: LSPBinder.h:81
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
void vlog(const char *Fmt, Ts &&... Vals)
Definition: Logger.h:72
llvm::unique_function< void(llvm::Expected< T >)> Callback
A Callback<T> is a void function that accepts Expected<T>.
Definition: Function.h:28
void elog(const char *Fmt, Ts &&... Vals)
Definition: Logger.h:61
bool fromJSON(const llvm::json::Value &Parameters, FuzzyFindRequest &Request, llvm::json::Path P)
Definition: Index.cpp:30
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
HandlerMap< void(JSON)> NotificationHandlers
Definition: LSPBinder.h:42
llvm::StringMap< llvm::unique_function< HandlerT > > HandlerMap
Definition: LSPBinder.h:40
HandlerMap< void(JSON, Callback< JSON >)> CommandHandlers
Definition: LSPBinder.h:44
HandlerMap< void(JSON, Callback< JSON >)> MethodHandlers
Definition: LSPBinder.h:43