diff --git a/src/vs/editor/contrib/codeAction/codeAction.ts b/src/vs/editor/contrib/codeAction/codeAction.ts index 93873f17ffbcd..f706070a89bb7 100644 --- a/src/vs/editor/contrib/codeAction/codeAction.ts +++ b/src/vs/editor/contrib/codeAction/codeAction.ts @@ -117,8 +117,8 @@ function getCodeActionProviders( } registerLanguageCommand('_executeCodeActionProvider', async function (accessor, args): Promise> { - const { resource, range, kind } = args; - if (!(resource instanceof URI) || !Range.isIRange(range)) { + const { resource, rangeOrSelection, kind } = args; + if (!(resource instanceof URI)) { throw illegalArgument(); } @@ -127,9 +127,19 @@ registerLanguageCommand('_executeCodeActionProvider', async function (accessor, throw illegalArgument(); } + const validatedRangeOrSelection = Selection.isISelection(rangeOrSelection) + ? Selection.liftSelection(rangeOrSelection) + : Range.isIRange(rangeOrSelection) + ? model.validateRange(rangeOrSelection) + : undefined; + + if (!validatedRangeOrSelection) { + throw illegalArgument(); + } + const codeActionSet = await getCodeActions( model, - model.validateRange(range), + validatedRangeOrSelection, { type: 'manual', filter: { includeSourceActions: true, kind: kind && kind.value ? new CodeActionKind(kind.value) : undefined } }, CancellationToken.None); diff --git a/src/vs/workbench/api/common/extHostApiCommands.ts b/src/vs/workbench/api/common/extHostApiCommands.ts index ff2a106f427f2..d84da000c3fdd 100644 --- a/src/vs/workbench/api/common/extHostApiCommands.ts +++ b/src/vs/workbench/api/common/extHostApiCommands.ts @@ -135,7 +135,7 @@ export class ExtHostApiCommands { description: 'Execute code action provider.', args: [ { name: 'uri', description: 'Uri of a text document', constraint: URI }, - { name: 'range', description: 'Range in a text document', constraint: types.Range }, + { name: 'rangeOrSelection', description: 'Range in a text document. Some refactoring provider requires Selection object.', constraint: types.Range }, { name: 'kind', description: '(optional) Code action kind to return code actions for', constraint: (value: any) => !value || typeof value.value === 'string' }, ], returns: 'A promise that resolves to an array of Command-instances.' @@ -480,10 +480,12 @@ export class ExtHostApiCommands { }); } - private _executeCodeActionProvider(resource: URI, range: types.Range, kind?: string): Promise<(vscode.CodeAction | vscode.Command | undefined)[] | undefined> { + private _executeCodeActionProvider(resource: URI, rangeOrSelection: types.Range | types.Selection, kind?: string): Promise<(vscode.CodeAction | vscode.Command | undefined)[] | undefined> { const args = { resource, - range: typeConverters.Range.from(range), + rangeOrSelection: types.Selection.isSelection(rangeOrSelection) + ? typeConverters.Selection.from(rangeOrSelection) + : typeConverters.Range.from(rangeOrSelection), kind }; return this._commands.executeCommand('_executeCodeActionProvider', args) @@ -504,6 +506,7 @@ export class ExtHostApiCommands { if (codeAction.command) { ret.command = this._commands.converter.fromInternal(codeAction.command); } + ret.isPreferred = codeAction.isPreferred; return ret; } })); diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 9b0cd9c9cc88d..e3c30b6e3a070 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -1075,6 +1075,8 @@ export class CodeAction { kind?: CodeActionKind; + isPreferred?: boolean; + constructor(title: string, kind?: CodeActionKind) { this.title = title; this.kind = kind; diff --git a/src/vs/workbench/test/electron-browser/api/extHostApiCommands.test.ts b/src/vs/workbench/test/electron-browser/api/extHostApiCommands.test.ts index 02dbd2c785243..f2c5b0043fe9a 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostApiCommands.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostApiCommands.test.ts @@ -631,6 +631,61 @@ suite('ExtHostLanguageFeatureCommands', function () { }); }); + test('vscode.executeCodeActionProvider passes Range to provider although Selection is passed in #77997', function () { + disposables.push(extHost.registerCodeActionProvider(nullExtensionDescription, defaultSelector, { + provideCodeActions(document, rangeOrSelection): vscode.CodeAction[] { + return [{ + command: { + arguments: [document, rangeOrSelection], + command: 'command', + title: 'command_title', + }, + kind: types.CodeActionKind.Empty.append('foo'), + title: 'title', + }]; + } + })); + + const selection = new types.Selection(0, 0, 1, 1); + + return rpcProtocol.sync().then(() => { + return commands.executeCommand('vscode.executeCodeActionProvider', model.uri, selection).then(value => { + assert.equal(value.length, 1); + const [first] = value; + assert.ok(first.command); + assert.ok(first.command!.arguments![1] instanceof types.Selection); + assert.ok(first.command!.arguments![1].isEqual(selection)); + }); + }); + }); + + test('vscode.executeCodeActionProvider results seem to be missing their `isPreferred` property #78098', function () { + disposables.push(extHost.registerCodeActionProvider(nullExtensionDescription, defaultSelector, { + provideCodeActions(document, rangeOrSelection): vscode.CodeAction[] { + return [{ + command: { + arguments: [document, rangeOrSelection], + command: 'command', + title: 'command_title', + }, + kind: types.CodeActionKind.Empty.append('foo'), + title: 'title', + isPreferred: true + }]; + } + })); + + const selection = new types.Selection(0, 0, 1, 1); + + return rpcProtocol.sync().then(() => { + return commands.executeCommand('vscode.executeCodeActionProvider', model.uri, selection).then(value => { + assert.equal(value.length, 1); + const [first] = value; + assert.equal(first.isPreferred, true); + }); + }); + }); + // --- code lens test('CodeLens, back and forth', function () {