clang-tools 24.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 static llvm::Error handleParseError(const llvm::json::Value &Raw,
105 llvm::StringRef PayloadName,
106 llvm::StringRef PayloadKind,
107 const llvm::json::Path::Root &Root);
108
109 RawHandlers &Raw;
110 RawOutgoing &Out;
111};
112
113template <typename T>
114llvm::Expected<T> LSPBinder::parse(const llvm::json::Value &Raw,
115 llvm::StringRef PayloadName,
116 llvm::StringRef PayloadKind) {
117 T Result;
118 llvm::json::Path::Root Root;
119 if (!fromJSON(Raw, Result, Root))
120 return handleParseError(Raw, PayloadName, PayloadKind, Root);
121 return std::move(Result);
122}
123
124template <typename Param, typename Result, typename ThisT>
125void LSPBinder::method(llvm::StringLiteral Method, ThisT *This,
126 void (ThisT::*Handler)(const Param &,
128 Raw.MethodHandlers[Method] = [Method, Handler, This](JSON RawParams,
129 Callback<JSON> Reply) {
130 auto P = LSPBinder::parse<Param>(RawParams, Method, "request");
131 if (!P)
132 return Reply(P.takeError());
133 (This->*Handler)(*P, std::move(Reply));
134 };
135}
136
137template <typename Param, typename ThisT>
138void LSPBinder::notification(llvm::StringLiteral Method, ThisT *This,
139 void (ThisT::*Handler)(const Param &)) {
140 Raw.NotificationHandlers[Method] = [Method, Handler, This](JSON RawParams) {
141 llvm::Expected<Param> P =
142 LSPBinder::parse<Param>(RawParams, Method, "request");
143 if (!P)
144 return llvm::consumeError(P.takeError());
145 (This->*Handler)(*P);
146 };
147}
148
149template <typename Param, typename Result, typename ThisT>
150void LSPBinder::command(llvm::StringLiteral Method, ThisT *This,
151 void (ThisT::*Handler)(const Param &,
153 Raw.CommandHandlers[Method] = [Method, Handler, This](JSON RawParams,
154 Callback<JSON> Reply) {
155 auto P = LSPBinder::parse<Param>(RawParams, Method, "command");
156 if (!P)
157 return Reply(P.takeError());
158 (This->*Handler)(*P, std::move(Reply));
159 };
160}
161
163 llvm::StringLiteral Method;
164 RawOutgoing *Out;
165 UntypedOutgoingNotification(llvm::StringLiteral Method, RawOutgoing *Out)
166 : Method(Method), Out(Out) {}
167 friend UntypedOutgoingNotification
169
170public:
171 template <typename Request> operator OutgoingNotification<Request>() && {
172 return
173 [Method(Method), Out(Out)](Request R) { Out->notify(Method, JSON(R)); };
174 }
175};
176
180}
181
183 llvm::StringLiteral Method;
184 RawOutgoing *Out;
185 UntypedOutgoingMethod(llvm::StringLiteral Method, RawOutgoing *Out)
186 : Method(Method), Out(Out) {}
187 friend UntypedOutgoingMethod LSPBinder::outgoingMethod(llvm::StringLiteral);
188
189public:
190 template <typename Request, typename Response>
192 return [Method(Method), Out(Out)](Request R, Callback<Response> Reply) {
193 Out->callMethod(
194 Method, JSON(R),
195 // FIXME: why keep ctx alive but not restore it for the callback?
196 [Reply(std::move(Reply)), Ctx(Context::current().clone()),
197 Method](llvm::Expected<JSON> RawRsp) mutable {
198 if (!RawRsp)
199 return Reply(RawRsp.takeError());
200 Reply(LSPBinder::parse<Response>(std::move(*RawRsp), Method,
201 "reply"));
202 });
203 };
204 }
205};
206
208LSPBinder::outgoingMethod(llvm::StringLiteral Method) {
209 return UntypedOutgoingMethod(Method, &Out);
210}
211
212} // namespace clangd
213} // namespace clang
214
215#endif
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
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:208
void method(llvm::StringLiteral Method, ThisT *This, void(ThisT::*Handler)(const Param &, Callback< Result >))
Bind a handler for an LSP method.
Definition LSPBinder.h:125
void notification(llvm::StringLiteral Method, ThisT *This, void(ThisT::*Handler)(const Param &))
Bind a handler for an LSP notification.
Definition LSPBinder.h:138
UntypedOutgoingNotification outgoingNotification(llvm::StringLiteral Method)
Bind a function object to be used for outgoing notifications.
Definition LSPBinder.h:178
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:150
FIXME: Skip testing on windows temporarily due to the different escaping code mode.
Definition AST.cpp:44
llvm::unique_function< void(llvm::Expected< T >)> Callback
A Callback<T> is a void function that accepts Expected<T>.
Definition Function.h:28
bool fromJSON(const llvm::json::Value &Parameters, FuzzyFindRequest &Request, llvm::json::Path P)
Definition Index.cpp:30
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
llvm::StringMap< llvm::unique_function< HandlerT > > HandlerMap
Definition LSPBinder.h:40
HandlerMap< void(JSON)> NotificationHandlers
Definition LSPBinder.h:42
HandlerMap< void(JSON, Callback< JSON >)> CommandHandlers
Definition LSPBinder.h:44
HandlerMap< void(JSON, Callback< JSON >)> MethodHandlers
Definition LSPBinder.h:43