Skip to content

Commit 8d7ecc1

Browse files
committed
Revert "Revert "[clangd] Implement "textDocument/documentLink" protocol support""
This reverts commit 079ef78. The revert describes a test failure without details, after offline discussion this in in a private/unsupported build system and doesn't seem to reflect a real upstream bug.
1 parent cd17c06 commit 8d7ecc1

File tree

11 files changed

+175
-0
lines changed

11 files changed

+175
-0
lines changed

clang-tools-extra/clangd/ClangdLSPServer.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -566,6 +566,10 @@ void ClangdLSPServer::onInitialize(const InitializeParams &Params,
566566
{"declarationProvider", true},
567567
{"definitionProvider", true},
568568
{"documentHighlightProvider", true},
569+
{"documentLinkProvider",
570+
llvm::json::Object{
571+
{"resolveProvider", false},
572+
}},
569573
{"hoverProvider", true},
570574
{"renameProvider", std::move(RenameProvider)},
571575
{"selectionRangeProvider", true},
@@ -1200,6 +1204,25 @@ void ClangdLSPServer::onSelectionRange(
12001204
});
12011205
}
12021206

1207+
void ClangdLSPServer::onDocumentLink(
1208+
const DocumentLinkParams &Params,
1209+
Callback<std::vector<DocumentLink>> Reply) {
1210+
1211+
// TODO(forster): This currently resolves all targets eagerly. This is slow,
1212+
// because it blocks on the preamble/AST being built. We could respond to the
1213+
// request faster by using string matching or the lexer to find the includes
1214+
// and resolving the targets lazily.
1215+
Server->documentLinks(
1216+
Params.textDocument.uri.file(),
1217+
[Reply = std::move(Reply)](
1218+
llvm::Expected<std::vector<DocumentLink>> Links) mutable {
1219+
if (!Links) {
1220+
return Reply(Links.takeError());
1221+
}
1222+
return Reply(std::move(Links));
1223+
});
1224+
}
1225+
12031226
ClangdLSPServer::ClangdLSPServer(
12041227
class Transport &Transp, const FileSystemProvider &FSProvider,
12051228
const clangd::CodeCompleteOptions &CCOpts,
@@ -1243,6 +1266,7 @@ ClangdLSPServer::ClangdLSPServer(
12431266
MsgHandler->bind("textDocument/typeHierarchy", &ClangdLSPServer::onTypeHierarchy);
12441267
MsgHandler->bind("typeHierarchy/resolve", &ClangdLSPServer::onResolveTypeHierarchy);
12451268
MsgHandler->bind("textDocument/selectionRange", &ClangdLSPServer::onSelectionRange);
1269+
MsgHandler->bind("textDocument/documentLink", &ClangdLSPServer::onDocumentLink);
12461270
// clang-format on
12471271
}
12481272

clang-tools-extra/clangd/ClangdLSPServer.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,8 @@ class ClangdLSPServer : private DiagnosticsConsumer {
111111
Callback<std::vector<SymbolDetails>>);
112112
void onSelectionRange(const SelectionRangeParams &,
113113
Callback<std::vector<SelectionRange>>);
114+
void onDocumentLink(const DocumentLinkParams &,
115+
Callback<std::vector<DocumentLink>>);
114116

115117
std::vector<Fix> getFixes(StringRef File, const clangd::Diagnostic &D);
116118

clang-tools-extra/clangd/ClangdServer.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -611,6 +611,17 @@ void ClangdServer::semanticRanges(PathRef File, Position Pos,
611611
WorkScheduler.runWithAST("SemanticRanges", File, std::move(Action));
612612
}
613613

614+
void ClangdServer::documentLinks(PathRef File,
615+
Callback<std::vector<DocumentLink>> CB) {
616+
auto Action =
617+
[CB = std::move(CB)](llvm::Expected<InputsAndAST> InpAST) mutable {
618+
if (!InpAST)
619+
return CB(InpAST.takeError());
620+
CB(clangd::getDocumentLinks(InpAST->AST));
621+
};
622+
WorkScheduler.runWithAST("DocumentLinks", File, std::move(Action));
623+
}
624+
614625
std::vector<std::pair<Path, std::size_t>>
615626
ClangdServer::getUsedBytesPerFile() const {
616627
return WorkScheduler.getUsedBytesPerFile();

clang-tools-extra/clangd/ClangdServer.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,9 @@ class ClangdServer {
287287
void semanticRanges(PathRef File, Position Pos,
288288
Callback<std::vector<Range>> CB);
289289

290+
/// Get all document links in a file.
291+
void documentLinks(PathRef File, Callback<std::vector<DocumentLink>> CB);
292+
290293
/// Returns estimated memory usage for each of the currently open files.
291294
/// The order of results is unspecified.
292295
/// Overall memory usage of clangd may be significantly more than reported

clang-tools-extra/clangd/Protocol.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1087,5 +1087,18 @@ llvm::json::Value toJSON(const SelectionRange &Out) {
10871087
}
10881088
return llvm::json::Object{{"range", Out.range}};
10891089
}
1090+
1091+
bool fromJSON(const llvm::json::Value &Params, DocumentLinkParams &R) {
1092+
llvm::json::ObjectMapper O(Params);
1093+
return O && O.map("textDocument", R.textDocument);
1094+
}
1095+
1096+
llvm::json::Value toJSON(const DocumentLink &DocumentLink) {
1097+
return llvm::json::Object{
1098+
{"range", DocumentLink.range},
1099+
{"target", DocumentLink.target},
1100+
};
1101+
}
1102+
10901103
} // namespace clangd
10911104
} // namespace clang

