clang-tools  15.0.0git
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 "support/Logger.h"
12 #include "llvm/ADT/None.h"
13 #include "llvm/Support/Error.h"
14 #include "llvm/Support/Path.h"
15 #include <algorithm>
16 #include <tuple>
17 
18 namespace clang {
19 namespace clangd {
20 llvm::Optional<std::string> doPathMapping(llvm::StringRef S,
22  const PathMappings &Mappings) {
23  // Return early to optimize for the common case, wherein S is not a file URI
24  if (!S.startswith("file://"))
25  return llvm::None;
26  auto Uri = URI::parse(S);
27  if (!Uri) {
28  llvm::consumeError(Uri.takeError());
29  return llvm::None;
30  }
31  for (const auto &Mapping : Mappings) {
32  const std::string &From = Dir == PathMapping::Direction::ClientToServer
33  ? Mapping.ClientPath
34  : Mapping.ServerPath;
35  const std::string &To = Dir == PathMapping::Direction::ClientToServer
36  ? Mapping.ServerPath
37  : Mapping.ClientPath;
38  llvm::StringRef Body = Uri->body();
39  if (Body.consume_front(From) && (Body.empty() || Body.front() == '/')) {
40  std::string MappedBody = (To + Body).str();
41  return URI(Uri->scheme(), Uri->authority(), MappedBody)
42  .toString();
43  }
44  }
45  return llvm::None;
46 }
47 
49  const PathMappings &Mappings) {
51  Kind K = V.kind();
52  if (K == Kind::Object) {
53  llvm::json::Object *Obj = V.getAsObject();
54  llvm::json::Object MappedObj;
55  // 1. Map all the Keys
56  for (auto &KV : *Obj) {
57  if (llvm::Optional<std::string> MappedKey =
58  doPathMapping(KV.first.str(), Dir, Mappings)) {
59  MappedObj.try_emplace(std::move(*MappedKey), std::move(KV.second));
60  } else {
61  MappedObj.try_emplace(std::move(KV.first), std::move(KV.second));
62  }
63  }
64  *Obj = std::move(MappedObj);
65  // 2. Map all the values
66  for (auto &KV : *Obj)
67  applyPathMappings(KV.second, Dir, Mappings);
68  } else if (K == Kind::Array) {
69  for (llvm::json::Value &Val : *V.getAsArray())
70  applyPathMappings(Val, Dir, Mappings);
71  } else if (K == Kind::String) {
72  if (llvm::Optional<std::string> Mapped =
73  doPathMapping(*V.getAsString(), Dir, Mappings))
74  V = std::move(*Mapped);
75  }
76 }
77 
78 namespace {
79 
80 class PathMappingMessageHandler : public Transport::MessageHandler {
81 public:
82  PathMappingMessageHandler(MessageHandler &Handler,
83  const PathMappings &Mappings)
84  : WrappedHandler(Handler), Mappings(Mappings) {}
85 
86  bool onNotify(llvm::StringRef Method, llvm::json::Value Params) override {
88  return WrappedHandler.onNotify(Method, std::move(Params));
89  }
90 
91  bool onCall(llvm::StringRef Method, llvm::json::Value Params,
92  llvm::json::Value ID) override {
94  return WrappedHandler.onCall(Method, std::move(Params), std::move(ID));
95  }
96 
97  bool onReply(llvm::json::Value ID,
98  llvm::Expected<llvm::json::Value> Result) override {
99  if (Result)
101  Mappings);
102  return WrappedHandler.onReply(std::move(ID), std::move(Result));
103  }
104 
105 private:
106  Transport::MessageHandler &WrappedHandler;
107  const PathMappings &Mappings;
108 };
109 
110 // Apply path mappings to all LSP messages by intercepting all params/results
111 // and then delegating to the normal transport
112 class PathMappingTransport : public Transport {
113 public:
114  PathMappingTransport(std::unique_ptr<Transport> Transp, PathMappings Mappings)
115  : WrappedTransport(std::move(Transp)), Mappings(std::move(Mappings)) {}
116 
117  void notify(llvm::StringRef Method, llvm::json::Value Params) override {
119  WrappedTransport->notify(Method, std::move(Params));
120  }
121 
122  void call(llvm::StringRef Method, llvm::json::Value Params,
123  llvm::json::Value ID) override {
125  WrappedTransport->call(Method, std::move(Params), std::move(ID));
126  }
127 
128  void reply(llvm::json::Value ID,
129  llvm::Expected<llvm::json::Value> Result) override {
130  if (Result)
132  Mappings);
133  WrappedTransport->reply(std::move(ID), std::move(Result));
134  }
135 
136  llvm::Error loop(MessageHandler &Handler) override {
137  PathMappingMessageHandler WrappedHandler(Handler, Mappings);
138  return WrappedTransport->loop(WrappedHandler);
139  }
140 
141 private:
142  std::unique_ptr<Transport> WrappedTransport;
143  PathMappings Mappings;
144 };
145 
146 // Converts a unix/windows path to the path portion of a file URI
147 // e.g. "C:\foo" -> "/C:/foo"
148 llvm::Expected<std::string> parsePath(llvm::StringRef Path) {
149  namespace path = llvm::sys::path;
150  if (path::is_absolute(Path, path::Style::posix)) {
151  return std::string(Path);
152  }
153  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 error("Path not absolute: {0}", Path);
160 }
161 
162 } // namespace
163 
164 llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const PathMapping &M) {
165  return OS << M.ClientPath << "=" << M.ServerPath;
166 }
167 
168 llvm::Expected<PathMappings>
169 parsePathMappings(llvm::StringRef RawPathMappings) {
170  llvm::StringRef ClientPath, ServerPath, PathPair, Rest = RawPathMappings;
171  PathMappings ParsedMappings;
172  while (!Rest.empty()) {
173  std::tie(PathPair, Rest) = Rest.split(",");
174  std::tie(ClientPath, ServerPath) = PathPair.split("=");
175  if (ClientPath.empty() || ServerPath.empty())
176  return error("Not a valid path mapping pair: {0}", PathPair);
177  llvm::Expected<std::string> ParsedClientPath = parsePath(ClientPath);
178  if (!ParsedClientPath)
179  return ParsedClientPath.takeError();
180  llvm::Expected<std::string> ParsedServerPath = parsePath(ServerPath);
181  if (!ParsedServerPath)
182  return ParsedServerPath.takeError();
183  ParsedMappings.push_back(
184  {std::move(*ParsedClientPath), std::move(*ParsedServerPath)});
185  }
186  return ParsedMappings;
187 }
188 
189 std::unique_ptr<Transport>
190 createPathMappingTransport(std::unique_ptr<Transport> Transp,
191  PathMappings Mappings) {
192  return std::make_unique<PathMappingTransport>(std::move(Transp), Mappings);
193 }
194 
195 } // namespace clangd
196 } // namespace clang
clang::clangd::Obj
llvm::json::Object Obj
Definition: LSPClient.cpp:172
clang::clangd::Path
std::string Path
A typedef to represent a file path.
Definition: Path.h:26
clang::clangd::error
llvm::Error error(std::error_code EC, const char *Fmt, Ts &&... Vals)
Definition: Logger.h:79
Kind
BindArgumentKind Kind
Definition: AvoidBindCheck.cpp:59
clang::clangd::PathMapping::Direction::ClientToServer
@ ClientToServer
clang::clangd::URI::parse
static llvm::Expected< URI > parse(llvm::StringRef Uri)
Parse a URI string "<scheme>:[//<authority>/]<path>".
Definition: URI.cpp:177
clang::clangd::PathMappings
std::vector< PathMapping > PathMappings
Definition: PathMapping.h:42
M
const google::protobuf::Message & M
Definition: Server.cpp:309
clang::clangd::applyPathMappings
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:48
clang::clangd::PathMapping::Direction::ServerToClient
@ ServerToClient
Logger.h
PathMapping.h
clang::clangd::operator<<
llvm::raw_ostream & operator<<(llvm::raw_ostream &OS, const CodeCompletion &C)
Definition: CodeComplete.cpp:2182
Transport.h
clang::clangd::URI::toString
std::string toString() const
Returns a string URI with all components percent-encoded.
Definition: URI.cpp:160
clang::clangd::doPathMapping
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:20
ID
static char ID
Definition: Logger.cpp:74
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
OS
llvm::raw_string_ostream OS
Definition: TraceTests.cpp:160
clang::clangd::parsePathMappings
llvm::Expected< PathMappings > parsePathMappings(llvm::StringRef RawPathMappings)
Parse the command line RawPathMappings (e.g.
Definition: PathMapping.cpp:169
URI.h
clang::clangd::createPathMappingTransport
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...
Definition: PathMapping.cpp:190
clang::clangd::URI
A URI describes the location of a source file.
Definition: URI.h:28
K
Kind K
Definition: Rename.cpp:436
clang::clangd::PathMapping
PathMappings are a collection of paired client and server paths.
Definition: PathMapping.h:37
Value
static constexpr bool Value
Definition: SuspiciousCallArgumentCheck.cpp:72
clang::clangd::PathMapping::Direction
Direction
Definition: PathMapping.h:40