jkorous created this revision. jkorous added reviewers: sammccall, arphaman, benlangmuir. Herald added subscribers: cfe-commits, kadircet, dexonsmith, MaskRay, ioeric, ilya-biryukov.
Hi, I implemented `textDocument/cursorInfo` method based on consensus in https://reviews.llvm.org/D54529. I'd like to ask for early feedback - what's still missing is relevant client capability. Couple things that I'd love to hear opinions about are: - conditional return in `getCursorInfo` - Should we return for example data with empty `USR`? - `containerName` of local variables - It's currently empty even if semantic parent has a name (say it's a function). (Please search for local.cpp in the test.) Is that what we want? - For now I used `getSymbolAtPosition()` as it's simpler and fits the context better. However I assume I could use this optimization from `tooling::getNamedDeclAt()` (in a separate patch): https://github.com/llvm-mirror/clang/blob/master/lib/Tooling/Refactoring/Rename/USRFinder.cpp#L82 - One thing I am wondering about is whether we could use (and whether it's a significant improvement) some early return in `indexTopLevelDecls` (using `DeclarationAndMacrosFinder`) also for hover and definition use-cases. Is it correct to assume that at a given `Location` there can be maximum of one declaration and one definition? P. S. Alex and Ben have thanksgiving break now so they'll probably add any feedback next week. Repository: rCTE Clang Tools Extra https://reviews.llvm.org/D54799 Files: ClangdLSPServer.cpp ClangdLSPServer.h ClangdServer.cpp ClangdServer.h Protocol.cpp Protocol.h XRefs.cpp XRefs.h clangd/cursor-info.test
Index: clangd/cursor-info.test =================================================================== --- /dev/null +++ clangd/cursor-info.test @@ -0,0 +1,46 @@ +# RUN: clangd -lit-test < %s | FileCheck %s +{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}} +--- +{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///simple.cpp","languageId":"cpp","version":1,"text":"void foo(); int main() { foo(); }\n"}}} +--- +{"jsonrpc":"2.0","id":1,"method":"textDocument/cursorInfo","params":{"textDocument":{"uri":"test:///simple.cpp"},"position":{"line":0,"character":27}}} +# CHECK: "containerName": "", +# CHECK-NEXT: "id": "CA2EBE44A1D76D2A1547D47BC6D51EBF", +# CHECK-NEXT: "name": "foo", +# CHECK-NEXT: "usr": "c:@F@foo#" +--- +{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///nested-decl.cpp","languageId":"cpp","version":1,"text":"namespace nnn { struct aaa {}; void foo() { aaa a; }; }"}}} +--- +{"jsonrpc":"2.0","id":1,"method":"textDocument/cursorInfo","params":{"textDocument":{"uri":"test:///nested-decl.cpp"},"position":{"line":0,"character":46}}} +# CHECK: "containerName": "nnn::", +# CHECK-NEXT: "id": "20237FF18EB405D842456DC5D578426D", +# CHECK-NEXT: "name": "aaa", +# CHECK-NEXT: "usr": "c:@N@nnn@S@aaa" +--- +{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///reference.cpp","languageId":"cpp","version":1,"text":"int value; void foo(int) {} void bar() { foo(value); }"}}} +--- +{"jsonrpc":"2.0","id":1,"method":"textDocument/cursorInfo","params":{"textDocument":{"uri":"test:///reference.cpp"},"position":{"line":0,"character":48}}} +# CHECK: "containerName": "", +# CHECK-NEXT: "id": "844613FB2393C9D40A2AFF25D5D316A1", +# CHECK-NEXT: "name": "value", +# CHECK-NEXT: "usr": "c:@value" +--- +{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///local.cpp","languageId":"cpp","version":1,"text":"void foo() { int aaa; int bbb = aaa; }"}}} +--- +{"jsonrpc":"2.0","id":1,"method":"textDocument/cursorInfo","params":{"textDocument":{"uri":"test:///local.cpp"},"position":{"line":0,"character":33}}} +# CHECK: "containerName": "", +# CHECK-NEXT: "id": "C05589F2664B06F392C2C438568E55E0", +# CHECK-NEXT: "name": "aaa", +# CHECK-NEXT: "usr": "c:local.cpp@13@F@foo#@aaa" +--- +{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///macro.cpp","languageId":"cpp","version":1,"text":"#define MACRO 5\nint i = MACRO;"}}} +--- +{"jsonrpc":"2.0","id":1,"method":"textDocument/cursorInfo","params":{"textDocument":{"uri":"test:///macro.cpp"},"position":{"line":1,"character":11}}} +# CHECK: "containerName": "", +# CHECK-NEXT: "id": "29EB506CBDF1BA6D1B6EC203FF03B384", +# CHECK-NEXT: "name": "MACRO", +# CHECK-NEXT: "usr": "c:macro.cpp@24@macro@MACRO" +--- +{"jsonrpc":"2.0","id":3,"method":"shutdown"} +--- +{"jsonrpc":"2.0","method":"exit"} Index: XRefs.h =================================================================== --- XRefs.h +++ XRefs.h @@ -38,6 +38,9 @@ std::vector<Location> findReferences(ParsedAST &AST, Position Pos, const SymbolIndex *Index = nullptr); +/// Get info about symbol at \p Cursor. +llvm::Optional<IdentifierData> getCursorInfo(ParsedAST &AST, Position Pos); + } // namespace clangd } // namespace clang Index: XRefs.cpp =================================================================== --- XRefs.cpp +++ XRefs.cpp @@ -92,11 +92,14 @@ const SourceLocation &SearchedLocation; const ASTContext &AST; Preprocessor &PP; + const bool StopOnFirstDeclFound; public: DeclarationAndMacrosFinder(const SourceLocation &SearchedLocation, - ASTContext &AST, Preprocessor &PP) - : SearchedLocation(SearchedLocation), AST(AST), PP(PP) {} + ASTContext &AST, Preprocessor &PP, + bool StopOnFirstDeclFound) + : SearchedLocation(SearchedLocation), AST(AST), PP(PP), + StopOnFirstDeclFound(StopOnFirstDeclFound) {} // Get all DeclInfo of the found declarations. // The results are sorted by "IsReferencedExplicitly" and declaration @@ -153,6 +156,12 @@ }; bool IsExplicit = !hasImplicitExpr(ASTNode.OrigE); + + if (StopOnFirstDeclFound) { + Decls[D] |= IsExplicit; + return false; + } + // Find and add definition declarations (for GoToDefinition). // We don't use parameter `D`, as Parameter `D` is the canonical // declaration, which is the first declaration of a redeclarable @@ -203,9 +212,10 @@ std::vector<MacroDecl> Macros; }; -IdentifiedSymbol getSymbolAtPosition(ParsedAST &AST, SourceLocation Pos) { - auto DeclMacrosFinder = DeclarationAndMacrosFinder(Pos, AST.getASTContext(), - AST.getPreprocessor()); +IdentifiedSymbol getSymbolAtPosition(ParsedAST &AST, SourceLocation Pos, + bool ReturnFirstDeclFound = false) { + auto DeclMacrosFinder = DeclarationAndMacrosFinder( + Pos, AST.getASTContext(), AST.getPreprocessor(), ReturnFirstDeclFound); index::IndexingOptions IndexOpts; IndexOpts.SystemSymbolFilter = index::IndexingOptions::SystemSymbolFilterKind::All; @@ -749,5 +759,42 @@ return Results; } +llvm::Optional<IdentifierData> getCursorInfo(ParsedAST &AST, Position Pos) { + const SourceManager &SM = AST.getASTContext().getSourceManager(); + + auto Loc = getBeginningOfIdentifier(AST, Pos, SM.getMainFileID()); + auto Symbols = getSymbolAtPosition(AST, Loc, true); + + IdentifierData Result; + + if (!Symbols.Decls.empty()) { + if (const NamedDecl *ND = dyn_cast<NamedDecl>(Symbols.Decls.front().D)) { + std::string QName = printQualifiedName(*ND); + std::tie(Result.containerName, Result.name) = splitQualifiedName(QName); + + StringRef ContainerNameRef = Result.containerName; + ContainerNameRef.consume_back("::"); + } + if (!index::generateUSRForDecl(Symbols.Decls.front().D, Result.USR)) { + Result.ID = SymbolID(Result.USR).str(); + } + // TODO discuss in the review + if (!Result.USR.empty()) + return Result; + } + + if (!Symbols.Macros.empty()) { + Result.name = Symbols.Macros.front().Name; + if (!index::generateUSRForMacro(Result.name, Loc, SM, Result.USR)) { + Result.ID = SymbolID(Result.USR).str(); + } + // TODO discuss in the review + if (!Result.USR.empty()) + return Result; + } + + return llvm::None; +} + } // namespace clangd } // namespace clang Index: Protocol.h =================================================================== --- Protocol.h +++ Protocol.h @@ -673,6 +673,24 @@ llvm::json::Value toJSON(const SymbolInformation &); llvm::raw_ostream &operator<<(llvm::raw_ostream &, const SymbolInformation &); +/// Represents information about identifier. +struct IdentifierData { + /// The name of this symbol. + std::string name; + + /// The name of the symbol containing this symbol. + std::string containerName; + + /// The USR of this symbol. + llvm::SmallString<128> USR; + + /// The hex-encoded ID of this symbol. + /// Size is dependent on clang::clangd::SymbolID::RawValue. + llvm::SmallString<32> ID; +}; +llvm::json::Value toJSON(const IdentifierData &); +llvm::raw_ostream &operator<<(llvm::raw_ostream &, const IdentifierData &); + /// The parameters of a Workspace Symbol Request. struct WorkspaceSymbolParams { /// A non-empty query string Index: Protocol.cpp =================================================================== --- Protocol.cpp +++ Protocol.cpp @@ -422,6 +422,21 @@ return O; } +llvm::json::Value toJSON(const IdentifierData &P) { + return json::Object{ + {"name", P.name}, + {"containerName", P.containerName}, + {"usr", P.USR}, + {"id", P.ID}, + }; +} + +llvm::raw_ostream &operator<<(llvm::raw_ostream &O, + const IdentifierData &SUCI) { + O << SUCI.containerName << "::" << SUCI.name << " - " << toJSON(SUCI); + return O; +} + bool fromJSON(const json::Value &Params, WorkspaceSymbolParams &R) { json::ObjectMapper O(Params); return O && O.map("query", R.query); Index: ClangdServer.h =================================================================== --- ClangdServer.h +++ ClangdServer.h @@ -201,6 +201,10 @@ /// Called when an event occurs for a watched file in the workspace. void onFileEvent(const DidChangeWatchedFilesParams &Params); + /// Get cursor info for given position. + void getCursorInfo(PathRef File, Position Pos, + Callback<llvm::Optional<IdentifierData>> CB); + /// Returns estimated memory usage for each of the currently open files. /// The order of results is unspecified. /// Overall memory usage of clangd may be significantly more than reported Index: ClangdServer.cpp =================================================================== --- ClangdServer.cpp +++ ClangdServer.cpp @@ -517,6 +517,18 @@ WorkScheduler.runWithAST("References", File, Bind(Action, std::move(CB))); } +void ClangdServer::getCursorInfo(PathRef File, Position Pos, + Callback<llvm::Optional<IdentifierData>> CB) { + auto Action = [Pos, this](Callback<llvm::Optional<IdentifierData>> CB, + Expected<InputsAndAST> InpAST) { + if (!InpAST) + return CB(InpAST.takeError()); + CB(clangd::getCursorInfo(InpAST->AST, Pos)); + }; + + WorkScheduler.runWithAST("CursorInfo", File, Bind(Action, std::move(CB))); +} + std::vector<std::pair<Path, std::size_t>> ClangdServer::getUsedBytesPerFile() const { return WorkScheduler.getUsedBytesPerFile(); Index: ClangdLSPServer.h =================================================================== --- ClangdLSPServer.h +++ ClangdLSPServer.h @@ -88,6 +88,8 @@ void onHover(const TextDocumentPositionParams &, Callback<llvm::Optional<Hover>>); void onChangeConfiguration(const DidChangeConfigurationParams &); + void onCursorInfo(const TextDocumentPositionParams &, + Callback<llvm::Optional<IdentifierData>>); std::vector<Fix> getFixes(StringRef File, const clangd::Diagnostic &D); Index: ClangdLSPServer.cpp =================================================================== --- ClangdLSPServer.cpp +++ ClangdLSPServer.cpp @@ -685,6 +685,12 @@ std::move(Reply)); } +void ClangdLSPServer::onCursorInfo(const TextDocumentPositionParams &Params, + Callback<Optional<IdentifierData>> Reply) { + Server->getCursorInfo(Params.textDocument.uri.file(), Params.position, + std::move(Reply)); +} + ClangdLSPServer::ClangdLSPServer(class Transport &Transp, const clangd::CodeCompleteOptions &CCOpts, Optional<Path> CompileCommandsDir, @@ -719,6 +725,7 @@ MsgHandler->bind("textDocument/didChange", &ClangdLSPServer::onDocumentDidChange); MsgHandler->bind("workspace/didChangeWatchedFiles", &ClangdLSPServer::onFileEvent); MsgHandler->bind("workspace/didChangeConfiguration", &ClangdLSPServer::onChangeConfiguration); + MsgHandler->bind("textDocument/cursorInfo", &ClangdLSPServer::onCursorInfo); // clang-format on }
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits