blob: ea2b0a28566629e16002e32fd6182f08f8306576 [file] [log] [blame]
// Copyright (c) 2018, 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.
import 'dart:async';
import 'dart:collection';
import 'package:analysis_server/lsp_protocol/protocol_generated.dart';
import 'package:analysis_server/lsp_protocol/protocol_special.dart';
import 'package:analysis_server/protocol/protocol_generated.dart';
import 'package:analysis_server/src/domains/completion/available_suggestions.dart';
import 'package:analysis_server/src/lsp/handlers/handlers.dart';
import 'package:analysis_server/src/lsp/lsp_analysis_server.dart';
import 'package:analysis_server/src/lsp/mapping.dart';
import 'package:analysis_server/src/provisional/completion/completion_core.dart';
import 'package:analysis_server/src/services/completion/completion_core.dart';
import 'package:analysis_server/src/services/completion/completion_performance.dart';
import 'package:analysis_server/src/services/completion/dart/completion_manager.dart';
import 'package:analyzer/dart/analysis/results.dart';
import 'package:analyzer_plugin/protocol/protocol_common.dart';
// If the client does not provide capabilities.completion.completionItemKind.valueSet
// then we must never send a kind that's not in this list.
final defaultSupportedCompletionKinds = new HashSet<CompletionItemKind>.of([
CompletionItemKind.Text,
CompletionItemKind.Method,
CompletionItemKind.Function,
CompletionItemKind.Constructor,
CompletionItemKind.Field,
CompletionItemKind.Variable,
CompletionItemKind.Class,
CompletionItemKind.Interface,
CompletionItemKind.Module,
CompletionItemKind.Property,
CompletionItemKind.Unit,
CompletionItemKind.Value,
CompletionItemKind.Enum,
CompletionItemKind.Keyword,
CompletionItemKind.Snippet,
CompletionItemKind.Color,
CompletionItemKind.File,
CompletionItemKind.Reference,
]);
class CompletionHandler
extends MessageHandler<CompletionParams, List<CompletionItem>> {
final bool suggestFromUnimportedLibraries;
CompletionHandler(
LspAnalysisServer server, this.suggestFromUnimportedLibraries)
: super(server);
Method get handlesMessage => Method.textDocument_completion;
@override
LspJsonHandler<CompletionParams> get jsonHandler =>
CompletionParams.jsonHandler;
Future<ErrorOr<List<CompletionItem>>> handle(CompletionParams params) async {
if (!isDartDocument(params.textDocument)) {
return success(const []);
}
final completionCapabilities =
server?.clientCapabilities?.textDocument?.completion;
final clientSupportedCompletionKinds =
completionCapabilities?.completionItemKind?.valueSet != null
? new HashSet<CompletionItemKind>.of(
completionCapabilities.completionItemKind.valueSet)
: defaultSupportedCompletionKinds;
final includeSuggestionSets = suggestFromUnimportedLibraries &&
server?.clientCapabilities?.workspace?.applyEdit == true;
final pos = params.position;
final path = pathOfDoc(params.textDocument);
final unit = await path.mapResult(requireResolvedUnit);
final offset = await unit.mapResult((unit) => toOffset(unit.lineInfo, pos));
return offset.mapResult((offset) => _getItems(
completionCapabilities,
clientSupportedCompletionKinds,
includeSuggestionSets,
unit.result,
offset,
));
}
Future<ErrorOr<List<CompletionItem>>> _getItems(
TextDocumentClientCapabilitiesCompletion completionCapabilities,
HashSet<CompletionItemKind> clientSupportedCompletionKinds,
bool includeSuggestionSets,
ResolvedUnitResult unit,
int offset,
) async {
final performance = new CompletionPerformance();
performance.path = unit.path;
performance.setContentsAndOffset(unit.content, offset);
server.performanceStats.completion.add(performance);
final completionRequest =
new CompletionRequestImpl(unit, offset, performance);
Set<ElementKind> includedElementKinds;
List<IncludedSuggestionRelevanceTag> includedSuggestionRelevanceTags;
if (includeSuggestionSets) {
includedElementKinds = Set<ElementKind>();
includedSuggestionRelevanceTags = <IncludedSuggestionRelevanceTag>[];
}
try {
CompletionContributor contributor = new DartCompletionManager(
includedElementKinds: includedElementKinds,
includedSuggestionRelevanceTags: includedSuggestionRelevanceTags,
);
final suggestions =
await contributor.computeSuggestions(completionRequest);
final results = suggestions
.map((item) => toCompletionItem(
completionCapabilities,
clientSupportedCompletionKinds,
unit.lineInfo,
item,
completionRequest.replacementOffset,
completionRequest.replacementLength,
))
.toList();
// Now compute items in suggestion sets.
List<IncludedSuggestionSet> includedSuggestionSets =
includedElementKinds == null || unit == null
? const []
: computeIncludedSetList(
server.declarationsTracker,
unit,
);
includedSuggestionSets.forEach((includedSet) {
final library = server.declarationsTracker.getLibrary(includedSet.id);
if (library == null) {
return;
}
// Make a fast lookup for tag relevance.
final tagBoosts = <String, int>{};
includedSuggestionRelevanceTags
.forEach((t) => tagBoosts[t.tag] = t.relevanceBoost);
final setResults = library.declarations
// Filter to only the kinds we should return.
.where((item) =>
includedElementKinds.contains(protocolElementKind(item.kind)))
.map((item) => declarationToCompletionItem(
completionCapabilities,
clientSupportedCompletionKinds,
unit.path,
offset,
includedSet,
library,
tagBoosts,
unit.lineInfo,
item,
completionRequest.replacementOffset,
completionRequest.replacementLength,
));
results.addAll(setResults);
});
performance.notificationCount = 1;
performance.suggestionCountFirst = results.length;
performance.suggestionCountLast = results.length;
performance.complete();
return success(results);
} on AbortCompletion {
return success([]);
}
}
}