clang-tools-extra/clangd/Protocol.h

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1250,6 +1250,39 @@ struct SelectionRange {
12501250
};
12511251
llvm::json::Value toJSON(const SelectionRange &);
12521252

1253+
/// Parameters for the document link request.
1254+
struct DocumentLinkParams {
1255+
/// The document to provide document links for.
1256+
TextDocumentIdentifier textDocument;
1257+
};
1258+
bool fromJSON(const llvm::json::Value &, DocumentLinkParams &);
1259+
1260+
/// A range in a text document that links to an internal or external resource,
1261+
/// like another text document or a web site.
1262+
struct DocumentLink {
1263+
/// The range this link applies to.
1264+
Range range;
1265+
1266+
/// The uri this link points to. If missing a resolve request is sent later.
1267+
URIForFile target;
1268+
1269+
// TODO(forster): The following optional fields defined by the language
1270+
// server protocol are unsupported:
1271+
//
1272+
// data?: any - A data entry field that is preserved on a document link
1273+
// between a DocumentLinkRequest and a
1274+
// DocumentLinkResolveRequest.
1275+
1276+
friend bool operator==(const DocumentLink &LHS, const DocumentLink &RHS) {
1277+
return LHS.range == RHS.range && LHS.target == RHS.target;
1278+
}
1279+
1280+
friend bool operator!=(const DocumentLink &LHS, const DocumentLink &RHS) {
1281+
return !(LHS == RHS);
1282+
}
1283+
};
1284+
llvm::json::Value toJSON(const DocumentLink &DocumentLink);
1285+
12531286
} // namespace clangd
12541287
} // namespace clang
12551288

clang-tools-extra/clangd/XRefs.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,26 @@ llvm::Optional<Location> makeLocation(ASTContext &AST, SourceLocation TokLoc,
166166

167167
} // namespace
168168

169+
std::vector<DocumentLink> getDocumentLinks(ParsedAST &AST) {
170+
const auto &SM = AST.getSourceManager();
171+
auto MainFilePath =
172+
getCanonicalPath(SM.getFileEntryForID(SM.getMainFileID()), SM);
173+
if (!MainFilePath) {
174+
elog("Failed to get a path for the main file, so no links");
175+
return {};
176+
}
177+
178+
std::vector<DocumentLink> Result;
179+
for (auto &Inc : AST.getIncludeStructure().MainFileIncludes) {
180+
if (!Inc.Resolved.empty()) {
181+
Result.push_back(DocumentLink(
182+
{Inc.R, URIForFile::canonicalize(Inc.Resolved, *MainFilePath)}));
183+
}
184+
}
185+
186+
return Result;
187+
}
188+
169189
std::vector<LocatedSymbol> locateSymbolAt(ParsedAST &AST, Position Pos,
170190
const SymbolIndex *Index) {
171191
const auto &SM = AST.getSourceManager();

clang-tools-extra/clangd/XRefs.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &, const LocatedSymbol &);
4949
std::vector<LocatedSymbol> locateSymbolAt(ParsedAST &AST, Position Pos,
5050
const SymbolIndex *Index = nullptr);
5151

