Skip to content

Commit 4d9c408

Browse files
DanTupCommit Queue
authored and
Commit Queue
committed
[analysis_server] Add support for LSP Document Links for example/api links
This extracts the code that finds "examples/api" paths in code from the navigation code so that it can be re-used by LSP's DocumentLink request. This allows the links to be more visible in the editor (they are underlined and clearly clickable like hyperlinks, whereas definition required you to hover and hold Ctrl before you'd seen an underline in VS Code). Example: https://github.com/Dart-Code/Dart-Code/assets/1078012/be9db467-dbc9-4206-9dd5-f8ca72d95596 There may be an argument for making this more general (so that links other than example/api could be detected) but for now the goal was to make these existing links more obvious. Fixes Dart-Code/Dart-Code#4186 Change-Id: Id00425175b24394e565e13406ea4bac5691d2c72 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/333082 Commit-Queue: Brian Wilkerson <[email protected]> Reviewed-by: Samuel Rawlins <[email protected]> Reviewed-by: Brian Wilkerson <[email protected]>
1 parent 7e8a203 commit 4d9c408

15 files changed

+280
-97
lines changed

pkg/analysis_server/lib/src/handler/legacy/analysis_get_navigation.dart

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,9 @@ class AnalysisGetNavigationHandler extends LegacyHandler
5151
var allResults = <AnalysisNavigationParams>[];
5252
var result = await server.getResolvedUnit(file);
5353
if (result != null) {
54-
var unit = result.unit;
5554
var collector = NavigationCollectorImpl();
5655
computeDartNavigation(
57-
server.resourceProvider, collector, unit, offset, length);
56+
server.resourceProvider, collector, result, offset, length);
5857
collector.createRegions();
5958
allResults.add(AnalysisNavigationParams(
6059
file, collector.regions, collector.targets, collector.files));

