Index: clangd/ClangdServer.cpp =================================================================== --- clangd/ClangdServer.cpp +++ clangd/ClangdServer.cpp @@ -286,6 +286,13 @@ auto U = URI::parse(Header); if (!U) return U.takeError(); + + auto IncludePath = URI::includeSpelling(*U); + if (!IncludePath) + return IncludePath.takeError(); + if (!IncludePath->empty()) + return HeaderFile{std::move(*IncludePath), /*Verbatim=*/true}; + auto Resolved = URI::resolve(*U, HintPath); if (!Resolved) return Resolved.takeError(); Index: clangd/URI.h =================================================================== --- clangd/URI.h +++ clangd/URI.h @@ -60,6 +60,16 @@ static llvm::Expected resolve(const URI &U, llvm::StringRef HintPath = ""); + /// Gets the preferred spelling of this file for #include, if there is one, + /// e.g. , "path/to/x.h". + /// + /// This allows URI schemas to provide their customized include paths. + /// + /// Returns an empty string if normal include-shortening based on the absolute + /// path should be used. + /// Fails if the URI is not valid in the schema. + static llvm::Expected includeSpelling(const URI &U); + friend bool operator==(const URI &LHS, const URI &RHS) { return std::tie(LHS.Scheme, LHS.Authority, LHS.Body) == std::tie(RHS.Scheme, RHS.Authority, RHS.Body); @@ -94,6 +104,13 @@ virtual llvm::Expected uriFromAbsolutePath(llvm::StringRef AbsolutePath) const = 0; + + /// Returns the include path of the file (e.g. , "path"), which can be + /// #included directly. See URI::includeSpelling for details. + virtual llvm::Expected + getIncludeSpelling(const URI& U) const { + return ""; // no customized include path for this scheme. + } }; /// By default, a "file" scheme is supported where URI paths are always absolute Index: clangd/URI.cpp =================================================================== --- clangd/URI.cpp +++ clangd/URI.cpp @@ -196,5 +196,12 @@ return S->get()->getAbsolutePath(Uri.Authority, Uri.Body, HintPath); } +llvm::Expected URI::includeSpelling(const URI &Uri) { + auto S = findSchemeByName(Uri.Scheme); + if (!S) + return S.takeError(); + return S->get()->getIncludeSpelling(Uri); +} + } // namespace clangd } // namespace clang Index: unittests/clangd/ClangdTests.cpp =================================================================== --- unittests/clangd/ClangdTests.cpp +++ unittests/clangd/ClangdTests.cpp @@ -152,6 +152,28 @@ } }; +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 @@ -961,6 +983,10 @@ /*Preferred=*/"", "")); EXPECT_TRUE(Inserted(OriginalHeader, PreferredHeader, "\"Y.h\"")); EXPECT_TRUE(Inserted("", PreferredHeader, "\"Y.h\"")); + auto TestURIHeader = + URI::parse(llvm::formatv("{0}:///x/y/z.h", ClangdTestScheme).str()); + EXPECT_TRUE(static_cast(TestURIHeader)); + EXPECT_TRUE(Inserted(TestURIHeader->toString(), "", "\"x/y/z.h\"")); // Check that includes are sorted. const auto Expected = R"cpp(