brintos

brintos / llvm-project-archived public Read only

0
0
Text · 7.0 KiB · 4b93ff2 Raw
197 lines · cpp
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-exception6//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,21                                         PathMapping::Direction Dir,22                                         const PathMappings &Mappings) {23  // Return early to optimize for the common case, wherein S is not a file URI24  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::ClientToServer33                                  ? Mapping.ClientPath34                                  : Mapping.ServerPath;35    const std::string &To = Dir == PathMapping::Direction::ClientToServer36                                ? Mapping.ServerPath37                                : 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 Keys56    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 values66    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 {87    applyPathMappings(Params, PathMapping::Direction::ClientToServer, Mappings);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 {93    applyPathMappings(Params, PathMapping::Direction::ClientToServer, Mappings);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)100      applyPathMappings(*Result, PathMapping::Direction::ClientToServer,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/results111// and then delegating to the normal transport112class 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 {118    applyPathMappings(Params, PathMapping::Direction::ServerToClient, Mappings);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 {124    applyPathMappings(Params, PathMapping::Direction::ServerToClient, Mappings);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)131      applyPathMappings(*Result, PathMapping::Direction::ServerToClient,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 URI147// 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} // namespace163 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 clangd196} // namespace clang197