[clangd] Add "Go to Declaration" functionality
Summary: This change allows to navigate to most identifiers' declarations in code. This is a first step towards implementing "Go to Definition". It reuses clangIndex in order to detect which occurrences corresponds to the position requested. The occurrences' Decls are then used to generate locations suitable for navigating to the declarations. Reviewers: krasimir, bkramer, ilya-biryukov Reviewed By: ilya-biryukov Subscribers: cfe-commits, mgorny Tags: #clang-tools-extra Differential Revision: https://reviews.llvm.org/D34269 llvm-svn: 306558
This commit is contained in:
parent
93cf232338
commit
2cbf03728a
|
@ -18,6 +18,7 @@ add_clang_library(clangDaemon
|
|||
clangBasic
|
||||
clangFormat
|
||||
clangFrontend
|
||||
clangIndex
|
||||
clangSema
|
||||
clangTooling
|
||||
clangToolingCore
|
||||
|
|
|
@ -69,6 +69,8 @@ public:
|
|||
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 @@ void ClangdLSPServer::LSPProtocolCallbacks::onInitialize(StringRef ID,
|
|||
"documentRangeFormattingProvider": true,
|
||||
"documentOnTypeFormattingProvider": {"firstTriggerCharacter":"}","moreTriggerCharacter":[]},
|
||||
"codeActionProvider": true,
|
||||
"completionProvider": {"resolveProvider": false, "triggerCharacters": [".",">"]}
|
||||
"completionProvider": {"resolveProvider": false, "triggerCharacters": [".",">"]},
|
||||
"definitionProvider": true
|
||||
}}})");
|
||||
}
|
||||
|
||||
|
@ -191,6 +194,25 @@ void ClangdLSPServer::LSPProtocolCallbacks::onCompletion(
|
|||
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) {}
|
||||
|
|
|
@ -271,3 +271,17 @@ std::string ClangdServer::dumpAST(PathRef File) {
|
|||
});
|
||||
return DumpFuture.get();
|
||||
}
|
||||
|
||||
Tagged<std::vector<Location>>
|
||||
ClangdServer::findDefinitions(PathRef File, Position Pos) {
|
||||
auto FileContents = DraftMgr.getDraft(File);
|
||||
assert(FileContents.Draft && "findDefinitions is called for non-added document");
|
||||
|
||||
std::vector<Location> Result;
|
||||
auto TaggedFS = FSProvider.getTaggedFileSystem(File);
|
||||
Units.runOnUnit(File, *FileContents.Draft, ResourceDir, CDB, PCHs,
|
||||
TaggedFS.Value, [&](ClangdUnit &Unit) {
|
||||
Result = Unit.findDefinitions(Pos);
|
||||
});
|
||||
return make_tagged(std::move(Result), TaggedFS.Tag);
|
||||
}
|
||||
|
|
|
@ -179,6 +179,8 @@ public:
|
|||
Tagged<std::vector<CompletionItem>>
|
||||
codeComplete(PathRef File, Position Pos,
|
||||
llvm::Optional<StringRef> OverridenContents = llvm::None);
|
||||
/// Get definition of symbol at a specified \p Line and \p Column in \p File.
|
||||
Tagged<std::vector<Location>> findDefinitions(PathRef File, Position Pos);
|
||||
|
||||
/// Run formatting for \p Rng inside \p File.
|
||||
std::vector<tooling::Replacement> formatRange(PathRef File, Range Rng);
|
||||
|
|
|
@ -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 <algorithm>
|
||||
|
||||
using namespace clang::clangd;
|
||||
using namespace clang;
|
||||
|
||||
|
@ -259,3 +267,145 @@ std::vector<DiagWithFixIts> ClangdUnit::getLocalDiagnostics() const {
|
|||
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<Location> DeclarationLocations;
|
||||
const SourceLocation &SearchedLocation;
|
||||
ASTUnit &Unit;
|
||||
public:
|
||||
DeclarationLocationsFinder(raw_ostream &OS,
|
||||
const SourceLocation &SearchedLocation, ASTUnit &Unit) :
|
||||
SearchedLocation(SearchedLocation), Unit(Unit) {
|
||||
}
|
||||
|
||||
std::vector<Location> 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<index::SymbolRelation> 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<FileID, unsigned int> 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<Location> 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<DeclarationLocationsFinder>(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;
|
||||
}
|
||||
|
|
|
@ -59,6 +59,8 @@ public:
|
|||
std::vector<CompletionItem>
|
||||
codeComplete(StringRef Contents, Position Pos,
|
||||
IntrusiveRefCntPtr<vfs::FileSystem> VFS);
|
||||
/// Get definition of symbol at a specified \p Line and \p Column in \p File.
|
||||
std::vector<Location> findDefinitions(Position Pos);
|
||||
/// Returns diagnostics and corresponding FixIts for each diagnostic that are
|
||||
/// located in the current file.
|
||||
std::vector<DiagWithFixIts> getLocalDiagnostics() const;
|
||||
|
@ -71,6 +73,8 @@ private:
|
|||
Path FileName;
|
||||
std::unique_ptr<ASTUnit> Unit;
|
||||
std::shared_ptr<PCHContainerOperations> PCHs;
|
||||
|
||||
SourceLocation getBeginningOfIdentifier(const Position& Pos, const FileEntry* FE) const;
|
||||
};
|
||||
|
||||
} // namespace clangd
|
||||
|
|
|
@ -54,7 +54,7 @@ URI URI::parse(llvm::yaml::ScalarNode *Param) {
|
|||
}
|
||||
|
||||
std::string URI::unparse(const URI &U) {
|
||||
return U.uri;
|
||||
return "\"" + U.uri + "\"";
|
||||
}
|
||||
|
||||
llvm::Optional<TextDocumentIdentifier>
|
||||
|
@ -162,6 +162,14 @@ std::string Range::unparse(const Range &P) {
|
|||
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>
|
||||
TextDocumentItem::parse(llvm::yaml::MappingNode *Params) {
|
||||
TextDocumentItem Result;
|
||||
|
|
|
@ -38,6 +38,18 @@ struct URI {
|
|||
|
||||
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 @@ struct Range {
|
|||
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.
|
||||
|
|
|
@ -186,6 +186,24 @@ private:
|
|||
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 @@ void clangd::regiterCallbackHandlers(JSONRPCDispatcher &Dispatcher,
|
|||
Dispatcher.registerHandler(
|
||||
"textDocument/completion",
|
||||
llvm::make_unique<CompletionHandler>(Out, Callbacks));
|
||||
Dispatcher.registerHandler("textDocument/definition",
|
||||
llvm::make_unique<GotoDefinitionHandler>(Out, Callbacks));
|
||||
}
|
||||
|
|
|
@ -46,6 +46,8 @@ public:
|
|||
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,
|
||||
|
|
|
@ -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 <typename MyTemplateParam>\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"}
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue