clang-tools 20.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/Support/Error.h"
13#include "llvm/Support/Path.h"
14#include <algorithm>
15#include <optional>
16#include <tuple>
17
18namespace clang {
19namespace clangd {
20std::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.starts_with("file://"))
25 return std::nullopt;
26 auto Uri = URI::parse(S);
27 if (!Uri) {
28 llvm::consumeError(Uri.takeError());
29 return std::nullopt;
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 std::nullopt;
46}
47
48void applyPathMappings(llvm::json::Value &V, PathMapping::Direction Dir,
49 const PathMappings &Mappings) {
50 using Kind = llvm::json::Value::Kind;
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 (std::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 (std::optional<std::string> Mapped =
73 doPathMapping(*V.getAsString(), Dir, Mappings))
74 V = std::move(*Mapped);
75 }
76}
77
78namespace {
79
80class PathMappingMessageHandler : public Transport::MessageHandler {
81public:
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
105private:
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
112class PathMappingTransport : public Transport {
113public:
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
141private:
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"
148llvm::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
164llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const PathMapping &M) {
165 return OS << M.ClientPath << "=" << M.ServerPath;
166}
167
168llvm::Expected<PathMappings>
169parsePathMappings(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
189std::unique_ptr<Transport>
190createPathMappingTransport(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
BindArgumentKind Kind
Kind K
Definition: Rename.cpp:474
const google::protobuf::Message & M
Definition: Server.cpp:309
llvm::raw_string_ostream OS
Definition: TraceTests.cpp:160
A URI describes the location of a source file.
Definition: URI.h:28
std::string toString() const
Returns a string URI with all components percent-encoded.
Definition: URI.cpp:160
static llvm::Expected< URI > parse(llvm::StringRef Uri)
Parse a URI string "<scheme>:[//<authority>/]<path>".
Definition: URI.cpp:176
std::string Path
A typedef to represent a file path.
Definition: Path.h:26
llvm::json::Object Obj
Definition: LSPClient.cpp:217
llvm::Error error(std::error_code EC, const char *Fmt, Ts &&... Vals)
Definition: Logger.h:79
llvm::Expected< PathMappings > parsePathMappings(llvm::StringRef RawPathMappings)
Parse the command line RawPathMappings (e.g.
llvm::raw_ostream & operator<<(llvm::raw_ostream &OS, const CodeCompletion &C)
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
std::vector< PathMapping > PathMappings
Definition: PathMapping.h:42
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...
std::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
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
PathMappings are a collection of paired client and server paths.
Definition: PathMapping.h:37