From 7862d324089311caef931ff5f45718a2b13c1b68 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Wed, 19 Apr 2023 10:06:27 -0700 Subject: [PATCH 1/2] Interactive refactor actions --- src/server/protocol.ts | 14 ++++++++++ src/services/types.ts | 14 +++++++++- .../reference/api/tsserverlibrary.d.ts | 26 ++++++++++++++++++- tests/baselines/reference/api/typescript.d.ts | 13 +++++++++- 4 files changed, 64 insertions(+), 3 deletions(-) diff --git a/src/server/protocol.ts b/src/server/protocol.ts index 819c4797084fc..6f08f3daabf5f 100644 --- a/src/server/protocol.ts +++ b/src/server/protocol.ts @@ -586,6 +586,14 @@ export interface GetApplicableRefactorsRequest extends Request { export type GetApplicableRefactorsRequestArgs = FileLocationOrRangeRequestArgs & { triggerReason?: RefactorTriggerReason; kind?: string; + /** + * Include refactor actions that require additional arguments to be passed when + * calling 'GetEditsForRefactor'. When true, clients should inspect the + * `isInteractive` property of each returned `RefactorActionInfo` + * and ensure they are able to collect the appropriate arguments for any + * interactive refactor before offering it. + */ + includeInteractiveActions?: boolean; }; export type RefactorTriggerReason = "implicit" | "invoked"; @@ -650,6 +658,12 @@ export interface RefactorActionInfo { * The hierarchical dotted name of the refactor action. */ kind?: string; + + /** + * Indicates that the action requires additional arguments to be passed + * when calling 'GetEditsForRefactor'. + */ + isInteractive?: boolean; } export interface GetEditsForRefactorRequest extends Request { diff --git a/src/services/types.ts b/src/services/types.ts index 08d983b8691c9..90587595dd9b1 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -630,7 +630,13 @@ export interface LanguageService { /** @deprecated `fileName` will be ignored */ applyCodeActionCommand(fileName: string, action: CodeActionCommand | CodeActionCommand[]): Promise; - getApplicableRefactors(fileName: string, positionOrRange: number | TextRange, preferences: UserPreferences | undefined, triggerReason?: RefactorTriggerReason, kind?: string): ApplicableRefactorInfo[]; + /** + * @param includeInteractiveActions Include refactor actions that require additional arguments to be + * passed when calling `getEditsForRefactor`. When true, clients should inspect the `isInteractive` + * property of each returned `RefactorActionInfo` and ensure they are able to collect the appropriate + * arguments for any interactive action before offering it. + */ + getApplicableRefactors(fileName: string, positionOrRange: number | TextRange, preferences: UserPreferences | undefined, triggerReason?: RefactorTriggerReason, kind?: string, includeInteractiveActions?: boolean): ApplicableRefactorInfo[]; getEditsForRefactor(fileName: string, formatOptions: FormatCodeSettings, positionOrRange: number | TextRange, refactorName: string, actionName: string, preferences: UserPreferences | undefined): RefactorEditInfo | undefined; organizeImports(args: OrganizeImportsArgs, formatOptions: FormatCodeSettings, preferences: UserPreferences | undefined): readonly FileTextChanges[]; getEditsForFileRename(oldFilePath: string, newFilePath: string, formatOptions: FormatCodeSettings, preferences: UserPreferences | undefined): readonly FileTextChanges[]; @@ -963,6 +969,12 @@ export interface RefactorActionInfo { * The hierarchical dotted name of the refactor action. */ kind?: string; + + /** + * Indicates that the action requires additional arguments to be passed + * when calling `getEditsForRefactor`. + */ + isInteractive?: boolean; } /** diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 249bd65f95a89..4f0a7d8abdd46 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -501,6 +501,14 @@ declare namespace ts { type GetApplicableRefactorsRequestArgs = FileLocationOrRangeRequestArgs & { triggerReason?: RefactorTriggerReason; kind?: string; + /** + * Include refactor actions that require additional arguments to be passed when + * calling 'GetEditsForRefactor'. When true, clients should inspect the + * `isInteractive` property of each returned `RefactorActionInfo` + * and ensure they are able to collect the appropriate arguments for any + * interactive refactor before offering it. + */ + includeInteractiveActions?: boolean; }; type RefactorTriggerReason = "implicit" | "invoked"; /** @@ -557,6 +565,11 @@ declare namespace ts { * The hierarchical dotted name of the refactor action. */ kind?: string; + /** + * Indicates that the action requires additional arguments to be passed + * when calling 'GetEditsForRefactor'. + */ + isInteractive?: boolean; } interface GetEditsForRefactorRequest extends Request { command: CommandTypes.GetEditsForRefactor; @@ -10129,7 +10142,13 @@ declare namespace ts { applyCodeActionCommand(fileName: string, action: CodeActionCommand[]): Promise; /** @deprecated `fileName` will be ignored */ applyCodeActionCommand(fileName: string, action: CodeActionCommand | CodeActionCommand[]): Promise; - getApplicableRefactors(fileName: string, positionOrRange: number | TextRange, preferences: UserPreferences | undefined, triggerReason?: RefactorTriggerReason, kind?: string): ApplicableRefactorInfo[]; + /** + * @param includeInteractiveActions Include refactor actions that require additional arguments to be + * passed when calling `getEditsForRefactor`. When true, clients should inspect the `isInteractive` + * property of each returned `RefactorActionInfo` and ensure they are able to collect the appropriate + * arguments for any interactive action before offering it. + */ + getApplicableRefactors(fileName: string, positionOrRange: number | TextRange, preferences: UserPreferences | undefined, triggerReason?: RefactorTriggerReason, kind?: string, includeInteractiveActions?: boolean): ApplicableRefactorInfo[]; getEditsForRefactor(fileName: string, formatOptions: FormatCodeSettings, positionOrRange: number | TextRange, refactorName: string, actionName: string, preferences: UserPreferences | undefined): RefactorEditInfo | undefined; organizeImports(args: OrganizeImportsArgs, formatOptions: FormatCodeSettings, preferences: UserPreferences | undefined): readonly FileTextChanges[]; getEditsForFileRename(oldFilePath: string, newFilePath: string, formatOptions: FormatCodeSettings, preferences: UserPreferences | undefined): readonly FileTextChanges[]; @@ -10401,6 +10420,11 @@ declare namespace ts { * The hierarchical dotted name of the refactor action. */ kind?: string; + /** + * Indicates that the action requires additional arguments to be passed + * when calling `getEditsForRefactor`. + */ + isInteractive?: boolean; } /** * A set of edits to make in response to a refactor action, plus an optional diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index ff62845a28f2c..5b2e1a9668e34 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -6198,7 +6198,13 @@ declare namespace ts { applyCodeActionCommand(fileName: string, action: CodeActionCommand[]): Promise; /** @deprecated `fileName` will be ignored */ applyCodeActionCommand(fileName: string, action: CodeActionCommand | CodeActionCommand[]): Promise; - getApplicableRefactors(fileName: string, positionOrRange: number | TextRange, preferences: UserPreferences | undefined, triggerReason?: RefactorTriggerReason, kind?: string): ApplicableRefactorInfo[]; + /** + * @param includeInteractiveActions Include refactor actions that require additional arguments to be + * passed when calling `getEditsForRefactor`. When true, clients should inspect the `isInteractive` + * property of each returned `RefactorActionInfo` and ensure they are able to collect the appropriate + * arguments for any interactive action before offering it. + */ + getApplicableRefactors(fileName: string, positionOrRange: number | TextRange, preferences: UserPreferences | undefined, triggerReason?: RefactorTriggerReason, kind?: string, includeInteractiveActions?: boolean): ApplicableRefactorInfo[]; getEditsForRefactor(fileName: string, formatOptions: FormatCodeSettings, positionOrRange: number | TextRange, refactorName: string, actionName: string, preferences: UserPreferences | undefined): RefactorEditInfo | undefined; organizeImports(args: OrganizeImportsArgs, formatOptions: FormatCodeSettings, preferences: UserPreferences | undefined): readonly FileTextChanges[]; getEditsForFileRename(oldFilePath: string, newFilePath: string, formatOptions: FormatCodeSettings, preferences: UserPreferences | undefined): readonly FileTextChanges[]; @@ -6470,6 +6476,11 @@ declare namespace ts { * The hierarchical dotted name of the refactor action. */ kind?: string; + /** + * Indicates that the action requires additional arguments to be passed + * when calling `getEditsForRefactor`. + */ + isInteractive?: boolean; } /** * A set of edits to make in response to a refactor action, plus an optional From d822d949f92d58f8cf61efb205655f2f2cf92158 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Wed, 19 Apr 2023 10:12:09 -0700 Subject: [PATCH 2/2] Thread through the parameter --- src/services/refactorProvider.ts | 4 ++-- src/services/services.ts | 4 ++-- src/services/types.ts | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/services/refactorProvider.ts b/src/services/refactorProvider.ts index 611c846c21c99..f8ef0d5e7f8bb 100644 --- a/src/services/refactorProvider.ts +++ b/src/services/refactorProvider.ts @@ -22,11 +22,11 @@ export function registerRefactor(name: string, refactor: Refactor) { } /** @internal */ -export function getApplicableRefactors(context: RefactorContext): ApplicableRefactorInfo[] { +export function getApplicableRefactors(context: RefactorContext, includeInteractiveActions?: boolean): ApplicableRefactorInfo[] { return arrayFrom(flatMapIterator(refactors.values(), refactor => context.cancellationToken && context.cancellationToken.isCancellationRequested() || !refactor.kinds?.some(kind => refactorKindBeginsWith(kind, context.kind)) ? undefined : - refactor.getAvailableActions(context))); + refactor.getAvailableActions(context, includeInteractiveActions))); } /** @internal */ diff --git a/src/services/services.ts b/src/services/services.ts index 59a4083be0a2e..acd845c7979c6 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -2971,10 +2971,10 @@ export function createLanguageService( return SmartSelectionRange.getSmartSelectionRange(position, syntaxTreeCache.getCurrentSourceFile(fileName)); } - function getApplicableRefactors(fileName: string, positionOrRange: number | TextRange, preferences: UserPreferences = emptyOptions, triggerReason: RefactorTriggerReason, kind: string): ApplicableRefactorInfo[] { + function getApplicableRefactors(fileName: string, positionOrRange: number | TextRange, preferences: UserPreferences = emptyOptions, triggerReason: RefactorTriggerReason, kind: string, includeInteractiveActions?: boolean): ApplicableRefactorInfo[] { synchronizeHostData(); const file = getValidSourceFile(fileName); - return refactor.getApplicableRefactors(getRefactorContext(file, positionOrRange, preferences, emptyOptions, triggerReason, kind)); + return refactor.getApplicableRefactors(getRefactorContext(file, positionOrRange, preferences, emptyOptions, triggerReason, kind), includeInteractiveActions); } function getEditsForRefactor( diff --git a/src/services/types.ts b/src/services/types.ts index 90587595dd9b1..80d9400535b58 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -1773,7 +1773,7 @@ export interface Refactor { getEditsForAction(context: RefactorContext, actionName: string): RefactorEditInfo | undefined; /** Compute (quickly) which actions are available here */ - getAvailableActions(context: RefactorContext): readonly ApplicableRefactorInfo[]; + getAvailableActions(context: RefactorContext, includeInteractive?: boolean): readonly ApplicableRefactorInfo[]; } /** @internal */