Skip to content

Commit

Permalink
[analysis_server] Migrate LSP Go-To handlers to new element model
Browse files Browse the repository at this point in the history
Change-Id: Ie517dcbba87c34d12a42707c3beea141d3fd509a
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/393880
Reviewed-by: Konstantin Shcheglov <[email protected]>
Reviewed-by: Brian Wilkerson <[email protected]>
Commit-Queue: Brian Wilkerson <[email protected]>
  • Loading branch information
DanTup authored and Commit Queue committed Nov 7, 2024
1 parent 082c710 commit d698ace
Show file tree
Hide file tree
Showing 6 changed files with 195 additions and 71 deletions.
3 changes: 0 additions & 3 deletions pkg/analysis_server/analyzer_use_new_elements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,6 @@ lib/src/lsp/handlers/code_actions/dart.dart
lib/src/lsp/handlers/commands/abstract_refactor.dart
lib/src/lsp/handlers/custom/abstract_go_to.dart
lib/src/lsp/handlers/custom/handler_imports.dart
lib/src/lsp/handlers/custom/handler_augmentation.dart
lib/src/lsp/handlers/custom/handler_augmented.dart
lib/src/lsp/handlers/custom/handler_super.dart
lib/src/lsp/handlers/handler_completion.dart
lib/src/lsp/handlers/handler_completion_resolve.dart
lib/src/lsp/handlers/handler_definition.dart
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,10 @@ import 'package:meta/meta.dart';
/// a location to navigate to a particular kind of related element, such as
/// Super, Augmentation Target or Augmentation.
abstract class AbstractGoToHandler
extends
SharedMessageHandler<
TextDocumentPositionParams,
Either2<Location, List<Location>>?
> {
// TODO(dantup): Remove this class after https://dart-review.googlesource.com/c/sdk/+/393861
// lands.
extends
SharedMessageHandler<TextDocumentPositionParams, Either2<Location, List<Location>>?> {
AbstractGoToHandler(super.server);

@override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,54 @@

import 'package:analysis_server/lsp_protocol/protocol.dart' hide Element;
import 'package:analysis_server/src/lsp/constants.dart';
import 'package:analysis_server/src/lsp/handlers/custom/abstract_go_to.dart';
import 'package:analyzer/dart/analysis/results.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/src/utilities/extensions/element.dart';
import 'package:analysis_server/src/lsp/error_or.dart';
import 'package:analysis_server/src/lsp/handlers/handlers.dart';
import 'package:analysis_server/src/lsp/mapping.dart';
import 'package:analyzer/src/dart/ast/ast.dart';
import 'package:analyzer/src/utilities/extensions/ast.dart';

class AugmentationHandler extends AbstractGoToHandler {
class AugmentationHandler
extends SharedMessageHandler<TextDocumentPositionParams, Location?> {
AugmentationHandler(super.server);

@override
Method get handlesMessage => CustomMethods.augmentation;

@override
LspJsonHandler<TextDocumentPositionParams> get jsonHandler =>
TextDocumentPositionParams.jsonHandler;

@override
bool get requiresTrustedCaller => false;

@override
Either2<Location?, List<Location>> findRelatedLocations(
Element element,
ResolvedLibraryResult libraryResult,
ResolvedUnitResult unit,
String? prefix) {
// Although the base class supports returning multiple elements, this
// handler is documented to only return a single element.
// Changing this to return a list could be a breaking change for
// clients.
return Either2.t1(elementToLocation(element.augmentation));
Future<ErrorOr<Location?>> handle(
TextDocumentPositionParams params,
MessageInfo message,
CancellationToken token,
) async {
if (!isDartDocument(params.textDocument)) {
return success(null);
}

var pos = params.position;
var path = pathOfDoc(params.textDocument);
var unit = await path.mapResult(requireResolvedUnit);
var offset = unit.mapResultSync((unit) => toOffset(unit.lineInfo, pos));

return (unit, offset).mapResultsSync((unit, offset) {
// Find the nearest node that could have fragments.
var node =
unit.unit
.nodeCovering(offset: offset)
?.thisOrAncestorOfType<FragmentDeclaration>();

var location = fragmentToLocation(
uriConverter,
// Augmentation = next fragment.
node?.declaredFragment?.nextFragment,
);
return success(location);
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,34 +4,54 @@

import 'package:analysis_server/lsp_protocol/protocol.dart' hide Element;
import 'package:analysis_server/src/lsp/constants.dart';
import 'package:analysis_server/src/lsp/handlers/custom/abstract_go_to.dart';
import 'package:analyzer/dart/analysis/results.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analysis_server/src/lsp/error_or.dart';
import 'package:analysis_server/src/lsp/handlers/handlers.dart';
import 'package:analysis_server/src/lsp/mapping.dart';
import 'package:analyzer/src/dart/ast/ast.dart';
import 'package:analyzer/src/utilities/extensions/ast.dart';

class AugmentedHandler extends AbstractGoToHandler {
class AugmentedHandler
extends SharedMessageHandler<TextDocumentPositionParams, Location?> {
AugmentedHandler(super.server);

@override
Method get handlesMessage => CustomMethods.augmented;

@override
LspJsonHandler<TextDocumentPositionParams> get jsonHandler =>
TextDocumentPositionParams.jsonHandler;

@override
bool get requiresTrustedCaller => false;

@override
Either2<Location?, List<Location>> findRelatedLocations(
Element element,
ResolvedLibraryResult libraryResult,
ResolvedUnitResult unit,
String? prefix) {
// Although the base class supports returning multiple elements, this
// handler is documented to only return a single element.
// Changing this to return a list could be a breaking change for
// clients.
return Either2.t1(elementToLocation(switch (element) {
ExecutableElement element => element.augmentationTarget,
InstanceElement element => element.augmentationTarget,
PropertyInducingElement element => element.augmentationTarget,
_ => null,
}));
Future<ErrorOr<Location?>> handle(
TextDocumentPositionParams params,
MessageInfo message,
CancellationToken token,
) async {
if (!isDartDocument(params.textDocument)) {
return success(null);
}

var pos = params.position;
var path = pathOfDoc(params.textDocument);
var unit = await path.mapResult(requireResolvedUnit);
var offset = unit.mapResultSync((unit) => toOffset(unit.lineInfo, pos));

return (unit, offset).mapResultsSync((unit, offset) {
// Find the nearest node that could have fragments.
var node =
unit.unit
.nodeCovering(offset: offset)
?.thisOrAncestorOfType<FragmentDeclaration>();

var location = fragmentToLocation(
uriConverter,
// Augmented = previous fragment.
node?.declaredFragment?.previousFragment,
);
return success(location);
});
}
}
106 changes: 77 additions & 29 deletions pkg/analysis_server/lib/src/lsp/handlers/custom/handler_super.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,71 +4,119 @@

import 'package:analysis_server/lsp_protocol/protocol.dart' hide Element;
import 'package:analysis_server/src/lsp/constants.dart';
import 'package:analysis_server/src/lsp/handlers/custom/abstract_go_to.dart';
import 'package:analyzer/dart/analysis/results.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analysis_server/src/lsp/error_or.dart';
import 'package:analysis_server/src/lsp/handlers/handlers.dart';
import 'package:analysis_server/src/lsp/mapping.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/element/element2.dart';
import 'package:analyzer/src/dart/analysis/session.dart';
import 'package:analyzer/src/dart/ast/element_locator.dart';
import 'package:analyzer/src/dart/element/inheritance_manager3.dart';
import 'package:analyzer/src/utilities/extensions/element.dart';
import 'package:analyzer/src/utilities/extensions/ast.dart';

class SuperHandler extends AbstractGoToHandler {
class SuperHandler
extends SharedMessageHandler<TextDocumentPositionParams, Location?> {
SuperHandler(super.server);

@override
Method get handlesMessage => CustomMethods.super_;

@override
LspJsonHandler<TextDocumentPositionParams> get jsonHandler =>
TextDocumentPositionParams.jsonHandler;

@override
bool get requiresTrustedCaller => false;

@override
Either2<Location?, List<Location>> findRelatedLocations(
Element element,
ResolvedLibraryResult libraryResult,
ResolvedUnitResult unit,
String? prefix) {
// Although the base class supports returning multiple elements, this
// handler is documented to only return a single element.
// Changing this to return a list could be a breaking change for
// clients.
return Either2.t1(
elementToLocation(_SuperComputer().computeSuper(element)));
Future<ErrorOr<Location?>> handle(
TextDocumentPositionParams params,
MessageInfo message,
CancellationToken token,
) async {
if (!isDartDocument(params.textDocument)) {
return success(null);
}

var pos = params.position;
var path = pathOfDoc(params.textDocument);
var unit = await path.mapResult(requireResolvedUnit);
var offset = unit.mapResultSync((unit) => toOffset(unit.lineInfo, pos));

return (unit, offset).mapResultsSync((unit, offset) {
// Find the nearest node that could have a super.
var node = unit.unit
.nodeCovering(offset: offset)
?.thisOrAncestorMatching(_canHaveSuper);
if (node == null) {
return success(null);
}

var element = ElementLocator.locate2(node);
if (element == null) {
return success(null);
}

var targetFragment = _SuperComputer().computeSuper(element);
var location = fragmentToLocation(uriConverter, targetFragment);
return success(location);
});
}

/// Returns whether [node] is something that can be considered to have a
/// "super" (a class or a class member).
bool _canHaveSuper(AstNode node) =>
node is ClassDeclaration || node is ClassMember;
}

class _SuperComputer {
Element? computeSuper(Element element) {
Fragment? computeSuper(Element2 element) {
return switch (element) {
ConstructorElement element => _findSuperConstructor(element),
InterfaceElement element => _findSuperClass(element),
ConstructorElement2 element => _findSuperConstructor(element),
InterfaceElement2 element => _findSuperClass(element),
_ => _findSuperMember(element),
};
}

Element? _findSuperClass(InterfaceElement element) {
return element.supertype?.element;
Fragment? _findSuperClass(InterfaceElement2 element) {
// For super classes, we use the first fragment (the original declaration).
// This differs from methods/getters because we jump to the end of the
// augmentation chain for those.
return element.supertype?.element3.firstFragment;
}

Element? _findSuperConstructor(ConstructorElement element) {
return element.superConstructor?.withAugmentations.last;
Fragment? _findSuperConstructor(ConstructorElement2 element) {
return _lastFragment(element.superConstructor2);
}

Element? _findSuperMember(Element element) {
Fragment? _findSuperMember(Element2 element) {
var session = element.session;
if (session is! AnalysisSessionImpl) {
return null;
}

var inheritanceManager = session.inheritanceManager;
var elementName = element.name;
var interfaceElement = element.thisOrAncestorOfType<InterfaceElement>();
var elementName = element.name3;
var interfaceElement = element.thisOrAncestorOfType2<InterfaceElement2>();

if (elementName == null || interfaceElement == null) {
return null;
}

var name = Name(interfaceElement.library.source.uri, elementName);
var member = inheritanceManager.getInherited2(interfaceElement, name);
var name = Name(
interfaceElement.library2.firstFragment.source.uri,
elementName,
);
var member = inheritanceManager.getInherited4(interfaceElement, name);

return member;
return _lastFragment(member);
}

Fragment? _lastFragment(FragmentedElement? element) {
Fragment? fragment = element?.firstFragment;
while (fragment?.nextFragment != null) {
fragment = fragment?.nextFragment;
}
return fragment;
}
}
36 changes: 36 additions & 0 deletions pkg/analysis_server/lib/src/lsp/mapping.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import 'package:analysis_server/src/services/completion/dart/feature_computer.da
import 'package:analysis_server/src/services/snippets/snippet.dart';
import 'package:analysis_server/src/utilities/extensions/string.dart';
import 'package:analyzer/dart/analysis/results.dart' as server;
import 'package:analyzer/dart/element/element2.dart';
import 'package:analyzer/error/error.dart' as server;
import 'package:analyzer/source/line_info.dart' as server;
import 'package:analyzer/source/line_info.dart';
Expand Down Expand Up @@ -497,6 +498,41 @@ lsp.SymbolKind elementKindToSymbolKind(
);
}

lsp.Location? fragmentToLocation(
ClientUriConverter uriConverter,
Fragment? fragment,
) {
if (fragment == null) {
return null;
}

var libraryFragment = fragment.libraryFragment;
var sourcePath = libraryFragment.source.fullName;

var nameOffset = fragment.nameOffset2;
var nameLength = fragment.name2?.length;

// For unnamed constructors, use the type name as the target location.
if (nameOffset == null && fragment is ConstructorFragment) {
nameOffset = fragment.typeNameOffset;
nameLength = fragment.typeName?.length;
}

if (nameOffset == null || nameLength == null) {
// This is some kind of synthetic fragment we can't navigate to.
return null;
}

return lsp.Location(
uri: uriConverter.toClientUri(sourcePath),
range: toRange(
libraryFragment.lineInfo,
nameOffset,
nameLength,
),
);
}

/// Returns additional details to be shown against a completion.
CompletionDetail getCompletionDetail(
server.CompletionSuggestion suggestion, {
Expand Down

0 comments on commit d698ace

Please sign in to comment.