diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_completion_resolve.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_completion_resolve.dart index 99305e12cd1..b01b3477f1e 100644 --- a/pkg/analysis_server/lib/src/lsp/handlers/handler_completion_resolve.dart +++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_completion_resolve.dart @@ -150,7 +150,7 @@ class CompletionResolveHandler return success(CompletionItem( label: item.label, kind: item.kind, - tags: null, // TODO(dantup): CompletionItemTags (eg. deprecated) + tags: item.tags, detail: data.displayUri != null && thisFilesChanges.isNotEmpty ? "Auto import from '${data.displayUri}'\n\n${item.detail ?? ''}" .trim() diff --git a/pkg/analysis_server/lib/src/lsp/mapping.dart b/pkg/analysis_server/lib/src/lsp/mapping.dart index c36554ec00e..5685c5d5a88 100644 --- a/pkg/analysis_server/lib/src/lsp/mapping.dart +++ b/pkg/analysis_server/lib/src/lsp/mapping.dart @@ -224,8 +224,12 @@ lsp.CompletionItem declarationToCompletionItem( label = declaration.name; } - final useDeprecated = + final supportsDeprecatedFlag = completionCapabilities?.completionItem?.deprecatedSupport == true; + final supportedTags = + completionCapabilities?.completionItem?.tagSupport?.valueSet ?? const []; + final supportsDeprecatedTag = + supportedTags.contains(lsp.CompletionItemTag.Deprecated); final completionKind = declarationKindToCompletionItemKind( supportedCompletionItemKinds, declaration.kind); @@ -243,10 +247,16 @@ lsp.CompletionItem declarationToCompletionItem( return lsp.CompletionItem( label: label, kind: completionKind, - tags: null, // TODO(dantup): CompletionItemTags - detail: getDeclarationCompletionDetail( - declaration, completionKind, useDeprecated), - deprecated: useDeprecated && declaration.isDeprecated ? true : null, + tags: supportedTags.isNotEmpty + ? [ + if (supportsDeprecatedTag && declaration.isDeprecated) + lsp.CompletionItemTag.Deprecated + ] + : null, + detail: getDeclarationCompletionDetail(declaration, completionKind, + supportsDeprecatedFlag || supportsDeprecatedTag), + deprecated: + supportsDeprecatedFlag && declaration.isDeprecated ? true : null, // Relevance is a number, highest being best. LSP does text sort so subtract // from a large number so that a text sort will result in the correct order. // 555 -> 999455 @@ -723,8 +733,12 @@ lsp.CompletionItem toCompletionItem( } } - final useDeprecated = + final supportsDeprecatedFlag = completionCapabilities?.completionItem?.deprecatedSupport == true; + final supportedTags = + completionCapabilities?.completionItem?.tagSupport?.valueSet ?? const []; + final supportsDeprecatedTag = + supportedTags.contains(lsp.CompletionItemTag.Deprecated); final formats = completionCapabilities?.completionItem?.documentationFormat; final supportsSnippets = completionCapabilities?.completionItem?.snippetSupport == true; @@ -751,11 +765,17 @@ lsp.CompletionItem toCompletionItem( return lsp.CompletionItem( label: label, kind: completionKind, - tags: null, // TODO(dantup): CompletionItemTags - detail: getCompletionDetail(suggestion, completionKind, useDeprecated), + tags: supportedTags.isNotEmpty + ? [ + if (supportsDeprecatedTag && suggestion.isDeprecated) + lsp.CompletionItemTag.Deprecated + ] + : null, + detail: getCompletionDetail(suggestion, completionKind, + supportsDeprecatedFlag || supportsDeprecatedTag), documentation: asStringOrMarkupContent(formats, cleanDartdoc(suggestion.docComplete)), - deprecated: useDeprecated && suggestion.isDeprecated ? true : null, + deprecated: supportsDeprecatedFlag && suggestion.isDeprecated ? true : null, // Relevance is a number, highest being best. LSP does text sort so subtract // from a large number so that a text sort will result in the correct order. // 555 -> 999455 diff --git a/pkg/analysis_server/test/lsp/completion_test.dart b/pkg/analysis_server/test/lsp/completion_test.dart index 66df9f177d6..9d045cf6461 100644 --- a/pkg/analysis_server/test/lsp/completion_test.dart +++ b/pkg/analysis_server/test/lsp/completion_test.dart @@ -300,7 +300,7 @@ class CompletionTest extends AbstractLspAnalysisServerTest { expect(item.detail.toLowerCase(), contains('deprecated')); } - Future test_isDeprecated_supported() async { + Future test_isDeprecated_supportedFlag() async { final content = ''' class MyClass { @deprecated @@ -314,7 +314,7 @@ class CompletionTest extends AbstractLspAnalysisServerTest { '''; await initialize( - textDocumentCapabilities: withCompletionItemDeprecatedSupport( + textDocumentCapabilities: withCompletionItemDeprecatedFlagSupport( emptyTextDocumentClientCapabilities)); await openFile(mainFileUri, withoutMarkers(content)); final res = await getCompletion(mainFileUri, positionFromMarker(content)); @@ -326,6 +326,32 @@ class CompletionTest extends AbstractLspAnalysisServerTest { expect(item.detail, isNot(contains('deprecated'))); } + Future test_isDeprecated_supportedTag() async { + final content = ''' + class MyClass { + @deprecated + String abcdefghij; + } + + main() { + MyClass a; + a.abc^ + } + '''; + + await initialize( + textDocumentCapabilities: withCompletionItemTagSupport( + emptyTextDocumentClientCapabilities, + [CompletionItemTag.Deprecated])); + await openFile(mainFileUri, withoutMarkers(content)); + final res = await getCompletion(mainFileUri, positionFromMarker(content)); + final item = res.singleWhere((c) => c.label == 'abcdefghij'); + expect(item.tags, contains(CompletionItemTag.Deprecated)); + // If the client says it supports the deprecated tag, we should not show + // deprecated in the details. + expect(item.detail, isNot(contains('deprecated'))); + } + Future test_namedArg_plainText() async { final content = ''' class A { const A({int one}); } diff --git a/pkg/analysis_server/test/lsp/server_abstract.dart b/pkg/analysis_server/test/lsp/server_abstract.dart index 795c5b7e241..9de60d074dc 100644 --- a/pkg/analysis_server/test/lsp/server_abstract.dart +++ b/pkg/analysis_server/test/lsp/server_abstract.dart @@ -227,7 +227,7 @@ mixin ClientCapabilitiesHelperMixin { }); } - TextDocumentClientCapabilities withCompletionItemDeprecatedSupport( + TextDocumentClientCapabilities withCompletionItemDeprecatedFlagSupport( TextDocumentClientCapabilities source, ) { return extendTextDocumentCapabilities(source, { @@ -260,6 +260,19 @@ mixin ClientCapabilitiesHelperMixin { }); } + TextDocumentClientCapabilities withCompletionItemTagSupport( + TextDocumentClientCapabilities source, + List tags, + ) { + return extendTextDocumentCapabilities(source, { + 'completion': { + 'completionItem': { + 'tagSupport': {'valueSet': tags.map((k) => k.toJson()).toList()} + } + } + }); + } + ClientCapabilitiesWorkspace withConfigurationSupport( ClientCapabilitiesWorkspace source, ) {