提交 8b955610 编写于 作者: J Johannes Rieken

show names of formatting edits and code actions providers when they block...

show names of formatting edits and code actions providers when they block save. also add links that links that open the settings editor for each feature, #90851
上级 df403aa0
......@@ -635,6 +635,9 @@ export interface CodeActionList extends IDisposable {
* @internal
*/
export interface CodeActionProvider {
displayName?: string
/**
* Provide commands for the given document and range.
*/
......
......@@ -16,6 +16,7 @@ import { ITextModel } from 'vs/editor/common/model';
import * as modes from 'vs/editor/common/modes';
import { IModelService } from 'vs/editor/common/services/modelService';
import { CodeActionFilter, CodeActionKind, CodeActionTrigger, filtersAction, mayIncludeActionsOfKind } from './types';
import { IProgress, Progress } from 'vs/platform/progress/common/progress';
export const codeActionCommandId = 'editor.action.codeAction';
export const refactorCommandId = 'editor.action.refactor';
......@@ -70,7 +71,8 @@ export function getCodeActions(
model: ITextModel,
rangeOrSelection: Range | Selection,
trigger: CodeActionTrigger,
token: CancellationToken
progress: IProgress<modes.CodeActionProvider>,
token: CancellationToken,
): Promise<CodeActionSet> {
const filter = trigger.filter || {};
......@@ -85,6 +87,7 @@ export function getCodeActions(
const disposables = new DisposableStore();
const promises = providers.map(async provider => {
try {
progress.report(provider);
const providedCodeActions = await provider.provideCodeActions(model, rangeOrSelection, codeActionContext, cts.token);
if (providedCodeActions) {
disposables.add(providedCodeActions);
......@@ -210,6 +213,7 @@ registerLanguageCommand('_executeCodeActionProvider', async function (accessor,
model,
validatedRangeOrSelection,
{ type: modes.CodeActionTriggerType.Manual, filter: { includeSourceActions: true, include: kind && kind.value ? new CodeActionKind(kind.value) : undefined } },
Progress.None,
CancellationToken.None);
setTimeout(() => codeActionSet.dispose(), 100);
......
......@@ -14,7 +14,7 @@ import { Selection } from 'vs/editor/common/core/selection';
import { CodeActionProviderRegistry, CodeActionTriggerType } from 'vs/editor/common/modes';
import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
import { IMarkerService } from 'vs/platform/markers/common/markers';
import { IEditorProgressService } from 'vs/platform/progress/common/progress';
import { IEditorProgressService, Progress } from 'vs/platform/progress/common/progress';
import { getCodeActions, CodeActionSet } from './codeAction';
import { CodeActionTrigger } from './types';
import { EditorOption } from 'vs/editor/common/config/editorOptions';
......@@ -213,7 +213,7 @@ export class CodeActionModel extends Disposable {
return;
}
const actions = createCancelablePromise(token => getCodeActions(model, trigger.selection, trigger.trigger, token));
const actions = createCancelablePromise(token => getCodeActions(model, trigger.selection, trigger.trigger, Progress.None, token));
if (this._progressService && trigger.trigger.type === CodeActionTriggerType.Manual) {
this._progressService.showWhile(actions, 250);
}
......
......@@ -12,6 +12,7 @@ import { getCodeActions } from 'vs/editor/contrib/codeAction/codeAction';
import { CodeActionKind } from 'vs/editor/contrib/codeAction/types';
import { IMarkerData, MarkerSeverity } from 'vs/platform/markers/common/markers';
import { CancellationToken } from 'vs/base/common/cancellation';
import { Progress } from 'vs/platform/progress/common/progress';
function staticCodeActionProvider(...actions: modes.CodeAction[]): modes.CodeActionProvider {
return new class implements modes.CodeActionProvider {
......@@ -125,7 +126,7 @@ suite('CodeAction', () => {
testData.tsLint.abc
];
const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: modes.CodeActionTriggerType.Manual }, CancellationToken.None);
const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: modes.CodeActionTriggerType.Manual }, Progress.None, CancellationToken.None);
assert.equal(actions.length, 6);
assert.deepEqual(actions, expected);
});
......@@ -140,20 +141,20 @@ suite('CodeAction', () => {
disposables.add(modes.CodeActionProviderRegistry.register('fooLang', provider));
{
const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: modes.CodeActionTriggerType.Auto, filter: { include: new CodeActionKind('a') } }, CancellationToken.None);
const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: modes.CodeActionTriggerType.Auto, filter: { include: new CodeActionKind('a') } }, Progress.None, CancellationToken.None);
assert.equal(actions.length, 2);
assert.strictEqual(actions[0].title, 'a');
assert.strictEqual(actions[1].title, 'a.b');
}
{
const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: modes.CodeActionTriggerType.Auto, filter: { include: new CodeActionKind('a.b') } }, CancellationToken.None);
const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: modes.CodeActionTriggerType.Auto, filter: { include: new CodeActionKind('a.b') } }, Progress.None, CancellationToken.None);
assert.equal(actions.length, 1);
assert.strictEqual(actions[0].title, 'a.b');
}
{
const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: modes.CodeActionTriggerType.Auto, filter: { include: new CodeActionKind('a.b.c') } }, CancellationToken.None);
const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: modes.CodeActionTriggerType.Auto, filter: { include: new CodeActionKind('a.b.c') } }, Progress.None, CancellationToken.None);
assert.equal(actions.length, 0);
}
});
......@@ -172,7 +173,7 @@ suite('CodeAction', () => {
disposables.add(modes.CodeActionProviderRegistry.register('fooLang', provider));
const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: modes.CodeActionTriggerType.Auto, filter: { include: new CodeActionKind('a') } }, CancellationToken.None);
const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: modes.CodeActionTriggerType.Auto, filter: { include: new CodeActionKind('a') } }, Progress.None, CancellationToken.None);
assert.equal(actions.length, 1);
assert.strictEqual(actions[0].title, 'a');
});
......@@ -186,13 +187,13 @@ suite('CodeAction', () => {
disposables.add(modes.CodeActionProviderRegistry.register('fooLang', provider));
{
const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: modes.CodeActionTriggerType.Auto }, CancellationToken.None);
const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: modes.CodeActionTriggerType.Auto }, Progress.None, CancellationToken.None);
assert.equal(actions.length, 1);
assert.strictEqual(actions[0].title, 'b');
}
{
const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: modes.CodeActionTriggerType.Auto, filter: { include: CodeActionKind.Source, includeSourceActions: true } }, CancellationToken.None);
const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: modes.CodeActionTriggerType.Auto, filter: { include: CodeActionKind.Source, includeSourceActions: true } }, Progress.None, CancellationToken.None);
assert.equal(actions.length, 1);
assert.strictEqual(actions[0].title, 'a');
}
......@@ -214,7 +215,7 @@ suite('CodeAction', () => {
excludes: [CodeActionKind.Source],
includeSourceActions: true,
}
}, CancellationToken.None);
}, Progress.None, CancellationToken.None);
assert.equal(actions.length, 1);
assert.strictEqual(actions[0].title, 'b');
}
......@@ -250,7 +251,7 @@ suite('CodeAction', () => {
include: baseType,
excludes: [subType],
}
}, CancellationToken.None);
}, Progress.None, CancellationToken.None);
assert.strictEqual(didInvoke, false);
assert.equal(actions.length, 1);
assert.strictEqual(actions[0].title, 'a');
......@@ -275,7 +276,7 @@ suite('CodeAction', () => {
filter: {
include: CodeActionKind.QuickFix
}
}, CancellationToken.None);
}, Progress.None, CancellationToken.None);
assert.strictEqual(actions.length, 0);
assert.strictEqual(wasInvoked, false);
});
......
......@@ -27,7 +27,7 @@ import { IDisposable } from 'vs/base/common/lifecycle';
import { LinkedList } from 'vs/base/common/linkedList';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
import { assertType } from 'vs/base/common/types';
import { IProgressStep, IProgress } from 'vs/platform/progress/common/progress';
import { IProgress } from 'vs/platform/progress/common/progress';
export function alertFormattingEdits(edits: ISingleEditOperation[]): void {
......@@ -210,7 +210,7 @@ export async function formatDocumentWithSelectedProvider(
accessor: ServicesAccessor,
editorOrModel: ITextModel | IActiveCodeEditor,
mode: FormattingMode,
progress: IProgress<IProgressStep>,
progress: IProgress<DocumentFormattingEditProvider>,
token: CancellationToken
): Promise<void> {
......@@ -219,7 +219,7 @@ export async function formatDocumentWithSelectedProvider(
const provider = getRealAndSyntheticDocumentFormattersOrdered(model);
const selected = await FormattingConflicts.select(provider, model, mode);
if (selected) {
progress.report({ message: nls.localize('formattingWith', "Formatting with '{0}'", selected.displayName || selected.extensionId && selected.extensionId.value || '???') });
progress.report(selected);
await instaService.invokeFunction(formatDocumentWithProvider, selected, editorOrModel, mode, token);
}
}
......
......@@ -40,6 +40,7 @@ import { IIdentifiedSingleEditOperation } from 'vs/editor/common/model';
import { EditorOption } from 'vs/editor/common/config/editorOptions';
import { Constants } from 'vs/base/common/uint';
import { textLinkForeground } from 'vs/platform/theme/common/colorRegistry';
import { Progress } from 'vs/platform/progress/common/progress';
const $ = dom.$;
......@@ -626,6 +627,7 @@ export class ModesContentHoverWidget extends ContentHoverWidget {
this._editor.getModel()!,
new Range(marker.startLineNumber, marker.startColumn, marker.endLineNumber, marker.endColumn),
markerCodeActionTrigger,
Progress.None,
cancellationToken);
});
}
......
......@@ -273,7 +273,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
// --- quick fix
$registerQuickFixSupport(handle: number, selector: IDocumentFilterDto[], metadata: ICodeActionProviderMetadataDto): void {
$registerQuickFixSupport(handle: number, selector: IDocumentFilterDto[], metadata: ICodeActionProviderMetadataDto, displayName: string): void {
this._registrations.set(handle, modes.CodeActionProviderRegistry.register(selector, <modes.CodeActionProvider>{
provideCodeActions: async (model: ITextModel, rangeOrSelection: EditorRange | Selection, context: modes.CodeActionContext, token: CancellationToken): Promise<modes.CodeActionList | undefined> => {
const listDto = await this._proxy.$provideCodeActions(handle, model.uri, rangeOrSelection, context, token);
......@@ -290,7 +290,8 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
};
},
providedCodeActionKinds: metadata.providedKinds,
documentation: metadata.documentation
documentation: metadata.documentation,
displayName
}));
}
......
......@@ -361,7 +361,7 @@ export interface MainThreadLanguageFeaturesShape extends IDisposable {
$registerEvaluatableExpressionProvider(handle: number, selector: IDocumentFilterDto[]): void;
$registerDocumentHighlightProvider(handle: number, selector: IDocumentFilterDto[]): void;
$registerReferenceSupport(handle: number, selector: IDocumentFilterDto[]): void;
$registerQuickFixSupport(handle: number, selector: IDocumentFilterDto[], metadata: ICodeActionProviderMetadataDto): void;
$registerQuickFixSupport(handle: number, selector: IDocumentFilterDto[], metadata: ICodeActionProviderMetadataDto, displayName: string): void;
$registerDocumentFormattingSupport(handle: number, selector: IDocumentFilterDto[], extensionId: ExtensionIdentifier, displayName: string): void;
$registerRangeFormattingSupport(handle: number, selector: IDocumentFilterDto[], extensionId: ExtensionIdentifier, displayName: string): void;
$registerOnTypeFormattingSupport(handle: number, selector: IDocumentFilterDto[], autoFormatTriggerCharacters: string[], extensionId: ExtensionIdentifier): void;
......
......@@ -1617,7 +1617,7 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF
kind: x.kind.value,
command: this._commands.converter.toInternal(x.command, store),
}))
});
}, ExtHostLanguageFeatures._extLabel(extension));
store.add(this._createDisposable(handle));
return store;
}
......
......@@ -13,7 +13,7 @@ import { Position } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range';
import { Selection } from 'vs/editor/common/core/selection';
import { ITextModel } from 'vs/editor/common/model';
import { CodeAction, CodeActionTriggerType } from 'vs/editor/common/modes';
import { CodeActionTriggerType, DocumentFormattingEditProvider, CodeActionProvider } from 'vs/editor/common/modes';
import { getCodeActions } from 'vs/editor/contrib/codeAction/codeAction';
import { applyCodeAction } from 'vs/editor/contrib/codeAction/codeActionCommands';
import { CodeActionKind } from 'vs/editor/contrib/codeAction/types';
......@@ -22,7 +22,7 @@ import { SnippetController2 } from 'vs/editor/contrib/snippet/snippetController2
import { localize } from 'vs/nls';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IProgressStep, IProgress } from 'vs/platform/progress/common/progress';
import { IProgressStep, IProgress, Progress } from 'vs/platform/progress/common/progress';
import { IResolvedTextFileEditorModel, ITextFileService, ITextFileSaveParticipant } from 'vs/workbench/services/textfile/common/textfiles';
import { SaveReason } from 'vs/workbench/common/editor';
import { Disposable } from 'vs/base/common/lifecycle';
......@@ -213,9 +213,17 @@ class FormatOnSaveParticipant implements ITextFileSaveParticipant {
return undefined;
}
progress.report({ message: localize('formatting', "Formatting") });
const nestedProgress = new Progress<DocumentFormattingEditProvider>(provider => {
progress.report({
message: localize(
'formatting',
"Running '{0}' Formatter ([configure](command:workbench.action.openSettings?%5B%22editor.formatOnSave%22%5D)).",
provider.displayName || provider.extensionId && provider.extensionId.value || '???'
)
});
});
const editorOrModel = findEditor(model, this.codeEditorService) || model;
await this.instantiationService.invokeFunction(formatDocumentWithSelectedProvider, editorOrModel, FormattingMode.Silent, progress, token);
await this.instantiationService.invokeFunction(formatDocumentWithSelectedProvider, editorOrModel, FormattingMode.Silent, nestedProgress, token);
}
}
......@@ -262,14 +270,37 @@ class CodeActionOnSaveParticipant implements ITextFileSaveParticipant {
.map(x => new CodeActionKind(x));
progress.report({ message: localize('codeaction', "Quick Fixes") });
await this.applyOnSaveActions(model, codeActionsOnSave, excludedActions, token);
await this.applyOnSaveActions(model, codeActionsOnSave, excludedActions, progress, token);
}
private async applyOnSaveActions(model: ITextModel, codeActionsOnSave: readonly CodeActionKind[], excludes: readonly CodeActionKind[], token: CancellationToken): Promise<void> {
private async applyOnSaveActions(model: ITextModel, codeActionsOnSave: readonly CodeActionKind[], excludes: readonly CodeActionKind[], progress: IProgress<IProgressStep>, token: CancellationToken): Promise<void> {
const getActionProgress = new class implements IProgress<CodeActionProvider> {
private _names: string[] = [];
private _report(): void {
progress.report({
message: localize(
'codeaction.get',
"Getting code actions from '{0}' ([configure](command:workbench.action.openSettings?%5B%22editor.codeActionsOnSave%22%5D)).",
this._names.map(name => `'${name}'`).join(', ')
)
});
}
report(provider: CodeActionProvider) {
if (provider.displayName) {
this._names.push(provider.displayName);
this._report();
}
}
};
for (const codeActionKind of codeActionsOnSave) {
const actionsToRun = await this.getActionsToRun(model, codeActionKind, excludes, token);
const actionsToRun = await this.getActionsToRun(model, codeActionKind, excludes, getActionProgress, token);
try {
await this.applyCodeActions(actionsToRun.validActions);
for (const action of actionsToRun.validActions) {
progress.report({ message: localize('codeAction.apply', "Applying code action '{0}'.", action.title) });
await this.instantiationService.invokeFunction(applyCodeAction, action);
}
} catch {
// Failure to apply a code action should not block other on save actions
} finally {
......@@ -278,17 +309,11 @@ class CodeActionOnSaveParticipant implements ITextFileSaveParticipant {
}
}
private async applyCodeActions(actionsToRun: readonly CodeAction[]) {
for (const action of actionsToRun) {
await this.instantiationService.invokeFunction(applyCodeAction, action);
}
}
private getActionsToRun(model: ITextModel, codeActionKind: CodeActionKind, excludes: readonly CodeActionKind[], token: CancellationToken) {
private getActionsToRun(model: ITextModel, codeActionKind: CodeActionKind, excludes: readonly CodeActionKind[], progress: IProgress<CodeActionProvider>, token: CancellationToken) {
return getCodeActions(model, model.getFullModelRange(), {
type: CodeActionTriggerType.Auto,
filter: { include: codeActionKind, excludes: excludes, includeSourceActions: true },
}, token);
}, progress, token);
}
}
......
......@@ -51,6 +51,7 @@ import { IFileService } from 'vs/platform/files/common/files';
import { domEvent } from 'vs/base/browser/event';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { KeyCode } from 'vs/base/common/keyCodes';
import { Progress } from 'vs/platform/progress/common/progress';
export type TreeElement = ResourceMarkers | Marker | RelatedInformation;
......@@ -642,7 +643,7 @@ export class MarkerViewModel extends Disposable {
this.codeActionsPromise = createCancelablePromise(cancellationToken => {
return getCodeActions(model, new Range(this.marker.range.startLineNumber, this.marker.range.startColumn, this.marker.range.endLineNumber, this.marker.range.endColumn), {
type: CodeActionTriggerType.Manual, filter: { include: CodeActionKind.QuickFix }
}, cancellationToken).then(actions => {
}, Progress.None, cancellationToken).then(actions => {
return this._register(actions);
});
});
......
......@@ -33,7 +33,7 @@ export class TextFileSaveParticipant extends Disposable {
const cts = new CancellationTokenSource(token);
return this.progressService.withProgress({
title: localize('saveParticipants', "Running Save Participants for '{0}'", model.name),
title: localize('saveParticipants', "Saving '{0}'", model.name),
location: ProgressLocation.Notification,
cancellable: true,
delay: model.isDirty() ? 3000 : 5000
......
......@@ -48,6 +48,7 @@ import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerServ
import { dispose } from 'vs/base/common/lifecycle';
import { withNullAsUndefined } from 'vs/base/common/types';
import { NullApiDeprecationService } from 'vs/workbench/api/common/extHostApiDeprecationService';
import { Progress } from 'vs/platform/progress/common/progress';
const defaultSelector = { scheme: 'far' };
const model: ITextModel = TextModel.createFromString(
......@@ -590,7 +591,7 @@ suite('ExtHostLanguageFeatures', function () {
}));
await rpcProtocol.sync();
const { validActions: actions } = await getCodeActions(model, model.getFullModelRange(), { type: modes.CodeActionTriggerType.Manual }, CancellationToken.None);
const { validActions: actions } = await getCodeActions(model, model.getFullModelRange(), { type: modes.CodeActionTriggerType.Manual }, Progress.None, CancellationToken.None);
assert.equal(actions.length, 2);
const [first, second] = actions;
assert.equal(first.title, 'Testing1');
......@@ -614,7 +615,7 @@ suite('ExtHostLanguageFeatures', function () {
}));
await rpcProtocol.sync();
const { validActions: actions } = await getCodeActions(model, model.getFullModelRange(), { type: modes.CodeActionTriggerType.Manual }, CancellationToken.None);
const { validActions: actions } = await getCodeActions(model, model.getFullModelRange(), { type: modes.CodeActionTriggerType.Manual }, Progress.None, CancellationToken.None);
assert.equal(actions.length, 1);
const [first] = actions;
assert.equal(first.title, 'Testing1');
......@@ -637,7 +638,7 @@ suite('ExtHostLanguageFeatures', function () {
}));
await rpcProtocol.sync();
const { validActions: actions } = await getCodeActions(model, model.getFullModelRange(), { type: modes.CodeActionTriggerType.Manual }, CancellationToken.None);
const { validActions: actions } = await getCodeActions(model, model.getFullModelRange(), { type: modes.CodeActionTriggerType.Manual }, Progress.None, CancellationToken.None);
assert.equal(actions.length, 1);
});
......@@ -655,7 +656,7 @@ suite('ExtHostLanguageFeatures', function () {
}));
await rpcProtocol.sync();
const { validActions: actions } = await getCodeActions(model, model.getFullModelRange(), { type: modes.CodeActionTriggerType.Manual }, CancellationToken.None);
const { validActions: actions } = await getCodeActions(model, model.getFullModelRange(), { type: modes.CodeActionTriggerType.Manual }, Progress.None, CancellationToken.None);
assert.equal(actions.length, 1);
});
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册