Skip to content

Commit 9682da5

Browse files
DanTupCommit Queue
authored andcommitted
[analysis_server] Migrate LSP "Go to Imports" command to new element model
Change-Id: I07f53420649b5be11088407b0344186bcb741ae8 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/393861 Reviewed-by: Samuel Rawlins <[email protected]> Commit-Queue: Samuel Rawlins <[email protected]> Reviewed-by: Brian Wilkerson <[email protected]> Commit-Queue: Brian Wilkerson <[email protected]>
1 parent 41db3ab commit 9682da5

File tree

2 files changed

+111
-59
lines changed

2 files changed

+111
-59
lines changed

pkg/analysis_server/analyzer_use_new_elements.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ lib/src/lsp/completion_utils.dart
2222
lib/src/lsp/handlers/code_actions/dart.dart
2323
lib/src/lsp/handlers/commands/abstract_refactor.dart
2424
lib/src/lsp/handlers/custom/abstract_go_to.dart
25-
lib/src/lsp/handlers/custom/handler_imports.dart
2625
lib/src/lsp/handlers/handler_completion.dart
2726
lib/src/lsp/handlers/handler_completion_resolve.dart
2827
lib/src/lsp/handlers/handler_definition.dart

pkg/analysis_server/lib/src/lsp/handlers/custom/handler_imports.dart

Lines changed: 111 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -3,98 +3,151 @@
33
// BSD-style license that can be found in the LICENSE file.
44

55
import 'package:analysis_server/lsp_protocol/protocol.dart' hide Element;
6+
import 'package:analysis_server/src/collections.dart';
67
import 'package:analysis_server/src/lsp/constants.dart';
7-
import 'package:analysis_server/src/lsp/handlers/custom/abstract_go_to.dart';
8+
import 'package:analysis_server/src/lsp/error_or.dart';
9+
import 'package:analysis_server/src/lsp/handlers/handlers.dart';
810
import 'package:analysis_server/src/lsp/mapping.dart';
911
import 'package:analyzer/dart/analysis/results.dart';
1012
import 'package:analyzer/dart/ast/ast.dart';
11-
import 'package:analyzer/dart/element/element.dart';
13+
import 'package:analyzer/dart/element/element2.dart';
14+
import 'package:analyzer/src/dart/ast/element_locator.dart';
15+
import 'package:analyzer/src/dart/ast/utilities.dart';
1216

13-
typedef _ImportRecord =
14-
({ImportDirective directive, LibraryImportElement import});
17+
typedef _ImportRecord = ({CompilationUnit unit, ImportDirective directive});
1518

