mirror of
https://github.com/microsoft/vscode.git
synced 2025-12-20 02:08:47 +00:00
Merge pull request #193810 from microsoft/revert-192602-ai-codefixes
Revert "Copilot-based TS refactors"
This commit is contained in:
@@ -145,58 +145,11 @@
|
|||||||
"title": "%configuration.typescript%",
|
"title": "%configuration.typescript%",
|
||||||
"order": 20,
|
"order": 20,
|
||||||
"properties": {
|
"properties": {
|
||||||
"typescript.experimental.aiCodeActions": {
|
"typescript.experimental.aiQuickFix": {
|
||||||
"type": "object",
|
"type": "boolean",
|
||||||
"default": {},
|
"default": false,
|
||||||
"description": "%typescript.experimental.aiCodeActions%",
|
"description": "%typescript.experimental.aiQuickFix%",
|
||||||
"scope": "resource",
|
"scope": "resource"
|
||||||
"properties": {
|
|
||||||
"classIncorrectlyImplementsInterface": {
|
|
||||||
"type": "boolean",
|
|
||||||
"default": false,
|
|
||||||
"description": "%typescript.experimental.aiCodeActions.classIncorrectlyImplementsInterface%"
|
|
||||||
},
|
|
||||||
"classDoesntImplementInheritedAbstractMember": {
|
|
||||||
"type": "boolean",
|
|
||||||
"default": false,
|
|
||||||
"description": "%typescript.experimental.aiCodeActions.classDoesntImplementInheritedAbstractMember%"
|
|
||||||
},
|
|
||||||
"missingFunctionDeclaration": {
|
|
||||||
"type": "boolean",
|
|
||||||
"default": false,
|
|
||||||
"description": "%typescript.experimental.aiCodeActions.missingFunctionDeclaration%"
|
|
||||||
},
|
|
||||||
"inferAndAddTypes": {
|
|
||||||
"type": "boolean",
|
|
||||||
"default": false,
|
|
||||||
"description": "%typescript.experimental.aiCodeActions.inferAndAddTypes%"
|
|
||||||
},
|
|
||||||
"addNameToNamelessParameter": {
|
|
||||||
"type": "boolean",
|
|
||||||
"default": false,
|
|
||||||
"description": "%typescript.experimental.aiCodeActions.addNameToNamelessParameter%"
|
|
||||||
},
|
|
||||||
"extractConstant": {
|
|
||||||
"type": "boolean",
|
|
||||||
"default": false,
|
|
||||||
"description": "%typescript.experimental.aiCodeActions.extractConstant%"
|
|
||||||
},
|
|
||||||
"extractFunction": {
|
|
||||||
"type": "boolean",
|
|
||||||
"default": false,
|
|
||||||
"description": "%typescript.experimental.aiCodeActions.extractFunction%"
|
|
||||||
},
|
|
||||||
"extractType": {
|
|
||||||
"type": "boolean",
|
|
||||||
"default": false,
|
|
||||||
"description": "%typescript.experimental.aiCodeActions.extractType%"
|
|
||||||
},
|
|
||||||
"extractInterface": {
|
|
||||||
"type": "boolean",
|
|
||||||
"default": false,
|
|
||||||
"description": "%typescript.experimental.aiCodeActions.extractInterface%"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"typescript.tsdk": {
|
"typescript.tsdk": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
|||||||
@@ -8,16 +8,7 @@
|
|||||||
"configuration.suggest.completeFunctionCalls": "Complete functions with their parameter signature.",
|
"configuration.suggest.completeFunctionCalls": "Complete functions with their parameter signature.",
|
||||||
"configuration.suggest.includeAutomaticOptionalChainCompletions": "Enable/disable showing completions on potentially undefined values that insert an optional chain call. Requires strict null checks to be enabled.",
|
"configuration.suggest.includeAutomaticOptionalChainCompletions": "Enable/disable showing completions on potentially undefined values that insert an optional chain call. Requires strict null checks to be enabled.",
|
||||||
"configuration.suggest.includeCompletionsForImportStatements": "Enable/disable auto-import-style completions on partially-typed import statements.",
|
"configuration.suggest.includeCompletionsForImportStatements": "Enable/disable auto-import-style completions on partially-typed import statements.",
|
||||||
"typescript.experimental.aiCodeActions": "Enable/disable AI-assisted code actions. Requires an extension providing AI chat functionality.",
|
"typescript.experimental.aiQuickFix": "Enable/disable AI-assisted quick fixes. Requires an extension providing AI chat functionality.",
|
||||||
"typescript.experimental.aiCodeActions.classIncorrectlyImplementsInterface": "Enable/disable AI assistance for Class Incorrectly Implements Interface quickfix. Requires an extension providing AI chat functionality.",
|
|
||||||
"typescript.experimental.aiCodeActions.classDoesntImplementInheritedAbstractMember": "Enable/disable AI assistance for Class Doesn't Implement Inherited Abstract Member quickfix. Requires an extension providing AI chat functionality.",
|
|
||||||
"typescript.experimental.aiCodeActions.missingFunctionDeclaration": "Enable/disable AI assistance for Missing Function Declaration quickfix. Requires an extension providing AI chat functionality.",
|
|
||||||
"typescript.experimental.aiCodeActions.inferAndAddTypes": "Enable/disable AI assistance for Infer and Add Types refactor. Requires an extension providing AI chat functionality.",
|
|
||||||
"typescript.experimental.aiCodeActions.addNameToNamelessParameter": "Enable/disable AI assistance for Add Name to Nameless Parameter quickfix. Requires an extension providing AI chat functionality.",
|
|
||||||
"typescript.experimental.aiCodeActions.extractConstant": "Enable/disable AI assistance for Extract Constant refactor. Requires an extension providing AI chat functionality.",
|
|
||||||
"typescript.experimental.aiCodeActions.extractFunction": "Enable/disable AI assistance for Extract Function refactor. Requires an extension providing AI chat functionality.",
|
|
||||||
"typescript.experimental.aiCodeActions.extractType": "Enable/disable AI assistance for Extract Type refactor. Requires an extension providing AI chat functionality.",
|
|
||||||
"typescript.experimental.aiCodeActions.extractInterface": "Enable/disable AI assistance for Extract Interface refactor. Requires an extension providing AI chat functionality.",
|
|
||||||
"typescript.tsdk.desc": "Specifies the folder path to the tsserver and `lib*.d.ts` files under a TypeScript install to use for IntelliSense, for example: `./node_modules/typescript/lib`.\n\n- When specified as a user setting, the TypeScript version from `typescript.tsdk` automatically replaces the built-in TypeScript version.\n- When specified as a workspace setting, `typescript.tsdk` allows you to switch to use that workspace version of TypeScript for IntelliSense with the `TypeScript: Select TypeScript version` command.\n\nSee the [TypeScript documentation](https://code.visualstudio.com/docs/typescript/typescript-compiling#_using-newer-typescript-versions) for more detail about managing TypeScript versions.",
|
"typescript.tsdk.desc": "Specifies the folder path to the tsserver and `lib*.d.ts` files under a TypeScript install to use for IntelliSense, for example: `./node_modules/typescript/lib`.\n\n- When specified as a user setting, the TypeScript version from `typescript.tsdk` automatically replaces the built-in TypeScript version.\n- When specified as a workspace setting, `typescript.tsdk` allows you to switch to use that workspace version of TypeScript for IntelliSense with the `TypeScript: Select TypeScript version` command.\n\nSee the [TypeScript documentation](https://code.visualstudio.com/docs/typescript/typescript-compiling#_using-newer-typescript-versions) for more detail about managing TypeScript versions.",
|
||||||
"typescript.disableAutomaticTypeAcquisition": "Disables [automatic type acquisition](https://code.visualstudio.com/docs/nodejs/working-with-javascript#_typings-and-automatic-type-acquisition). Automatic type acquisition fetches `@types` packages from npm to improve IntelliSense for external libraries.",
|
"typescript.disableAutomaticTypeAcquisition": "Disables [automatic type acquisition](https://code.visualstudio.com/docs/nodejs/working-with-javascript#_typings-and-automatic-type-acquisition). Automatic type acquisition fetches `@types` packages from npm to improve IntelliSense for external libraries.",
|
||||||
"typescript.enablePromptUseWorkspaceTsdk": "Enables prompting of users to use the TypeScript version configured in the workspace for Intellisense.",
|
"typescript.enablePromptUseWorkspaceTsdk": "Enables prompting of users to use the TypeScript version configured in the workspace for Intellisense.",
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ import { DiagnosticsManager } from './diagnostics';
|
|||||||
import FileConfigurationManager from './fileConfigurationManager';
|
import FileConfigurationManager from './fileConfigurationManager';
|
||||||
import { applyCodeActionCommands, getEditForCodeAction } from './util/codeAction';
|
import { applyCodeActionCommands, getEditForCodeAction } from './util/codeAction';
|
||||||
import { conditionalRegistration, requireSomeCapability } from './util/dependentRegistration';
|
import { conditionalRegistration, requireSomeCapability } from './util/dependentRegistration';
|
||||||
import { Expand, EditorChatFollowUp, CompositeCommand } from './util/copilot';
|
|
||||||
|
|
||||||
type ApplyCodeActionCommand_args = {
|
type ApplyCodeActionCommand_args = {
|
||||||
readonly document: vscode.TextDocument;
|
readonly document: vscode.TextDocument;
|
||||||
@@ -27,6 +26,42 @@ type ApplyCodeActionCommand_args = {
|
|||||||
readonly followupAction?: Command;
|
readonly followupAction?: Command;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class EditorChatFollowUp implements Command {
|
||||||
|
|
||||||
|
id: string = '_typescript.quickFix.editorChatFollowUp';
|
||||||
|
|
||||||
|
constructor(private readonly prompt: string, private readonly document: vscode.TextDocument, private readonly range: vscode.Range, private readonly client: ITypeScriptServiceClient) {
|
||||||
|
}
|
||||||
|
|
||||||
|
async execute() {
|
||||||
|
const findScopeEndLineFromNavTree = (startLine: number, navigationTree: Proto.NavigationTree[]): vscode.Range | undefined => {
|
||||||
|
for (const node of navigationTree) {
|
||||||
|
const range = typeConverters.Range.fromTextSpan(node.spans[0]);
|
||||||
|
if (startLine === range.start.line) {
|
||||||
|
return range;
|
||||||
|
} else if (startLine > range.start.line && startLine <= range.end.line && node.childItems) {
|
||||||
|
return findScopeEndLineFromNavTree(startLine, node.childItems);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
};
|
||||||
|
const filepath = this.client.toOpenTsFilePath(this.document);
|
||||||
|
if (!filepath) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const response = await this.client.execute('navtree', { file: filepath }, nulToken);
|
||||||
|
if (response.type !== 'response' || !response.body?.childItems) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const startLine = this.range.start.line;
|
||||||
|
const enclosingRange = findScopeEndLineFromNavTree(startLine, response.body.childItems);
|
||||||
|
if (!enclosingRange) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await vscode.commands.executeCommand('vscode.editorChat.start', { initialRange: enclosingRange, message: this.prompt, autoSend: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class ApplyCodeActionCommand implements Command {
|
class ApplyCodeActionCommand implements Command {
|
||||||
public static readonly ID = '_typescript.applyCodeActionCommand';
|
public static readonly ID = '_typescript.applyCodeActionCommand';
|
||||||
public readonly id = ApplyCodeActionCommand.ID;
|
public readonly id = ApplyCodeActionCommand.ID;
|
||||||
@@ -220,10 +255,8 @@ class TypeScriptQuickFixProvider implements vscode.CodeActionProvider<VsCodeCode
|
|||||||
private readonly diagnosticsManager: DiagnosticsManager,
|
private readonly diagnosticsManager: DiagnosticsManager,
|
||||||
telemetryReporter: TelemetryReporter
|
telemetryReporter: TelemetryReporter
|
||||||
) {
|
) {
|
||||||
commandManager.register(new CompositeCommand());
|
|
||||||
commandManager.register(new ApplyCodeActionCommand(client, diagnosticsManager, telemetryReporter));
|
commandManager.register(new ApplyCodeActionCommand(client, diagnosticsManager, telemetryReporter));
|
||||||
commandManager.register(new ApplyFixAllCodeAction(client, telemetryReporter));
|
commandManager.register(new ApplyFixAllCodeAction(client, telemetryReporter));
|
||||||
commandManager.register(new EditorChatFollowUp(client));
|
|
||||||
|
|
||||||
this.supportedCodeActionProvider = new SupportedCodeActionProvider(client);
|
this.supportedCodeActionProvider = new SupportedCodeActionProvider(client);
|
||||||
}
|
}
|
||||||
@@ -307,89 +340,42 @@ class TypeScriptQuickFixProvider implements vscode.CodeActionProvider<VsCodeCode
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const tsCodeFix of response.body) {
|
for (const tsCodeFix of response.body) {
|
||||||
for (const action of this.getFixesForTsCodeAction(document, diagnostic, tsCodeFix)) {
|
this.addAllFixesForTsCodeAction(results, document, file, diagnostic, tsCodeFix as Proto.CodeFixAction);
|
||||||
results.addAction(action);
|
|
||||||
}
|
|
||||||
this.addFixAllForTsCodeAction(results, document.uri, file, diagnostic, tsCodeFix as Proto.CodeFixAction);
|
|
||||||
}
|
}
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
private getFixesForTsCodeAction(
|
private addAllFixesForTsCodeAction(
|
||||||
|
results: CodeActionSet,
|
||||||
|
document: vscode.TextDocument,
|
||||||
|
file: string,
|
||||||
|
diagnostic: vscode.Diagnostic,
|
||||||
|
tsAction: Proto.CodeFixAction
|
||||||
|
): CodeActionSet {
|
||||||
|
results.addAction(this.getSingleFixForTsCodeAction(document, diagnostic, tsAction));
|
||||||
|
this.addFixAllForTsCodeAction(results, document.uri, file, diagnostic, tsAction as Proto.CodeFixAction);
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
private getSingleFixForTsCodeAction(
|
||||||
document: vscode.TextDocument,
|
document: vscode.TextDocument,
|
||||||
diagnostic: vscode.Diagnostic,
|
diagnostic: vscode.Diagnostic,
|
||||||
action: Proto.CodeFixAction
|
tsAction: Proto.CodeFixAction
|
||||||
): VsCodeCodeAction[] {
|
): VsCodeCodeAction {
|
||||||
const actions: VsCodeCodeAction[] = [];
|
const aiQuickFixEnabled = vscode.workspace.getConfiguration('typescript').get('experimental.aiQuickFix');
|
||||||
let message: string | undefined;
|
let followupAction: Command | undefined;
|
||||||
let expand: Expand | undefined;
|
if (aiQuickFixEnabled && tsAction.fixName === fixNames.classIncorrectlyImplementsInterface) {
|
||||||
let title = action.description;
|
followupAction = new EditorChatFollowUp('Implement the class using the interface', document, diagnostic.range, this.client);
|
||||||
if (vscode.workspace.getConfiguration('typescript').get('experimental.aiCodeActions')) {
|
|
||||||
if (action.fixName === fixNames.classIncorrectlyImplementsInterface && vscode.workspace.getConfiguration('typescript').get('experimental.aiCodeActions.classIncorrectlyImplementsInterface')) {
|
|
||||||
title += ' with Copilot';
|
|
||||||
message = `Implement the stubbed-out class members for ${document.getText(diagnostic.range)} with a useful implementation.`;
|
|
||||||
expand = { kind: 'code-action', action };
|
|
||||||
}
|
|
||||||
else if (action.fixName === fixNames.fixClassDoesntImplementInheritedAbstractMember && vscode.workspace.getConfiguration('typescript').get('experimental.aiCodeActions.classDoesntImplementInheritedAbstractMember')) {
|
|
||||||
title += ' with Copilot';
|
|
||||||
message = `Implement the stubbed-out class members for ${document.getText(diagnostic.range)} with a useful implementation.`;
|
|
||||||
expand = { kind: 'code-action', action };
|
|
||||||
}
|
|
||||||
else if (action.fixName === fixNames.fixMissingFunctionDeclaration && vscode.workspace.getConfiguration('typescript').get('experimental.aiCodeActions.missingFunctionDeclaration')) {
|
|
||||||
title += `Implement missing function declaration '${document.getText(diagnostic.range)}' using Copilot`;
|
|
||||||
message = `Provide a reasonable implementation of the function ${document.getText(diagnostic.range)} given its type and the context it's called in.`;
|
|
||||||
expand = { kind: 'code-action', action };
|
|
||||||
}
|
|
||||||
else if (action.fixName === fixNames.inferFromUsage && vscode.workspace.getConfiguration('typescript').get('experimental.aiCodeActions.inferAndAddTypes')) {
|
|
||||||
const inferFromBody = new VsCodeCodeAction(action, 'Infer types using Copilot', vscode.CodeActionKind.QuickFix);
|
|
||||||
inferFromBody.edit = new vscode.WorkspaceEdit();
|
|
||||||
inferFromBody.diagnostics = [diagnostic];
|
|
||||||
inferFromBody.command = {
|
|
||||||
command: EditorChatFollowUp.ID,
|
|
||||||
arguments: [<EditorChatFollowUp.Args>{
|
|
||||||
message: 'Add types to this code. Add separate interfaces when possible. Do not change the code except for adding types.',
|
|
||||||
expand: { kind: 'navtree-function', pos: diagnostic.range.start },
|
|
||||||
document
|
|
||||||
}],
|
|
||||||
title: ''
|
|
||||||
};
|
|
||||||
actions.push(inferFromBody);
|
|
||||||
}
|
|
||||||
else if (action.fixName === fixNames.addNameToNamelessParameter && vscode.workspace.getConfiguration('typescript').get('experimental.aiCodeActions.addNameToNamelessParameter')) {
|
|
||||||
const newText = action.changes.map(change => change.textChanges.map(textChange => textChange.newText).join('')).join('');
|
|
||||||
title = 'Add meaningful parameter name with Copilot';
|
|
||||||
message = `Rename the parameter ${newText} with a more meaningful name.`;
|
|
||||||
expand = {
|
|
||||||
kind: 'navtree-function',
|
|
||||||
pos: diagnostic.range.start
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
const codeAction = new VsCodeCodeAction(action, title, vscode.CodeActionKind.QuickFix);
|
const codeAction = new VsCodeCodeAction(tsAction, tsAction.description, vscode.CodeActionKind.QuickFix);
|
||||||
codeAction.edit = getEditForCodeAction(this.client, action);
|
codeAction.edit = getEditForCodeAction(this.client, tsAction);
|
||||||
codeAction.diagnostics = [diagnostic];
|
codeAction.diagnostics = [diagnostic];
|
||||||
codeAction.command = {
|
codeAction.command = {
|
||||||
command: ApplyCodeActionCommand.ID,
|
command: ApplyCodeActionCommand.ID,
|
||||||
arguments: [<ApplyCodeActionCommand_args>{ action: action, diagnostic, document }],
|
arguments: [<ApplyCodeActionCommand_args>{ action: tsAction, diagnostic, document, followupAction }],
|
||||||
title: ''
|
title: ''
|
||||||
};
|
};
|
||||||
if (expand && message !== undefined) {
|
return codeAction;
|
||||||
codeAction.command = {
|
|
||||||
command: CompositeCommand.ID,
|
|
||||||
title: '',
|
|
||||||
arguments: [codeAction.command, {
|
|
||||||
command: EditorChatFollowUp.ID,
|
|
||||||
title: '',
|
|
||||||
arguments: [<EditorChatFollowUp.Args>{
|
|
||||||
message,
|
|
||||||
expand,
|
|
||||||
document
|
|
||||||
}],
|
|
||||||
}],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
actions.push(codeAction);
|
|
||||||
return actions;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private addFixAllForTsCodeAction(
|
private addFixAllForTsCodeAction(
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ import { coalesce } from '../utils/arrays';
|
|||||||
import { nulToken } from '../utils/cancellation';
|
import { nulToken } from '../utils/cancellation';
|
||||||
import FormattingOptionsManager from './fileConfigurationManager';
|
import FormattingOptionsManager from './fileConfigurationManager';
|
||||||
import { conditionalRegistration, requireSomeCapability } from './util/dependentRegistration';
|
import { conditionalRegistration, requireSomeCapability } from './util/dependentRegistration';
|
||||||
import { EditorChatFollowUp, CompositeCommand } from './util/copilot';
|
|
||||||
|
|
||||||
function toWorkspaceEdit(client: ITypeScriptServiceClient, edits: readonly Proto.FileCodeEdits[]): vscode.WorkspaceEdit {
|
function toWorkspaceEdit(client: ITypeScriptServiceClient, edits: readonly Proto.FileCodeEdits[]): vscode.WorkspaceEdit {
|
||||||
const workspaceEdit = new vscode.WorkspaceEdit();
|
const workspaceEdit = new vscode.WorkspaceEdit();
|
||||||
@@ -35,6 +34,17 @@ function toWorkspaceEdit(client: ITypeScriptServiceClient, edits: readonly Proto
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class CompositeCommand implements Command {
|
||||||
|
public static readonly ID = '_typescript.compositeCommand';
|
||||||
|
public readonly id = CompositeCommand.ID;
|
||||||
|
|
||||||
|
public async execute(...commands: vscode.Command[]): Promise<void> {
|
||||||
|
for (const command of commands) {
|
||||||
|
await vscode.commands.executeCommand(command.command, ...(command.arguments ?? []));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
namespace DidApplyRefactoringCommand {
|
namespace DidApplyRefactoringCommand {
|
||||||
export interface Args {
|
export interface Args {
|
||||||
readonly action: string;
|
readonly action: string;
|
||||||
@@ -345,17 +355,15 @@ class InlinedCodeAction extends vscode.CodeAction {
|
|||||||
public readonly refactor: Proto.ApplicableRefactorInfo,
|
public readonly refactor: Proto.ApplicableRefactorInfo,
|
||||||
public readonly action: Proto.RefactorActionInfo,
|
public readonly action: Proto.RefactorActionInfo,
|
||||||
public readonly range: vscode.Range,
|
public readonly range: vscode.Range,
|
||||||
public readonly copilotRename?: (info: Proto.RefactorEditInfo) => vscode.Command,
|
|
||||||
) {
|
) {
|
||||||
const title = copilotRename ? action.description + ' and suggest a name with Copilot.' : action.description;
|
super(action.description, InlinedCodeAction.getKind(action));
|
||||||
super(title, InlinedCodeAction.getKind(action));
|
|
||||||
|
|
||||||
if (action.notApplicableReason) {
|
if (action.notApplicableReason) {
|
||||||
this.disabled = { reason: action.notApplicableReason };
|
this.disabled = { reason: action.notApplicableReason };
|
||||||
}
|
}
|
||||||
|
|
||||||
this.command = {
|
this.command = {
|
||||||
title,
|
title: action.description,
|
||||||
command: DidApplyRefactoringCommand.ID,
|
command: DidApplyRefactoringCommand.ID,
|
||||||
arguments: [<DidApplyRefactoringCommand.Args>{ action: action.name }],
|
arguments: [<DidApplyRefactoringCommand.Args>{ action: action.name }],
|
||||||
};
|
};
|
||||||
@@ -387,21 +395,18 @@ class InlinedCodeAction extends vscode.CodeAction {
|
|||||||
if (response.body.renameLocation) {
|
if (response.body.renameLocation) {
|
||||||
// Disable renames in interactive playground https://github.com/microsoft/vscode/issues/75137
|
// Disable renames in interactive playground https://github.com/microsoft/vscode/issues/75137
|
||||||
if (this.document.uri.scheme !== fileSchemes.walkThroughSnippet) {
|
if (this.document.uri.scheme !== fileSchemes.walkThroughSnippet) {
|
||||||
if (this.copilotRename && this.command) {
|
|
||||||
this.command.title = 'Copilot: ' + this.command.title;
|
|
||||||
}
|
|
||||||
this.command = {
|
this.command = {
|
||||||
command: CompositeCommand.ID,
|
command: CompositeCommand.ID,
|
||||||
title: '',
|
title: '',
|
||||||
arguments: coalesce([
|
arguments: coalesce([
|
||||||
this.command,
|
this.command,
|
||||||
this.copilotRename ? this.copilotRename(response.body) : {
|
{
|
||||||
command: 'editor.action.rename',
|
command: 'editor.action.rename',
|
||||||
arguments: [[
|
arguments: [[
|
||||||
this.document.uri,
|
this.document.uri,
|
||||||
typeConverters.Position.fromLocation(response.body.renameLocation)
|
typeConverters.Position.fromLocation(response.body.renameLocation)
|
||||||
]]
|
]]
|
||||||
},
|
}
|
||||||
])
|
])
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -451,6 +456,7 @@ class SelectCodeAction extends vscode.CodeAction {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type TsCodeAction = InlinedCodeAction | MoveToFileCodeAction | SelectCodeAction;
|
type TsCodeAction = InlinedCodeAction | MoveToFileCodeAction | SelectCodeAction;
|
||||||
|
|
||||||
class TypeScriptRefactorProvider implements vscode.CodeActionProvider<TsCodeAction> {
|
class TypeScriptRefactorProvider implements vscode.CodeActionProvider<TsCodeAction> {
|
||||||
@@ -465,7 +471,6 @@ class TypeScriptRefactorProvider implements vscode.CodeActionProvider<TsCodeActi
|
|||||||
commandManager.register(new CompositeCommand());
|
commandManager.register(new CompositeCommand());
|
||||||
commandManager.register(new SelectRefactorCommand(this.client));
|
commandManager.register(new SelectRefactorCommand(this.client));
|
||||||
commandManager.register(new MoveToFileRefactorCommand(this.client, didApplyRefactoringCommand));
|
commandManager.register(new MoveToFileRefactorCommand(this.client, didApplyRefactoringCommand));
|
||||||
commandManager.register(new EditorChatFollowUp(this.client));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static readonly metadata: vscode.CodeActionProviderMetadata = {
|
public static readonly metadata: vscode.CodeActionProviderMetadata = {
|
||||||
@@ -577,36 +582,7 @@ class TypeScriptRefactorProvider implements vscode.CodeActionProvider<TsCodeActi
|
|||||||
if (action.name === 'Move to file') {
|
if (action.name === 'Move to file') {
|
||||||
codeAction = new MoveToFileCodeAction(document, action, rangeOrSelection);
|
codeAction = new MoveToFileCodeAction(document, action, rangeOrSelection);
|
||||||
} else {
|
} else {
|
||||||
let copilotRename: ((info: Proto.RefactorEditInfo) => vscode.Command) | undefined;
|
codeAction = new InlinedCodeAction(this.client, document, refactor, action, rangeOrSelection);
|
||||||
if (vscode.workspace.getConfiguration('typescript', null).get('experimental.aiCodeActions')) {
|
|
||||||
if (Extract_Constant.matches(action) && vscode.workspace.getConfiguration('typescript').get('experimental.aiCodeActions.extractConstant')
|
|
||||||
|| Extract_Function.matches(action) && vscode.workspace.getConfiguration('typescript').get('experimental.aiCodeActions.extractFunction')
|
|
||||||
|| Extract_Type.matches(action) && vscode.workspace.getConfiguration('typescript').get('experimental.aiCodeActions.extractType')
|
|
||||||
|| Extract_Interface.matches(action) && vscode.workspace.getConfiguration('typescript').get('experimental.aiCodeActions.extractInterface')) {
|
|
||||||
const newName = Extract_Constant.matches(action) ? 'newLocal'
|
|
||||||
: Extract_Function.matches(action) ? 'newFunction'
|
|
||||||
: Extract_Type.matches(action) ? 'NewType'
|
|
||||||
: Extract_Interface.matches(action) ? 'NewInterface'
|
|
||||||
: '';
|
|
||||||
copilotRename = info => ({
|
|
||||||
title: '',
|
|
||||||
command: EditorChatFollowUp.ID,
|
|
||||||
arguments: [<EditorChatFollowUp.Args>{
|
|
||||||
message: `Rename ${newName} to a better name based on usage.`,
|
|
||||||
expand: Extract_Constant.matches(action) ? {
|
|
||||||
kind: 'navtree-function',
|
|
||||||
pos: typeConverters.Position.fromLocation(info.renameLocation!),
|
|
||||||
} : {
|
|
||||||
kind: 'refactor-info',
|
|
||||||
refactor: info,
|
|
||||||
},
|
|
||||||
document,
|
|
||||||
}]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
codeAction = new InlinedCodeAction(this.client, document, refactor, action, rangeOrSelection, copilotRename);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
codeAction.isPreferred = TypeScriptRefactorProvider.isPreferred(action, allActions);
|
codeAction.isPreferred = TypeScriptRefactorProvider.isPreferred(action, allActions);
|
||||||
|
|||||||
@@ -1,147 +0,0 @@
|
|||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
import * as vscode from 'vscode';
|
|
||||||
import { Command } from '../../commands/commandManager';
|
|
||||||
import { nulToken } from '../../utils/cancellation';
|
|
||||||
import type * as Proto from '../../tsServer/protocol/protocol';
|
|
||||||
import * as typeConverters from '../../typeConverters';
|
|
||||||
import { ITypeScriptServiceClient } from '../../typescriptService';
|
|
||||||
|
|
||||||
export class EditorChatFollowUp implements Command {
|
|
||||||
public static readonly ID = '_typescript.quickFix.editorChatReplacement2';
|
|
||||||
public readonly id = EditorChatFollowUp.ID;
|
|
||||||
constructor(private readonly client: ITypeScriptServiceClient) { }
|
|
||||||
async execute({ message, document, expand }: EditorChatFollowUp.Args) {
|
|
||||||
const initialRange =
|
|
||||||
expand.kind === 'navtree-function'
|
|
||||||
? await findScopeEndLineFromNavTree(
|
|
||||||
this.client,
|
|
||||||
document,
|
|
||||||
expand.pos.line
|
|
||||||
)
|
|
||||||
: expand.kind === 'refactor-info'
|
|
||||||
? await findEditScope(
|
|
||||||
this.client,
|
|
||||||
document,
|
|
||||||
expand.refactor.edits.flatMap((e) => e.textChanges)
|
|
||||||
)
|
|
||||||
: expand.kind === 'code-action'
|
|
||||||
? await findEditScope(
|
|
||||||
this.client,
|
|
||||||
document,
|
|
||||||
expand.action.changes.flatMap((c) => c.textChanges)
|
|
||||||
)
|
|
||||||
: expand.range;
|
|
||||||
await vscode.commands.executeCommand('vscode.editorChat.start', {
|
|
||||||
initialRange,
|
|
||||||
message,
|
|
||||||
autoSend: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
export namespace EditorChatFollowUp {
|
|
||||||
export interface Args {
|
|
||||||
readonly message: string;
|
|
||||||
readonly document: vscode.TextDocument;
|
|
||||||
readonly expand: Expand;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class CompositeCommand implements Command {
|
|
||||||
public static readonly ID = '_typescript.compositeCommand';
|
|
||||||
public readonly id = CompositeCommand.ID;
|
|
||||||
|
|
||||||
public async execute(...commands: vscode.Command[]): Promise<void> {
|
|
||||||
for (const command of commands) {
|
|
||||||
await vscode.commands.executeCommand(
|
|
||||||
command.command,
|
|
||||||
...(command.arguments ?? [])
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export type Expand =
|
|
||||||
| { kind: 'none'; readonly range: vscode.Range }
|
|
||||||
| { kind: 'navtree-function'; readonly pos: vscode.Position }
|
|
||||||
| { kind: 'refactor-info'; readonly refactor: Proto.RefactorEditInfo }
|
|
||||||
| { kind: 'code-action'; readonly action: Proto.CodeAction };
|
|
||||||
|
|
||||||
function findScopeEndLineFromNavTreeWorker(
|
|
||||||
startLine: number,
|
|
||||||
navigationTree: Proto.NavigationTree[]
|
|
||||||
): vscode.Range | undefined {
|
|
||||||
for (const node of navigationTree) {
|
|
||||||
const range = typeConverters.Range.fromTextSpan(node.spans[0]);
|
|
||||||
if (startLine === range.start.line) {
|
|
||||||
return range;
|
|
||||||
} else if (
|
|
||||||
startLine > range.start.line &&
|
|
||||||
startLine <= range.end.line &&
|
|
||||||
node.childItems
|
|
||||||
) {
|
|
||||||
return findScopeEndLineFromNavTreeWorker(startLine, node.childItems);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function findScopeEndLineFromNavTree(
|
|
||||||
client: ITypeScriptServiceClient,
|
|
||||||
document: vscode.TextDocument,
|
|
||||||
startLine: number
|
|
||||||
) {
|
|
||||||
const filepath = client.toOpenTsFilePath(document);
|
|
||||||
if (!filepath) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const response = await client.execute(
|
|
||||||
'navtree',
|
|
||||||
{ file: filepath },
|
|
||||||
nulToken
|
|
||||||
);
|
|
||||||
if (response.type !== 'response' || !response.body?.childItems) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
return findScopeEndLineFromNavTreeWorker(startLine, response.body.childItems);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function findEditScope(
|
|
||||||
client: ITypeScriptServiceClient,
|
|
||||||
document: vscode.TextDocument,
|
|
||||||
edits: Proto.CodeEdit[]
|
|
||||||
): Promise<vscode.Range> {
|
|
||||||
let first = typeConverters.Position.fromLocation(edits[0].start);
|
|
||||||
let firstEdit = edits[0];
|
|
||||||
let lastEdit = edits[0];
|
|
||||||
let last = typeConverters.Position.fromLocation(edits[0].start);
|
|
||||||
for (const edit of edits) {
|
|
||||||
const start = typeConverters.Position.fromLocation(edit.start);
|
|
||||||
const end = typeConverters.Position.fromLocation(edit.end);
|
|
||||||
if (start.compareTo(first) < 0) {
|
|
||||||
first = start;
|
|
||||||
firstEdit = edit;
|
|
||||||
}
|
|
||||||
if (end.compareTo(last) > 0) {
|
|
||||||
last = end;
|
|
||||||
lastEdit = edit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const text = document.getText();
|
|
||||||
const startIndex = text.indexOf(firstEdit.newText);
|
|
||||||
const start = startIndex > -1 ? document.positionAt(startIndex) : first;
|
|
||||||
const endIndex = text.lastIndexOf(lastEdit.newText);
|
|
||||||
const end =
|
|
||||||
endIndex > -1
|
|
||||||
? document.positionAt(endIndex + lastEdit.newText.length)
|
|
||||||
: last;
|
|
||||||
const expandEnd = await findScopeEndLineFromNavTree(
|
|
||||||
client,
|
|
||||||
document,
|
|
||||||
end.line
|
|
||||||
);
|
|
||||||
return new vscode.Range(start, expandEnd?.end ?? end);
|
|
||||||
}
|
|
||||||
@@ -16,9 +16,5 @@ export const fixImport = 'import';
|
|||||||
export const forgottenThisPropertyAccess = 'forgottenThisPropertyAccess';
|
export const forgottenThisPropertyAccess = 'forgottenThisPropertyAccess';
|
||||||
export const removeUnnecessaryAwait = 'removeUnnecessaryAwait';
|
export const removeUnnecessaryAwait = 'removeUnnecessaryAwait';
|
||||||
export const spelling = 'spelling';
|
export const spelling = 'spelling';
|
||||||
export const inferFromUsage = 'inferFromUsage';
|
|
||||||
export const addNameToNamelessParameter = 'addNameToNamelessParameter';
|
|
||||||
export const fixMissingFunctionDeclaration = 'fixMissingFunctionDeclaration';
|
|
||||||
export const fixClassDoesntImplementInheritedAbstractMember = 'fixClassDoesntImplementInheritedAbstractMember';
|
|
||||||
export const unreachableCode = 'fixUnreachableCode';
|
export const unreachableCode = 'fixUnreachableCode';
|
||||||
export const unusedIdentifier = 'unusedIdentifier';
|
export const unusedIdentifier = 'unusedIdentifier';
|
||||||
|
|||||||
@@ -11,6 +11,6 @@
|
|||||||
"include": [
|
"include": [
|
||||||
"src/**/*",
|
"src/**/*",
|
||||||
"../../src/vscode-dts/vscode.d.ts",
|
"../../src/vscode-dts/vscode.d.ts",
|
||||||
"../../src/vscode-dts/vscode.proposed.workspaceTrust.d.ts"
|
"../../src/vscode-dts/vscode.proposed.workspaceTrust.d.ts",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user