Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Making TypeScript code actions move to new file and move to appear less often #198775

Merged
merged 5 commits into from
Nov 22, 2023
Merged
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { nulToken } from '../utils/cancellation';
import FormattingOptionsManager from './fileConfigurationManager';
import { conditionalRegistration, requireSomeCapability } from './util/dependentRegistration';
import { EditorChatFollowUp, EditorChatFollowUp_Args, CompositeCommand } from './util/copilot';
import * as PConst from '../tsServer/protocol/protocol.const';

function toWorkspaceEdit(client: ITypeScriptServiceClient, edits: readonly Proto.FileCodeEdits[]): vscode.WorkspaceEdit {
const workspaceEdit = new vscode.WorkspaceEdit();
Expand Down Expand Up @@ -435,6 +436,48 @@ class MoveToFileCodeAction extends vscode.CodeAction {
arguments: [<MoveToFileRefactorCommand.Args>{ action, document, range }]
};
}

private static readonly _scopesOfInterest = new Set([
PConst.Kind.module,
PConst.Kind.class,
PConst.Kind.interface
]);

private static _navTreeNameSpanContainsRange(navigationTree: Proto.NavigationTree, range: vscode.Range): boolean {
return !!navigationTree.nameSpan && typeConverters.Range.fromTextSpan(navigationTree.nameSpan).contains(range) && MoveToFileCodeAction._scopesOfInterest.has(navigationTree.kind);
}

private static _navTreeSpansContainRange(navigationTree: Proto.NavigationTree, range: vscode.Range): boolean {
return navigationTree.spans.some(span => typeConverters.Range.fromTextSpan(span).contains(range));
}

public static shouldIncludeMoveToAction(
navigationTree: Proto.NavigationTree | undefined,
range: vscode.Range
): boolean {
if (!navigationTree || !MoveToFileCodeAction._navTreeSpansContainRange(navigationTree, range)) {
return false;
}
return MoveToFileCodeAction._shouldIncludeMoveToAction(navigationTree, range);
}

private static _shouldIncludeMoveToAction(
navigationTree: Proto.NavigationTree,
range: vscode.Range
): boolean {
if (MoveToFileCodeAction._navTreeNameSpanContainsRange(navigationTree, range)) {
return true;
}
const childTrees = navigationTree.childItems;
if (childTrees) {
for (const childTree of childTrees) {
if (MoveToFileCodeAction._navTreeSpansContainRange(childTree, range)) {
return this._shouldIncludeMoveToAction(childTree, range);
}
}
}
return MoveToFileCodeAction._navTreeNameSpanContainsRange(navigationTree, range);
aiday-mar marked this conversation as resolved.
Show resolved Hide resolved
}
}

class SelectCodeAction extends vscode.CodeAction {
Expand Down Expand Up @@ -516,16 +559,24 @@ class TypeScriptRefactorProvider implements vscode.CodeActionProvider<TsCodeActi
return undefined;
}

const actions = Array.from(this.convertApplicableRefactors(document, response.body, rangeOrSelection)).filter(action => {
const applicableRefactors = await this.convertApplicableRefactors(document, response.body, rangeOrSelection);
const actions = await Promise.all(applicableRefactors.map(async (action) => {
if (this.client.apiVersion.lt(API.v430)) {
// Don't show 'infer return type' refactoring unless it has been explicitly requested
// https://github.com/microsoft/TypeScript/issues/42993
if (!context.only && action.kind?.value === 'refactor.rewrite.function.returnType') {
return false;
}
}
if (action.kind?.value === Move_NewFile.kind.value) {
const navigationTree = await this.client.execute('navtree', { file: document.uri.path }, nulToken);
if (navigationTree.type !== 'response') {
return;
}
return MoveToFileCodeAction.shouldIncludeMoveToAction(navigationTree.body, rangeOrSelection);
}
return true;
});
})).then((mappedRefactors) => applicableRefactors.filter((_, index) => mappedRefactors[index]));

if (!context.only) {
return actions;
Expand All @@ -547,31 +598,44 @@ class TypeScriptRefactorProvider implements vscode.CodeActionProvider<TsCodeActi
return context.triggerKind === vscode.CodeActionTriggerKind.Invoke ? 'invoked' : 'implicit';
}

private *convertApplicableRefactors(
private async convertApplicableRefactors(
document: vscode.TextDocument,
refactors: readonly Proto.ApplicableRefactorInfo[],
rangeOrSelection: vscode.Range | vscode.Selection
): Iterable<TsCodeAction> {
): Promise<Array<TsCodeAction>> {
const actions: Array<TsCodeAction> = [];
for (const refactor of refactors) {
if (refactor.inlineable === false) {
yield new SelectCodeAction(refactor, document, rangeOrSelection);
actions.push(new SelectCodeAction(refactor, document, rangeOrSelection));
} else {
for (const action of refactor.actions) {
yield this.refactorActionToCodeAction(document, refactor, action, rangeOrSelection, refactor.actions);
const refactorAction = await this.refactorActionToCodeAction(document, refactor, action, rangeOrSelection, refactor.actions);
if (refactorAction) {
actions.push(refactorAction);
}
}
}
}
return actions;
}

private refactorActionToCodeAction(
private async refactorActionToCodeAction(
document: vscode.TextDocument,
refactor: Proto.ApplicableRefactorInfo,
action: Proto.RefactorActionInfo,
rangeOrSelection: vscode.Range | vscode.Selection,
allActions: readonly Proto.RefactorActionInfo[],
): TsCodeAction {
): Promise<TsCodeAction | undefined> {
let codeAction: TsCodeAction;
if (action.name === 'Move to file') {
const navigationTree = await this.client.execute('navtree', { file: document.uri.path }, nulToken);
if (navigationTree.type !== 'response') {
return;
}
const shouldIncludeMoveToAction = MoveToFileCodeAction.shouldIncludeMoveToAction(navigationTree.body, rangeOrSelection);
if (!shouldIncludeMoveToAction) {
return;
}
codeAction = new MoveToFileCodeAction(document, action, rangeOrSelection);
} else {
let copilotRename: ((info: Proto.RefactorEditInfo) => vscode.Command) | undefined;
Expand Down
Loading