Index: clangd/CMakeLists.txt =================================================================== --- clangd/CMakeLists.txt +++ clangd/CMakeLists.txt @@ -18,6 +18,7 @@ clangBasic clangFormat clangFrontend + clangIndex clangSema clangTooling clangToolingCore Index: clangd/ClangdLSPServer.cpp =================================================================== --- clangd/ClangdLSPServer.cpp +++ clangd/ClangdLSPServer.cpp @@ -69,6 +69,8 @@ JSONOutput &Out) override; void onCompletion(TextDocumentPositionParams Params, StringRef ID, JSONOutput &Out) override; + void onGoToDefinition(TextDocumentPositionParams Params, StringRef ID, + JSONOutput &Out) override; private: ClangdLSPServer &LangServer; @@ -84,7 +86,8 @@ "documentRangeFormattingProvider": true, "documentOnTypeFormattingProvider": {"firstTriggerCharacter":"}","moreTriggerCharacter":[]}, "codeActionProvider": true, - "completionProvider": {"resolveProvider": false, "triggerCharacters": [".",">"]} + "completionProvider": {"resolveProvider": false, "triggerCharacters": [".",">"]}, + "definitionProvider": true }}})"); } @@ -191,6 +194,25 @@ R"(,"result":[)" + Completions + R"(]})"); } +void ClangdLSPServer::LSPProtocolCallbacks::onGoToDefinition( + TextDocumentPositionParams Params, StringRef ID, JSONOutput &Out) { + + auto Items = LangServer.Server.findDefinitions( + Params.textDocument.uri.file, + Position{Params.position.line, Params.position.character}).Value; + + std::string Locations; + for (const auto &Item : Items) { + Locations += Location::unparse(Item); + Locations += ","; + } + if (!Locations.empty()) + Locations.pop_back(); + Out.writeMessage( + R"({"jsonrpc":"2.0","id":)" + ID.str() + + R"(,"result":[)" + Locations + 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 @@ -174,6 +174,8 @@ Tagged> codeComplete(PathRef File, Position Pos, llvm::Optional OverridenContents = llvm::None); + /// Get definition of symbol at a specified \p Line and \p Column in \p File. + Tagged> findDefinitions(PathRef File, Position Pos); /// Run formatting for \p Rng inside \p File. std::vector formatRange(PathRef File, Range Rng); Index: clangd/ClangdServer.cpp =================================================================== --- clangd/ClangdServer.cpp +++ clangd/ClangdServer.cpp @@ -264,3 +264,17 @@ }); return DumpFuture.get(); } + +Tagged> +ClangdServer::findDefinitions(PathRef File, Position Pos) { + auto FileContents = DraftMgr.getDraft(File); + assert(FileContents.Draft && "findDefinitions is called for non-added document"); + + std::vector Result; + auto TaggedFS = FSProvider.getTaggedFileSystem(File); + Units.runOnUnit( + File, *FileContents.Draft, CDB, PCHs, TaggedFS.Value, [&](ClangdUnit &Unit) { + Result = Unit.findDefinitions(Pos); + }); + return make_tagged(std::move(Result), TaggedFS.Tag); +} Index: clangd/ClangdUnit.h =================================================================== --- clangd/ClangdUnit.h +++ clangd/ClangdUnit.h @@ -59,6 +59,8 @@ std::vector codeComplete(StringRef Contents, Position Pos, IntrusiveRefCntPtr VFS); + /// Get definition of symbol at a specified \p Line and \p Column in \p File. + std::vector findDefinitions(Position Pos); /// Returns diagnostics and corresponding FixIts for each diagnostic that are /// located in the current file. std::vector getLocalDiagnostics() const; @@ -71,6 +73,8 @@ Path FileName; std::unique_ptr Unit; std::shared_ptr PCHs; + + SourceLocation getBeginningOfIdentifier(const Position& Pos, const FileEntry* FE) const; }; } // namespace clangd Index: clangd/ClangdUnit.cpp =================================================================== --- clangd/ClangdUnit.cpp +++ clangd/ClangdUnit.cpp @@ -8,13 +8,21 @@ //===---------------------------------------------------------------------===// #include "ClangdUnit.h" + #include "clang/Frontend/ASTUnit.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/CompilerInvocation.h" #include "clang/Frontend/Utils.h" +#include "clang/Index/IndexingAction.h" +#include "clang/Index/IndexDataConsumer.h" +#include "clang/Lex/Lexer.h" +#include "clang/Lex/MacroInfo.h" +#include "clang/Lex/Preprocessor.h" #include "clang/Tooling/CompilationDatabase.h" #include "llvm/Support/Format.h" +#include + using namespace clang::clangd; using namespace clang; @@ -261,3 +269,145 @@ void ClangdUnit::dumpAST(llvm::raw_ostream &OS) const { Unit->getASTContext().getTranslationUnitDecl()->dump(OS, true); } + +namespace { +/// Finds declarations locations that a given source location refers to. +class DeclarationLocationsFinder : public index::IndexDataConsumer { + std::vector DeclarationLocations; + const SourceLocation &SearchedLocation; + ASTUnit &Unit; +public: + DeclarationLocationsFinder(raw_ostream &OS, + const SourceLocation &SearchedLocation, ASTUnit &Unit) : + SearchedLocation(SearchedLocation), Unit(Unit) { + } + + std::vector takeLocations() { + // Don't keep the same location multiple times. + // This can happen when nodes in the AST are visited twice. + std::sort(DeclarationLocations.begin(), DeclarationLocations.end()); + auto last = std::unique(DeclarationLocations.begin(), DeclarationLocations.end()); + DeclarationLocations.erase(last, DeclarationLocations.end()); + return std::move(DeclarationLocations); + } + + bool handleDeclOccurence(const Decl* D, index::SymbolRoleSet Roles, + ArrayRef Relations, FileID FID, unsigned Offset, + index::IndexDataConsumer::ASTNodeInfo ASTNode) override + { + if (isSearchedLocation(FID, Offset)) { + addDeclarationLocation(D->getSourceRange()); + } + return true; + } + +private: + bool isSearchedLocation(FileID FID, unsigned Offset) const { + const SourceManager &SourceMgr = Unit.getSourceManager(); + return SourceMgr.getFileOffset(SearchedLocation) == Offset + && SourceMgr.getFileID(SearchedLocation) == FID; + } + + void addDeclarationLocation(const SourceRange& ValSourceRange) { + const SourceManager& SourceMgr = Unit.getSourceManager(); + const LangOptions& LangOpts = Unit.getLangOpts(); + SourceLocation LocStart = ValSourceRange.getBegin(); + SourceLocation LocEnd = Lexer::getLocForEndOfToken(ValSourceRange.getEnd(), + 0, SourceMgr, LangOpts); + Position P1; + P1.line = SourceMgr.getSpellingLineNumber(LocStart) - 1; + P1.character = SourceMgr.getSpellingColumnNumber(LocStart) - 1; + Position P2; + P2.line = SourceMgr.getSpellingLineNumber(LocEnd) - 1; + P2.character = SourceMgr.getSpellingColumnNumber(LocEnd) - 1; + Range R = { P1, P2 }; + Location L; + L.uri = URI::fromFile( + SourceMgr.getFilename(SourceMgr.getSpellingLoc(LocStart))); + L.range = R; + DeclarationLocations.push_back(L); + } + + void finish() override + { + // Also handle possible macro at the searched location. + Token Result; + if (!Lexer::getRawToken(SearchedLocation, Result, Unit.getSourceManager(), + Unit.getASTContext().getLangOpts(), false)) { + if (Result.is(tok::raw_identifier)) { + Unit.getPreprocessor().LookUpIdentifierInfo(Result); + } + IdentifierInfo* IdentifierInfo = Result.getIdentifierInfo(); + if (IdentifierInfo && IdentifierInfo->hadMacroDefinition()) { + std::pair DecLoc = + Unit.getSourceManager().getDecomposedExpansionLoc(SearchedLocation); + // Get the definition just before the searched location so that a macro + // referenced in a '#undef MACRO' can still be found. + SourceLocation BeforeSearchedLocation = Unit.getLocation( + Unit.getSourceManager().getFileEntryForID(DecLoc.first), + DecLoc.second - 1); + MacroDefinition MacroDef = + Unit.getPreprocessor().getMacroDefinitionAtLoc(IdentifierInfo, + BeforeSearchedLocation); + MacroInfo* MacroInf = MacroDef.getMacroInfo(); + if (MacroInf) { + addDeclarationLocation( + SourceRange(MacroInf->getDefinitionLoc(), + MacroInf->getDefinitionEndLoc())); + } + } + } + } +}; +} // namespace + +std::vector ClangdUnit::findDefinitions(Position Pos) { + const FileEntry *FE = Unit->getFileManager().getFile(Unit->getMainFileName()); + if (!FE) + return {}; + + SourceLocation SourceLocationBeg = getBeginningOfIdentifier(Pos, FE); + + auto DeclLocationsFinder = std::make_shared(llvm::errs(), + SourceLocationBeg, *Unit); + index::IndexingOptions IndexOpts; + IndexOpts.SystemSymbolFilter = + index::IndexingOptions::SystemSymbolFilterKind::All; + IndexOpts.IndexFunctionLocals = true; + index::indexASTUnit(*Unit, DeclLocationsFinder, IndexOpts); + + return DeclLocationsFinder->takeLocations(); +} + +SourceLocation ClangdUnit::getBeginningOfIdentifier(const Position &Pos, + const FileEntry *FE) const { + // The language server protocol uses zero-based line and column numbers. + // Clang uses one-based numbers. + SourceLocation InputLocation = Unit->getLocation(FE, Pos.line + 1, + Pos.character + 1); + + if (Pos.character == 0) { + return InputLocation; + } + + // This handle cases where the position is in the middle of a token or right + // after the end of a token. In theory we could just use GetBeginningOfToken + // to find the start of the token at the input position, but this doesn't + // work when right after the end, i.e. foo|. + // So try to go back by one and see if we're still inside the an identifier + // token. If so, Take the beginning of this token. + // (It should be the same identifier because you can't have two adjacent + // identifiers without another token in between.) + SourceLocation PeekBeforeLocation = Unit->getLocation(FE, Pos.line + 1, + Pos.character); + const SourceManager &SourceMgr = Unit->getSourceManager(); + Token Result; + Lexer::getRawToken(PeekBeforeLocation, Result, SourceMgr, + Unit->getASTContext().getLangOpts(), false); + if (Result.is(tok::raw_identifier)) { + return Lexer::GetBeginningOfToken(PeekBeforeLocation, SourceMgr, + Unit->getASTContext().getLangOpts()); + } + + return InputLocation; +} Index: clangd/Protocol.h =================================================================== --- clangd/Protocol.h +++ clangd/Protocol.h @@ -38,6 +38,18 @@ static URI parse(llvm::yaml::ScalarNode *Param); static std::string unparse(const URI &U); + + friend bool operator==(const URI &LHS, const URI &RHS) { + return LHS.uri == RHS.uri; + } + + friend bool operator!=(const URI &LHS, const URI &RHS) { + return !(LHS == RHS); + } + + friend bool operator<(const URI &LHS, const URI &RHS) { + return LHS.uri < RHS.uri; + } }; struct TextDocumentIdentifier { @@ -86,6 +98,26 @@ static std::string unparse(const Range &P); }; +struct Location { + /// The text document's URI. + URI uri; + Range range; + + friend bool operator==(const Location &LHS, const Location &RHS) { + return LHS.uri == RHS.uri && LHS.range == RHS.range; + } + + friend bool operator!=(const Location &LHS, const Location &RHS) { + return !(LHS == RHS); + } + + friend bool operator<(const Location &LHS, const Location &RHS) { + return std::tie(LHS.uri, LHS.range) < std::tie(RHS.uri, RHS.range); + } + + static std::string unparse(const Location &P); +}; + struct TextEdit { /// The range of the text document to be manipulated. To insert /// text into a document create a range where start === end. Index: clangd/Protocol.cpp =================================================================== --- clangd/Protocol.cpp +++ clangd/Protocol.cpp @@ -54,7 +54,7 @@ } std::string URI::unparse(const URI &U) { - return U.uri; + return "\"" + U.uri + "\""; } llvm::Optional @@ -162,6 +162,14 @@ return Result; } +std::string Location::unparse(const Location &P) { + std::string Result; + llvm::raw_string_ostream(Result) << llvm::format( + R"({"uri": %s, "range": %s})", URI::unparse(P.uri).c_str(), + Range::unparse(P.range).c_str()); + return Result; +} + llvm::Optional TextDocumentItem::parse(llvm::yaml::MappingNode *Params) { TextDocumentItem Result; Index: clangd/ProtocolHandlers.h =================================================================== --- clangd/ProtocolHandlers.h +++ clangd/ProtocolHandlers.h @@ -46,6 +46,8 @@ JSONOutput &Out) = 0; virtual void onCompletion(TextDocumentPositionParams Params, StringRef ID, JSONOutput &Out) = 0; + virtual void onGoToDefinition(TextDocumentPositionParams Params, StringRef ID, + JSONOutput &Out) = 0; }; void regiterCallbackHandlers(JSONRPCDispatcher &Dispatcher, JSONOutput &Out, Index: clangd/ProtocolHandlers.cpp =================================================================== --- clangd/ProtocolHandlers.cpp +++ clangd/ProtocolHandlers.cpp @@ -186,6 +186,24 @@ ProtocolCallbacks &Callbacks; }; +struct GotoDefinitionHandler : Handler { + GotoDefinitionHandler(JSONOutput &Output, ProtocolCallbacks &Callbacks) + : Handler(Output), Callbacks(Callbacks) {} + + void handleMethod(llvm::yaml::MappingNode *Params, StringRef ID) override { + auto TDPP = TextDocumentPositionParams::parse(Params); + if (!TDPP) { + Output.log("Failed to decode TextDocumentPositionParams!\n"); + return; + } + + Callbacks.onGoToDefinition(*TDPP, ID, Output); + } + +private: + ProtocolCallbacks &Callbacks; +}; + } // namespace void clangd::regiterCallbackHandlers(JSONRPCDispatcher &Dispatcher, @@ -219,4 +237,6 @@ Dispatcher.registerHandler( "textDocument/completion", llvm::make_unique(Out, Callbacks)); + Dispatcher.registerHandler("textDocument/definition", + llvm::make_unique(Out, Callbacks)); } Index: test/clangd/definitions.test =================================================================== --- /dev/null +++ test/clangd/definitions.test @@ -0,0 +1,168 @@ +# RUN: clangd -run-synchronously < %s | FileCheck %s +# It is absolutely vital that this file has CRLF line endings. +# +Content-Length: 125 + +{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}} + +Content-Length: 172 + +{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"file:///main.cpp","languageId":"cpp","version":1,"text":"int main() {\nint a;\na;\n}\n"}}} + +Content-Length: 148 + +{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":2,"character":0}}} +# Go to local variable +# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 1, "character": 0}, "end": {"line": 1, "character": 5}}}]} + +Content-Length: 148 + +{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":2,"character":1}}} +# Go to local variable, end of token +# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 1, "character": 0}, "end": {"line": 1, "character": 5}}}]} + +Content-Length: 214 + +{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":2},"contentChanges":[{"text":"struct Foo {\nint x;\n};\nint main() {\n Foo bar = { x : 1 };\n}\n"}]}} + +Content-Length: 149 + +{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":4,"character":14}}} +# Go to field, GNU old-style field designator +# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 1, "character": 0}, "end": {"line": 1, "character": 5}}}]} + +Content-Length: 215 + +{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":3},"contentChanges":[{"text":"struct Foo {\nint x;\n};\nint main() {\n Foo baz = { .x = 2 };\n}\n"}]}} + +Content-Length: 149 + +{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":4,"character":15}}} +# Go to field, field designator +# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 1, "character": 0}, "end": {"line": 1, "character": 5}}}]} + +Content-Length: 187 + +{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":4},"contentChanges":[{"text":"int main() {\n main();\n return 0;\n}"}]}} + +Content-Length: 148 + +{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":1,"character":3}}} +# Go to function declaration, function call +# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 0, "character": 0}, "end": {"line": 3, "character": 1}}}]} + +Content-Length: 208 + +{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":5},"contentChanges":[{"text":"struct Foo {\n};\nint main() {\n Foo bar;\n return 0;\n}\n"}]}} + +Content-Length: 148 + +{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":3,"character":3}}} +# Go to struct declaration, new struct instance +# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 0, "character": 0}, "end": {"line": 1, "character": 1}}}]} + +Content-Length: 231 + +{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":5},"contentChanges":[{"text":"namespace n1 {\nstruct Foo {\n};\n}\nint main() {\n n1::Foo bar;\n return 0;\n}\n"}]}} + +Content-Length: 148 + +{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":5,"character":4}}} +# Go to struct declaration, new struct instance, qualified name +# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 0, "character": 0}, "end": {"line": 3, "character": 1}}}]} + +Content-Length: 215 + +{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":6},"contentChanges":[{"text":"struct Foo {\n int x;\n};\nint main() {\n Foo bar;\n bar.x;\n}\n"}]}} + +Content-Length: 148 + +{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":5,"character":7}}} +# Go to field declaration, field reference +# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 1, "character": 2}, "end": {"line": 1, "character": 7}}}]} + +Content-Length: 220 + +{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":7},"contentChanges":[{"text":"struct Foo {\n void x();\n};\nint main() {\n Foo bar;\n bar.x();\n}\n"}]}} + +Content-Length: 148 + +{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":5,"character":7}}} +# Go to method declaration, method call +# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 1, "character": 2}, "end": {"line": 1, "character": 10}}}]} + +Content-Length: 240 + +{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":7},"contentChanges":[{"text":"struct Foo {\n};\ntypedef Foo TypedefFoo;\nint main() {\n TypedefFoo bar;\n return 0;\n}\n"}]}} + +Content-Length: 149 + +{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":4,"character":10}}} +# Go to typedef +# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 2, "character": 0}, "end": {"line": 2, "character": 22}}}]} + +Content-Length: 254 + +{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":7},"contentChanges":[{"text":"template \nvoid foo() {\n MyTemplateParam a;\n}\nint main() {\n return 0;\n}\n"}]}} + +Content-Length: 149 + +{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":2,"character":13}}} +# Go to template type parameter. Fails until clangIndex is modified to handle those. +# no-CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 0, "character": 10}, "end": {"line": 0, "character": 34}}}]} + +Content-Length: 256 + +{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":7},"contentChanges":[{"text":"namespace ns {\nstruct Foo {\nstatic void bar() {}\n};\n}\nint main() {\n ns::Foo::bar();\n return 0;\n}\n"}]}} + +Content-Length: 148 + +{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":6,"character":4}}} +# Go to namespace, static method call +# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 0, "character": 0}, "end": {"line": 4, "character": 1}}}]} + +Content-Length: 265 + +{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":7},"contentChanges":[{"text":"namespace ns {\nstruct Foo {\n int field;\n Foo(int param) : field(param) {}\n};\n}\nint main() {\n return 0;\n}\n"}]}} + +Content-Length: 149 + +{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":3,"character":21}}} +# Go to field, member initializer +# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 2, "character": 2}, "end": {"line": 2, "character": 11}}}]} + +Content-Length: 204 + +{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":7},"contentChanges":[{"text":"#define MY_MACRO 0\nint main() {\n return MY_MACRO;\n}\n"}]}} + +Content-Length: 148 + +{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":2,"character":9}}} +# Go to macro +# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 0, "character": 8}, "end": {"line": 0, "character": 18}}}]} + +Content-Length: 217 + +{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":7},"contentChanges":[{"text":"#define FOO 1\nint a = FOO;\n#define FOO 2\nint b = FOO;\n#undef FOO\n"}]}} + +Content-Length: 148 + +{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":1,"character":8}}} +# Go to macro, re-defined later +# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 0, "character": 8}, "end": {"line": 0, "character": 13}}}]} + +Content-Length: 148 + +{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":3,"character":8}}} +# Go to macro, undefined later +# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 2, "character": 8}, "end": {"line": 2, "character": 13}}}]} + +Content-Length: 148 + +{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":4,"character":7}}} +# Go to macro, being undefined +# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 2, "character": 8}, "end": {"line": 2, "character": 13}}}]} + +Content-Length: 44 + +{"jsonrpc":"2.0","id":3,"method":"shutdown"} Index: test/clangd/formatting.test =================================================================== --- test/clangd/formatting.test +++ test/clangd/formatting.test @@ -4,14 +4,15 @@ Content-Length: 125 {"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}} -# CHECK: Content-Length: 424 +# CHECK: Content-Length: 462 # CHECK: {"jsonrpc":"2.0","id":0,"result":{"capabilities":{ # CHECK: "textDocumentSync": 1, # CHECK: "documentFormattingProvider": true, # CHECK: "documentRangeFormattingProvider": true, # CHECK: "documentOnTypeFormattingProvider": {"firstTriggerCharacter":"}","moreTriggerCharacter":[]}, # CHECK: "codeActionProvider": true, -# CHECK: "completionProvider": {"resolveProvider": false, "triggerCharacters": [".",">"]} +# CHECK: "completionProvider": {"resolveProvider": false, "triggerCharacters": [".",">"]}, +# CHECK: "definitionProvider": true # CHECK: }}} # Content-Length: 193