clang-tools  10.0.0
PathMapping.cpp
Go to the documentation of this file.
1 //===--- PathMapping.cpp - apply path mappings to LSP messages -===//
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 #include "PathMapping.h"
9 #include "Transport.h"
10 #include "URI.h"
11 #include "llvm/ADT/None.h"
12 #include "llvm/ADT/STLExtras.h"
13 #include "llvm/Support/Errno.h"
14 #include "llvm/Support/Error.h"
15 #include "llvm/Support/Path.h"
16 #include <algorithm>
17 #include <tuple>
18 
19 namespace clang {
20 namespace clangd {
21 llvm::Optional<std::string> doPathMapping(llvm::StringRef S,
23  const PathMappings &Mappings) {
24  // Retrun early to optimize for the common case, wherein S is not a file URI
25  if (!S.startswith("file://"))
26  return llvm::None;
27  auto Uri = URI::parse(S);
28  if (!Uri) {
29  llvm::consumeError(Uri.takeError());
30  return llvm::None;
31  }
32  for (const auto &Mapping : Mappings) {
33  const std::string &From = Dir == PathMapping::Direction::ClientToServer
34  ? Mapping.ClientPath
35  : Mapping.ServerPath;
36  const std::string &To = Dir == PathMapping::Direction::ClientToServer
37  ? Mapping.ServerPath
38  : Mapping.ClientPath;
39  llvm::StringRef Body = Uri->body();
40  if (Body.consume_front(From) && (Body.empty() || Body.front() == '/')) {
41  std::string MappedBody = (To + Body).str();
42  return URI(Uri->scheme(), Uri->authority(), MappedBody.c_str())
43  .toString();
44  }
45  }
46  return llvm::None;
47 }
48 
49 void applyPathMappings(llvm::json::Value &V, PathMapping::Direction Dir,
50  const PathMappings &Mappings) {
52  Kind K = V.kind();
53  if (K == Kind::Object) {
54  llvm::json::Object *Obj = V.getAsObject();
55  llvm::json::Object MappedObj;
56  // 1. Map all the Keys
57  for (auto &KV : *Obj) {
58  if (llvm::Optional<std::string> MappedKey =
59  doPathMapping(KV.first.str(), Dir, Mappings)) {
60  MappedObj.try_emplace(std::move(*MappedKey), std::move(KV.second));
61  } else {
62  MappedObj.try_emplace(std::move(KV.first), std::move(KV.second));
63  }
64  }
65  *Obj = std::move(MappedObj);
66  // 2. Map all the values
67  for (auto &KV : *Obj)
68  applyPathMappings(KV.second, Dir, Mappings);
69  } else if (K == Kind::Array) {
70  for (llvm::json::Value &Val : *V.getAsArray())
71  applyPathMappings(Val, Dir, Mappings);
72  } else if (K == Kind::String) {
73  if (llvm::Optional<std::string> Mapped =
74  doPathMapping(*V.getAsString(), Dir, Mappings))
75  V = std::move(*Mapped);
76  }
77 }
78 
79 namespace {
80 
81 class PathMappingMessageHandler : public Transport::MessageHandler {
82 public:
83  PathMappingMessageHandler(MessageHandler &Handler,
84  const PathMappings &Mappings)
85  : WrappedHandler(Handler), Mappings(Mappings) {}
86 
87  bool onNotify(llvm::StringRef Method, llvm::json::Value Params) override {
89  return WrappedHandler.onNotify(Method, std::move(Params));
90  }
91 
92  bool onCall(llvm::StringRef Method, llvm::json::Value Params,
93  llvm::json::Value ID) override {
95  return WrappedHandler.onCall(Method, std::move(Params), std::move(ID));
96  }
97 
98  bool onReply(llvm::json::Value ID,
99  llvm::Expected<llvm::json::Value> Result) override {
100  if (Result)
102  Mappings);
103  return WrappedHandler.onReply(std::move(ID), std::move(Result));
104  }
105 
106 private:
107  Transport::MessageHandler &WrappedHandler;
108  const PathMappings &Mappings;
109 };
110 
111 // Apply path mappings to all LSP messages by intercepting all params/results
112 // and then delegating to the normal transport
113 class PathMappingTransport : public Transport {
114 public:
115  PathMappingTransport(std::unique_ptr<Transport> Transp, PathMappings Mappings)
116  : WrappedTransport(std::move(Transp)), Mappings(std::move(Mappings)) {}
117 
118  void notify(llvm::StringRef Method, llvm::json::Value Params) override {
120  WrappedTransport->notify(Method, std::move(Params));
121  }
122 
123  void call(llvm::StringRef Method, llvm::json::Value Params,
124  llvm::json::Value ID) override {
126  WrappedTransport->call(Method, std::move(Params), std::move(ID));
127  }
128 
129  void reply(llvm::json::Value ID,
130  llvm::Expected<llvm::json::Value> Result) override {
131  if (Result)
133  Mappings);
134  WrappedTransport->reply(std::move(ID), std::move(Result));
135  }
136 
137  llvm::Error loop(MessageHandler &Handler) override {
138  PathMappingMessageHandler WrappedHandler(Handler, Mappings);
139  return WrappedTransport->loop(WrappedHandler);
140  }
141 
142 private:
143  std::unique_ptr<Transport> WrappedTransport;
144  PathMappings Mappings;
145 };
146 
147 // Converts a unix/windows path to the path portion of a file URI
148 // e.g. "C:\foo" -> "/C:/foo"
149 llvm::Expected<std::string> parsePath(llvm::StringRef Path) {
150  namespace path = llvm::sys::path;
151  if (path::is_absolute(Path, path::Style::posix)) {
152  return Path;
153  } else if (path::is_absolute(Path, path::Style::windows)) {
154  std::string Converted = path::convert_to_slash(Path, path::Style::windows);
155  if (Converted.front() != '/')
156  Converted = "/" + Converted;
157  return Converted;
158  }
159  return llvm::createStringError(llvm::inconvertibleErrorCode(),
160  "Path not absolute: " + Path);
161 }
162 
163 } // namespace
164 
165 llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const PathMapping &M) {
166  return OS << M.ClientPath << "=" << M.ServerPath;
167 }
168 
169 llvm::Expected<PathMappings>
170 parsePathMappings(llvm::StringRef RawPathMappings) {
171  llvm::StringRef ClientPath, ServerPath, PathPair, Rest = RawPathMappings;
172  PathMappings ParsedMappings;
173  while (!Rest.empty()) {
174  std::tie(PathPair, Rest) = Rest.split(",");
175  std::tie(ClientPath, ServerPath) = PathPair.split("=");
176  if (ClientPath.empty() || ServerPath.empty())
177  return llvm::createStringError(llvm::inconvertibleErrorCode(),
178  "Not a valid path mapping pair: " +
179  PathPair);
180  llvm::Expected<std::string> ParsedClientPath = parsePath(ClientPath);
181  if (!ParsedClientPath)
182  return ParsedClientPath.takeError();
183  llvm::Expected<std::string> ParsedServerPath = parsePath(ServerPath);
184  if (!ParsedServerPath)
185  return ParsedServerPath.takeError();
186  ParsedMappings.push_back(
187  {std::move(*ParsedClientPath), std::move(*ParsedServerPath)});
188  }
189  return ParsedMappings;
190 }
191 
192 std::unique_ptr<Transport>
193 createPathMappingTransport(std::unique_ptr<Transport> Transp,
194  PathMappings Mappings) {
195  return std::make_unique<PathMappingTransport>(std::move(Transp), Mappings);
196 }
197 
198 } // namespace clangd
199 } // namespace clang
PathMappings are a collection of paired client and server paths.
Definition: PathMapping.h:33
std::vector< PathMapping > PathMappings
Definition: PathMapping.h:38
Documents should not be synced at all.
void applyPathMappings(llvm::json::Value &V, PathMapping::Direction Dir, const PathMappings &Mappings)
Applies the Mappings to all the file:// URIs in Params.
Definition: PathMapping.cpp:49
BindArgumentKind Kind
static const char * toString(OffsetEncoding OE)
Definition: Protocol.cpp:1031
std::string Path
A typedef to represent a file path.
Definition: Path.h:20
llvm::Expected< PathMappings > parsePathMappings(llvm::StringRef RawPathMappings)
Parse the command line RawPathMappings (e.g.
std::unique_ptr< Transport > createPathMappingTransport(std::unique_ptr< Transport > Transp, PathMappings Mappings)
Creates a wrapping transport over Transp that applies the Mappings to all inbound and outbound LSP me...
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
A URI describes the location of a source file.
Definition: URI.h:28
static llvm::Expected< URI > parse(llvm::StringRef Uri)
Parse a URI string "<scheme>:[//<authority>/]<path>".
Definition: URI.cpp:164
llvm::raw_ostream & operator<<(llvm::raw_ostream &OS, const CodeCompletion &C)
llvm::Optional< std::string > doPathMapping(llvm::StringRef S, PathMapping::Direction Dir, const PathMappings &Mappings)
Returns a modified S with the first matching path in Mappings substituted, if applicable.
Definition: PathMapping.cpp:21