From b7fad0df6d5ff051fb810a44b7081daaefd2c0a6 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 22 Sep 2023 16:05:04 +0200 Subject: [PATCH] Revert "Copilot-based TS refactors" --- .../typescript-language-features/package.json | 57 +------ .../package.nls.json | 11 +- .../src/languageFeatures/quickFix.ts | 134 +++++++--------- .../src/languageFeatures/refactor.ts | 58 ++----- .../src/languageFeatures/util/copilot.ts | 147 ------------------ .../src/tsServer/protocol/fixNames.ts | 4 - .../tsconfig.json | 2 +- 7 files changed, 84 insertions(+), 329 deletions(-) delete mode 100644 extensions/typescript-language-features/src/languageFeatures/util/copilot.ts diff --git a/extensions/typescript-language-features/package.json b/extensions/typescript-language-features/package.json index 607bb3aabe0..bf132f58168 100644 --- a/extensions/typescript-language-features/package.json +++ b/extensions/typescript-language-features/package.json @@ -145,58 +145,11 @@ "title": "%configuration.typescript%", "order": 20, "properties": { - "typescript.experimental.aiCodeActions": { - "type": "object", - "default": {}, - "description": "%typescript.experimental.aiCodeActions%", - "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.experimental.aiQuickFix": { + "type": "boolean", + "default": false, + "description": "%typescript.experimental.aiQuickFix%", + "scope": "resource" }, "typescript.tsdk": { "type": "string", diff --git a/extensions/typescript-language-features/package.nls.json b/extensions/typescript-language-features/package.nls.json index 355f8cbddfe..dc902befdb2 100644 --- a/extensions/typescript-language-features/package.nls.json +++ b/extensions/typescript-language-features/package.nls.json @@ -8,16 +8,7 @@ "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.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.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.experimental.aiQuickFix": "Enable/disable AI-assisted quick fixes. 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.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.", diff --git a/extensions/typescript-language-features/src/languageFeatures/quickFix.ts b/extensions/typescript-language-features/src/languageFeatures/quickFix.ts index 23d13ba6b93..bc2442046aa 100644 --- a/extensions/typescript-language-features/src/languageFeatures/quickFix.ts +++ b/extensions/typescript-language-features/src/languageFeatures/quickFix.ts @@ -18,7 +18,6 @@ import { DiagnosticsManager } from './diagnostics'; import FileConfigurationManager from './fileConfigurationManager'; import { applyCodeActionCommands, getEditForCodeAction } from './util/codeAction'; import { conditionalRegistration, requireSomeCapability } from './util/dependentRegistration'; -import { Expand, EditorChatFollowUp, CompositeCommand } from './util/copilot'; type ApplyCodeActionCommand_args = { readonly document: vscode.TextDocument; @@ -27,6 +26,42 @@ type ApplyCodeActionCommand_args = { 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 { public static readonly ID = '_typescript.applyCodeActionCommand'; public readonly id = ApplyCodeActionCommand.ID; @@ -220,10 +255,8 @@ class TypeScriptQuickFixProvider implements vscode.CodeActionProvider{ - 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 - }; - } + tsAction: Proto.CodeFixAction + ): VsCodeCodeAction { + const aiQuickFixEnabled = vscode.workspace.getConfiguration('typescript').get('experimental.aiQuickFix'); + let followupAction: Command | undefined; + if (aiQuickFixEnabled && tsAction.fixName === fixNames.classIncorrectlyImplementsInterface) { + followupAction = new EditorChatFollowUp('Implement the class using the interface', document, diagnostic.range, this.client); } - const codeAction = new VsCodeCodeAction(action, title, vscode.CodeActionKind.QuickFix); - codeAction.edit = getEditForCodeAction(this.client, action); + const codeAction = new VsCodeCodeAction(tsAction, tsAction.description, vscode.CodeActionKind.QuickFix); + codeAction.edit = getEditForCodeAction(this.client, tsAction); codeAction.diagnostics = [diagnostic]; codeAction.command = { command: ApplyCodeActionCommand.ID, - arguments: [{ action: action, diagnostic, document }], + arguments: [{ action: tsAction, diagnostic, document, followupAction }], title: '' }; - if (expand && message !== undefined) { - codeAction.command = { - command: CompositeCommand.ID, - title: '', - arguments: [codeAction.command, { - command: EditorChatFollowUp.ID, - title: '', - arguments: [{ - message, - expand, - document - }], - }], - }; - } - actions.push(codeAction); - return actions; + return codeAction; } private addFixAllForTsCodeAction( diff --git a/extensions/typescript-language-features/src/languageFeatures/refactor.ts b/extensions/typescript-language-features/src/languageFeatures/refactor.ts index 718ebe447f7..ae36fbffb22 100644 --- a/extensions/typescript-language-features/src/languageFeatures/refactor.ts +++ b/extensions/typescript-language-features/src/languageFeatures/refactor.ts @@ -20,7 +20,6 @@ import { coalesce } from '../utils/arrays'; import { nulToken } from '../utils/cancellation'; import FormattingOptionsManager from './fileConfigurationManager'; import { conditionalRegistration, requireSomeCapability } from './util/dependentRegistration'; -import { EditorChatFollowUp, CompositeCommand } from './util/copilot'; function toWorkspaceEdit(client: ITypeScriptServiceClient, edits: readonly Proto.FileCodeEdits[]): 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 { + for (const command of commands) { + await vscode.commands.executeCommand(command.command, ...(command.arguments ?? [])); + } + } +} + namespace DidApplyRefactoringCommand { export interface Args { readonly action: string; @@ -345,17 +355,15 @@ class InlinedCodeAction extends vscode.CodeAction { public readonly refactor: Proto.ApplicableRefactorInfo, public readonly action: Proto.RefactorActionInfo, 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(title, InlinedCodeAction.getKind(action)); + super(action.description, InlinedCodeAction.getKind(action)); if (action.notApplicableReason) { this.disabled = { reason: action.notApplicableReason }; } this.command = { - title, + title: action.description, command: DidApplyRefactoringCommand.ID, arguments: [{ action: action.name }], }; @@ -387,21 +395,18 @@ class InlinedCodeAction extends vscode.CodeAction { if (response.body.renameLocation) { // Disable renames in interactive playground https://github.com/microsoft/vscode/issues/75137 if (this.document.uri.scheme !== fileSchemes.walkThroughSnippet) { - if (this.copilotRename && this.command) { - this.command.title = 'Copilot: ' + this.command.title; - } this.command = { command: CompositeCommand.ID, title: '', arguments: coalesce([ this.command, - this.copilotRename ? this.copilotRename(response.body) : { + { command: 'editor.action.rename', arguments: [[ this.document.uri, typeConverters.Position.fromLocation(response.body.renameLocation) ]] - }, + } ]) }; } @@ -451,6 +456,7 @@ class SelectCodeAction extends vscode.CodeAction { }; } } + type TsCodeAction = InlinedCodeAction | MoveToFileCodeAction | SelectCodeAction; class TypeScriptRefactorProvider implements vscode.CodeActionProvider { @@ -465,7 +471,6 @@ class TypeScriptRefactorProvider implements vscode.CodeActionProvider vscode.Command) | undefined; - 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: [{ - 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 = new InlinedCodeAction(this.client, document, refactor, action, rangeOrSelection); } codeAction.isPreferred = TypeScriptRefactorProvider.isPreferred(action, allActions); diff --git a/extensions/typescript-language-features/src/languageFeatures/util/copilot.ts b/extensions/typescript-language-features/src/languageFeatures/util/copilot.ts deleted file mode 100644 index 79c668db884..00000000000 --- a/extensions/typescript-language-features/src/languageFeatures/util/copilot.ts +++ /dev/null @@ -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 { - 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 { - 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); -} diff --git a/extensions/typescript-language-features/src/tsServer/protocol/fixNames.ts b/extensions/typescript-language-features/src/tsServer/protocol/fixNames.ts index c4234010302..82df3b9cc46 100644 --- a/extensions/typescript-language-features/src/tsServer/protocol/fixNames.ts +++ b/extensions/typescript-language-features/src/tsServer/protocol/fixNames.ts @@ -16,9 +16,5 @@ export const fixImport = 'import'; export const forgottenThisPropertyAccess = 'forgottenThisPropertyAccess'; export const removeUnnecessaryAwait = 'removeUnnecessaryAwait'; 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 unusedIdentifier = 'unusedIdentifier'; diff --git a/extensions/typescript-language-features/tsconfig.json b/extensions/typescript-language-features/tsconfig.json index 73957dde932..59169c18bfd 100644 --- a/extensions/typescript-language-features/tsconfig.json +++ b/extensions/typescript-language-features/tsconfig.json @@ -11,6 +11,6 @@ "include": [ "src/**/*", "../../src/vscode-dts/vscode.d.ts", - "../../src/vscode-dts/vscode.proposed.workspaceTrust.d.ts" + "../../src/vscode-dts/vscode.proposed.workspaceTrust.d.ts", ] }