提交 4cc8710a 编写于 作者: M Matt Bierner

Add CodeAction.disabled

For #85160
上级 37f18e8f
......@@ -548,6 +548,7 @@ export interface CodeAction {
diagnostics?: IMarkerData[];
kind?: string;
isPreferred?: boolean;
disabled?: string;
}
/**
......
......@@ -24,7 +24,8 @@ export const organizeImportsCommandId = 'editor.action.organizeImports';
export const fixAllCommandId = 'editor.action.fixAll';
export interface CodeActionSet extends IDisposable {
readonly actions: readonly CodeAction[];
readonly validActions: readonly CodeAction[];
readonly allActions: readonly CodeAction[];
readonly hasAutoFix: boolean;
}
......@@ -44,16 +45,18 @@ class ManagedCodeActionSet extends Disposable implements CodeActionSet {
}
}
public readonly actions: readonly CodeAction[];
public readonly validActions: readonly CodeAction[];
public readonly allActions: readonly CodeAction[];
public constructor(actions: readonly CodeAction[], disposables: DisposableStore) {
super();
this._register(disposables);
this.actions = mergeSort([...actions], ManagedCodeActionSet.codeActionsComparator);
this.allActions = mergeSort([...actions], ManagedCodeActionSet.codeActionsComparator);
this.validActions = this.allActions.filter(action => !action.disabled);
}
public get hasAutoFix() {
return this.actions.some(fix => !!fix.kind && CodeActionKind.QuickFix.contains(new CodeActionKind(fix.kind)) && !!fix.isPreferred);
return this.validActions.some(fix => !!fix.kind && CodeActionKind.QuickFix.contains(new CodeActionKind(fix.kind)) && !!fix.isPreferred);
}
}
......@@ -150,5 +153,5 @@ registerLanguageCommand('_executeCodeActionProvider', async function (accessor,
CancellationToken.None);
setTimeout(() => codeActionSet.dispose(), 100);
return codeActionSet.actions;
return codeActionSet.validActions;
});
......@@ -68,20 +68,19 @@ export class CodeActionUi extends Disposable {
this._lightBulbWidget.getValue().update(actions, newState.position);
if (!actions.actions.length && newState.trigger.context) {
if (!actions.validActions.length && newState.trigger.context) {
MessageController.get(this._editor).showMessage(newState.trigger.context.notAvailableMessage, newState.trigger.context.position);
this._activeCodeActions.value = actions;
return;
}
if (newState.trigger.type === 'manual') {
if (newState.trigger.filter && newState.trigger.filter.include) {
// Triggered for specific scope
if (actions.actions.length > 0) {
if (newState.trigger.filter?.include) { // Triggered for specific scope
if (actions.validActions.length > 0) {
// Apply if we only have one action or requested autoApply
if (newState.trigger.autoApply === CodeActionAutoApply.First || (newState.trigger.autoApply === CodeActionAutoApply.IfSingle && actions.actions.length === 1)) {
if (newState.trigger.autoApply === CodeActionAutoApply.First || (newState.trigger.autoApply === CodeActionAutoApply.IfSingle && actions.validActions.length === 1)) {
try {
await this.delegate.applyCodeAction(actions.actions[0], false);
await this.delegate.applyCodeAction(actions.validActions[0], false);
} finally {
actions.dispose();
}
......
......@@ -35,7 +35,7 @@ class CodeActionAction extends Action {
public readonly action: CodeAction,
callback: () => Promise<void>,
) {
super(action.command ? action.command.id : action.title, action.title, undefined, true, callback);
super(action.command ? action.command.id : action.title, action.title, undefined, !action.disabled, callback);
}
}
......@@ -64,7 +64,7 @@ export class CodeActionWidget extends Disposable {
}
public async show(codeActions: CodeActionSet, at: IAnchor | IPosition): Promise<void> {
if (!codeActions.actions.length) {
if (!codeActions.validActions.length) {
this._visible = false;
return;
}
......@@ -78,7 +78,7 @@ export class CodeActionWidget extends Disposable {
this._visible = true;
this._showingActions.value = codeActions;
const actions = codeActions.actions.map(action =>
const actions = codeActions.validActions.map(action =>
new CodeActionAction(action, () => this._delegate.onSelectCodeAction(action)));
const anchor = Position.isIPosition(at) ? this._toCoords(at) : at || { x: 0, y: 0 };
......
......@@ -137,7 +137,7 @@ export class LightBulbWidget extends Disposable implements IContentWidget {
}
public update(actions: CodeActionSet, atPosition: IPosition) {
if (actions.actions.length <= 0) {
if (actions.validActions.length <= 0) {
return this.hide();
}
......
......@@ -125,7 +125,7 @@ suite('CodeAction', () => {
testData.tsLint.abc
];
const { actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'manual' }, CancellationToken.None);
const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'manual' }, CancellationToken.None);
assert.equal(actions.length, 6);
assert.deepEqual(actions, expected);
});
......@@ -140,20 +140,20 @@ suite('CodeAction', () => {
disposables.add(modes.CodeActionProviderRegistry.register('fooLang', provider));
{
const { actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'auto', filter: { include: new CodeActionKind('a') } }, CancellationToken.None);
const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'auto', filter: { include: new CodeActionKind('a') } }, CancellationToken.None);
assert.equal(actions.length, 2);
assert.strictEqual(actions[0].title, 'a');
assert.strictEqual(actions[1].title, 'a.b');
}
{
const { actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'auto', filter: { include: new CodeActionKind('a.b') } }, CancellationToken.None);
const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'auto', filter: { include: new CodeActionKind('a.b') } }, CancellationToken.None);
assert.equal(actions.length, 1);
assert.strictEqual(actions[0].title, 'a.b');
}
{
const { actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'auto', filter: { include: new CodeActionKind('a.b.c') } }, CancellationToken.None);
const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'auto', filter: { include: new CodeActionKind('a.b.c') } }, CancellationToken.None);
assert.equal(actions.length, 0);
}
});
......@@ -172,7 +172,7 @@ suite('CodeAction', () => {
disposables.add(modes.CodeActionProviderRegistry.register('fooLang', provider));
const { actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'auto', filter: { include: new CodeActionKind('a') } }, CancellationToken.None);
const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'auto', filter: { include: new CodeActionKind('a') } }, CancellationToken.None);
assert.equal(actions.length, 1);
assert.strictEqual(actions[0].title, 'a');
});
......@@ -186,13 +186,13 @@ suite('CodeAction', () => {
disposables.add(modes.CodeActionProviderRegistry.register('fooLang', provider));
{
const { actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'auto' }, CancellationToken.None);
const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'auto' }, CancellationToken.None);
assert.equal(actions.length, 1);
assert.strictEqual(actions[0].title, 'b');
}
{
const { actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'auto', filter: { include: CodeActionKind.Source, includeSourceActions: true } }, CancellationToken.None);
const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'auto', filter: { include: CodeActionKind.Source, includeSourceActions: true } }, CancellationToken.None);
assert.equal(actions.length, 1);
assert.strictEqual(actions[0].title, 'a');
}
......@@ -208,7 +208,7 @@ suite('CodeAction', () => {
disposables.add(modes.CodeActionProviderRegistry.register('fooLang', provider));
{
const { actions } = await getCodeActions(model, new Range(1, 1, 2, 1), {
const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), {
type: 'auto', filter: {
include: CodeActionKind.Source.append('test'),
excludes: [CodeActionKind.Source],
......@@ -233,7 +233,7 @@ suite('CodeAction', () => {
disposables.add(modes.CodeActionProviderRegistry.register('fooLang', provider));
const { actions } = await getCodeActions(model, new Range(1, 1, 2, 1), {
const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), {
type: 'auto',
filter: {
include: CodeActionKind.QuickFix
......
......@@ -64,7 +64,7 @@ suite('CodeActionModel', () => {
e.actions.then(fixes => {
model.dispose();
assert.equal(fixes.actions.length, 1);
assert.equal(fixes.validActions.length, 1);
done();
}, done);
}));
......@@ -104,7 +104,7 @@ suite('CodeActionModel', () => {
assert.ok(e.actions);
e.actions.then(fixes => {
model.dispose();
assert.equal(fixes.actions.length, 1);
assert.equal(fixes.validActions.length, 1);
resolve(undefined);
}, reject);
}));
......
......@@ -548,7 +548,7 @@ export class ModesContentHoverWidget extends ContentHoverWidget {
quickfixPlaceholderElement.style.transition = '';
quickfixPlaceholderElement.style.opacity = '1';
if (!actions.actions.length) {
if (!actions.validActions.length) {
actions.dispose();
quickfixPlaceholderElement.textContent = nls.localize('noQuickFixes', "No quick fixes available");
return;
......
......@@ -4977,6 +4977,7 @@ declare namespace monaco.languages {
diagnostics?: editor.IMarkerData[];
kind?: string;
isPreferred?: boolean;
disabled?: string;
}
export interface CodeActionList extends IDisposable {
......
......@@ -1326,4 +1326,18 @@ declare module 'vscode' {
}
//#endregion
//#region mjbvz - Surfacing reasons why a code action cannot be applied to users — https://github.com/microsoft/vscode/issues/85160
export interface CodeAction {
/**
* Marks that the code action cannot currently be applied.
*
* This should be a human readable description of why the code action is currently disabled. Disabled code actions
* will be surfaced in the refactor UI but cannot be applied.
*/
disabled?: string;
}
//#endregion
}
......@@ -307,7 +307,7 @@ class CodeActionOnSaveParticipant implements ISaveParticipant {
for (const codeActionKind of codeActionsOnSave) {
const actionsToRun = await this.getActionsToRun(model, codeActionKind, excludes, token);
try {
await this.applyCodeActions(actionsToRun.actions);
await this.applyCodeActions(actionsToRun.validActions);
} catch {
// Failure to apply a code action should not block other on save actions
} finally {
......
......@@ -1086,6 +1086,7 @@ export interface ICodeActionDto {
command?: ICommandDto;
kind?: string;
isPreferred?: boolean;
disabled?: string;
}
export interface ICodeActionListDto {
......
......@@ -389,6 +389,7 @@ class CodeActionAdapter {
edit: candidate.edit && typeConvert.WorkspaceEdit.from(candidate.edit),
kind: candidate.kind && candidate.kind.value,
isPreferred: candidate.isPreferred,
disabled: candidate.disabled
});
}
}
......
......@@ -564,7 +564,7 @@ export class MarkerViewModel extends Disposable {
}
private toActions(codeActions: CodeActionSet): IAction[] {
return codeActions.actions.map(codeAction => new Action(
return codeActions.validActions.map(codeAction => new Action(
codeAction.command ? codeAction.command.id : codeAction.title,
codeAction.title,
undefined,
......
......@@ -589,7 +589,7 @@ suite('ExtHostLanguageFeatures', function () {
}));
await rpcProtocol.sync();
const { actions } = await getCodeActions(model, model.getFullModelRange(), { type: 'manual' }, CancellationToken.None);
const { validActions: actions } = await getCodeActions(model, model.getFullModelRange(), { type: 'manual' }, CancellationToken.None);
assert.equal(actions.length, 2);
const [first, second] = actions;
assert.equal(first.title, 'Testing1');
......@@ -613,7 +613,7 @@ suite('ExtHostLanguageFeatures', function () {
}));
await rpcProtocol.sync();
const { actions } = await getCodeActions(model, model.getFullModelRange(), { type: 'manual' }, CancellationToken.None);
const { validActions: actions } = await getCodeActions(model, model.getFullModelRange(), { type: 'manual' }, CancellationToken.None);
assert.equal(actions.length, 1);
const [first] = actions;
assert.equal(first.title, 'Testing1');
......@@ -636,7 +636,7 @@ suite('ExtHostLanguageFeatures', function () {
}));
await rpcProtocol.sync();
const { actions } = await getCodeActions(model, model.getFullModelRange(), { type: 'manual' }, CancellationToken.None);
const { validActions: actions } = await getCodeActions(model, model.getFullModelRange(), { type: 'manual' }, CancellationToken.None);
assert.equal(actions.length, 1);
});
......@@ -654,7 +654,7 @@ suite('ExtHostLanguageFeatures', function () {
}));
await rpcProtocol.sync();
const { actions } = await getCodeActions(model, model.getFullModelRange(), { type: 'manual' }, CancellationToken.None);
const { validActions: actions } = await getCodeActions(model, model.getFullModelRange(), { type: 'manual' }, CancellationToken.None);
assert.equal(actions.length, 1);
});
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册