blob: b1dbd21b07478b29545ff7777cf0249b72b13869 [file] [log] [blame]
// Copyright (c) 2021, 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.dart';
/// The key in the client capabilities experimental object that enables the Dart
/// TextDocumentContentProvider.
///
/// The presence of this key indicates that the client supports our
/// (non-standard) way of using TextDocumentContentProvider. This will need to
/// continue to be supported after switching to standard LSP support for some
/// period to support outdated extensions.
const dartExperimentalTextDocumentContentProviderKey =
'supportsDartTextDocumentContentProvider';
/// The original key used for [dartExperimentalTextDocumentContentProviderKey].
///
/// This is temporarily supported to avoid the macro support vanishing for users
/// for a period if their SDK is updated before Dart-Code passes the standard
/// flag.
///
const dartExperimentalTextDocumentContentProviderLegacyKey =
// TODO(dantup): Remove this after the next beta branch.
'supportsDartTextDocumentContentProviderEXP1';
/// Wraps the client (editor) capabilities to improve performance.
///
/// Sets transferred as arrays in JSON will be converted to Sets for faster
/// lookups and default values and nulls will be handled here.
class LspClientCapabilities {
/// If the client does not provide capabilities.completion.completionItemKind.valueSet
/// then we must never send a kind that's not in this list.
static final Set<CompletionItemKind> defaultSupportedCompletionKinds = {
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,
};
/// If the client does not provide capabilities.documentSymbol.symbolKind.valueSet
/// then we must never send a kind that's not in this list.
static final Set<SymbolKind> defaultSupportedSymbolKinds = {
SymbolKind.File,
SymbolKind.Module,
SymbolKind.Namespace,
SymbolKind.Package,
SymbolKind.Class,
SymbolKind.Method,
SymbolKind.Property,
SymbolKind.Field,
SymbolKind.Constructor,
SymbolKind.Enum,
SymbolKind.Interface,
SymbolKind.Function,
SymbolKind.Variable,
SymbolKind.Constant,
SymbolKind.Str,
SymbolKind.Number,
SymbolKind.Boolean,
SymbolKind.Array,
};
final ClientCapabilities raw;
final bool documentChanges;
final bool changeAnnotations;
final bool configuration;
final bool createResourceOperations;
final bool renameResourceOperations;
final bool completionDeprecatedFlag;
final bool applyEdit;
final bool workDoneProgress;
final bool completionSnippets;
final bool renameValidation;
final bool literalCodeActions;
final bool insertReplaceCompletionRanges;
final bool definitionLocationLink;
final bool typeDefinitionLocationLink;
final bool hierarchicalSymbols;
final bool diagnosticCodeDescription;
final bool lineFoldingOnly;
final Set<CodeActionKind> codeActionKinds;
final Set<CompletionItemTag> completionItemTags;
final Set<DiagnosticTag> diagnosticTags;
final Set<MarkupKind>? completionDocumentationFormats;
final Set<MarkupKind>? signatureHelpDocumentationFormats;
final Set<MarkupKind>? hoverContentFormats;
final Set<SymbolKind> documentSymbolKinds;
final Set<SymbolKind> workspaceSymbolKinds;
final Set<CompletionItemKind> completionItemKinds;
final Set<InsertTextMode> completionInsertTextModes;
final bool completionLabelDetails;
final bool completionDefaultEditRange;
final bool completionDefaultTextMode;
final bool experimentalSnippetTextEdit;
final Set<String> codeActionCommandParameterSupportedKinds;
final bool supportsShowMessageRequest;
/// Whether the client supports the custom Dart TextDocumentContentProvider,
/// meaning it can request file contents from the server for custom URI
/// schemes.
final bool supportsDartExperimentalTextDocumentContentProvider;
/// A set of commands that exist on the client that the server may call.
final Set<String> supportedCommands;
factory LspClientCapabilities(ClientCapabilities raw) {
var workspace = raw.workspace;
var workspaceEdit = workspace?.workspaceEdit;
var resourceOperations = workspaceEdit?.resourceOperations;
var textDocument = raw.textDocument;
var completion = textDocument?.completion;
var completionItem = completion?.completionItem;
var completionList = completion?.completionList;
var completionDefaults = _listToSet(completionList?.itemDefaults);
var codeAction = textDocument?.codeAction;
var codeActionLiteral = codeAction?.codeActionLiteralSupport;
var documentSymbol = textDocument?.documentSymbol;
var publishDiagnostics = textDocument?.publishDiagnostics;
var signatureHelp = textDocument?.signatureHelp;
var signatureInformation = signatureHelp?.signatureInformation;
var hover = textDocument?.hover;
var definition = textDocument?.definition;
var typeDefinition = textDocument?.typeDefinition;
var workspaceSymbol = workspace?.symbol;
var experimental = _mapOrEmpty(raw.experimental);
var experimentalActions = _mapOrEmpty(experimental['dartCodeAction']);
var applyEdit = workspace?.applyEdit ?? false;
var codeActionKinds =
_listToSet(codeActionLiteral?.codeActionKind.valueSet);
var completionDeprecatedFlag = completionItem?.deprecatedSupport ?? false;
var completionDocumentationFormats =
_listToNullableSet(completionItem?.documentationFormat);
var completionInsertTextModes =
_listToSet(completionItem?.insertTextModeSupport?.valueSet);
var completionItemKinds = _listToSet(
completion?.completionItemKind?.valueSet,
defaults: defaultSupportedCompletionKinds);
var completionLabelDetails = completionItem?.labelDetailsSupport ?? false;
var completionSnippets = completionItem?.snippetSupport ?? false;
var completionDefaultEditRange = completionDefaults.contains('editRange');
var completionDefaultTextMode =
completionDefaults.contains('insertTextMode');
var configuration = workspace?.configuration ?? false;
var createResourceOperations =
resourceOperations?.contains(ResourceOperationKind.Create) ?? false;
var renameResourceOperations =
resourceOperations?.contains(ResourceOperationKind.Rename) ?? false;
var definitionLocationLink = definition?.linkSupport ?? false;
var typeDefinitionLocationLink = typeDefinition?.linkSupport ?? false;
var completionItemTags = _listToSet(completionItem?.tagSupport?.valueSet);
var diagnosticTags = _listToSet(publishDiagnostics?.tagSupport?.valueSet);
var documentChanges = workspaceEdit?.documentChanges ?? false;
var changeAnnotations = workspaceEdit?.changeAnnotationSupport != null;
var documentSymbolKinds = _listToSet(documentSymbol?.symbolKind?.valueSet,
defaults: defaultSupportedSymbolKinds);
var hierarchicalSymbols =
documentSymbol?.hierarchicalDocumentSymbolSupport ?? false;
var diagnosticCodeDescription =
publishDiagnostics?.codeDescriptionSupport ?? false;
var hoverContentFormats = _listToNullableSet(hover?.contentFormat);
var insertReplaceCompletionRanges =
completionItem?.insertReplaceSupport ?? false;
var lineFoldingOnly = textDocument?.foldingRange?.lineFoldingOnly ?? false;
var literalCodeActions = codeActionLiteral != null;
var renameValidation = textDocument?.rename?.prepareSupport ?? false;
var signatureHelpDocumentationFormats =
_listToNullableSet(signatureInformation?.documentationFormat);
var workDoneProgress = raw.window?.workDoneProgress ?? false;
var workspaceSymbolKinds = _listToSet(workspaceSymbol?.symbolKind?.valueSet,
defaults: defaultSupportedSymbolKinds);
var experimentalSnippetTextEdit = experimental['snippetTextEdit'] == true;
var commandParameterSupport =
_mapOrEmpty(experimentalActions['commandParameterSupport']);
var commandParameterSupportedKinds =
_listToSet(commandParameterSupport['supportedKinds'] as List?)
.cast<String>();
var supportsDartExperimentalTextDocumentContentProvider =
(experimental[dartExperimentalTextDocumentContentProviderKey] ??
experimental[
dartExperimentalTextDocumentContentProviderLegacyKey]) !=
null;
var supportedCommands =
_listToSet(experimental['commands'] as List?).cast<String>();
/// At the time of writing (2023-02-01) there is no official capability for
/// supporting 'showMessageRequest' because LSP assumed all clients
/// supported it.
///
/// This turned out to not be the case, so to avoid sending prompts that
/// might not be seen, we will only use this functionality if we _know_ the
/// client supports it via a custom flag in 'experimental' that is passed by
/// the Dart-Code VS Code extension since version v3.58.0 (2023-01-25).
var supportsShowMessageRequest =
experimental['supportsWindowShowMessageRequest'] == true;
return LspClientCapabilities._(
raw,
documentChanges: documentChanges,
changeAnnotations: changeAnnotations,
configuration: configuration,
createResourceOperations: createResourceOperations,
renameResourceOperations: renameResourceOperations,
completionDeprecatedFlag: completionDeprecatedFlag,
applyEdit: applyEdit,
workDoneProgress: workDoneProgress,
completionSnippets: completionSnippets,
renameValidation: renameValidation,
literalCodeActions: literalCodeActions,
insertReplaceCompletionRanges: insertReplaceCompletionRanges,
definitionLocationLink: definitionLocationLink,
typeDefinitionLocationLink: typeDefinitionLocationLink,
hierarchicalSymbols: hierarchicalSymbols,
lineFoldingOnly: lineFoldingOnly,
diagnosticCodeDescription: diagnosticCodeDescription,
codeActionKinds: codeActionKinds,
completionItemTags: completionItemTags,
diagnosticTags: diagnosticTags,
completionDocumentationFormats: completionDocumentationFormats,
signatureHelpDocumentationFormats: signatureHelpDocumentationFormats,
hoverContentFormats: hoverContentFormats,
documentSymbolKinds: documentSymbolKinds,
workspaceSymbolKinds: workspaceSymbolKinds,
completionItemKinds: completionItemKinds,
completionInsertTextModes: completionInsertTextModes,
completionLabelDetails: completionLabelDetails,
completionDefaultEditRange: completionDefaultEditRange,
completionDefaultTextMode: completionDefaultTextMode,
experimentalSnippetTextEdit: experimentalSnippetTextEdit,
codeActionCommandParameterSupportedKinds: commandParameterSupportedKinds,
supportsShowMessageRequest: supportsShowMessageRequest,
supportsDartExperimentalTextDocumentContentProvider:
supportsDartExperimentalTextDocumentContentProvider,
supportedCommands: supportedCommands,
);
}
LspClientCapabilities._(
this.raw, {
required this.documentChanges,
required this.changeAnnotations,
required this.configuration,
required this.createResourceOperations,
required this.renameResourceOperations,
required this.completionDeprecatedFlag,
required this.applyEdit,
required this.workDoneProgress,
required this.completionSnippets,
required this.renameValidation,
required this.literalCodeActions,
required this.insertReplaceCompletionRanges,
required this.definitionLocationLink,
required this.typeDefinitionLocationLink,
required this.hierarchicalSymbols,
required this.lineFoldingOnly,
required this.diagnosticCodeDescription,
required this.codeActionKinds,
required this.completionItemTags,
required this.diagnosticTags,
required this.completionDocumentationFormats,
required this.signatureHelpDocumentationFormats,
required this.hoverContentFormats,
required this.documentSymbolKinds,
required this.workspaceSymbolKinds,
required this.completionItemKinds,
required this.completionInsertTextModes,
required this.completionLabelDetails,
required this.completionDefaultEditRange,
required this.completionDefaultTextMode,
required this.experimentalSnippetTextEdit,
required this.codeActionCommandParameterSupportedKinds,
required this.supportsShowMessageRequest,
required this.supportsDartExperimentalTextDocumentContentProvider,
required this.supportedCommands,
});
/// Converts a list to a `Set`, returning null if the list is null.
static Set<T>? _listToNullableSet<T>(List<T>? items) {
return items != null ? {...items} : null;
}
/// Converts a list to a `Set`, returning [defaults] if the list is null.
///
/// If [defaults] is not supplied, will return an empty set.
static Set<T> _listToSet<T>(List<T>? items, {Set<T> defaults = const {}}) {
return items != null ? {...items} : defaults;
}
static Map<String, Object?> _mapOrEmpty(Object? item) {
return item is Map<String, Object?> ? item : const {};
}
}