blob: cb0344bdae9b2146ec3dadb26c30bdd12ce561e2 [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 'package:analysis_server/lsp_protocol/protocol_generated.dart';
import 'package:analysis_server/lsp_protocol/protocol_special.dart';
import 'package:analysis_server/src/computer/computer_signature.dart';
import 'package:analysis_server/src/computer/computer_type_arguments_signature.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:analyzer/dart/ast/ast.dart';
import 'package:analyzer/src/dartdoc/dartdoc_directive_info.dart';
class SignatureHelpHandler
extends MessageHandler<SignatureHelpParams, SignatureHelp?> {
SignatureHelpHandler(LspAnalysisServer server) : super(server);
@override
Method get handlesMessage => Method.textDocument_signatureHelp;
@override
LspJsonHandler<SignatureHelpParams> get jsonHandler =>
SignatureHelpParams.jsonHandler;
@override
Future<ErrorOr<SignatureHelp?>> handle(
SignatureHelpParams params, CancellationToken token) async {
if (!isDartDocument(params.textDocument)) {
return success(null);
}
final clientCapabilities = server.clientCapabilities;
if (clientCapabilities == null) {
// This should not happen unless a client misbehaves.
return serverNotInitializedError;
}
// If triggered automatically by pressing the trigger character, we will
// only provide results if the character we typed was the one that actually
// starts the argument list. This is to avoid popping open signature help
// whenever the user types a `(` that might not be the start of an argument
// list, as the client does not have any context and will always send the
// request.
final autoTriggered = params.context?.triggerKind ==
SignatureHelpTriggerKind.TriggerCharacter &&
// Retriggers can be ignored (treated as manual invocations) as it's
// fine to always generate results if the signature help is already
// visible on the client (it will just update, it doesn't pop up new UI).
params.context?.isRetrigger == false;
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) {
final formats = clientCapabilities.signatureHelpDocumentationFormats;
final dartDocInfo = server.getDartdocDirectiveInfoFor(unit.result);
// First check if we're in a type args list and if so build some
// signature help for that.
final typeArgsSignature = _tryGetTypeArgsSignatureHelp(
dartDocInfo,
unit.result.unit,
offset,
autoTriggered,
formats,
);
if (typeArgsSignature != null) {
return success(typeArgsSignature);
}
final computer =
DartUnitSignatureComputer(dartDocInfo, unit.result.unit, offset);
if (!computer.offsetIsValid) {
return success(null); // No error, just no valid hover.
}
final signature = computer.compute();
if (signature == null) {
return success(null); // No error, just no valid hover.
}
// Skip results if this was an auto-trigger but not from the start of the
// argument list.
// The ArgumentList's offset is before the paren, but the request offset
// will be after.
if (autoTriggered && offset != computer.argumentList.offset + 1) {
return success(null);
}
return success(toSignatureHelp(formats, signature));
});
}
/// Tries to create signature information for a surrounding [TypeArgumentList].
///
/// Returns `null` if [offset] is in an invalid location, not inside a type
/// argument list or was auto-triggered in a location that was not the start
/// of a type argument list.
SignatureHelp? _tryGetTypeArgsSignatureHelp(
DartdocDirectiveInfo dartDocInfo,
CompilationUnit unit,
int offset,
bool autoTriggered,
Set<MarkupKind>? formats,
) {
final typeArgsComputer =
DartTypeArgumentsSignatureComputer(dartDocInfo, unit, offset, formats);
if (!typeArgsComputer.offsetIsValid) {
return null;
}
final typeSignature = typeArgsComputer.compute();
if (typeSignature == null) {
return null;
}
// If auto-triggered from typing a `<`, only show if that `<` was at
// the start of the arg list (to avoid triggering on other `<`s).
if (autoTriggered && offset != typeArgsComputer.argumentList.offset + 1) {
return null;
}
return typeSignature;
}
}