Index: clang-tools-extra/trunk/clangd/ClangdServer.h =================================================================== --- clang-tools-extra/trunk/clangd/ClangdServer.h +++ clang-tools-extra/trunk/clangd/ClangdServer.h @@ -79,6 +79,10 @@ /// opened files and uses the index to augment code completion results. bool BuildDynamicSymbolIndex = false; + /// URI schemes to use when building the dynamic index. + /// If empty, the default schemes in SymbolCollector will be used. + std::vector URISchemes; + /// If set, use this index to augment code completion results. SymbolIndex *StaticIndex = nullptr; Index: clang-tools-extra/trunk/clangd/ClangdServer.cpp =================================================================== --- clang-tools-extra/trunk/clangd/ClangdServer.cpp +++ clang-tools-extra/trunk/clangd/ClangdServer.cpp @@ -87,7 +87,8 @@ : CDB(CDB), DiagConsumer(DiagConsumer), FSProvider(FSProvider), ResourceDir(Opts.ResourceDir ? Opts.ResourceDir->str() : getStandardResourceDir()), - FileIdx(Opts.BuildDynamicSymbolIndex ? new FileIndex() : nullptr), + FileIdx(Opts.BuildDynamicSymbolIndex ? new FileIndex(Opts.URISchemes) + : nullptr), PCHs(std::make_shared()), // Pass a callback into `WorkScheduler` to extract symbols from a newly // parsed file and rebuild the file index synchronously each time an AST Index: clang-tools-extra/trunk/clangd/index/FileIndex.h =================================================================== --- clang-tools-extra/trunk/clangd/index/FileIndex.h +++ clang-tools-extra/trunk/clangd/index/FileIndex.h @@ -56,6 +56,10 @@ /// \brief This manages symbls from files and an in-memory index on all symbols. class FileIndex : public SymbolIndex { public: + /// If URISchemes is empty, the default schemes in SymbolCollector will be + /// used. + FileIndex(std::vector URISchemes = {}); + /// \brief Update symbols in \p Path with symbols in \p AST. If \p AST is /// nullptr, this removes all symbols in the file. /// If \p AST is not null, \p PP cannot be null and it should be the @@ -72,11 +76,14 @@ private: FileSymbols FSymbols; MemIndex Index; + std::vector URISchemes; }; /// Retrieves namespace and class level symbols in \p AST. /// Exposed to assist in unit tests. -SymbolSlab indexAST(ASTContext &AST, std::shared_ptr PP); +/// If URISchemes is empty, the default schemes in SymbolCollector will be used. +SymbolSlab indexAST(ASTContext &AST, std::shared_ptr PP, + llvm::ArrayRef URISchemes = {}); } // namespace clangd } // namespace clang Index: clang-tools-extra/trunk/clangd/index/FileIndex.cpp =================================================================== --- clang-tools-extra/trunk/clangd/index/FileIndex.cpp +++ clang-tools-extra/trunk/clangd/index/FileIndex.cpp @@ -15,7 +15,8 @@ namespace clang { namespace clangd { -SymbolSlab indexAST(ASTContext &AST, std::shared_ptr PP) { +SymbolSlab indexAST(ASTContext &AST, std::shared_ptr PP, + llvm::ArrayRef URISchemes) { SymbolCollector::Options CollectorOpts; // FIXME(ioeric): we might also want to collect include headers. We would need // to make sure all includes are canonicalized (with CanonicalIncludes), which @@ -24,6 +25,8 @@ // CommentHandler for IWYU pragma) to canonicalize includes. CollectorOpts.CollectIncludePath = false; CollectorOpts.CountReferences = false; + if (!URISchemes.empty()) + CollectorOpts.URISchemes = URISchemes; SymbolCollector Collector(std::move(CollectorOpts)); Collector.setPreprocessor(PP); @@ -41,6 +44,9 @@ return Collector.takeSymbols(); } +FileIndex::FileIndex(std::vector URISchemes) + : URISchemes(std::move(URISchemes)) {} + void FileSymbols::update(PathRef Path, std::unique_ptr Slab) { std::lock_guard Lock(Mutex); if (!Slab) @@ -79,7 +85,7 @@ } else { assert(PP); auto Slab = llvm::make_unique(); - *Slab = indexAST(*AST, PP); + *Slab = indexAST(*AST, PP, URISchemes); FSymbols.update(Path, std::move(Slab)); } auto Symbols = FSymbols.allSymbols(); Index: clang-tools-extra/trunk/unittests/clangd/ClangdTests.cpp =================================================================== --- clang-tools-extra/trunk/unittests/clangd/ClangdTests.cpp +++ clang-tools-extra/trunk/unittests/clangd/ClangdTests.cpp @@ -152,28 +152,6 @@ } }; -constexpr const char* ClangdTestScheme = "ClangdTests"; -class TestURIScheme : public URIScheme { -public: - llvm::Expected - getAbsolutePath(llvm::StringRef /*Authority*/, llvm::StringRef Body, - llvm::StringRef /*HintPath*/) const override { - llvm_unreachable("ClangdTests never makes absolute path."); - } - - llvm::Expected - uriFromAbsolutePath(llvm::StringRef AbsolutePath) const override { - llvm_unreachable("ClangdTest never creates a test URI."); - } - - llvm::Expected getIncludeSpelling(const URI &U) const override { - return ("\"" + U.body().trim("/") + "\"").str(); - } -}; - -static URISchemeRegistry::Add - X(ClangdTestScheme, "Test scheme for ClangdTests."); - TEST_F(ClangdVFSTest, Parse) { // FIXME: figure out a stable format for AST dumps, so that we can check the // output of the dump itself is equal to the expected one, not just that it's Index: clang-tools-extra/trunk/unittests/clangd/FileIndexTests.cpp =================================================================== --- clang-tools-extra/trunk/unittests/clangd/FileIndexTests.cpp +++ clang-tools-extra/trunk/unittests/clangd/FileIndexTests.cpp @@ -96,6 +96,20 @@ M.update(File.Filename, &AST.getASTContext(), AST.getPreprocessorPtr()); } +TEST(FileIndexTest, CustomizedURIScheme) { + FileIndex M({"unittest"}); + update(M, "f", "class string {};"); + + FuzzyFindRequest Req; + Req.Query = ""; + bool SeenSymbol = false; + M.fuzzyFind(Req, [&](const Symbol &Sym) { + EXPECT_EQ(Sym.CanonicalDeclaration.FileURI, "unittest:///f.h"); + SeenSymbol = true; + }); + EXPECT_TRUE(SeenSymbol); +} + TEST(FileIndexTest, IndexAST) { FileIndex M; update(M, "f1", "namespace ns { void f() {} class X {}; }"); Index: clang-tools-extra/trunk/unittests/clangd/SymbolCollectorTests.cpp =================================================================== --- clang-tools-extra/trunk/unittests/clangd/SymbolCollectorTests.cpp +++ clang-tools-extra/trunk/unittests/clangd/SymbolCollectorTests.cpp @@ -372,17 +372,15 @@ UnorderedElementsAre(AllOf(QName("Foo"), DeclURI(TestHeaderURI)))); } -#ifndef _WIN32 TEST_F(SymbolCollectorTest, CustomURIScheme) { // Use test URI scheme from URITests.cpp CollectorOpts.URISchemes.insert(CollectorOpts.URISchemes.begin(), "unittest"); - TestHeaderName = testPath("test-root/x.h"); - TestFileName = testPath("test-root/x.cpp"); + TestHeaderName = testPath("x.h"); + TestFileName = testPath("x.cpp"); runSymbolCollector("class Foo {};", /*Main=*/""); - EXPECT_THAT(Symbols, - UnorderedElementsAre(AllOf(QName("Foo"), DeclURI("unittest:x.h")))); + EXPECT_THAT(Symbols, UnorderedElementsAre( + AllOf(QName("Foo"), DeclURI("unittest:///x.h")))); } -#endif TEST_F(SymbolCollectorTest, InvalidURIScheme) { // Use test URI scheme from URITests.cpp Index: clang-tools-extra/trunk/unittests/clangd/TestFS.h =================================================================== --- clang-tools-extra/trunk/unittests/clangd/TestFS.h +++ clang-tools-extra/trunk/unittests/clangd/TestFS.h @@ -56,6 +56,11 @@ // Returns a suitable absolute path for this OS. std::string testPath(PathRef File); +// unittest: is a scheme that refers to files relative to testRoot() +// This anchor is used to force the linker to link in the generated object file +// and thus register unittest: URI scheme plugin. +extern volatile int UnittestSchemeAnchorSource; + } // namespace clangd } // namespace clang #endif Index: clang-tools-extra/trunk/unittests/clangd/TestFS.cpp =================================================================== --- clang-tools-extra/trunk/unittests/clangd/TestFS.cpp +++ clang-tools-extra/trunk/unittests/clangd/TestFS.cpp @@ -7,7 +7,11 @@ // //===----------------------------------------------------------------------===// #include "TestFS.h" +#include "URI.h" +#include "clang/AST/DeclCXX.h" +#include "llvm/ADT/StringRef.h" #include "llvm/Support/Errc.h" +#include "llvm/Support/Path.h" #include "gtest/gtest.h" namespace clang { @@ -62,5 +66,38 @@ return Path.str(); } +/// unittest: is a scheme that refers to files relative to testRoot() +class TestScheme : public URIScheme { +public: + static const char *Scheme; + + llvm::Expected + getAbsolutePath(llvm::StringRef /*Authority*/, llvm::StringRef Body, + llvm::StringRef HintPath) const override { + assert(HintPath.startswith(testRoot())); + llvm::SmallString<16> Path(Body.begin(), Body.end()); + llvm::sys::path::native(Path); + return testPath(Path); + } + + llvm::Expected + uriFromAbsolutePath(llvm::StringRef AbsolutePath) const override { + llvm::StringRef Body = AbsolutePath; + if (!Body.consume_front(testRoot())) + return llvm::make_error( + AbsolutePath + "does not start with " + testRoot(), + llvm::inconvertibleErrorCode()); + + return URI(Scheme, /*Authority=*/"", + llvm::sys::path::convert_to_slash(Body)); + } +}; + +const char *TestScheme::Scheme = "unittest"; + +static URISchemeRegistry::Add X(TestScheme::Scheme, "Test schema"); + +volatile int UnittestSchemeAnchorSource = 0; + } // namespace clangd } // namespace clang Index: clang-tools-extra/trunk/unittests/clangd/URITests.cpp =================================================================== --- clang-tools-extra/trunk/unittests/clangd/URITests.cpp +++ clang-tools-extra/trunk/unittests/clangd/URITests.cpp @@ -14,6 +14,11 @@ namespace clang { namespace clangd { + +// Force the unittest URI scheme to be linked, +static int LLVM_ATTRIBUTE_UNUSED UnittestSchemeAnchorDest = + UnittestSchemeAnchorSource; + namespace { using ::testing::AllOf; @@ -22,38 +27,6 @@ MATCHER_P(Authority, A, "") { return arg.authority() == A; } MATCHER_P(Body, B, "") { return arg.body() == B; } -// 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 TestScheme : public URIScheme { -public: - static const char *Scheme; - - static const char *TestRoot; - - llvm::Expected - getAbsolutePath(llvm::StringRef /*Authority*/, llvm::StringRef Body, - llvm::StringRef HintPath) const override { - auto Pos = HintPath.find(TestRoot); - assert(Pos != llvm::StringRef::npos); - return (HintPath.substr(0, Pos + llvm::StringRef(TestRoot).size()) + Body) - .str(); - } - - llvm::Expected - uriFromAbsolutePath(llvm::StringRef AbsolutePath) const override { - auto Pos = AbsolutePath.find(TestRoot); - assert(Pos != llvm::StringRef::npos); - return URI(Scheme, /*Authority=*/"", - AbsolutePath.substr(Pos + llvm::StringRef(TestRoot).size())); - } -}; - -const char *TestScheme::Scheme = "unittest"; -const char *TestScheme::TestRoot = "/test-root/"; - -static URISchemeRegistry::Add X(TestScheme::Scheme, "Test schema"); - std::string createOrDie(llvm::StringRef AbsolutePath, llvm::StringRef Scheme = "file") { auto Uri = URI::create(AbsolutePath, Scheme); @@ -167,12 +140,12 @@ #else EXPECT_EQ(resolveOrDie(parseOrDie("file:/a/b/c")), "/a/b/c"); EXPECT_EQ(resolveOrDie(parseOrDie("file://auth/a/b/c")), "/a/b/c"); - EXPECT_EQ(resolveOrDie(parseOrDie("unittest:a/b/c"), "/dir/test-root/x/y/z"), - "/dir/test-root/a/b/c"); EXPECT_THAT(resolveOrDie(parseOrDie("file://au%3dth/%28x%29/y/%20z")), "/(x)/y/ z"); EXPECT_THAT(resolveOrDie(parseOrDie("file:///c:/x/y/z")), "c:/x/y/z"); #endif + EXPECT_EQ(resolveOrDie(parseOrDie("unittest:a"), testPath("x")), + testPath("a")); } TEST(URITest, Platform) {