Index: clangd/ClangdServer.cpp =================================================================== --- clangd/ClangdServer.cpp +++ clangd/ClangdServer.cpp @@ -115,10 +115,15 @@ } void ClangdServer::setRootPath(PathRef RootPath) { - std::string NewRootPath = llvm::sys::path::convert_to_slash( - RootPath, llvm::sys::path::Style::posix); - if (llvm::sys::fs::is_directory(NewRootPath)) - this->RootPath = NewRootPath; + auto FS = FSProvider.getFileSystem(); + auto Status = FS->status(RootPath); + if (!Status) + log("Failed to get status for RootPath " + RootPath + ": " + + Status.getError().message()); + else if (Status->isDirectory()) + this->RootPath = RootPath; + else + log("The provided RootPath " + RootPath + " is not a directory."); } void ClangdServer::addDocument(PathRef File, StringRef Contents, @@ -446,7 +451,8 @@ void ClangdServer::workspaceSymbols( StringRef Query, int Limit, Callback> CB) { - CB(clangd::getWorkspaceSymbols(Query, Limit, Index)); + CB(clangd::getWorkspaceSymbols(Query, Limit, Index, + RootPath ? *RootPath : "")); } std::vector> Index: clangd/FindSymbols.h =================================================================== --- clangd/FindSymbols.h +++ clangd/FindSymbols.h @@ -27,9 +27,11 @@ /// "::". For example, "std::" will list all children of the std namespace and /// "::" alone will list all children of the global namespace. /// \p Limit limits the number of results returned (0 means no limit). +/// \p RootPath Root path of the workspace. This is used as the hint path when +/// resolving URIs. llvm::Expected> getWorkspaceSymbols(llvm::StringRef Query, int Limit, - const SymbolIndex *const Index); + const SymbolIndex *const Index, llvm::StringRef RootPath); } // namespace clangd } // namespace clang Index: clangd/FindSymbols.cpp =================================================================== --- clangd/FindSymbols.cpp +++ clangd/FindSymbols.cpp @@ -95,8 +95,8 @@ } // namespace llvm::Expected> -getWorkspaceSymbols(StringRef Query, int Limit, - const SymbolIndex *const Index) { +getWorkspaceSymbols(StringRef Query, int Limit, const SymbolIndex *const Index, + StringRef RootPath) { std::vector Result; if (Query.empty() || !Index) return Result; @@ -116,7 +116,7 @@ Req.MaxCandidateCount = Limit; TopN Top(Req.MaxCandidateCount); FuzzyMatcher Filter(Req.Query); - Index->fuzzyFind(Req, [&Top, &Filter](const Symbol &Sym) { + Index->fuzzyFind(Req, [RootPath, &Top, &Filter](const Symbol &Sym) { // Prefer the definition over e.g. a function declaration in a header auto &CD = Sym.Definition ? Sym.Definition : Sym.CanonicalDeclaration; auto Uri = URI::parse(CD.FileURI); @@ -126,9 +126,7 @@ CD.FileURI, Sym.Name)); return; } - // FIXME: Passing no HintPath here will work for "file" and "test" schemes - // because they don't use it but this might not work for other custom ones. - auto Path = URI::resolve(*Uri); + auto Path = URI::resolve(*Uri, RootPath); if (!Path) { log(llvm::formatv("Workspace symbol: Could not resolve path for URI " "'{0}' for symbol '{1}'.", Index: unittests/clangd/FileIndexTests.cpp =================================================================== --- unittests/clangd/FileIndexTests.cpp +++ unittests/clangd/FileIndexTests.cpp @@ -104,7 +104,7 @@ Req.Query = ""; bool SeenSymbol = false; M.fuzzyFind(Req, [&](const Symbol &Sym) { - EXPECT_EQ(Sym.CanonicalDeclaration.FileURI, "unittest:///f.h"); + EXPECT_EQ(Sym.CanonicalDeclaration.FileURI, "unittest:f.h"); SeenSymbol = true; }); EXPECT_TRUE(SeenSymbol); Index: unittests/clangd/FindSymbolsTests.cpp =================================================================== --- unittests/clangd/FindSymbolsTests.cpp +++ unittests/clangd/FindSymbolsTests.cpp @@ -40,13 +40,18 @@ ClangdServer::Options optsForTests() { auto ServerOpts = ClangdServer::optsForTest(); ServerOpts.BuildDynamicSymbolIndex = true; + ServerOpts.URISchemes = {"unittest", "file"}; return ServerOpts; } class WorkspaceSymbolsTest : public ::testing::Test { public: WorkspaceSymbolsTest() - : Server(CDB, FSProvider, DiagConsumer, optsForTests()) {} + : Server(CDB, FSProvider, DiagConsumer, optsForTests()) { + // Make sure the test root directory is created. + FSProvider.Files[testPath("unused")] = ""; + Server.setRootPath(testRoot()); + } protected: MockFSProvider FSProvider; Index: unittests/clangd/SymbolCollectorTests.cpp =================================================================== --- unittests/clangd/SymbolCollectorTests.cpp +++ unittests/clangd/SymbolCollectorTests.cpp @@ -379,7 +379,7 @@ TestFileName = testPath("x.cpp"); runSymbolCollector("class Foo {};", /*Main=*/""); EXPECT_THAT(Symbols, UnorderedElementsAre( - AllOf(QName("Foo"), DeclURI("unittest:///x.h")))); + AllOf(QName("Foo"), DeclURI("unittest:x.h")))); } TEST_F(SymbolCollectorTest, InvalidURIScheme) { Index: unittests/clangd/TestFS.cpp =================================================================== --- unittests/clangd/TestFS.cpp +++ unittests/clangd/TestFS.cpp @@ -66,7 +66,8 @@ return Path.str(); } -/// unittest: is a scheme that refers to files relative to testRoot() +/// unittest: is a scheme that refers to files relative to testRoot(). +/// URI body is relative path to testRoot(). class TestScheme : public URIScheme { public: static const char *Scheme; @@ -89,7 +90,7 @@ llvm::inconvertibleErrorCode()); return URI(Scheme, /*Authority=*/"", - llvm::sys::path::convert_to_slash(Body)); + StringRef(llvm::sys::path::convert_to_slash(Body)).ltrim('/')); } };