MLIR  18.0.0git
Transport.h
Go to the documentation of this file.
1 //===--- Transport.h - Sending and Receiving LSP messages -------*- 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 // The language server protocol is usually implemented by writing messages as
10 // JSON-RPC over the stdin/stdout of a subprocess. This file contains a JSON
11 // transport interface that handles this communication.
12 //
13 //===----------------------------------------------------------------------===//
14 
15 #ifndef MLIR_TOOLS_LSPSERVERSUPPORT_TRANSPORT_H
16 #define MLIR_TOOLS_LSPSERVERSUPPORT_TRANSPORT_H
17 
18 #include "mlir/Support/LLVM.h"
22 #include "llvm/ADT/FunctionExtras.h"
23 #include "llvm/ADT/StringMap.h"
24 #include "llvm/ADT/StringRef.h"
25 #include "llvm/Support/FormatAdapters.h"
26 #include "llvm/Support/JSON.h"
27 #include "llvm/Support/raw_ostream.h"
28 #include <atomic>
29 
30 namespace mlir {
31 namespace lsp {
32 class MessageHandler;
33 
34 //===----------------------------------------------------------------------===//
35 // JSONTransport
36 //===----------------------------------------------------------------------===//
37 
38 /// The encoding style of the JSON-RPC messages (both input and output).
40  /// Encoding per the LSP specification, with mandatory Content-Length header.
42  /// Messages are delimited by a '// -----' line. Comment lines start with //.
43  Delimited
44 };
45 
46 /// A transport class that performs the JSON-RPC communication with the LSP
47 /// client.
49 public:
50  JSONTransport(std::FILE *in, raw_ostream &out,
52  bool prettyOutput = false)
53  : in(in), out(out), style(style), prettyOutput(prettyOutput) {}
54 
55  /// The following methods are used to send a message to the LSP client.
56  void notify(StringRef method, llvm::json::Value params);
57  void call(StringRef method, llvm::json::Value params, llvm::json::Value id);
58  void reply(llvm::json::Value id, llvm::Expected<llvm::json::Value> result);
59 
60  /// Start executing the JSON-RPC transport.
61  llvm::Error run(MessageHandler &handler);
62 
63 private:
64  /// Dispatches the given incoming json message to the message handler.
65  bool handleMessage(llvm::json::Value msg, MessageHandler &handler);
66  /// Writes the given message to the output stream.
67  void sendMessage(llvm::json::Value msg);
68 
69  /// Read in a message from the input stream.
70  LogicalResult readMessage(std::string &json) {
71  return style == JSONStreamStyle::Delimited ? readDelimitedMessage(json)
72  : readStandardMessage(json);
73  }
74  LogicalResult readDelimitedMessage(std::string &json);
75  LogicalResult readStandardMessage(std::string &json);
76 
77  /// An output buffer used when building output messages.
78  SmallVector<char, 0> outputBuffer;
79  /// The input file stream.
80  std::FILE *in;
81  /// The output file stream.
82  raw_ostream &out;
83  /// The JSON stream style to use.
84  JSONStreamStyle style;
85  /// If the output JSON should be formatted for easier readability.
86  bool prettyOutput;
87 };
88 
89 //===----------------------------------------------------------------------===//
90 // MessageHandler
91 //===----------------------------------------------------------------------===//
92 
93 /// A Callback<T> is a void function that accepts Expected<T>. This is
94 /// accepted by functions that logically return T.
95 template <typename T>
96 using Callback = llvm::unique_function<void(llvm::Expected<T>)>;
97 
98 /// An OutgoingNotification<T> is a function used for outgoing notifications
99 /// send to the client.
100 template <typename T>
101 using OutgoingNotification = llvm::unique_function<void(const T &)>;
102 
103 /// A handler used to process the incoming transport messages.
105 public:
106  MessageHandler(JSONTransport &transport) : transport(transport) {}
107 
108  bool onNotify(StringRef method, llvm::json::Value value);
109  bool onCall(StringRef method, llvm::json::Value params, llvm::json::Value id);
110  bool onReply(llvm::json::Value id, llvm::Expected<llvm::json::Value> result);
111 
112  template <typename T>
113  static llvm::Expected<T> parse(const llvm::json::Value &raw,
114  StringRef payloadName, StringRef payloadKind) {
115  T result;
116  llvm::json::Path::Root root;
117  if (fromJSON(raw, result, root))
118  return std::move(result);
119 
120  // Dump the relevant parts of the broken message.
121  std::string context;
122  llvm::raw_string_ostream os(context);
123  root.printErrorContext(raw, os);
124 
125  // Report the error (e.g. to the client).
126  return llvm::make_error<LSPError>(
127  llvm::formatv("failed to decode {0} {1}: {2}", payloadName, payloadKind,
128  fmt_consume(root.getError())),
130  }
131 
132  template <typename Param, typename Result, typename ThisT>
133  void method(llvm::StringLiteral method, ThisT *thisPtr,
134  void (ThisT::*handler)(const Param &, Callback<Result>)) {
135  methodHandlers[method] = [method, handler,
136  thisPtr](llvm::json::Value rawParams,
138  llvm::Expected<Param> param = parse<Param>(rawParams, method, "request");
139  if (!param)
140  return reply(param.takeError());
141  (thisPtr->*handler)(*param, std::move(reply));
142  };
143  }
144 
145  template <typename Param, typename ThisT>
146  void notification(llvm::StringLiteral method, ThisT *thisPtr,
147  void (ThisT::*handler)(const Param &)) {
148  notificationHandlers[method] = [method, handler,
149  thisPtr](llvm::json::Value rawParams) {
150  llvm::Expected<Param> param = parse<Param>(rawParams, method, "request");
151  if (!param)
152  return llvm::consumeError(param.takeError());
153  (thisPtr->*handler)(*param);
154  };
155  }
156 
157  /// Create an OutgoingNotification object used for the given method.
158  template <typename T>
160  return [&, method](const T &params) {
161  std::lock_guard<std::mutex> transportLock(transportOutputMutex);
162  Logger::info("--> {0}", method);
163  transport.notify(method, llvm::json::Value(params));
164  };
165  }
166 
167 private:
168  template <typename HandlerT>
169  using HandlerMap = llvm::StringMap<llvm::unique_function<HandlerT>>;
170 
171  HandlerMap<void(llvm::json::Value)> notificationHandlers;
172  HandlerMap<void(llvm::json::Value, Callback<llvm::json::Value>)>
173  methodHandlers;
174 
175  JSONTransport &transport;
176 
177  /// Mutex to guard sending output messages to the transport.
178  std::mutex transportOutputMutex;
179 };
180 
181 } // namespace lsp
182 } // namespace mlir
183 
184 #endif
A transport class that performs the JSON-RPC communication with the LSP client.
Definition: Transport.h:48
void notify(StringRef method, llvm::json::Value params)
The following methods are used to send a message to the LSP client.
Definition: Transport.cpp:168
JSONTransport(std::FILE *in, raw_ostream &out, JSONStreamStyle style=JSONStreamStyle::Standard, bool prettyOutput=false)
Definition: Transport.h:50
void call(StringRef method, llvm::json::Value params, llvm::json::Value id)
Definition: Transport.cpp:175
llvm::Error run(MessageHandler &handler)
Start executing the JSON-RPC transport.
Definition: Transport.cpp:201
void reply(llvm::json::Value id, llvm::Expected< llvm::json::Value > result)
Definition: Transport.cpp:184
static void info(const char *fmt, Ts &&...vals)
Definition: Logging.h:38
A handler used to process the incoming transport messages.
Definition: Transport.h:104
void notification(llvm::StringLiteral method, ThisT *thisPtr, void(ThisT::*handler)(const Param &))
Definition: Transport.h:146
bool onCall(StringRef method, llvm::json::Value params, llvm::json::Value id)
Definition: Transport.cpp:101
void method(llvm::StringLiteral method, ThisT *thisPtr, void(ThisT::*handler)(const Param &, Callback< Result >))
Definition: Transport.h:133
bool onReply(llvm::json::Value id, llvm::Expected< llvm::json::Value > result)
Definition: Transport.cpp:117
OutgoingNotification< T > outgoingNotification(llvm::StringLiteral method)
Create an OutgoingNotification object used for the given method.
Definition: Transport.h:159
static llvm::Expected< T > parse(const llvm::json::Value &raw, StringRef payloadName, StringRef payloadKind)
Definition: Transport.h:113
MessageHandler(JSONTransport &transport)
Definition: Transport.h:106
bool onNotify(StringRef method, llvm::json::Value value)
Definition: Transport.cpp:86
llvm::unique_function< void(const T &)> OutgoingNotification
An OutgoingNotification<T> is a function used for outgoing notifications send to the client.
Definition: Transport.h:101
llvm::unique_function< void(llvm::Expected< T >)> Callback
A Callback<T> is a void function that accepts Expected<T>.
Definition: Transport.h:96
JSONStreamStyle
The encoding style of the JSON-RPC messages (both input and output).
Definition: Transport.h:39
@ Delimited
Messages are delimited by a '// --—' line. Comment lines start with //.
Definition: Transport.h:43
@ Standard
Encoding per the LSP specification, with mandatory Content-Length header.
Definition: Transport.h:41
bool fromJSON(const llvm::json::Value &value, URIForFile &result, llvm::json::Path path)
Definition: Protocol.cpp:248
This header declares functions that assist transformations in the MemRef dialect.
This class represents an efficient way to signal success or failure.
Definition: LogicalResult.h:26