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