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`
This commit is contained in:
Matt Bierner
2017-11-08 16:07:34 -08:00
committed by GitHub
parent 8a79656f6a
commit be88547c66
23 changed files with 274 additions and 104 deletions

View File

@@ -531,6 +531,7 @@ export function createApiFactory(
debug,
// types
CancellationTokenSource: CancellationTokenSource,
CodeAction: extHostTypes.CodeAction,
CodeLens: extHostTypes.CodeLens,
Color: extHostTypes.Color,
ColorPresentation: extHostTypes.ColorPresentation,

View File

@@ -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[]>;

View File

@@ -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;

View File

@@ -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));
}

View File

@@ -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 {

View File

@@ -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