ioeric created this revision.
ioeric added a reviewer: sammccall.
Herald added subscribers: cfe-commits, ilya-biryukov, mgorny, klimek.

DO NOT SUBMIT: We should replace the existing URI struct in Protocol.h
with the new URI and rename FileURI to URI. But before doing that, I'd like to
early feedback on names and APIs.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D41946

Files:
  clangd/CMakeLists.txt
  clangd/URI.cpp
  clangd/URI.h
  unittests/clangd/CMakeLists.txt
  unittests/clangd/URITests.cpp

Index: unittests/clangd/URITests.cpp
===================================================================
--- /dev/null
+++ unittests/clangd/URITests.cpp
@@ -0,0 +1,75 @@
+//===-- URITests.cpp  ---------------------------------*- C++ -*-----------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "URI.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace clangd {
+namespace {
+
+TEST(URITest, URIStrings) {
+  EXPECT_EQ(FileURI::fromURI("abc:///x/y/z").toString(), "abc:///x/y/z");
+  EXPECT_EQ(FileURI::fromURI("abc:///x/y/z"), FileURI("abc", "/x/y/z"));
+  EXPECT_EQ(FileURI::fromURI("abc://x/y/z"), FileURI("abc", "x/y/z"));
+
+  EXPECT_TRUE(FileURI::fromURI("abc:///x/y/z").IsValid());
+  EXPECT_FALSE(FileURI::fromURI("://x/y/z").IsValid());
+  EXPECT_FALSE(FileURI::fromURI("/x/y/z").IsValid());
+}
+
+TEST(URITest, FileSystemSchema) {
+  FileURI Uri{"file", "/a/b/c"};
+  FileSystemSchema S;
+  EXPECT_EQ(S.getAbsolutePath(/*CurrentFile*/ "", Uri), "/a/b/c");
+  EXPECT_EQ(S.uriFromAbsolutePath("/a/b/c"), Uri);
+}
+
+// Assume all files in the schema have a "test-root/" root directory, and the
+// schema path is the relative path to the root directory.
+// So the schema of "/some-dir/test-root/x/y/z" is "test://x/y/z".
+class TestSchema : public URISchema {
+public:
+  static const char *Schema;
+
+  static const char *TestRoot;
+
+  std::string getAbsolutePath(llvm::StringRef CurrentFile,
+                              const FileURI &Uri) const override {
+    auto Pos = CurrentFile.find(TestRoot);
+    assert(Pos != llvm::StringRef::npos);
+    return CurrentFile.substr(0, Pos + llvm::StringRef(TestRoot).size()).str() +
+           Uri.Path;
+  }
+
+  FileURI uriFromAbsolutePath(llvm::StringRef AbsolutePath) const override {
+    auto Pos = AbsolutePath.find(TestRoot);
+    assert(Pos != llvm::StringRef::npos);
+    return {Schema,
+            AbsolutePath.substr(Pos + Pos + llvm::StringRef(TestRoot).size())};
+  }
+};
+
+const char *TestSchema::Schema = "test";
+const char *TestSchema::TestRoot = "/test-root/";
+
+static URISchemaRegistry::Add<TestSchema> X(TestSchema::Schema, "Test schema");
+
+TEST(URITest, ResolveURIs) {
+  EXPECT_EQ(resolve("", FileURI("file", "/a/b/c")), "/a/b/c");
+  EXPECT_EQ(resolve("/dir/test-root/x/y/z", FileURI("test", "a/b/c")),
+            "/dir/test-root/a/b/c");
+  // Unsupported schema.
+  EXPECT_EQ(resolve("/x/y/z", FileURI("invalid", "a/b/c")), "");
+}
+
+} // namespace
+} // namespace clangd
+} // namespace clang
Index: unittests/clangd/CMakeLists.txt
===================================================================
--- unittests/clangd/CMakeLists.txt
+++ unittests/clangd/CMakeLists.txt
@@ -18,6 +18,7 @@
   FuzzyMatchTests.cpp
   IndexTests.cpp
   JSONExprTests.cpp
+  URITests.cpp
   TestFS.cpp
   TraceTests.cpp
   SourceCodeTests.cpp