pkg/analysis_server/lib/src/legacy_analysis_server.dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1035,7 +1035,7 @@ class ServerContextManagerCallbacks
10351035
_notificationManager.recordNavigationParams(
10361036
NotificationManager.serverId,
10371037
path,
1038-
_computeNavigationParams(path, unit));
1038+
_computeNavigationParams(path, result));
10391039
});
10401040
}
10411041
if (analysisServer._hasAnalysisServiceSubscription(
@@ -1093,9 +1093,9 @@ class ServerContextManagerCallbacks
10931093
}
10941094

10951095
server.AnalysisNavigationParams _computeNavigationParams(
1096-
String path, CompilationUnit unit) {
1096+
String path, ParsedUnitResult result) {
10971097
var collector = NavigationCollectorImpl();
1098-
computeDartNavigation(resourceProvider, collector, unit, null, null);
1098+
computeDartNavigation(resourceProvider, collector, result, null, null);
10991099
collector.createRegions();
11001100
return server.AnalysisNavigationParams(
11011101
path, collector.regions, collector.targets, collector.files);

pkg/analysis_server/lib/src/lsp/handlers/handler_definition.dart

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,9 @@ class DefinitionHandler extends LspMessageHandler<TextDocumentPositionParams,
5656
final collector = NavigationCollectorImpl();
5757

5858
final result = await server.getResolvedUnit(path);
59-
final unit = result?.unit;
60-
if (unit != null) {
59+
if (result != null) {
6160
computeDartNavigation(
62-
server.resourceProvider, collector, unit, offset, 0);
61+
server.resourceProvider, collector, result, offset, 0);
6362
if (supportsLocationLink) {
6463
await _updateTargetsWithCodeLocations(collector);
6564
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'package:analysis_server/lsp_protocol/protocol.dart' hide Element;
6+
import 'package:analysis_server/src/lsp/constants.dart';
7+
import 'package:analysis_server/src/lsp/handlers/handlers.dart';
8+
import 'package:analysis_server/src/lsp/mapping.dart';
9+
import 'package:analysis_server/src/lsp/registration/feature_registration.dart';
10+
import 'package:analyzer/source/line_info.dart';
11+
import 'package:analyzer_plugin/utilities/navigation/document_links.dart';
12+
13+
class DocumentLinkHandler
14+
extends LspMessageHandler<DocumentLinkParams, List<DocumentLink>?>
15+
with LspPluginRequestHandlerMixin {
16+
DocumentLinkHandler(super.server);
17+
18+
@override
19+
Method get handlesMessage => Method.textDocument_documentLink;
20+
21+
@override
22+
LspJsonHandler<DocumentLinkParams> get jsonHandler =>
23+
DocumentLinkParams.jsonHandler;
24+
25+
@override
26+
Future<ErrorOr<List<DocumentLink>?>> handle(DocumentLinkParams params,
27+
MessageInfo message, CancellationToken token) async {
28+
if (!isDartDocument(params.textDocument)) {
29+
return success(const []);
30+
}
31+
32+
final path = pathOfDoc(params.textDocument);
33+
final parsedUnit = await path.mapResult(requireUnresolvedUnit);
34+
35+
return parsedUnit.mapResult((unit) async {
36+
/// Helper to convert using LineInfo.
37+
DocumentLink convert(DartDocumentLink link) {
38+
return _convert(link, unit.lineInfo);
39+
}
40+
41+
final visitor = DartDocumentLinkVisitor(server.resourceProvider, unit);
42+
final links = visitor.findLinks(unit.unit);
43+
44+
return success(links.map(convert).toList());
45+
});
46+
}
47+
48+
DocumentLink _convert(DartDocumentLink link, LineInfo lineInfo) {
49+
return DocumentLink(
50+
range: toRange(lineInfo, link.offset, link.length),
51+
target: Uri.file(link.targetPath).toString(),
52+
);
53+
}
54+
}
55+
56+
class DocumentLinkRegistrations extends FeatureRegistration
57+
with SingleDynamicRegistration, StaticRegistration<DocumentLinkOptions> {
58+
DocumentLinkRegistrations(super.info);
59+
60+
@override
61+
ToJsonable? get options => DocumentLinkRegistrationOptions(
62+
documentSelector: [dartFiles],
63+
resolveProvider: false,
64+
);
65+
66+
@override
67+
Method get registrationMethod => Method.textDocument_documentLink;
68+
69+
@override
70+
DocumentLinkOptions get staticOptions =>
71+
DocumentLinkOptions(resolveProvider: false);
72+
73+
@override
74+
bool get supportsDynamic => clientDynamic.documentLink;
75+
}

pkg/analysis_server/lib/src/lsp/handlers/handler_references.dart

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,10 @@ class ReferencesHandler
4747
unit.result, offset, params, unit.result, performance)));
4848
}
4949

