Skip to content

Commit

Permalink
only evaluate whether completion items are deprecated for all complet…
Browse files Browse the repository at this point in the history
…ions if the client doesn't support tags in `completionItem/resolve`
  • Loading branch information
DetachHead committed Feb 16, 2025
1 parent 7a91c7d commit 4ba442c
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ export interface ClientCapabilities {
supportsUnnecessaryDiagnosticTag: boolean;
supportsTaskItemDiagnosticTag: boolean;
completionItemResolveSupportsAdditionalTextEdits: boolean;
completionItemResolveSupportsTags: boolean;
}

export interface LanguageServerBaseInterface {
Expand Down
10 changes: 7 additions & 3 deletions packages/pyright-internal/src/languageServerBase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ export abstract class LanguageServerBase implements LanguageServerInterface, Dis
supportsUnnecessaryDiagnosticTag: false,
supportsTaskItemDiagnosticTag: false,
completionItemResolveSupportsAdditionalTextEdits: false,
completionItemResolveSupportsTags: false,
};

protected defaultClientConfig: any;
Expand Down Expand Up @@ -610,10 +611,11 @@ export abstract class LanguageServerBase implements LanguageServerInterface, Dis
this.client.supportsTaskItemDiagnosticTag = this.client.hasVisualStudioExtensionsCapability;
this.client.hasWindowProgressCapability = !!capabilities.window?.workDoneProgress;
this.client.hasGoToDeclarationCapability = !!capabilities.textDocument?.declaration;
const completionResolveProperties =
capabilities.textDocument?.completion?.completionItem?.resolveSupport?.properties ?? [];
this.client.completionItemResolveSupportsAdditionalTextEdits =
!!capabilities.textDocument?.completion?.completionItem?.resolveSupport?.properties.some(
(p) => p === 'additionalTextEdits'
);
completionResolveProperties.includes('additionalTextEdits');
this.client.completionItemResolveSupportsTags = completionResolveProperties.includes('tags');

// Create a service instance for each of the workspace folders.
this.workspaceFactory.handleInitialize(params);
Expand Down Expand Up @@ -972,6 +974,7 @@ export abstract class LanguageServerBase implements LanguageServerInterface, Dis
snippet: this.client.completionSupportsSnippet,
lazyEdit: false,
triggerCharacter: params?.context?.triggerCharacter,
checkDeprecatedWhenResolving: this.client.completionItemResolveSupportsTags,
},
token,
false
Expand Down Expand Up @@ -1002,6 +1005,7 @@ export abstract class LanguageServerBase implements LanguageServerInterface, Dis
format: this.client.completionDocFormat,
snippet: this.client.completionSupportsSnippet,
lazyEdit: false,
checkDeprecatedWhenResolving: this.client.completionItemResolveSupportsTags,
},
token,
false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ export class CodeActionProvider {
format: 'plaintext',
lazyEdit: false,
snippet: false,
// we don't care about deprecations here so make sure they don't get evaluated unnecessarily
// (we don't call resolveCompletionItem)
checkDeprecatedWhenResolving: true,
},
token,
true
Expand Down
55 changes: 37 additions & 18 deletions packages/pyright-internal/src/languageService/completionProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,7 @@ export interface CompletionOptions {
readonly snippet: boolean;
readonly lazyEdit: boolean;
readonly triggerCharacter?: string;
readonly checkDeprecatedWhenResolving: boolean;
}

interface RecentCompletionInfo {
Expand Down Expand Up @@ -665,6 +666,21 @@ export class CompletionProvider {
return true;
}

protected checkIfDeprecated = (
type: Type,
primaryDecl: Declaration | undefined,
name: string,
detail: SymbolDetail
): boolean =>
((isFunction(type) || isClass(type)) && type.shared.deprecatedMessage !== undefined) ||
(!!primaryDecl &&
!!this.evaluator.deprecatedTypingAlias(
AnalyzerNodeInfo.getFileInfo(primaryDecl.node),
name,
type,
detail.autoImportSource === 'typing'
));

protected addSymbol(
name: string,
symbol: Symbol,
Expand Down Expand Up @@ -695,24 +711,7 @@ export class CompletionProvider {
? this.getAutoImportText(name, detail.autoImportSource, detail.autoImportAlias)
: undefined;

// This call can be expensive to perform on every completion item
// that we return, so we used to do it lazily in the "resolve" callback
// but unfortunately it needs to be done here to check whether a symbol
// is deprecated because most clients don't support tags in completionItem/resolve.
// see these issues:
// https://github.com/microsoft/vscode/issues/240863
// https://github.com/Saghen/blink.cmp/issues/1221
const type = this.evaluator.getEffectiveTypeOfSymbol(symbol);

const isDeprecated =
((isFunction(type) || isClass(type)) && type.shared.deprecatedMessage !== undefined) ||
(primaryDecl &&
!!this.evaluator.deprecatedTypingAlias(
AnalyzerNodeInfo.getFileInfo(primaryDecl.node),
name,
type,
detail.autoImportSource === 'typing'
));
let isDeprecated: boolean;

// Are we resolving a completion item? If so, see if this symbol
// is the one that we're trying to match.
Expand All @@ -735,11 +734,18 @@ export class CompletionProvider {
return;
}

const type = this.evaluator.getEffectiveTypeOfSymbol(symbol);

if (!type) {
// Can't resolve. so bail out.
return;
}

// if resolveTags is false then this check has already happened so we don't need to do it again
if (this.options.checkDeprecatedWhenResolving && this.checkIfDeprecated(type, primaryDecl, name, detail)) {
this.itemToResolve.tags = [CompletionItemTag.Deprecated];
}

const typeDetail = getTypeDetail(
this.evaluator,
type,
Expand Down Expand Up @@ -774,6 +780,19 @@ export class CompletionProvider {

// Bail out. We don't need to add items to completion.
return;
} else if (!this.options.checkDeprecatedWhenResolving) {
// This call can be expensive to perform on every completion item
// that we return, so we would ideally always do it lazily in the "resolve" callback
// but unfortunately it often needs to be done here to check whether a symbol
// is deprecated because most clients don't support tags in completionItem/resolve.
// see these issues:
// https://github.com/microsoft/vscode/issues/240863
// https://github.com/Saghen/blink.cmp/issues/1221
const type = this.evaluator.getEffectiveTypeOfSymbol(symbol);
isDeprecated = this.checkIfDeprecated(type, primaryDecl, name, detail);
} else {
// this will be determined later when this.itemToResolve is set
isDeprecated = false;
}

if (primaryDecl) {
Expand Down

0 comments on commit 4ba442c

Please sign in to comment.