Skip to content

Commit

Permalink
Extract code action filtering logic to own files
Browse files Browse the repository at this point in the history
  • Loading branch information
mjbvz committed Jan 23, 2019
1 parent 3ff312e commit cc0be8d
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 39 deletions.
48 changes: 9 additions & 39 deletions src/vs/editor/contrib/codeAction/codeAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { flatten, mergeSort, isNonEmptyArray } from 'vs/base/common/arrays';
import { flatten, isNonEmptyArray, mergeSort } from 'vs/base/common/arrays';
import { CancellationToken } from 'vs/base/common/cancellation';
import { illegalArgument, isPromiseCanceledError, onUnexpectedExternalError } from 'vs/base/common/errors';
import { URI } from 'vs/base/common/uri';
Expand All @@ -13,47 +13,37 @@ import { Selection } from 'vs/editor/common/core/selection';
import { ITextModel } from 'vs/editor/common/model';
import { CodeAction, CodeActionContext, CodeActionProviderRegistry, CodeActionTrigger as CodeActionTriggerKind } from 'vs/editor/common/modes';
import { IModelService } from 'vs/editor/common/services/modelService';
import { CodeActionFilter, CodeActionKind, CodeActionTrigger } from './codeActionTrigger';
import { CodeActionKind, CodeActionTrigger, filtersAction, mayIncludeActionsOfKind } from './codeActionTrigger';

export function getCodeActions(
model: ITextModel,
rangeOrSelection: Range | Selection,
trigger: CodeActionTrigger,
token: CancellationToken
): Promise<CodeAction[]> {
const filter = trigger.filter || {};

const codeActionContext: CodeActionContext = {
only: trigger.filter && trigger.filter.kind ? trigger.filter.kind.value : undefined,
only: filter.kind ? filter.kind.value : undefined,
trigger: trigger.type === 'manual' ? CodeActionTriggerKind.Manual : CodeActionTriggerKind.Automatic
};

const promises = CodeActionProviderRegistry.all(model)
// Avoid calling providers that we know will not return code actions of interest
.filter(provider => {
if (!provider.providedCodeActionKinds) {
// We don't know what type of actions this provider will return.
return true;
}

// Avoid calling providers that we know will not return code actions of interest
return provider.providedCodeActionKinds.map(kind => new CodeActionKind(kind)).some(providedKind => {
// Filter out actions by kind
// The provided kind can be either a subset of a superset of the filtered kind
if (trigger.filter && trigger.filter.kind && !trigger.filter.kind.intersects(providedKind)) {
return false;
}

// Don't return source actions unless they are explicitly requested
if (CodeActionKind.Source.contains(providedKind) && (!trigger.filter || !trigger.filter.includeSourceActions)) {
return false;
}

return true;
});
return provider.providedCodeActionKinds.some(kind => mayIncludeActionsOfKind(filter, new CodeActionKind(kind)));
})
.map(support => {
return Promise.resolve(support.provideCodeActions(model, rangeOrSelection, codeActionContext, token)).then(providedCodeActions => {
if (!Array.isArray(providedCodeActions)) {
return [];
}
return providedCodeActions.filter(action => isValidAction(trigger.filter, action));
return providedCodeActions.filter(action => action && filtersAction(filter, action));
}, (err): CodeAction[] => {
if (isPromiseCanceledError(err)) {
throw err;
Expand All @@ -69,26 +59,6 @@ export function getCodeActions(
.then(allCodeActions => mergeSort(allCodeActions, codeActionsComparator));
}

function isValidAction(filter: CodeActionFilter | undefined, action: CodeAction): boolean {
return action
&& isValidActionKind(filter, action.kind ? new CodeActionKind(action.kind) : undefined)
&& (filter && filter.onlyIncludePreferredActions ? !!action.isPreferred : true);
}

function isValidActionKind(filter: CodeActionFilter | undefined, kind: CodeActionKind | undefined): boolean {
// Filter out actions by kind
if (filter && filter.kind && (!kind || !filter.kind.contains(kind))) {
return false;
}

// Don't return source actions unless they are explicitly requested
if (kind && CodeActionKind.Source.contains(kind) && (!filter || !filter.includeSourceActions)) {
return false;
}

return true;
}

function codeActionsComparator(a: CodeAction, b: CodeAction): number {
if (isNonEmptyArray(a.diagnostics)) {
if (isNonEmptyArray(b.diagnostics)) {
Expand Down
42 changes: 42 additions & 0 deletions src/vs/editor/contrib/codeAction/codeActionTrigger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*--------------------------------------------------------------------------------------------*/

import { startsWith } from 'vs/base/common/strings';
import { CodeAction } from 'vs/editor/common/modes';

export class CodeActionKind {
private static readonly sep = '.';
Expand Down Expand Up @@ -40,6 +41,47 @@ export interface CodeActionFilter {
readonly onlyIncludePreferredActions?: boolean;
}

export function mayIncludeActionsOfKind(filter: CodeActionFilter, providedKind: CodeActionKind): boolean {
// A provided kind may be a subset or superset of our filtered kind.
if (filter.kind && !filter.kind.intersects(providedKind)) {
return false;
}

// Don't return source actions unless they are explicitly requested
if (CodeActionKind.Source.contains(providedKind) && !filter.includeSourceActions) {
return false;
}

return true;
}


export function filtersAction(filter: CodeActionFilter, action: CodeAction): boolean {
const actionKind = action.kind ? new CodeActionKind(action.kind) : undefined;

// Filter out actions by kind
if (filter.kind) {
if (!actionKind || !filter.kind.contains(actionKind)) {
return false;
}
}

// Don't return source actions unless they are explicitly requested
if (!filter.includeSourceActions) {
if (actionKind && CodeActionKind.Source.contains(actionKind)) {
return false;
}
}

if (filter.onlyIncludePreferredActions) {
if (!action.isPreferred) {
return false;
}
}

return true;
}

export interface CodeActionTrigger {
readonly type: 'auto' | 'manual';
readonly filter?: CodeActionFilter;
Expand Down

0 comments on commit cc0be8d

Please sign in to comment.