50-
List<Location> _getDeclarations(CompilationUnit unit, int offset) {
50+
List<Location> _getDeclarations(ParsedUnitResult result, int offset) {
5151
final collector = NavigationCollectorImpl();
52-
computeDartNavigation(server.resourceProvider, collector, unit, offset, 0);
52+
computeDartNavigation(
53+
server.resourceProvider, collector, result, offset, 0);
5354

5455
return convert(collector.targets, (NavigationTarget target) {
5556
final targetFilePath = collector.files[target.fileIndex];
@@ -99,11 +100,10 @@ class ReferencesHandler
99100
final referenceResults = performance.run(
100101
'convert', (_) => convert(results, toLocation).whereNotNull().toList());
101102

102-
final compilationUnit = unit.unit;
103103
if (params.context.includeDeclaration == true) {
104104
// Also include the definition for the symbol at this location.
105-
referenceResults.addAll(performance.run('_getDeclarations',
106-
(_) => _getDeclarations(compilationUnit, offset)));
105+
referenceResults.addAll(performance.run(
106+
'_getDeclarations', (_) => _getDeclarations(unit, offset)));
107107
}
108108

109109
return success(referenceResults);

pkg/analysis_server/lib/src/lsp/handlers/handler_states.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import 'package:analysis_server/src/lsp/handlers/handler_definition.dart';
1919
import 'package:analysis_server/src/lsp/handlers/handler_document_color.dart';
2020
import 'package:analysis_server/src/lsp/handlers/handler_document_color_presentation.dart';
2121
import 'package:analysis_server/src/lsp/handlers/handler_document_highlights.dart';
22+
import 'package:analysis_server/src/lsp/handlers/handler_document_link.dart';
2223
import 'package:analysis_server/src/lsp/handlers/handler_document_symbols.dart';
2324
import 'package:analysis_server/src/lsp/handlers/handler_execute_command.dart';
2425
import 'package:analysis_server/src/lsp/handlers/handler_exit.dart';
@@ -76,6 +77,7 @@ class InitializedLspStateMessageHandler extends InitializedStateMessageHandler {
7677
CompletionHandler.new,
7778
CompletionResolveHandler.new,
7879
DefinitionHandler.new,
80+
DocumentLinkHandler.new,
7981
SuperHandler.new,
8082
ReferencesHandler.new,
8183
CodeActionHandler.new,

pkg/analysis_server/lib/src/lsp/registration/feature_registration.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import 'package:analysis_server/src/lsp/handlers/handler_completion.dart';
1313
import 'package:analysis_server/src/lsp/handlers/handler_definition.dart';
1414
import 'package:analysis_server/src/lsp/handlers/handler_document_color.dart';
1515
import 'package:analysis_server/src/lsp/handlers/handler_document_highlights.dart';
16+
import 'package:analysis_server/src/lsp/handlers/handler_document_link.dart';
1617
import 'package:analysis_server/src/lsp/handlers/handler_document_symbols.dart';
1718
import 'package:analysis_server/src/lsp/handlers/handler_execute_command.dart';
1819
import 'package:analysis_server/src/lsp/handlers/handler_folding.dart';
@@ -85,6 +86,7 @@ class LspFeatures {
8586
final CodeActionRegistrations codeActions;
8687
final CompletionRegistrations completion;
8788
final DefinitionRegistrations definition;
89+
final DocumentLinkRegistrations documentLink;
8890
final DocumentColorRegistrations colors;
8991
final DocumentHighlightsRegistrations documentHighlight;
9092
final DocumentSymbolsRegistrations documentSymbol;
@@ -116,6 +118,7 @@ class LspFeatures {
116118
colors = DocumentColorRegistrations(context),
117119
completion = CompletionRegistrations(context),
118120
definition = DefinitionRegistrations(context),
121+
documentLink = DocumentLinkRegistrations(context),
119122
format = FormattingRegistrations(context),
120123
documentHighlight = DocumentHighlightsRegistrations(context),
121124
formatOnType = FormatOnTypeRegistrations(context),
@@ -145,6 +148,7 @@ class LspFeatures {
145148
codeActions,
146149
completion,
147150
definition,
151+
documentLink,
148152
colors,
149153
documentHighlight,
150154
documentSymbol,

pkg/analysis_server/lib/src/lsp/server_capabilities_computer.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,9 @@ class ClientDynamicRegistrations {
7777
_capabilities.textDocument?.documentHighlight?.dynamicRegistration ??
7878
false;
7979

80+
bool get documentLink =>
81+
_capabilities.textDocument?.documentLink?.dynamicRegistration ?? false;
82+
8083
bool get documentSymbol =>
8184
_capabilities.textDocument?.documentSymbol?.dynamicRegistration ?? false;
8285

@@ -170,6 +173,7 @@ class ServerCapabilitiesComputer {
170173
hoverProvider: features.hover.staticRegistration,
171174
signatureHelpProvider: features.signatureHelp.staticRegistration,
172175
definitionProvider: features.definition.staticRegistration,
176+
documentLinkProvider: features.documentLink.staticRegistration,
173177
implementationProvider: features.implementation.staticRegistration,
174178
referencesProvider: features.references.staticRegistration,
175179
documentHighlightProvider: features.documentHighlight.staticRegistration,
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'package:analyzer/src/test_utilities/test_code_format.dart';
6+
import 'package:test/test.dart';
7+
import 'package:test_reflective_loader/test_reflective_loader.dart';
8+
9+
import '../utils/test_code_extensions.dart';
10+
import 'server_abstract.dart';
11+
12+
void main() {
13+
defineReflectiveSuite(() {
14+
defineReflectiveTests(DocumentLinkTest);
15+
});
16+
}
17+
18+
@reflectiveTest
19+
class DocumentLinkTest extends AbstractLspAnalysisServerTest {
20+
Future<void> test_exampleLink() async {
21+
final exampleFolderPath = join(projectFolderPath, 'examples', 'api');
22+
final exampleFileUri = Uri.file(join(exampleFolderPath, 'foo.dart'));
23+
24+
final code = TestCode.parse('''
25+
/// {@tool dartpad}
26+
/// ** See code in [!examples/api/foo.dart!] **
27+
/// {@end-tool}
28+
class A {}
29+
''');
30+
31+
newFolder(exampleFolderPath);
32+
newFile(mainFilePath, code.code);
33+
34+
await initialize();
35+
final links = await getDocumentLinks(mainFileUri);
36+
37+
final link = links!.single;
38+
expect(link.range, code.range.range);
39+
expect(link.target, exampleFileUri.toString());
40+
}
41+
}

pkg/analysis_server/test/lsp/initialization_test.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,10 @@ class InitializationTest extends AbstractLspAnalysisServerTest {
281281
assertDynamicRegistration(
282282
'documentHighlight', {Method.textDocument_documentHighlight});
283283

284+
Future<void> test_dynamicRegistration_config_documentLink() =>
285+
assertDynamicRegistration(
286+
'documentLink', {Method.textDocument_documentLink});
287+
284288
Future<void> test_dynamicRegistration_config_documentSymbol() =>
285289
assertDynamicRegistration(
286290
'documentSymbol', {Method.textDocument_documentSymbol});

pkg/analysis_server/test/lsp/request_helpers_mixin.dart

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,17 @@ mixin LspRequestHelpersMixin {
319319
request, _fromJsonList(DocumentHighlight.fromJson));
320320
}
321321

322+
Future<List<DocumentLink>?> getDocumentLinks(Uri uri) {
323+
final request = makeRequest(
324+
Method.textDocument_documentLink,
325+
DocumentLinkParams(textDocument: TextDocumentIdentifier(uri: uri)),
326+
);
327+
return expectSuccessfulResponseTo(
328+
request,
329+
_fromJsonList(DocumentLink.fromJson),
330+
);
331+
}
332+
322333
Future<Either2<List<DocumentSymbol>, List<SymbolInformation>>>
323334
getDocumentSymbols(Uri uri) {
324335
final request = makeRequest(

pkg/analysis_server/test/lsp/test_all.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import 'diagnostic_test.dart' as diagnostic;
2323
import 'document_changes_test.dart' as document_changes;
2424
import 'document_color_test.dart' as document_color;
2525
import 'document_highlights_test.dart' as document_highlights;
26+
import 'document_link_test.dart' as document_link;
2627
import 'document_symbols_test.dart' as document_symbols;
2728
import 'file_modification_test.dart' as file_modification;
2829
import 'flutter_outline_test.dart' as flutter_outline;
@@ -73,6 +74,7 @@ void main() {
7374
document_changes.main();
7475
document_color.main();
7576
document_highlights.main();
77+
document_link.main();
7678
document_symbols.main();
7779
file_modification.main();
7880
flutter_outline.main();

0 commit comments

Comments
 (0)