52+
/// Get all document links
53+
std::vector<DocumentLink> getDocumentLinks(ParsedAST &AST);
54+
5255
/// Returns highlights for all usages of a symbol at \p Pos.
5356
std::vector<DocumentHighlight> findDocumentHighlights(ParsedAST &AST,
5457
Position Pos);
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# RUN: clangd -lit-test < %s | FileCheck -strict-whitespace %s
2+
{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}}
3+
---
4+
{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///main.cpp","languageId":"cpp","version":1,"text":"#include <stdint.h>\n#include <stddef.h>"}}}
5+
---
6+
{"jsonrpc":"2.0","id":2,"method":"textDocument/documentLink","params":{"textDocument":{"uri":"test:///main.cpp"}}}
7+
# CHECK: "id": 2,
8+
# CHECK-NEXT: "jsonrpc": "2.0",
9+
# CHECK-NEXT: "result": [
10+
# CHECK-NEXT: {
11+
# CHECK-NEXT: "range": {
12+
# CHECK-NEXT: "end": {
13+
# CHECK-NEXT: "character": 19,
14+
# CHECK-NEXT: "line": 0
15+
# CHECK-NEXT: },
16+
# CHECK-NEXT: "start": {
17+
# CHECK-NEXT: "character": 9,
18+
# CHECK-NEXT: "line": 0
19+
# CHECK-NEXT: }
20+
# CHECK-NEXT: },
21+
# CHECK-NEXT: "target": "file://{{.*}}/stdint.h"
22+
# CHECK-NEXT: },
23+
# CHECK-NEXT: {
24+
# CHECK-NEXT: "range": {
25+
# CHECK-NEXT: "end": {
26+
# CHECK-NEXT: "character": 19,
27+
# CHECK-NEXT: "line": 1
28+
# CHECK-NEXT: },
29+
# CHECK-NEXT: "start": {
30+
# CHECK-NEXT: "character": 9,
31+
# CHECK-NEXT: "line": 1
32+
# CHECK-NEXT: }
33+
# CHECK-NEXT: },
34+
# CHECK-NEXT: "target": "file://{{.*}}/stddef.h"
35+
# CHECK-NEXT: }
36+
# CHECK-NEXT: ]
37+
# CHECK-NEXT:}
38+
39+
---
40+
{"jsonrpc":"2.0","id":3,"method":"shutdown"}
41+
---
42+
{"jsonrpc":"2.0","method":"exit"}

clang-tools-extra/clangd/test/initialize-params.test

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@
1818
# CHECK-NEXT: "definitionProvider": true,
1919
# CHECK-NEXT: "documentFormattingProvider": true,
2020
# CHECK-NEXT: "documentHighlightProvider": true,
21+
# CHECK-NEXT: "documentLinkProvider": {
22+
# CHECK-NEXT: "resolveProvider": false
23+
# CHECK-NEXT: }
2124
# CHECK-NEXT: "documentOnTypeFormattingProvider": {
2225
# CHECK-NEXT: "firstTriggerCharacter": "\n",
2326
# CHECK-NEXT: "moreTriggerCharacter": []

clang-tools-extra/clangd/unittests/XRefsTests.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1121,6 +1121,27 @@ TEST(GetNonLocalDeclRefs, All) {
11211121
}
11221122
}
11231123

1124+
TEST(DocumentLinks, All) {
1125+
Annotations MainCpp(R"cpp(
1126+
#include $foo[["foo.h"]]
1127+
int end_of_preamble = 0;
1128+
#include $bar[["bar.h"]]
1129+
)cpp");
1130+
1131+
TestTU TU;
1132+
TU.Code = MainCpp.code();
1133+
TU.AdditionalFiles = {{"foo.h", ""}, {"bar.h", ""}};
1134+
auto AST = TU.build();
1135+
1136+
EXPECT_THAT(
1137+
clangd::getDocumentLinks(AST),
1138+
ElementsAre(
1139+
DocumentLink({MainCpp.range("foo"),
1140+
URIForFile::canonicalize(testPath("foo.h"), "")}),
1141+
DocumentLink({MainCpp.range("bar"),
1142+
URIForFile::canonicalize(testPath("bar.h"), "")})));
1143+
}
1144+
11241145
} // namespace
11251146
} // namespace clangd
11261147
} // namespace clang

0 commit comments

Comments
 (0)