16-
class ImportsHandler extends AbstractGoToHandler {
19+
class ImportsHandler
20+
extends SharedMessageHandler<TextDocumentPositionParams, List<Location>?> {
1721
ImportsHandler(super.server);
1822

1923
@override
2024
Method get handlesMessage => CustomMethods.imports;
2125

26+
@override
27+
LspJsonHandler<TextDocumentPositionParams> get jsonHandler =>
28+
TextDocumentPositionParams.jsonHandler;
29+
2230
@override
2331
bool get requiresTrustedCaller => false;
2432

25-
/// Returns the import directives that import the given [element].
26-
/// Although the base class supports returning a single element?, this
27-
/// handler is documented to return a list of elements.
28-
/// If no element is found, an empty list is returned.
29-
/// Changing this to return a single element could be a breaking change for
30-
/// clients.
3133
@override
32-
Either2<Location?, List<Location>> findRelatedLocations(
33-
Element element,
34+
Future<ErrorOr<List<Location>?>> handle(
35+
TextDocumentPositionParams params,
36+
MessageInfo message,
37+
CancellationToken token,
38+
) async {
39+
if (!isDartDocument(params.textDocument)) {
40+
return success(null);
41+
}
42+
43+
var pos = params.position;
44+
var path = pathOfDoc(params.textDocument);
45+
var library = await path.mapResult(requireResolvedLibrary);
46+
var unit = (path, library).mapResultsSync<ResolvedUnitResult>((
47+
path,
48+
library,
49+
) {
50+
var unit = library.unitWithPath(path);
51+
return unit != null
52+
? success(unit)
53+
: error(
54+
ErrorCodes.InternalError,
55+
'The library containing a path did not contain the path.',
56+
);
57+
});
58+
var offset = unit.mapResultSync(
59+
(unit) => toOffset(unit.unit.lineInfo, pos),
60+
);
61+
62+
return (library, unit, offset).mapResults((library, unit, offset) async {
63+
var node = NodeLocator(offset).searchWithin(unit.unit);
64+
if (node == null) {
65+
return success(null);
66+
}
67+
68+
var element = ElementLocator.locate2(node);
69+
if (element == null) {
70+
return success(null);
71+
}
72+
73+
String? prefix;
74+
if (node is NamedType) {
75+
prefix = node.importPrefix?.name.lexeme;
76+
} else if (node.thisOrAncestorOfType<PrefixedIdentifier>()
77+
case PrefixedIdentifier identifier) {
78+
prefix = identifier.prefix.name;
79+
} else if (node is SimpleIdentifier) {
80+
if (node.parent case MethodInvocation(
81+
target: SimpleIdentifier target?,
82+
)) {
83+
prefix = target.toString();
84+
}
85+
}
86+
87+
var enclosingElement = element.enclosingElement2;
88+
if (enclosingElement is ExtensionElement2) {
89+
element = enclosingElement;
90+
}
91+
92+
var locations = _getImportLocations(element, library, unit, prefix);
93+
94+
return success(nullIfEmpty(locations));
95+
});
96+
}
97+
98+
/// Returns [Location]s for imports that import the given [element] into
99+
/// [unit].
100+
List<Location> _getImportLocations(
101+
Element2 element,
34102
ResolvedLibraryResult libraryResult,
35103
ResolvedUnitResult unit,
36104
String? prefix,
37105
) {
38-
var elementName = element.name;
106+
var elementName = element.name3;
39107
if (elementName == null) {
40-
return Either2.t1(null);
108+
return [];
41109
}
42110

43111
var imports = _getImports(libraryResult);
112+
var results = <Location>[];
44113

45-
var directives = <ImportDirective>[];
46-
for (var (:directive, :import) in imports) {
47-
Element? namespaceElement;
114+
for (var (:unit, :directive) in imports) {
115+
var import = directive.libraryImport;
116+
if (import == null) continue;
48117

49-
if (prefix == null) {
50-
namespaceElement = import.namespace.get(elementName);
51-
} else {
52-
namespaceElement = import.namespace.getPrefixed(prefix, elementName);
53-
}
118+
var importedElement =
119+
prefix == null
120+
? import.namespace.get2(elementName)
121+
: import.namespace.getPrefixed2(prefix, elementName);
54122

55-
if (element is MultiplyDefinedElement) {
56-
if (element.conflictingElements.contains(namespaceElement)) {
57-
directives.add(directive);
58-
}
59-
} else if (namespaceElement == element) {
60-
directives.add(directive);
123+
var isMatch =
124+
element is MultiplyDefinedElement2
125+
? element.conflictingElements2.contains(importedElement)
126+
: element == importedElement;
127+
128+
if (isMatch) {
129+
var uri = uriConverter.toClientUri(
130+
unit.declaredFragment!.source.fullName,
131+
);
132+
var lineInfo = unit.lineInfo;
133+
var range = toRange(lineInfo, directive.offset, directive.length);
134+
results.add(Location(uri: uri, range: range));
61135
}
62136
}
63-
return Either2.t2(directives.map(_importToLocation).nonNulls.toList());
137+
138+
return results;
64139
}
65140

66141
List<_ImportRecord> _getImports(ResolvedLibraryResult libraryResult) {
67-
// TODO(dantup): Confirm that `units.first` is always the containing
68-
// library.
69-
var containingUnit = libraryResult.units.firstOrNull?.unit;
70-
if (containingUnit == null) {
71-
return const [];
72-
}
73-
var directives = containingUnit.directives.whereType<ImportDirective>();
74142
var imports = <_ImportRecord>[];
75-
for (var directive in directives) {
76-
if (directive.element case var import?) {
77-
imports.add((directive: directive, import: import));
78-
}
79-
}
80-
return imports;
81-
}
82143

83-
Location? _importToLocation(ImportDirective directive) {
84-
var sourcePath = directive.element?.declaration.source?.fullName;
85-
if (sourcePath == null) {
86-
return null;
144+
// TODO(dantup): With enhanced parts, we may need to look at more than
145+
// just the first fragment.
146+
var unit = libraryResult.units.first.unit;
147+
for (var directive in unit.directives.whereType<ImportDirective>()) {
148+
imports.add((unit: unit, directive: directive));
87149
}
88150

89-
// TODO(FMorschel): Remove this when migrating to the new element model.
90-
var locationLineInfo = server.getLineInfo(sourcePath);
91-
if (locationLineInfo == null) {
92-
return null;
93-
}
94-
95-
return Location(
96-
uri: uriConverter.toClientUri(sourcePath),
97-
range: toRange(locationLineInfo, directive.offset, directive.length),
98-
);
151+
return imports;
99152
}
100153
}

0 commit comments

Comments
 (0)