Skip to content

Commit

Permalink
feat(language-service): allow code refactorings to compute edits asyn…
Browse files Browse the repository at this point in the history
…chronously (#57214)

VSCode explicitly split code actions into two stages:

 - what actions are active?
 - what are the edits, if the user presses the button.

The latter stage may take longer to compute complex edits, perform
analysis. This stage is currently implemented via our non-LSP standard
`applyRefactoring` method. We should make it asynchronous, so that it
can easily integrate with migrations that aren't synchronous/or compute
in parallel.

Long-term we may want to revisit this given integration in 1P with the
language service as an actual TS server plugin; but it's not necessary
right now and we shouldn't block the effort on this for now.

PR Close #57214
  • Loading branch information
devversion authored and AndrewKushnir committed Aug 29, 2024
1 parent 4900225 commit 56ee47f
Show file tree
Hide file tree
Showing 4 changed files with 19 additions and 5 deletions.
2 changes: 1 addition & 1 deletion packages/language-service/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ export interface NgLanguageService extends ts.LanguageService {
positionOrRange: number | ts.TextRange,
refactorName: string,
reportProgress: ApplyRefactoringProgressFn,
): ts.RefactorEditInfo | undefined;
): Promise<ts.RefactorEditInfo | undefined>;

hasCodeFixesForErrorCode(errorCode: number): boolean;
}
Expand Down
18 changes: 16 additions & 2 deletions packages/language-service/src/language_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -549,12 +549,26 @@ export class LanguageService {
);
}

applyRefactoring(
/**
* Computes edits for applying the specified refactoring.
*
* VSCode explicitly split code actions into two stages:
*
* - 1) what actions are active?
* - 2) what are the edits? <- if the user presses the button
*
* The latter stage may take longer to compute complex edits, perform
* analysis. This stage is currently implemented via our non-LSP standard
* `applyRefactoring` method. We implemented it in a way to support asynchronous
* computation, so that it can easily integrate with migrations that aren't
* synchronous/or compute edits in parallel.
*/
async applyRefactoring(
fileName: string,
positionOrRange: number | ts.TextRange,
refactorName: string,
reportProgress: ApplyRefactoringProgressFn,
): ts.RefactorEditInfo | undefined {
): Promise<ts.RefactorEditInfo | undefined> {
const matchingRefactoring = allRefactorings.find((r) => r.id === refactorName);
if (matchingRefactoring === undefined) {
return undefined;
Expand Down
2 changes: 1 addition & 1 deletion packages/language-service/src/refactorings/refactoring.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export interface Refactoring {
fileName: string,
positionOrRange: number | ts.TextRange,
reportProgress: ApplyRefactoringProgressFn,
): ts.RefactorEditInfo;
): Promise<ts.RefactorEditInfo>;
}

export const allRefactorings: Refactoring[] = [];
2 changes: 1 addition & 1 deletion packages/language-service/src/ts_plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@ export function create(info: ts.server.PluginCreateInfo): NgLanguageService {
positionOrRange: number | ts.TextRange,
refactorName: string,
reportProgress: ApplyRefactoringProgressFn,
): ts.RefactorEditInfo | undefined {
): Promise<ts.RefactorEditInfo | undefined> {
return ngLS.applyRefactoring(fileName, positionOrRange, refactorName, reportProgress);
}

Expand Down

0 comments on commit 56ee47f

Please sign in to comment.