Index: clangd/URI.h
===================================================================
--- /dev/null
+++ clangd/URI.h
@@ -0,0 +1,95 @@
+//===--- URI.h - File URIs with schemas --------------------------*- C++-*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_PATHURI_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_PATHURI_H
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Registry.h"
+
+namespace clang {
+namespace clangd {
+
+/// \brief An URI for a file path in a certain schema. This is invalid if either
+/// schema or path is empty.
+struct FileURI {
+  std::string Schema;
+  // This is interpreted according to the schema.
+  std::string Path;
+
+  FileURI() = default;
+
+  FileURI(std::string Schema, std::string Path)
+      : Schema(std::move(Schema)), Path(std::move(Path)) {}
+
+  bool IsValid() const { return !Schema.empty() && !Path.empty(); }
+
+  /// \brief Returns a URI string "<schema>://<path>".
+  std::string toString() const;
+
+  /// \brief Parse a URI string "<schema>://<path>". If this is an invalid URI,
+  /// this returns an empty URI with empty schema and path.
+  static FileURI fromURI(llvm::StringRef Uri);
+
+  friend bool operator==(const FileURI &LHS, const FileURI &RHS) {
+    return std::tie(LHS.Schema, LHS.Path) == std::tie(RHS.Schema, RHS.Path);
+  }
+};
+
+/// \brief This manages file paths in different schemas. Different
+/// codebases/projects can have different file schemas, and clangd interprets a
+/// file path according to the schema. For example, a file path provided by a
+/// remote symbol index can follow a certain schema (e.g. relative to a project
+/// root directory), and clangd needs to combine the schema path with execution
+/// environment (e.g. working/build directory) in order to get a file path in
+/// the file system.
+///
+/// By default, a "file" schema is supported where URI paths are always absolute
+/// in the file system.
+class URISchema {
+public:
+  virtual ~URISchema() = default;
+
+  /// \brief Returns the absolute path of the file corresponding to \p Uri in
+  /// the file system. \p CurrentFile is the file from which the request is
+  /// issued. This is needed because the same URI in different workspace may
+  /// correspond to different files.
+  ///
+  /// Returns "" if \p Uri is not in the schema.
+  virtual std::string getAbsolutePath(llvm::StringRef CurrentFile,
+                                      const FileURI &Uri) const = 0;
+
+  /// \brief Creates an URI for the file \p AbsolutePath in this schema.
+  virtual FileURI uriFromAbsolutePath(llvm::StringRef AbsolutePath) const = 0;
+};
+
+/// \brief This manages file paths in the file system. All paths in the schema
+/// are absolute (with leading '/').
+class FileSystemSchema : public URISchema {
+public:
+  static const char *Schema;
+
+  std::string getAbsolutePath(llvm::StringRef /*CurrentFile*/,
+                              const FileURI &Uri) const override;
+
+  FileURI uriFromAbsolutePath(llvm::StringRef AbsolutePath) const override;
+};
+
+typedef llvm::Registry<URISchema> URISchemaRegistry;
+
+/// \brief Resolves the absolute path of \p Uri with the first matching schema
+/// registered.
+///
+/// Returns "" if there is no matching schema.
+std::string resolve(llvm::StringRef CurrentFile, const FileURI &Uri);
+
+} // namespace clangd
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_PATHURI_H
Index: clangd/URI.cpp
===================================================================
--- /dev/null
+++ clangd/URI.cpp
@@ -0,0 +1,54 @@
+//===---- URI.h - File URIs with schemas -------------------------*- C++-*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "URI.h"
+
+LLVM_INSTANTIATE_REGISTRY(clang::clangd::URISchemaRegistry)
+
+namespace clang {
+namespace clangd {
+
+std::string FileURI::toString() const { return Schema + "://" + Path; }
+
+FileURI FileURI::fromURI(llvm::StringRef Uri) {
+  auto Pos = Uri.find("://");
+  if (Pos == llvm::StringRef::npos)
+    return {"", ""};
+  return {Uri.substr(0, Pos), Uri.substr(Pos + 3)};
+}
+
+const char *FileSystemSchema::Schema = "file";
+
+std::string FileSystemSchema::getAbsolutePath(llvm::StringRef /*CurrentFile*/,
+                                              const FileURI &Uri) const {
+  return Schema == Uri.Schema ? Uri.Path : "";
+}
+
+FileURI
+FileSystemSchema::uriFromAbsolutePath(llvm::StringRef AbsolutePath) const {
+  assert(AbsolutePath.startswith("/") && "Expect an absolute path.");
+  return {Schema, AbsolutePath};
+}
+
+static URISchemaRegistry::Add<FileSystemSchema>
+    X(FileSystemSchema::Schema,
+      "URI schema for absolute paths in the file system.");
+
+std::string resolve(llvm::StringRef CurrentFile, const FileURI &Uri) {
+  for (auto I = URISchemaRegistry::begin(), E = URISchemaRegistry::end();
+       I != E; ++I) {
+    if (I->getName() != Uri.Schema)
+      continue;
+    return I->instantiate()->getAbsolutePath(CurrentFile, Uri);
+  }
+  return "";
+}
+
+} // namespace clangd
+} // namespace clang
Index: clangd/CMakeLists.txt
===================================================================
--- clangd/CMakeLists.txt
+++ clangd/CMakeLists.txt
@@ -21,6 +21,7 @@
   ProtocolHandlers.cpp
   SourceCode.cpp
   Trace.cpp
+  URI.cpp
   XRefs.cpp
   index/FileIndex.cpp
   index/Index.cpp
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to