未验证 提交 be88547c 编写于 作者: M Matt Bierner 提交者: GitHub

Prototyping new CodeAction API (#36316)

* Add CodeAction Type

Adds skeleton on a new CodeActionType and allows codeActionProvider to return either `Command`s or `CodeAction`s

Move proposed CodeAction API to proposed and try using it in TS

Split CodeAction into quickfix and refactoring classes

Update proposed interface

Update for new API

Adding basic docs

* Support workspace edits and text edits in codeactions

* Remove placeholders

* Resolving conflicts and making PR suggested changes

* Fix quick fix test

* Revert change to only use `CodeAction` instead of `CodeAction | Command` in modes since this will break `vscode.executeCodeActionProvider`
上级 8a79656f
......@@ -74,7 +74,7 @@ declare module monaco.languages {
#includeAll(vs/editor/standalone/browser/standaloneLanguages;modes.=>;editorCommon.=>editor.;IMarkerData=>editor.IMarkerData):
#includeAll(vs/editor/common/modes/languageConfiguration):
#includeAll(vs/editor/common/modes;editorCommon.IRange=>IRange;editorCommon.IPosition=>IPosition;editorCommon.=>editor.):
#includeAll(vs/editor/common/modes;editorCommon.IRange=>IRange;editorCommon.IPosition=>IPosition;editorCommon.=>editor.;IMarkerData=>editor.IMarkerData):
#include(vs/editor/common/services/modeService): ILanguageExtensionPoint
#includeAll(vs/editor/standalone/common/monarch/monarchTypes):
......
......@@ -9,44 +9,36 @@ import * as Proto from '../protocol';
import { ITypeScriptServiceClient } from '../typescriptService';
import { vsRangeToTsFileRange } from '../utils/convert';
import FormattingConfigurationManager from './formattingConfigurationManager';
import { applyCodeAction } from '../utils/codeAction';
import { CommandManager, Command } from '../utils/commandManager';
import { getEditForCodeAction } from '../utils/codeAction';
interface NumberSet {
[key: number]: boolean;
}
class ApplyCodeActionCommand implements Command {
public static readonly ID: string = '_typescript.applyCodeAction';
public readonly id: string = ApplyCodeActionCommand.ID;
constructor(
private readonly client: ITypeScriptServiceClient
) { }
execute(action: Proto.CodeAction, file: string): void {
applyCodeAction(this.client, action, file);
}
}
export default class TypeScriptCodeActionProvider implements vscode.CodeActionProvider {
private _supportedCodeActions?: Thenable<NumberSet>;
constructor(
private readonly client: ITypeScriptServiceClient,
private readonly formattingConfigurationManager: FormattingConfigurationManager,
commandManager: CommandManager
private readonly formattingConfigurationManager: FormattingConfigurationManager
) { }
public provideCodeActions(
_document: vscode.TextDocument,
_range: vscode.Range,
_context: vscode.CodeActionContext,
_token: vscode.CancellationToken
) {
commandManager.register(new ApplyCodeActionCommand(this.client));
// Uses provideCodeActions2 instead
return [];
}
public async provideCodeActions(
public async provideCodeActions2(
document: vscode.TextDocument,
range: vscode.Range,
context: vscode.CodeActionContext,
token: vscode.CancellationToken
): Promise<vscode.Command[]> {
): Promise<vscode.CodeAction[]> {
if (!this.client.apiVersion.has213Features()) {
return [];
}
......@@ -68,7 +60,7 @@ export default class TypeScriptCodeActionProvider implements vscode.CodeActionPr
errorCodes: Array.from(supportedActions)
};
const response = await this.client.execute('getCodeFixes', args, token);
return (response.body || []).map(action => this.getCommandForAction(action, file));
return (response.body || []).map(action => this.getCommandForAction(action));
}
private get supportedCodeActions(): Thenable<NumberSet> {
......@@ -92,11 +84,11 @@ export default class TypeScriptCodeActionProvider implements vscode.CodeActionPr
.filter(code => supportedActions[code]));
}
private getCommandForAction(action: Proto.CodeAction, file: string): vscode.Command {
private getCommandForAction(action: Proto.CodeAction): vscode.CodeAction {
return {
title: action.description,
command: ApplyCodeActionCommand.ID,
arguments: [action, file]
edits: getEditForCodeAction(this.client, action),
diagnostics: []
};
}
}
\ No newline at end of file
......@@ -106,12 +106,17 @@ export default class TypeScriptRefactorProvider implements vscode.CodeActionProv
commandManager.register(new SelectRefactorCommand(doRefactoringCommand));
}
public async provideCodeActions(
public async provideCodeActions() {
// Uses provideCodeActions2 instead
return [];
}
public async provideCodeActions2(
document: vscode.TextDocument,
range: vscode.Range,
_context: vscode.CodeActionContext,
token: vscode.CancellationToken
): Promise<vscode.Command[]> {
): Promise<vscode.CodeAction[]> {
if (!this.client.apiVersion.has240Features()) {
return [];
}
......@@ -128,20 +133,26 @@ export default class TypeScriptRefactorProvider implements vscode.CodeActionProv
return [];
}
const actions: vscode.Command[] = [];
const actions: vscode.CodeAction[] = [];
for (const info of response.body) {
if (info.inlineable === false) {
actions.push({
title: info.description,
command: SelectRefactorCommand.ID,
arguments: [document, file, info, range]
command: {
title: info.description,
command: SelectRefactorCommand.ID,
arguments: [document, file, info, range]
}
});
} else {
for (const action of info.actions) {
actions.push({
title: action.description,
command: ApplyRefactoringCommand.ID,
arguments: [document, file, info.name, action.name, range]
title: info.description,
command: {
title: info.description,
command: ApplyRefactoringCommand.ID,
arguments: [document, file, info.name, action.name, range]
}
});
}
}
......
......@@ -317,7 +317,7 @@ class LanguageProvider {
this.disposables.push(languages.registerDocumentSymbolProvider(selector, new (await import('./features/documentSymbolProvider')).default(client)));
this.disposables.push(languages.registerSignatureHelpProvider(selector, new (await import('./features/signatureHelpProvider')).default(client), '(', ','));
this.disposables.push(languages.registerRenameProvider(selector, new (await import('./features/renameProvider')).default(client)));
this.disposables.push(languages.registerCodeActionsProvider(selector, new (await import('./features/codeActionProvider')).default(client, this.formattingOptionsManager, this.commandManager)));
this.disposables.push(languages.registerCodeActionsProvider(selector, new (await import('./features/codeActionProvider')).default(client, this.formattingOptionsManager)));
this.disposables.push(languages.registerCodeActionsProvider(selector, new (await import('./features/refactorProvider')).default(client, this.formattingOptionsManager, this.commandManager)));
this.registerVersionDependentProviders();
......
......@@ -8,12 +8,10 @@ import * as Proto from '../protocol';
import { tsTextSpanToVsRange } from './convert';
import { ITypeScriptServiceClient } from '../typescriptService';
export async function applyCodeAction(
export function getEditForCodeAction(
client: ITypeScriptServiceClient,
action: Proto.CodeAction,
file: string
): Promise<boolean> {
action: Proto.CodeAction
): WorkspaceEdit | undefined {
if (action.changes && action.changes.length) {
const workspaceEdit = new WorkspaceEdit();
for (const change of action.changes) {
......@@ -24,11 +22,30 @@ export async function applyCodeAction(
}
}
return workspaceEdit;
}
return undefined;
}
export async function applyCodeAction(
client: ITypeScriptServiceClient,
action: Proto.CodeAction,
file: string
): Promise<boolean> {
const workspaceEdit = getEditForCodeAction(client, action);
if (workspaceEdit) {
if (!(await workspace.applyEdit(workspaceEdit))) {
return false;
}
}
return applyCodeActionCommands(client, action, file);
}
export async function applyCodeActionCommands(
client: ITypeScriptServiceClient,
action: Proto.CodeAction,
file: string
): Promise<boolean> {
if (action.commands && action.commands.length) {
for (const command of action.commands) {
const response = await client.execute('applyCodeActionCommand', { file, command });
......
......@@ -16,6 +16,7 @@ import { Range, IRange } from 'vs/editor/common/core/range';
import Event from 'vs/base/common/event';
import { TokenizationRegistryImpl } from 'vs/editor/common/modes/tokenizationRegistry';
import { Color } from 'vs/base/common/color';
import { IMarkerData } from 'vs/platform/markers/common/markers';
/**
* Open ended enum at runtime
......@@ -275,6 +276,13 @@ export interface ISuggestSupport {
resolveCompletionItem?(model: editorCommon.IModel, position: Position, item: ISuggestion, token: CancellationToken): ISuggestion | Thenable<ISuggestion>;
}
export interface CodeAction {
title: string;
command?: Command;
edits?: WorkspaceEdit;
diagnostics?: IMarkerData[];
}
/**
* The code action interface defines the contract between extensions and
* the [light bulb](https://code.visualstudio.com/docs/editor/editingevolved#_code-action) feature.
......@@ -284,7 +292,7 @@ export interface CodeActionProvider {
/**
* Provide commands for the given document and range.
*/
provideCodeActions(model: editorCommon.IReadOnlyModel, range: Range, token: CancellationToken): Command[] | Thenable<Command[]>;
provideCodeActions(model: editorCommon.IReadOnlyModel, range: Range, token: CancellationToken): (CodeAction | Command)[] | Thenable<(Command | CodeAction)[]>;
}
/**
......
......@@ -7,16 +7,16 @@
import URI from 'vs/base/common/uri';
import { IReadOnlyModel } from 'vs/editor/common/editorCommon';
import { Range } from 'vs/editor/common/core/range';
import { Command, CodeActionProviderRegistry } from 'vs/editor/common/modes';
import { CodeActionProviderRegistry, CodeAction, Command } from 'vs/editor/common/modes';
import { asWinJsPromise } from 'vs/base/common/async';
import { TPromise } from 'vs/base/common/winjs.base';
import { onUnexpectedExternalError, illegalArgument } from 'vs/base/common/errors';
import { IModelService } from 'vs/editor/common/services/modelService';
import { CommonEditorRegistry } from 'vs/editor/common/editorCommonExtensions';
export function getCodeActions(model: IReadOnlyModel, range: Range): TPromise<Command[]> {
export function getCodeActions(model: IReadOnlyModel, range: Range): TPromise<(CodeAction | Command)[]> {
const allResults: Command[] = [];
const allResults: (CodeAction | Command)[] = [];
const promises = CodeActionProviderRegistry.all(model).map(support => {
return asWinJsPromise(token => support.provideCodeActions(model, range, token)).then(result => {
if (Array.isArray(result)) {
......
......@@ -20,6 +20,11 @@ import { registerEditorContribution } from 'vs/editor/browser/editorBrowserExten
import { QuickFixContextMenu } from './quickFixWidget';
import { LightBulbWidget } from './lightBulbWidget';
import { QuickFixModel, QuickFixComputeEvent } from './quickFixModel';
import { TPromise } from 'vs/base/common/winjs.base';
import { CodeAction } from 'vs/editor/common/modes';
import { createBulkEdit } from 'vs/editor/common/services/bulkEdit';
import { IFileService } from 'vs/platform/files/common/files';
import { ITextModelService } from 'vs/editor/common/services/resolverService';
export class QuickFixController implements IEditorContribution {
......@@ -38,13 +43,15 @@ export class QuickFixController implements IEditorContribution {
constructor(editor: ICodeEditor,
@IMarkerService markerService: IMarkerService,
@IContextKeyService contextKeyService: IContextKeyService,
@ICommandService commandService: ICommandService,
@ICommandService private readonly _commandService: ICommandService,
@IContextMenuService contextMenuService: IContextMenuService,
@IKeybindingService private _keybindingService: IKeybindingService
@IKeybindingService private readonly _keybindingService: IKeybindingService,
@ITextModelService private readonly _textModelService: ITextModelService,
@IFileService private _fileService: IFileService
) {
this._editor = editor;
this._model = new QuickFixModel(this._editor, markerService);
this._quickFixContextMenu = new QuickFixContextMenu(editor, contextMenuService, commandService);
this._quickFixContextMenu = new QuickFixContextMenu(editor, contextMenuService, action => this._onApplyCodeAction(action));
this._lightBulbWidget = new LightBulbWidget(editor);
this._updateLightBulbTitle();
......@@ -102,6 +109,18 @@ export class QuickFixController implements IEditorContribution {
}
this._lightBulbWidget.title = title;
}
private async _onApplyCodeAction(action: CodeAction): TPromise<void> {
if (action.edits) {
const edit = createBulkEdit(this._textModelService, this._editor, this._fileService);
edit.add(action.edits.edits);
await edit.finish();
}
if (action.command) {
await this._commandService.executeCommand(action.command.id, ...action.command.arguments);
}
}
}
export class QuickFixAction extends EditorAction {
......
......@@ -12,7 +12,7 @@ import { IMarkerService } from 'vs/platform/markers/common/markers';
import { Range } from 'vs/editor/common/core/range';
import { Selection } from 'vs/editor/common/core/selection';
import { ICommonCodeEditor } from 'vs/editor/common/editorCommon';
import { CodeActionProviderRegistry, Command } from 'vs/editor/common/modes';
import { CodeActionProviderRegistry, CodeAction, Command } from 'vs/editor/common/modes';
import { getCodeActions } from './quickFix';
import { Position } from 'vs/editor/common/core/position';
......@@ -112,11 +112,22 @@ export class QuickFixOracle {
const model = this._editor.getModel();
const range = model.validateRange(rangeOrSelection);
const position = rangeOrSelection instanceof Selection ? rangeOrSelection.getPosition() : rangeOrSelection.getStartPosition();
const fixes = getCodeActions(model, range).then(actions =>
actions.map(action => {
if ('id' in action) {
// must be a command
const command = action as Command;
return { title: command.title, command: command } as CodeAction;
}
return action;
}));
this._signalChange({
type,
range,
position,
fixes: getCodeActions(model, range)
fixes
});
}
}
......@@ -126,7 +137,7 @@ export interface QuickFixComputeEvent {
type: 'auto' | 'manual';
range: Range;
position: Position;
fixes: TPromise<Command[]>;
fixes: TPromise<CodeAction[]>;
}
export class QuickFixModel {
......
......@@ -10,38 +10,33 @@ import { always } from 'vs/base/common/async';
import { getDomNodePagePosition } from 'vs/base/browser/dom';
import { Position } from 'vs/editor/common/core/position';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { Command } from 'vs/editor/common/modes';
import { CodeAction } from 'vs/editor/common/modes';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { Action } from 'vs/base/common/actions';
import Event, { Emitter } from 'vs/base/common/event';
import { ScrollType } from 'vs/editor/common/editorCommon';
export class QuickFixContextMenu {
private _editor: ICodeEditor;
private _contextMenuService: IContextMenuService;
private _commandService: ICommandService;
private _visible: boolean;
private _onDidExecuteCodeAction = new Emitter<void>();
readonly onDidExecuteCodeAction: Event<void> = this._onDidExecuteCodeAction.event;
constructor(editor: ICodeEditor, contextMenuService: IContextMenuService, commandService: ICommandService) {
this._editor = editor;
this._contextMenuService = contextMenuService;
this._commandService = commandService;
}
constructor(
private readonly _editor: ICodeEditor,
private readonly _contextMenuService: IContextMenuService,
private readonly _onApplyCodeAction: (action: CodeAction) => TPromise<any>
) { }
show(fixes: TPromise<Command[]>, at: { x: number; y: number } | Position) {
show(fixes: TPromise<CodeAction[]>, at: { x: number; y: number } | Position) {
const actions = fixes.then(value => {
return value.map(command => {
return new Action(command.id, command.title, undefined, true, () => {
return value.map(action => {
return new Action(action.command ? action.command.id : action.title, action.title, undefined, true, () => {
return always(
this._commandService.executeCommand(command.id, ...command.arguments),
() => this._onDidExecuteCodeAction.fire(undefined)
);
this._onApplyCodeAction(action),
() => this._onDidExecuteCodeAction.fire(undefined));
});
});
});
......
......@@ -329,7 +329,7 @@ export function registerCodeLensProvider(languageId: string, provider: modes.Cod
*/
export function registerCodeActionProvider(languageId: string, provider: CodeActionProvider): IDisposable {
return modes.CodeActionProviderRegistry.register(languageId, {
provideCodeActions: (model: editorCommon.IReadOnlyModel, range: Range, token: CancellationToken): modes.Command[] | Thenable<modes.Command[]> => {
provideCodeActions: (model: editorCommon.IReadOnlyModel, range: Range, token: CancellationToken): (modes.Command | modes.CodeAction)[] | Thenable<(modes.Command | modes.CodeAction)[]> => {
let markers = StaticServices.markerService.get().read({ resource: model.uri }).filter(m => {
return Range.areIntersectingOrTouching(m, range);
});
......@@ -411,7 +411,7 @@ export interface CodeActionProvider {
/**
* Provide commands for the given document and range.
*/
provideCodeActions(model: editorCommon.IReadOnlyModel, range: Range, context: CodeActionContext, token: CancellationToken): modes.Command[] | Thenable<modes.Command[]>;
provideCodeActions(model: editorCommon.IReadOnlyModel, range: Range, context: CodeActionContext, token: CancellationToken): (modes.Command | modes.CodeAction)[] | Thenable<(modes.Command | modes.CodeAction)[]>;
}
/**
......
......@@ -4095,7 +4095,7 @@ declare module monaco.languages {
/**
* Provide commands for the given document and range.
*/
provideCodeActions(model: editor.IReadOnlyModel, range: Range, context: CodeActionContext, token: CancellationToken): Command[] | Thenable<Command[]>;
provideCodeActions(model: editor.IReadOnlyModel, range: Range, context: CodeActionContext, token: CancellationToken): (Command | CodeAction)[] | Thenable<(Command | CodeAction)[]>;
}
/**
......@@ -4531,6 +4531,13 @@ declare module monaco.languages {
TriggerCharacter = 1,
}
export interface CodeAction {
title: string;
command?: Command;
edits?: WorkspaceEdit;
diagnostics?: editor.IMarkerData[];
}
/**
* Represents a parameter of a callable-signature. A parameter can
* have a label and a doc-comment.
......
......@@ -1799,7 +1799,6 @@ declare module 'vscode' {
* a [code action](#CodeActionProvider.provideCodeActions) is run.
*/
export interface CodeActionContext {
/**
* An array of diagnostics.
*/
......
......@@ -224,4 +224,54 @@ declare module 'vscode' {
*/
export let console: DebugConsole;
}
/**
* Represents an action that can be performed in code.
*
* Shown using the [light bulb](https://code.visualstudio.com/docs/editor/editingevolved#_code-action)
*/
export class CodeAction {
/**
* Label used to identify the code action in UI.
*/
title: string;
/**
* Optional command that performs the code action.
*
* Executed after `edits` if any edits are provided. Either `command` or `edits` must be provided for a `CodeAction`.
*/
command?: Command;
/**
* Optional edit that performs the code action.
*
* Either `command` or `edits` must be provided for a `CodeAction`.
*/
edits?: TextEdit[] | WorkspaceEdit;
/**
* Diagnostics that this code action resolves.
*/
diagnostics?: Diagnostic[];
constructor(title: string, edits?: TextEdit[] | WorkspaceEdit);
}
export interface CodeActionProvider {
/**
* Provide commands for the given document and range.
*
* If implemented, overrides `provideCodeActions`
*
* @param document The document in which the command was invoked.
* @param range The range for which the command was invoked.
* @param context Context carrying additional information.
* @param token A cancellation token.
* @return An array of commands, quick fixes, or refactorings or a thenable of such. The lack of a result can be
* signaled by returning `undefined`, `null`, or an empty array.
*/
provideCodeActions2?(document: TextDocument, range: Range, context: CodeActionContext, token: CancellationToken): ProviderResult<(Command | CodeAction)[]>;
}
}
......@@ -163,7 +163,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
$registerQuickFixSupport(handle: number, selector: vscode.DocumentSelector): TPromise<any> {
this._registrations[handle] = modes.CodeActionProviderRegistry.register(selector, <modes.CodeActionProvider>{
provideCodeActions: (model: IReadOnlyModel, range: EditorRange, token: CancellationToken): Thenable<modes.Command[]> => {
provideCodeActions: (model: IReadOnlyModel, range: EditorRange, token: CancellationToken): Thenable<(modes.Command | modes.CodeAction)[]> => {
return this._heapService.trackRecursive(wireCancellationToken(token, this._proxy.$provideCodeActions(handle, model.uri, range)));
}
});
......
......@@ -531,6 +531,7 @@ export function createApiFactory(
debug,
// types
CancellationTokenSource: CancellationTokenSource,
CodeAction: extHostTypes.CodeAction,
CodeLens: extHostTypes.CodeLens,
Color: extHostTypes.Color,
ColorPresentation: extHostTypes.ColorPresentation,
......
......@@ -581,7 +581,7 @@ export interface ExtHostLanguageFeaturesShape {
$provideHover(handle: number, resource: URI, position: IPosition): TPromise<modes.Hover>;
$provideDocumentHighlights(handle: number, resource: URI, position: IPosition): TPromise<modes.DocumentHighlight[]>;
$provideReferences(handle: number, resource: URI, position: IPosition, context: modes.ReferenceContext): TPromise<modes.Location[]>;
$provideCodeActions(handle: number, resource: URI, range: IRange): TPromise<modes.Command[]>;
$provideCodeActions(handle: number, resource: URI, range: IRange): TPromise<(modes.Command | modes.CodeAction)[]>;
$provideDocumentFormattingEdits(handle: number, resource: URI, options: modes.FormattingOptions): TPromise<editorCommon.ISingleEditOperation[]>;
$provideDocumentRangeFormattingEdits(handle: number, resource: URI, range: IRange, options: modes.FormattingOptions): TPromise<editorCommon.ISingleEditOperation[]>;
$provideOnTypeFormattingEdits(handle: number, resource: URI, position: IPosition, ch: string, options: modes.FormattingOptions): TPromise<editorCommon.ISingleEditOperation[]>;
......
......@@ -111,7 +111,7 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection {
orderLoop: for (let i = 0; i < 4; i++) {
for (let diagnostic of diagnostics) {
if (diagnostic.severity === order[i]) {
const len = marker.push(DiagnosticCollection._toMarkerData(diagnostic));
const len = marker.push(DiagnosticCollection.toMarkerData(diagnostic));
if (len === DiagnosticCollection._maxDiagnosticsPerFile) {
break orderLoop;
}
......@@ -129,7 +129,7 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection {
endColumn: marker[marker.length - 1].endColumn
});
} else {
marker = diagnostics.map(DiagnosticCollection._toMarkerData);
marker = diagnostics.map(DiagnosticCollection.toMarkerData);
}
}
......@@ -179,7 +179,7 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection {
}
}
private static _toMarkerData(diagnostic: vscode.Diagnostic): IMarkerData {
public static toMarkerData(diagnostic: vscode.Diagnostic): IMarkerData {
let range = diagnostic.range;
......
......@@ -15,7 +15,7 @@ import * as modes from 'vs/editor/common/modes';
import { ExtHostHeapService } from 'vs/workbench/api/node/extHostHeapService';
import { ExtHostDocuments } from 'vs/workbench/api/node/extHostDocuments';
import { ExtHostCommands, CommandsConverter } from 'vs/workbench/api/node/extHostCommands';
import { ExtHostDiagnostics } from 'vs/workbench/api/node/extHostDiagnostics';
import { ExtHostDiagnostics, DiagnosticCollection } from 'vs/workbench/api/node/extHostDiagnostics';
import { asWinJsPromise } from 'vs/base/common/async';
import { MainContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, ObjectIdentifier, IRawColorInfo, IMainContext, IExtHostSuggestResult, IExtHostSuggestion, IWorkspaceSymbols, IWorkspaceSymbol, IdObject } from './extHost.protocol';
import { regExpLeadsToEndlessLoop } from 'vs/base/common/strings';
......@@ -269,7 +269,7 @@ class QuickFixAdapter {
this._provider = provider;
}
provideCodeActions(resource: URI, range: IRange): TPromise<modes.Command[]> {
provideCodeActions(resource: URI, range: IRange): TPromise<(modes.CodeAction | modes.Command)[]> {
const doc = this._documents.getDocumentData(resource).document;
const ran = <vscode.Range>TypeConverters.toRange(range);
......@@ -285,11 +285,37 @@ class QuickFixAdapter {
}
});
return asWinJsPromise(token => this._provider.provideCodeActions(doc, ran, { diagnostics: allDiagnostics }, token)).then(commands => {
return asWinJsPromise(token =>
this._provider.provideCodeActions2
? this._provider.provideCodeActions2(doc, ran, { diagnostics: allDiagnostics }, token)
: this._provider.provideCodeActions(doc, ran, { diagnostics: allDiagnostics }, token)
).then(commands => {
if (!Array.isArray(commands)) {
return undefined;
}
return commands.map(command => this._commands.toInternal(command));
return commands.map((action): modes.CodeAction => {
if (!action) {
return undefined;
}
if (typeof action.command === 'string') {
return this._commands.toInternal(action as vscode.Command);
}
const codeAction = action as vscode.CodeAction;
return {
title: codeAction.title,
command: codeAction.command ? this._commands.toInternal(codeAction.command) : undefined,
edits: codeAction.edits
? Array.isArray(codeAction.edits)
? TypeConverters.WorkspaceEdit.fromTextEdits(resource, codeAction.edits)
: TypeConverters.WorkspaceEdit.from(codeAction.edits)
: undefined,
diagnostics: codeAction.diagnostics
? codeAction.diagnostics.map(DiagnosticCollection.toMarkerData)
: undefined
} as modes.CodeAction;
});
});
}
}
......@@ -438,22 +464,7 @@ class RenameAdapter {
if (!value) {
return undefined;
}
let result = <modes.WorkspaceEdit>{
edits: []
};
for (let entry of value.entries()) {
let [uri, textEdits] = entry;
for (let textEdit of textEdits) {
result.edits.push({
resource: uri,
newText: textEdit.newText,
range: TypeConverters.fromRange(textEdit.range)
});
}
}
return result;
return TypeConverters.WorkspaceEdit.from(value);
}, err => {
if (typeof err === 'string') {
return <modes.WorkspaceEdit>{
......@@ -917,7 +928,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
return this._createDisposable(handle);
}
$provideCodeActions(handle: number, resource: URI, range: IRange): TPromise<modes.Command[]> {
$provideCodeActions(handle: number, resource: URI, range: IRange): TPromise<(modes.Command | modes.CodeAction)[]> {
return this._withAdapter(handle, QuickFixAdapter, adapter => adapter.provideCodeActions(resource, range));
}
......
......@@ -223,6 +223,35 @@ export const TextEdit = {
}
};
export namespace WorkspaceEdit {
export function from(value: vscode.WorkspaceEdit): modes.WorkspaceEdit {
const result: modes.WorkspaceEdit = { edits: [] };
for (let entry of value.entries()) {
let [uri, textEdits] = entry;
for (let textEdit of textEdits) {
result.edits.push({
resource: uri,
newText: textEdit.newText,
range: fromRange(textEdit.range)
});
}
}
return result;
}
export function fromTextEdits(uri: vscode.Uri, textEdits: vscode.TextEdit[]): modes.WorkspaceEdit {
const result: modes.WorkspaceEdit = { edits: [] };
for (let textEdit of textEdits) {
result.edits.push({
resource: uri,
newText: textEdit.newText,
range: fromRange(textEdit.range)
});
}
return result;
}
}
export namespace SymbolKind {
......
......@@ -808,6 +808,21 @@ export class SymbolInformation {
}
}
export class CodeAction {
title: string;
command?: vscode.Command;
edits?: TextEdit[] | WorkspaceEdit;
dianostics?: Diagnostic[];
constructor(title: string, edits?: TextEdit[] | WorkspaceEdit) {
this.title = title;
this.edits = edits;
}
}
export class CodeLens {
range: Range;
......@@ -889,6 +904,11 @@ export class SignatureHelp {
}
}
export enum CodeActionType {
QuickFix = 1,
Refactoring = 2
}
export enum CompletionTriggerKind {
Invoke = 0,
TriggerCharacter = 1
......
......@@ -369,8 +369,8 @@ suite('ExtHostLanguageFeatureCommands', function () {
// --- quickfix
test('QuickFix, back and forth', function () {
disposables.push(extHost.registerCodeActionProvider(defaultSelector, <vscode.CodeActionProvider>{
provideCodeActions(): any {
disposables.push(extHost.registerCodeActionProvider(defaultSelector, {
provideCodeActions(): vscode.Command[] {
return [{ command: 'testing', title: 'Title', arguments: [1, 2, true] }];
}
}));
......
......@@ -25,7 +25,7 @@ import { IHeapService } from 'vs/workbench/api/electron-browser/mainThreadHeapSe
import { ExtHostDocuments } from 'vs/workbench/api/node/extHostDocuments';
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/node/extHostDocumentsAndEditors';
import { getDocumentSymbols } from 'vs/editor/contrib/quickOpen/quickOpen';
import { DocumentSymbolProviderRegistry, DocumentHighlightKind, Hover } from 'vs/editor/common/modes';
import { DocumentSymbolProviderRegistry, DocumentHighlightKind, Hover, Command } from 'vs/editor/common/modes';
import { getCodeLensData } from 'vs/editor/contrib/codelens/codelens';
import { getDefinitionsAtPosition, getImplementationsAtPosition, getTypeDefinitionsAtPosition } from 'vs/editor/contrib/goToDeclaration/goToDeclaration';
import { getHover } from 'vs/editor/contrib/hover/getHover';
......@@ -642,11 +642,11 @@ suite('ExtHostLanguageFeatures', function () {
test('Quick Fix, data conversion', function () {
disposables.push(extHost.registerCodeActionProvider(defaultSelector, <vscode.CodeActionProvider>{
provideCodeActions(): any {
disposables.push(extHost.registerCodeActionProvider(defaultSelector, {
provideCodeActions(): vscode.Command[] {
return [
<vscode.Command>{ command: 'test1', title: 'Testing1' },
<vscode.Command>{ command: 'test2', title: 'Testing2' }
{ command: 'test1', title: 'Testing1' },
{ command: 'test2', title: 'Testing2' }
];
}
}));
......@@ -655,7 +655,7 @@ suite('ExtHostLanguageFeatures', function () {
return getCodeActions(model, model.getFullModelRange()).then(value => {
assert.equal(value.length, 2);
let [first, second] = value;
const [first, second]: Command[] = value as any;
assert.equal(first.title, 'Testing1');
assert.equal(first.id, 'test1');
assert.equal(second.title, 'Testing2');
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册