| Index: pkg/analysis_server/lib/src/services/completion/dart/label_contributor.dart
|
| diff --git a/pkg/analysis_server/lib/src/services/completion/dart/label_contributor.dart b/pkg/analysis_server/lib/src/services/completion/dart/label_contributor.dart
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..64ed3f89eeb359de63066fae1f20c25bb9b9ffa7
|
| --- /dev/null
|
| +++ b/pkg/analysis_server/lib/src/services/completion/dart/label_contributor.dart
|
| @@ -0,0 +1,286 @@
|
| +// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
|
| +// for details. All rights reserved. Use of this source code is governed by a
|
| +// BSD-style license that can be found in the LICENSE file.
|
| +
|
| +library services.completion.contributor.dart.local_ref;
|
| +
|
| +import 'dart:async';
|
| +
|
| +import 'package:analysis_server/plugin/protocol/protocol.dart' as protocol
|
| + show Element, ElementKind;
|
| +import 'package:analysis_server/src/provisional/completion/dart/completion_dart.dart';
|
| +import 'package:analysis_server/src/services/completion/dart/completion_manager.dart'
|
| + show DartCompletionRequestImpl;
|
| +import 'package:analysis_server/src/services/completion/local_declaration_visitor.dart'
|
| + show LocalDeclarationVisitor;
|
| +import 'package:analysis_server/src/services/completion/optype.dart';
|
| +import 'package:analyzer/src/generated/ast.dart';
|
| +import 'package:analyzer/src/generated/scanner.dart';
|
| +import 'package:analyzer/src/generated/source.dart';
|
| +
|
| +import '../../../protocol_server.dart'
|
| + show CompletionSuggestion, CompletionSuggestionKind, Location;
|
| +
|
| +const DYNAMIC = 'dynamic';
|
| +
|
| +final TypeName NO_RETURN_TYPE = new TypeName(
|
| + new SimpleIdentifier(new StringToken(TokenType.IDENTIFIER, '', 0)), null);
|
| +
|
| +/**
|
| + * A contributor for calculating label suggestions.
|
| + */
|
| +class LabelContributor extends DartCompletionContributor {
|
| + @override
|
| + Future<List<CompletionSuggestion>> computeSuggestions(
|
| + DartCompletionRequest request) async {
|
| + OpType optype = (request as DartCompletionRequestImpl).opType;
|
| +
|
| + // Collect suggestions from the specific child [AstNode] that contains
|
| + // the completion offset and all of its parents recursively.
|
| + List<CompletionSuggestion> suggestions = <CompletionSuggestion>[];
|
| + if (!optype.isPrefixed) {
|
| + if (optype.includeStatementLabelSuggestions ||
|
| + optype.includeCaseLabelSuggestions) {
|
| + new _LabelVisitor(request, optype.includeStatementLabelSuggestions,
|
| + optype.includeCaseLabelSuggestions, suggestions)
|
| + .visit(request.target.containingNode);
|
| + }
|
| + }
|
| + return suggestions;
|
| + }
|
| +}
|
| +
|
| +/**
|
| + * A visitor for collecting suggestions for break and continue labels.
|
| + */
|
| +class _LabelVisitor extends LocalDeclarationVisitor {
|
| + final DartCompletionRequest request;
|
| + final List<CompletionSuggestion> suggestions;
|
| +
|
| + /**
|
| + * True if statement labels should be included as suggestions.
|
| + */
|
| + final bool includeStatementLabels;
|
| +
|
| + /**
|
| + * True if case labels should be included as suggestions.
|
| + */
|
| + final bool includeCaseLabels;
|
| +
|
| + _LabelVisitor(DartCompletionRequest request, this.includeStatementLabels,
|
| + this.includeCaseLabels, this.suggestions)
|
| + : super(request.offset),
|
| + request = request;
|
| +
|
| + @override
|
| + void declaredClass(ClassDeclaration declaration) {
|
| + // ignored
|
| + }
|
| +
|
| + @override
|
| + void declaredClassTypeAlias(ClassTypeAlias declaration) {
|
| + // ignored
|
| + }
|
| +
|
| + @override
|
| + void declaredField(FieldDeclaration fieldDecl, VariableDeclaration varDecl) {
|
| + // ignored
|
| + }
|
| +
|
| + @override
|
| + void declaredFunction(FunctionDeclaration declaration) {
|
| + // ignored
|
| + }
|
| +
|
| + @override
|
| + void declaredFunctionTypeAlias(FunctionTypeAlias declaration) {
|
| + // ignored
|
| + }
|
| +
|
| + @override
|
| + void declaredLabel(Label label, bool isCaseLabel) {
|
| + if (isCaseLabel ? includeCaseLabels : includeStatementLabels) {
|
| + CompletionSuggestion suggestion = _addSuggestion(label.label);
|
| + if (suggestion != null) {
|
| + suggestion.element = createLocalElement(
|
| + request.source, protocol.ElementKind.LABEL, label.label,
|
| + returnType: NO_RETURN_TYPE);
|
| + }
|
| + }
|
| + }
|
| +
|
| + @override
|
| + void declaredLocalVar(SimpleIdentifier name, TypeName type) {
|
| + // ignored
|
| + }
|
| +
|
| + @override
|
| + void declaredMethod(MethodDeclaration declaration) {
|
| + // ignored
|
| + }
|
| +
|
| + @override
|
| + void declaredParam(SimpleIdentifier name, TypeName type) {
|
| + // ignored
|
| + }
|
| +
|
| + @override
|
| + void declaredTopLevelVar(
|
| + VariableDeclarationList varList, VariableDeclaration varDecl) {
|
| + // ignored
|
| + }
|
| +
|
| + @override
|
| + void visitFunctionExpression(FunctionExpression node) {
|
| + // Labels are only accessible within the local function, so stop visiting
|
| + // once we reach a function boundary.
|
| + finished();
|
| + }
|
| +
|
| + @override
|
| + void visitMethodDeclaration(MethodDeclaration node) {
|
| + // Labels are only accessible within the local function, so stop visiting
|
| + // once we reach a function boundary.
|
| + finished();
|
| + }
|
| +
|
| + CompletionSuggestion _addSuggestion(SimpleIdentifier id) {
|
| + if (id != null) {
|
| + String completion = id.name;
|
| + if (completion != null && completion.length > 0 && completion != '_') {
|
| + CompletionSuggestion suggestion = new CompletionSuggestion(
|
| + CompletionSuggestionKind.IDENTIFIER,
|
| + DART_RELEVANCE_DEFAULT,
|
| + completion,
|
| + completion.length,
|
| + 0,
|
| + false,
|
| + false);
|
| + suggestions.add(suggestion);
|
| + return suggestion;
|
| + }
|
| + }
|
| + return null;
|
| + }
|
| +}
|
| +
|
| +/**
|
| +* Create a new protocol Element for inclusion in a completion suggestion.
|
| +*/
|
| +protocol.Element createLocalElement(
|
| + Source source, protocol.ElementKind kind, SimpleIdentifier id,
|
| + {String parameters,
|
| + TypeName returnType,
|
| + bool isAbstract: false,
|
| + bool isDeprecated: false}) {
|
| + String name;
|
| + Location location;
|
| + if (id != null) {
|
| + name = id.name;
|
| + // TODO(danrubel) use lineInfo to determine startLine and startColumn
|
| + location = new Location(source.fullName, id.offset, id.length, 0, 0);
|
| + } else {
|
| + name = '';
|
| + location = new Location(source.fullName, -1, 0, 1, 0);
|
| + }
|
| + int flags = protocol.Element.makeFlags(
|
| + isAbstract: isAbstract,
|
| + isDeprecated: isDeprecated,
|
| + isPrivate: Identifier.isPrivateName(name));
|
| + return new protocol.Element(kind, name, flags,
|
| + location: location,
|
| + parameters: parameters,
|
| + returnType: nameForType(returnType));
|
| +}
|
| +
|
| +/**
|
| +* Create a new suggestion for the given field.
|
| +* Return the new suggestion or `null` if it could not be created.
|
| +*/
|
| +CompletionSuggestion createLocalFieldSuggestion(
|
| + Source source, FieldDeclaration fieldDecl, VariableDeclaration varDecl) {
|
| + bool deprecated = isDeprecated(fieldDecl) || isDeprecated(varDecl);
|
| + TypeName type = fieldDecl.fields.type;
|
| + return createLocalSuggestion(
|
| + varDecl.name, deprecated, DART_RELEVANCE_LOCAL_FIELD, type,
|
| + classDecl: fieldDecl.parent,
|
| + element: createLocalElement(
|
| + source, protocol.ElementKind.FIELD, varDecl.name,
|
| + returnType: type, isDeprecated: deprecated));
|
| +}
|
| +
|
| +/**
|
| +* Create a new suggestion based upon the given information.
|
| +* Return the new suggestion or `null` if it could not be created.
|
| +*/
|
| +CompletionSuggestion createLocalSuggestion(SimpleIdentifier id,
|
| + bool isDeprecated, int defaultRelevance, TypeName returnType,
|
| + {ClassDeclaration classDecl, protocol.Element element}) {
|
| + if (id == null) {
|
| + return null;
|
| + }
|
| + String completion = id.name;
|
| + if (completion == null || completion.length <= 0 || completion == '_') {
|
| + return null;
|
| + }
|
| + CompletionSuggestion suggestion = new CompletionSuggestion(
|
| + CompletionSuggestionKind.INVOCATION,
|
| + isDeprecated ? DART_RELEVANCE_LOW : defaultRelevance,
|
| + completion,
|
| + completion.length,
|
| + 0,
|
| + isDeprecated,
|
| + false,
|
| + returnType: nameForType(returnType),
|
| + element: element);
|
| + if (classDecl != null) {
|
| + SimpleIdentifier classId = classDecl.name;
|
| + if (classId != null) {
|
| + String className = classId.name;
|
| + if (className != null && className.length > 0) {
|
| + suggestion.declaringType = className;
|
| + }
|
| + }
|
| + }
|
| + return suggestion;
|
| +}
|
| +
|
| +/**
|
| +* Return `true` if the @deprecated annotation is present
|
| +*/
|
| +bool isDeprecated(AnnotatedNode node) {
|
| + if (node != null) {
|
| + NodeList<Annotation> metadata = node.metadata;
|
| + if (metadata != null) {
|
| + return metadata.any((Annotation a) {
|
| + return a.name is SimpleIdentifier && a.name.name == 'deprecated';
|
| + });
|
| + }
|
| + }
|
| + return false;
|
| +}
|
| +
|
| +/**
|
| +* Return the name for the given type.
|
| +*/
|
| +String nameForType(TypeName type) {
|
| + if (type == NO_RETURN_TYPE) {
|
| + return null;
|
| + }
|
| + if (type == null) {
|
| + return DYNAMIC;
|
| + }
|
| + Identifier id = type.name;
|
| + if (id == null) {
|
| + return DYNAMIC;
|
| + }
|
| + String name = id.name;
|
| + if (name == null || name.length <= 0) {
|
| + return DYNAMIC;
|
| + }
|
| + TypeArgumentList typeArgs = type.typeArguments;
|
| + if (typeArgs != null) {
|
| + //TODO (danrubel) include type arguments
|
| + }
|
| + return name;
|
| +}
|
|
|