brintos

brintos / llvm-project-archived public Read only

0
0
Text · 8.6 KiB · 8542112 Raw
224 lines · c
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-exception6//7//===----------------------------------------------------------------------===//8 9#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_LSPBINDER_H10#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_LSPBINDER_H11 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 42    HandlerMap<void(JSON)> NotificationHandlers;43    HandlerMap<void(JSON, Callback<JSON>)> MethodHandlers;44    HandlerMap<void(JSON, Callback<JSON>)> CommandHandlers;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>.83  class UntypedOutgoingMethod;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>.92  class UntypedOutgoingNotification;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 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;106  RawOutgoing &Out;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())),127        ErrorCode::InvalidParams);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 &,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 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 &,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 170class LSPBinder::UntypedOutgoingNotification {171  llvm::StringLiteral Method;172  RawOutgoing *Out;173  UntypedOutgoingNotification(llvm::StringLiteral Method, RawOutgoing *Out)174      : Method(Method), Out(Out) {}175  friend UntypedOutgoingNotification176      LSPBinder::outgoingNotification(llvm::StringLiteral);177 178public:179  template <typename Request> operator OutgoingNotification<Request>() && {180    return181        [Method(Method), Out(Out)](Request R) { Out->notify(Method, JSON(R)); };182  }183};184 185inline LSPBinder::UntypedOutgoingNotification186LSPBinder::outgoingNotification(llvm::StringLiteral Method) {187  return UntypedOutgoingNotification(Method, &Out);188}189 190class LSPBinder::UntypedOutgoingMethod {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 197public:198  template <typename Request, typename Response>199  operator OutgoingMethod<Request, 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 215inline LSPBinder::UntypedOutgoingMethod216LSPBinder::outgoingMethod(llvm::StringLiteral Method) {217  return UntypedOutgoingMethod(Method, &Out);218}219 220} // namespace clangd221} // namespace clang222 223#endif224