Index: clangd/ClangdLSPServer.cpp =================================================================== --- clangd/ClangdLSPServer.cpp +++ clangd/ClangdLSPServer.cpp @@ -71,6 +71,8 @@ JSONOutput &Out) override; void onGoToDefinition(TextDocumentPositionParams Params, StringRef ID, JSONOutput &Out) override; + void onSwitchSourceHeader(TextDocumentIdentifier Params, StringRef ID, + JSONOutput &Out) override; private: ClangdLSPServer &LangServer; @@ -216,6 +218,17 @@ R"(,"result":[)" + Locations + R"(]})"); } +void ClangdLSPServer::LSPProtocolCallbacks::onSwitchSourceHeader( + TextDocumentIdentifier Params, StringRef ID, JSONOutput &Out) { + + PathRef ref = PathRef((Params.uri.uri).c_str()); + Path result = LangServer.Server.switchSourceHeader(ref); + + Out.writeMessage( + R"({"jsonrpc":"2.0","id":)" + ID.str() + + R"(,"result":)" + result + R"(})"); +} + ClangdLSPServer::ClangdLSPServer(JSONOutput &Out, bool RunSynchronously) : Out(Out), DiagConsumer(*this), Server(CDB, DiagConsumer, FSProvider, RunSynchronously) {} Index: clangd/ClangdServer.h =================================================================== --- clangd/ClangdServer.h +++ clangd/ClangdServer.h @@ -182,6 +182,9 @@ /// Get definition of symbol at a specified \p Line and \p Column in \p File. Tagged> findDefinitions(PathRef File, Position Pos); + // Helper function to change from one header file to it's corresponding source file and vice versa + Path switchSourceHeader(PathRef path); + /// Run formatting for \p Rng inside \p File. std::vector formatRange(PathRef File, Range Rng); /// Run formatting for the whole \p File. Index: clangd/ClangdServer.cpp =================================================================== --- clangd/ClangdServer.cpp +++ clangd/ClangdServer.cpp @@ -16,6 +16,7 @@ #include "llvm/ADT/ArrayRef.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/raw_ostream.h" +#include "llvm/Support/Path.h" #include using namespace clang; @@ -23,6 +24,7 @@ namespace { + std::vector formatCode(StringRef Code, StringRef Filename, ArrayRef Ranges) { // Call clang-format. @@ -286,3 +288,86 @@ }); return make_tagged(std::move(Result), TaggedFS.Tag); } + +Path ClangdServer::switchSourceHeader(PathRef path) { + + std::string DEFAULT_SOURCE_EXTENSIONS[] = { ".cpp", ".c", ".cc", ".cxx", + ".c++", ".C", ".m", ".mm" }; + std::string DEFAULT_HEADER_EXTENSIONS[] = { ".h", ".hh", ".hpp", ".hxx", + ".inc" }; + + const int sourceSize = sizeof(DEFAULT_SOURCE_EXTENSIONS)/sizeof(DEFAULT_SOURCE_EXTENSIONS[0]); + const int headerSize = sizeof(DEFAULT_HEADER_EXTENSIONS)/sizeof(DEFAULT_HEADER_EXTENSIONS[0]); + + std::string pathDataRef = std::string(path.data()); + bool isSourceFile = false, foundExtension = false; + SmallString<128> NewPath; + int i = 0; + + while (!foundExtension && i < sourceSize) + { + if (llvm::sys::path::extension(pathDataRef) == DEFAULT_SOURCE_EXTENSIONS[i]) + { + std::string fileExtension = std::string(DEFAULT_SOURCE_EXTENSIONS[i]); + NewPath = pathDataRef; + pathDataRef = pathDataRef.substr(0, (pathDataRef.length() - fileExtension.size())); + isSourceFile = true; + foundExtension = true; + } + i++; + } + i = 0; + + if (!foundExtension) + { + while (!foundExtension && i < headerSize) + { + if (llvm::sys::path::extension(pathDataRef) == DEFAULT_HEADER_EXTENSIONS[i]) + { + std::string fileExtension = std::string(DEFAULT_HEADER_EXTENSIONS[i]); + NewPath = pathDataRef; + pathDataRef = pathDataRef.substr(0, (pathDataRef.length() - fileExtension.size())); + isSourceFile = false; + foundExtension = true; + } + i++; + } + } + + std::string temp = std::string(pathDataRef); + SmallString<128> CurrentPath; + CurrentPath = temp; + bool done = false; + std::vector EXTENSIONS_ARRAY; + + if (!isSourceFile) + EXTENSIONS_ARRAY.assign(DEFAULT_SOURCE_EXTENSIONS, DEFAULT_SOURCE_EXTENSIONS + sourceSize); + else + EXTENSIONS_ARRAY.assign(DEFAULT_HEADER_EXTENSIONS, DEFAULT_HEADER_EXTENSIONS + headerSize); + + i = 0; + while (!done && i < EXTENSIONS_ARRAY.size()) + { + std::string CurrentPathString = CurrentPath.str(); + std::string NewPathString = NewPath.str(); + if (i == 0) + CurrentPathString = pathDataRef.append(std::string(EXTENSIONS_ARRAY[i])); + else + CurrentPathString = CurrentPathString.append(std::string(EXTENSIONS_ARRAY[i])); + + std::string temp = CurrentPathString.substr(7, CurrentPathString.size()); + FILE * file; + file = fopen(temp.c_str(), "r"); + if (file) + { + done = true; + fclose(file); + llvm::sys::path::replace_extension(NewPath, EXTENSIONS_ARRAY[i]); + std::string returnValue = std::string(NewPath.str()); + return Path("\"" + returnValue + "\""); + } + llvm::sys::path::replace_extension(CurrentPath, ""); + CurrentPathString = CurrentPath.str(); + i++; + } +} Index: clangd/ProtocolHandlers.h =================================================================== --- clangd/ProtocolHandlers.h +++ clangd/ProtocolHandlers.h @@ -48,6 +48,8 @@ JSONOutput &Out) = 0; virtual void onGoToDefinition(TextDocumentPositionParams Params, StringRef ID, JSONOutput &Out) = 0; + virtual void onSwitchSourceHeader(TextDocumentIdentifier Params, StringRef ID, + JSONOutput &Out) = 0; }; void regiterCallbackHandlers(JSONRPCDispatcher &Dispatcher, JSONOutput &Out, Index: clangd/ProtocolHandlers.cpp =================================================================== --- clangd/ProtocolHandlers.cpp +++ clangd/ProtocolHandlers.cpp @@ -204,6 +204,23 @@ ProtocolCallbacks &Callbacks; }; +struct SwitchSourceHeaderHandler : Handler { + SwitchSourceHeaderHandler(JSONOutput &Output, ProtocolCallbacks &Callbacks) + : Handler(Output), Callbacks(Callbacks) {} + + void handleMethod(llvm::yaml::MappingNode *Params, StringRef ID) override { + auto TDPP = TextDocumentIdentifier::parse(Params); + if (!TDPP) { + return; + } + + Callbacks.onSwitchSourceHeader(*TDPP, ID, Output); + } + +private: + ProtocolCallbacks &Callbacks; +}; + } // namespace void clangd::regiterCallbackHandlers(JSONRPCDispatcher &Dispatcher, @@ -239,4 +256,7 @@ llvm::make_unique(Out, Callbacks)); Dispatcher.registerHandler("textDocument/definition", llvm::make_unique(Out, Callbacks)); + Dispatcher.registerHandler( + "textDocument/switchSourceHeader", + llvm::make_unique(Out, Callbacks)); }