Mercurial > hg > CbC > CbC_llvm
view clang-tools-extra/clangd/PathMapping.cpp @ 150:1d019706d866
LLVM10
author | anatofuz |
---|---|
date | Thu, 13 Feb 2020 15:10:13 +0900 |
parents | |
children | 0572611fdcc8 |
line wrap: on
line source
//===--- PathMapping.cpp - apply path mappings to LSP messages -===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "PathMapping.h" #include "Transport.h" #include "URI.h" #include "llvm/ADT/None.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Support/Errno.h" #include "llvm/Support/Error.h" #include "llvm/Support/Path.h" #include <algorithm> #include <tuple> namespace clang { namespace clangd { llvm::Optional<std::string> doPathMapping(llvm::StringRef S, PathMapping::Direction Dir, const PathMappings &Mappings) { // Retrun early to optimize for the common case, wherein S is not a file URI if (!S.startswith("file://")) return llvm::None; auto Uri = URI::parse(S); if (!Uri) { llvm::consumeError(Uri.takeError()); return llvm::None; } for (const auto &Mapping : Mappings) { const std::string &From = Dir == PathMapping::Direction::ClientToServer ? Mapping.ClientPath : Mapping.ServerPath; const std::string &To = Dir == PathMapping::Direction::ClientToServer ? Mapping.ServerPath : Mapping.ClientPath; llvm::StringRef Body = Uri->body(); if (Body.consume_front(From) && (Body.empty() || Body.front() == '/')) { std::string MappedBody = (To + Body).str(); return URI(Uri->scheme(), Uri->authority(), MappedBody.c_str()) .toString(); } } return llvm::None; } void applyPathMappings(llvm::json::Value &V, PathMapping::Direction Dir, const PathMappings &Mappings) { using Kind = llvm::json::Value::Kind; Kind K = V.kind(); if (K == Kind::Object) { llvm::json::Object *Obj = V.getAsObject(); llvm::json::Object MappedObj; // 1. Map all the Keys for (auto &KV : *Obj) { if (llvm::Optional<std::string> MappedKey = doPathMapping(KV.first.str(), Dir, Mappings)) { MappedObj.try_emplace(std::move(*MappedKey), std::move(KV.second)); } else { MappedObj.try_emplace(std::move(KV.first), std::move(KV.second)); } } *Obj = std::move(MappedObj); // 2. Map all the values for (auto &KV : *Obj) applyPathMappings(KV.second, Dir, Mappings); } else if (K == Kind::Array) { for (llvm::json::Value &Val : *V.getAsArray()) applyPathMappings(Val, Dir, Mappings); } else if (K == Kind::String) { if (llvm::Optional<std::string> Mapped = doPathMapping(*V.getAsString(), Dir, Mappings)) V = std::move(*Mapped); } } namespace { class PathMappingMessageHandler : public Transport::MessageHandler { public: PathMappingMessageHandler(MessageHandler &Handler, const PathMappings &Mappings) : WrappedHandler(Handler), Mappings(Mappings) {} bool onNotify(llvm::StringRef Method, llvm::json::Value Params) override { applyPathMappings(Params, PathMapping::Direction::ClientToServer, Mappings); return WrappedHandler.onNotify(Method, std::move(Params)); } bool onCall(llvm::StringRef Method, llvm::json::Value Params, llvm::json::Value ID) override { applyPathMappings(Params, PathMapping::Direction::ClientToServer, Mappings); return WrappedHandler.onCall(Method, std::move(Params), std::move(ID)); } bool onReply(llvm::json::Value ID, llvm::Expected<llvm::json::Value> Result) override { if (Result) applyPathMappings(*Result, PathMapping::Direction::ClientToServer, Mappings); return WrappedHandler.onReply(std::move(ID), std::move(Result)); } private: Transport::MessageHandler &WrappedHandler; const PathMappings &Mappings; }; // Apply path mappings to all LSP messages by intercepting all params/results // and then delegating to the normal transport class PathMappingTransport : public Transport { public: PathMappingTransport(std::unique_ptr<Transport> Transp, PathMappings Mappings) : WrappedTransport(std::move(Transp)), Mappings(std::move(Mappings)) {} void notify(llvm::StringRef Method, llvm::json::Value Params) override { applyPathMappings(Params, PathMapping::Direction::ServerToClient, Mappings); WrappedTransport->notify(Method, std::move(Params)); } void call(llvm::StringRef Method, llvm::json::Value Params, llvm::json::Value ID) override { applyPathMappings(Params, PathMapping::Direction::ServerToClient, Mappings); WrappedTransport->call(Method, std::move(Params), std::move(ID)); } void reply(llvm::json::Value ID, llvm::Expected<llvm::json::Value> Result) override { if (Result) applyPathMappings(*Result, PathMapping::Direction::ServerToClient, Mappings); WrappedTransport->reply(std::move(ID), std::move(Result)); } llvm::Error loop(MessageHandler &Handler) override { PathMappingMessageHandler WrappedHandler(Handler, Mappings); return WrappedTransport->loop(WrappedHandler); } private: std::unique_ptr<Transport> WrappedTransport; PathMappings Mappings; }; // Converts a unix/windows path to the path portion of a file URI // e.g. "C:\foo" -> "/C:/foo" llvm::Expected<std::string> parsePath(llvm::StringRef Path) { namespace path = llvm::sys::path; if (path::is_absolute(Path, path::Style::posix)) { return std::string(Path); } else if (path::is_absolute(Path, path::Style::windows)) { std::string Converted = path::convert_to_slash(Path, path::Style::windows); if (Converted.front() != '/') Converted = "/" + Converted; return Converted; } return llvm::createStringError(llvm::inconvertibleErrorCode(), "Path not absolute: " + Path); } } // namespace llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const PathMapping &M) { return OS << M.ClientPath << "=" << M.ServerPath; } llvm::Expected<PathMappings> parsePathMappings(llvm::StringRef RawPathMappings) { llvm::StringRef ClientPath, ServerPath, PathPair, Rest = RawPathMappings; PathMappings ParsedMappings; while (!Rest.empty()) { std::tie(PathPair, Rest) = Rest.split(","); std::tie(ClientPath, ServerPath) = PathPair.split("="); if (ClientPath.empty() || ServerPath.empty()) return llvm::createStringError(llvm::inconvertibleErrorCode(), "Not a valid path mapping pair: " + PathPair); llvm::Expected<std::string> ParsedClientPath = parsePath(ClientPath); if (!ParsedClientPath) return ParsedClientPath.takeError(); llvm::Expected<std::string> ParsedServerPath = parsePath(ServerPath); if (!ParsedServerPath) return ParsedServerPath.takeError(); ParsedMappings.push_back( {std::move(*ParsedClientPath), std::move(*ParsedServerPath)}); } return ParsedMappings; } std::unique_ptr<Transport> createPathMappingTransport(std::unique_ptr<Transport> Transp, PathMappings Mappings) { return std::make_unique<PathMappingTransport>(std::move(Transp), Mappings); } } // namespace clangd } // namespace clang