diff --git a/src/vs/editor/contrib/codeAction/browser/codeAction.ts b/src/vs/editor/contrib/codeAction/browser/codeAction.ts index 72d71d496f0..ff3e5abdb7b 100644 --- a/src/vs/editor/contrib/codeAction/browser/codeAction.ts +++ b/src/vs/editor/contrib/codeAction/browser/codeAction.ts @@ -16,7 +16,7 @@ import * as languages from 'vs/editor/common/languages'; import { IModelService } from 'vs/editor/common/services/model'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { IProgress, Progress } from 'vs/platform/progress/common/progress'; -import { CodeActionFilter, CodeActionKind, CodeActionTrigger, filtersAction, mayIncludeActionsOfKind } from './types'; +import { CodeActionFilter, CodeActionKind, CodeActionTrigger, CodeActionTriggerSource, filtersAction, mayIncludeActionsOfKind } from './types'; import { LanguageFeatureRegistry } from 'vs/editor/common/languageFeatureRegistry'; import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; @@ -224,7 +224,6 @@ function getDocumentation( } } } - return undefined; } @@ -254,7 +253,7 @@ CommandsRegistry.registerCommand('_executeCodeActionProvider', async function (a codeActionProvider, model, validatedRangeOrSelection, - { type: languages.CodeActionTriggerType.Invoke, filter: { includeSourceActions: true, include } }, + { type: languages.CodeActionTriggerType.Invoke, triggerAction: CodeActionTriggerSource.Default, filter: { includeSourceActions: true, include } }, Progress.None, CancellationToken.None); diff --git a/src/vs/editor/contrib/codeAction/browser/codeActionCommands.ts b/src/vs/editor/contrib/codeAction/browser/codeActionCommands.ts index 7a4920f5dd6..408079626e5 100644 --- a/src/vs/editor/contrib/codeAction/browser/codeActionCommands.ts +++ b/src/vs/editor/contrib/codeAction/browser/codeActionCommands.ts @@ -31,7 +31,7 @@ import { IEditorProgressService } from 'vs/platform/progress/common/progress'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { CodeActionModel, CodeActionsState, SUPPORTED_CODE_ACTIONS } from './codeActionModel'; -import { CodeActionAutoApply, CodeActionCommandArgs, CodeActionFilter, CodeActionKind, CodeActionTrigger } from './types'; +import { CodeActionAutoApply, CodeActionCommandArgs, CodeActionFilter, CodeActionKind, CodeActionTrigger, CodeActionTriggerSource } from './types'; function contextKeyForSupportedActions(kind: CodeActionKind) { return ContextKeyExpr.regex( @@ -39,7 +39,7 @@ function contextKeyForSupportedActions(kind: CodeActionKind) { new RegExp('(\\s|^)' + escapeRegExpCharacters(kind.value) + '\\b')); } -function RefactorTrigger(editor: ICodeEditor, userArgs: any, preview: boolean) { +function refactorTrigger(editor: ICodeEditor, userArgs: any, preview: boolean, codeActionFrom: CodeActionTriggerSource) { const args = CodeActionCommandArgs.fromUser(userArgs, { kind: CodeActionKind.Refactor, apply: CodeActionAutoApply.Never @@ -56,7 +56,7 @@ function RefactorTrigger(editor: ICodeEditor, userArgs: any, preview: boolean) { include: CodeActionKind.Refactor.contains(args.kind) ? args.kind : CodeActionKind.None, onlyIncludePreferredActions: args.preferred }, - args.apply, preview); + args.apply, preview, codeActionFrom); } const argsSchema: IJSONSchema = { @@ -120,7 +120,7 @@ export class QuickFixController extends Disposable implements IEditorContributio await this._applyCodeAction(action, preview); } finally { if (retrigger) { - this._trigger({ type: CodeActionTriggerType.Auto, filter: {} }); + this._trigger({ type: CodeActionTriggerType.Auto, triggerAction: CodeActionTriggerSource.QuickFix, filter: {} }); } } } @@ -133,14 +133,16 @@ export class QuickFixController extends Disposable implements IEditorContributio } public showCodeActions(trigger: CodeActionTrigger, actions: CodeActionSet, at: IAnchor | IPosition) { - return this._ui.getValue().showCodeActionList(trigger, actions, at, { includeDisabledActions: false }); + return this._ui.getValue().showCodeActionList(trigger, actions, at, { includeDisabledActions: false, fromLightbulb: false }); } public manualTriggerAtCurrentPosition( notAvailableMessage: string, + triggerAction: CodeActionTriggerSource, filter?: CodeActionFilter, autoApply?: CodeActionAutoApply, - preview?: boolean + preview?: boolean, + ): void { if (!this._editor.hasModel()) { return; @@ -148,7 +150,7 @@ export class QuickFixController extends Disposable implements IEditorContributio MessageController.get(this._editor)?.closeMessage(); const triggerPosition = this._editor.getPosition(); - this._trigger({ type: CodeActionTriggerType.Invoke, filter, autoApply, context: { notAvailableMessage, position: triggerPosition }, preview }); + this._trigger({ type: CodeActionTriggerType.Invoke, triggerAction, filter, autoApply, context: { notAvailableMessage, position: triggerPosition }, preview }); } private _trigger(trigger: CodeActionTrigger) { @@ -166,6 +168,7 @@ export enum ApplyCodeActionReason { FromCodeActions = 'fromCodeActions' } + export async function applyCodeAction( accessor: ServicesAccessor, item: CodeActionItem, @@ -181,13 +184,13 @@ export async function applyCodeAction( codeActionTitle: string; codeActionKind: string | undefined; codeActionIsPreferred: boolean; - reason?: ApplyCodeActionReason; + reason: ApplyCodeActionReason; }; type ApplyCodeEventClassification = { codeActionTitle: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The display label of the applied code action' }; codeActionKind: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The kind (refactor, quickfix) of the applied code action' }; codeActionIsPreferred: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Was the code action marked as being a preferred action?' }; - reason?: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The kind of action used to trigger apply code action.' }; + reason: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The kind of action used to trigger apply code action.' }; owner: 'mjbvz'; comment: 'Event used to gain insights into which code actions are being triggered'; }; @@ -196,7 +199,7 @@ export async function applyCodeAction( codeActionTitle: item.action.title, codeActionKind: item.action.kind, codeActionIsPreferred: !!item.action.isPreferred, - reason: codeActionReason + reason: codeActionReason, }); await item.resolve(CancellationToken.None); @@ -240,11 +243,12 @@ function triggerCodeActionsForEditorSelection( notAvailableMessage: string, filter: CodeActionFilter | undefined, autoApply: CodeActionAutoApply | undefined, - preview: boolean = false + preview: boolean = false, + triggerAction: CodeActionTriggerSource = CodeActionTriggerSource.Default ): void { if (editor.hasModel()) { const controller = QuickFixController.get(editor); - controller?.manualTriggerAtCurrentPosition(notAvailableMessage, filter, autoApply, preview); + controller?.manualTriggerAtCurrentPosition(notAvailableMessage, triggerAction, filter, autoApply, preview); } } @@ -267,7 +271,7 @@ export class QuickFixAction extends EditorAction { } public run(_accessor: ServicesAccessor, editor: ICodeEditor): void { - return triggerCodeActionsForEditorSelection(editor, nls.localize('editor.action.quickFix.noneMessage', "No code actions available"), undefined, undefined); + return triggerCodeActionsForEditorSelection(editor, nls.localize('editor.action.quickFix.noneMessage', "No code actions available"), undefined, undefined, false, CodeActionTriggerSource.QuickFix); } } @@ -338,7 +342,7 @@ export class RefactorAction extends EditorAction { } public run(_accessor: ServicesAccessor, editor: ICodeEditor, userArgs: any): void { - return RefactorTrigger(editor, userArgs, false); + return refactorTrigger(editor, userArgs, false, CodeActionTriggerSource.Refactor); } } @@ -358,7 +362,7 @@ export class RefactorPreview extends EditorAction { } public run(_accessor: ServicesAccessor, editor: ICodeEditor, userArgs: any): void { - return RefactorTrigger(editor, userArgs, true); + return refactorTrigger(editor, userArgs, true, CodeActionTriggerSource.RefactorPreview); } } @@ -402,7 +406,7 @@ export class SourceAction extends EditorAction { includeSourceActions: true, onlyIncludePreferredActions: args.preferred, }, - args.apply); + args.apply, undefined, CodeActionTriggerSource.SourceAction); } } @@ -428,7 +432,7 @@ export class OrganizeImportsAction extends EditorAction { return triggerCodeActionsForEditorSelection(editor, nls.localize('editor.action.organize.noneMessage', "No organize imports action available"), { include: CodeActionKind.SourceOrganizeImports, includeSourceActions: true }, - CodeActionAutoApply.IfSingle); + CodeActionAutoApply.IfSingle, undefined, CodeActionTriggerSource.OrganizeImports); } } @@ -449,7 +453,7 @@ export class FixAllAction extends EditorAction { return triggerCodeActionsForEditorSelection(editor, nls.localize('fixAll.noneMessage', "No fix all action available"), { include: CodeActionKind.SourceFixAll, includeSourceActions: true }, - CodeActionAutoApply.IfSingle); + CodeActionAutoApply.IfSingle, undefined, CodeActionTriggerSource.FixAll); } } @@ -483,6 +487,6 @@ export class AutoFixAction extends EditorAction { include: CodeActionKind.QuickFix, onlyIncludePreferredActions: true }, - CodeActionAutoApply.IfSingle); + CodeActionAutoApply.IfSingle, undefined, CodeActionTriggerSource.AutoFix); } } diff --git a/src/vs/editor/contrib/codeAction/browser/codeActionMenu.ts b/src/vs/editor/contrib/codeAction/browser/codeActionMenu.ts index 56da9f50fb3..5536c1c1a45 100644 --- a/src/vs/editor/contrib/codeAction/browser/codeActionMenu.ts +++ b/src/vs/editor/contrib/codeAction/browser/codeActionMenu.ts @@ -17,10 +17,11 @@ import { ScrollType } from 'vs/editor/common/editorCommon'; import { CodeAction, Command } from 'vs/editor/common/languages'; import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; import { codeActionCommandId, CodeActionItem, CodeActionSet, fixAllCommandId, organizeImportsCommandId, refactorCommandId, sourceActionCommandId } from 'vs/editor/contrib/codeAction/browser/codeAction'; -import { CodeActionAutoApply, CodeActionCommandArgs, CodeActionKind, CodeActionTrigger } from 'vs/editor/contrib/codeAction/browser/types'; +import { CodeActionAutoApply, CodeActionCommandArgs, CodeActionKind, CodeActionTrigger, CodeActionTriggerSource } from 'vs/editor/contrib/codeAction/browser/types'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; interface CodeActionWidgetDelegate { onSelectCodeAction: (action: CodeActionItem, trigger: CodeActionTrigger) => Promise; @@ -47,6 +48,7 @@ function stripNewlines(str: string): string { export interface CodeActionShowOptions { readonly includeDisabledActions: boolean; + readonly fromLightbulb?: boolean; } export class CodeActionMenu extends Disposable { @@ -62,6 +64,7 @@ export class CodeActionMenu extends Disposable { @IContextMenuService private readonly _contextMenuService: IContextMenuService, @IKeybindingService keybindingService: IKeybindingService, @ILanguageFeaturesService private readonly _languageFeaturesService: ILanguageFeaturesService, + @ITelemetryService private readonly _telemetryService: ITelemetryService ) { super(); @@ -101,7 +104,30 @@ export class CodeActionMenu extends Disposable { domForShadowRoot: useShadowDOM ? this._editor.getDomNode()! : undefined, getAnchor: () => anchor, getActions: () => menuActions, - onHide: () => { + onHide: (didCancel) => { + const openedFromString = (options.fromLightbulb) ? CodeActionTriggerSource.Lightbulb : trigger.triggerAction; + + type ApplyCodeActionEvent = { + codeActionFrom: CodeActionTriggerSource; + validCodeActions: number; + cancelled: boolean; + }; + + type ApplyCodeEventClassification = { + codeActionFrom: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The kind of action used to opened the code action.' }; + validCodeActions: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The total number of valid actions that are highlighted and can be used.' }; + cancelled: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The indicator if the menu was selected or cancelled.' }; + owner: 'mjbvz'; + comment: 'Event used to gain insights into how code actions are being triggered'; + }; + + this._telemetryService.publicLog2('codeAction.applyCodeAction', { + codeActionFrom: openedFromString, + validCodeActions: codeActions.validActions.length, + cancelled: didCancel, + + }); + this._visible = false; this._editor.focus(); }, @@ -116,7 +142,6 @@ export class CodeActionMenu extends Disposable { documentation: readonly Command[] ): IAction[] { const toCodeActionAction = (item: CodeActionItem): CodeActionAction => new CodeActionAction(item.action, () => this._delegate.onSelectCodeAction(item, trigger)); - const result: IAction[] = actionsToShow .map(toCodeActionAction); diff --git a/src/vs/editor/contrib/codeAction/browser/codeActionModel.ts b/src/vs/editor/contrib/codeAction/browser/codeActionModel.ts index 6eb46ebab45..d2eeb268d1d 100644 --- a/src/vs/editor/contrib/codeAction/browser/codeActionModel.ts +++ b/src/vs/editor/contrib/codeAction/browser/codeActionModel.ts @@ -20,7 +20,7 @@ import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/cont import { IMarkerService } from 'vs/platform/markers/common/markers'; import { IEditorProgressService, Progress } from 'vs/platform/progress/common/progress'; import { CodeActionSet, getCodeActions } from './codeAction'; -import { CodeActionTrigger } from './types'; +import { CodeActionTrigger, CodeActionTriggerSource } from './types'; export const SUPPORTED_CODE_ACTIONS = new RawContextKey('supportedCodeAction', ''); @@ -58,14 +58,14 @@ class CodeActionOracle extends Disposable { if (resources.some(resource => isEqual(resource, model.uri))) { this._autoTriggerTimer.cancelAndSet(() => { - this.trigger({ type: CodeActionTriggerType.Auto }); + this.trigger({ type: CodeActionTriggerType.Auto, triggerAction: CodeActionTriggerSource.Default }); }, this._delay); } } private _onCursorChange(): void { this._autoTriggerTimer.cancelAndSet(() => { - this.trigger({ type: CodeActionTriggerType.Auto }); + this.trigger({ type: CodeActionTriggerType.Auto, triggerAction: CodeActionTriggerSource.Default }); }, this._delay); } @@ -256,7 +256,7 @@ export class CodeActionModel extends Disposable { this.setState(new CodeActionsState.Triggered(trigger.trigger, trigger.selection, trigger.position, actions)); }, undefined); - this._codeActionOracle.value.trigger({ type: CodeActionTriggerType.Auto }); + this._codeActionOracle.value.trigger({ type: CodeActionTriggerType.Auto, triggerAction: CodeActionTriggerSource.Default }); } else { this._supportedCodeActions.reset(); } diff --git a/src/vs/editor/contrib/codeAction/browser/codeActionUi.ts b/src/vs/editor/contrib/codeAction/browser/codeActionUi.ts index 09c82ccd2ea..0f8cd9b237d 100644 --- a/src/vs/editor/contrib/codeAction/browser/codeActionUi.ts +++ b/src/vs/editor/contrib/codeAction/browser/codeActionUi.ts @@ -47,7 +47,7 @@ export class CodeActionUi extends Disposable { this._lightBulbWidget = new Lazy(() => { const widget = this._register(instantiationService.createInstance(LightBulbWidget, this._editor, quickFixActionId, preferredFixActionId)); - this._register(widget.onClick(e => this.showCodeActionList(e.trigger, e.actions, e, { includeDisabledActions: false }))); + this._register(widget.onClick(e => this.showCodeActionList(e.trigger, e.actions, e, { includeDisabledActions: false, fromLightbulb: true }))); return widget; }); } @@ -114,7 +114,7 @@ export class CodeActionUi extends Disposable { } this._activeCodeActions.value = actions; - this._codeActionWidget.getValue().show(newState.trigger, actions, newState.position, { includeDisabledActions }); + this._codeActionWidget.getValue().show(newState.trigger, actions, newState.position, { includeDisabledActions, fromLightbulb: false }); } else { // auto magically triggered if (this._codeActionWidget.getValue().isVisible) { diff --git a/src/vs/editor/contrib/codeAction/browser/types.ts b/src/vs/editor/contrib/codeAction/browser/types.ts index cfc6d344980..b5c4199d390 100644 --- a/src/vs/editor/contrib/codeAction/browser/types.ts +++ b/src/vs/editor/contrib/codeAction/browser/types.ts @@ -44,6 +44,21 @@ export const enum CodeActionAutoApply { Never = 'never', } +export enum CodeActionTriggerSource { + Refactor = 'refactor', + RefactorPreview = 'refactor preview', + Lightbulb = 'lightbulb', + Default = 'other (default)', + SourceAction = 'source action', + QuickFix = 'quick fix action', + FixAll = 'fix all', + OrganizeImports = 'organize imports', + AutoFix = 'auto fix', + QuickFixHover = 'quick fix hover window', + OnSave = 'save participants', + ProblemsView = 'problems view' +} + export interface CodeActionFilter { readonly include?: CodeActionKind; readonly excludes?: readonly CodeActionKind[]; @@ -116,6 +131,7 @@ function excludesAction(providedKind: CodeActionKind, exclude: CodeActionKind, i export interface CodeActionTrigger { readonly type: CodeActionTriggerType; + readonly triggerAction: CodeActionTriggerSource; readonly filter?: CodeActionFilter; readonly autoApply?: CodeActionAutoApply; readonly context?: { diff --git a/src/vs/editor/contrib/codeAction/test/browser/codeAction.test.ts b/src/vs/editor/contrib/codeAction/test/browser/codeAction.test.ts index 44e44b5fae5..d1be6b9d645 100644 --- a/src/vs/editor/contrib/codeAction/test/browser/codeAction.test.ts +++ b/src/vs/editor/contrib/codeAction/test/browser/codeAction.test.ts @@ -11,7 +11,7 @@ import { LanguageFeatureRegistry } from 'vs/editor/common/languageFeatureRegistr import * as languages from 'vs/editor/common/languages'; import { TextModel } from 'vs/editor/common/model/textModel'; import { CodeActionItem, getCodeActions } from 'vs/editor/contrib/codeAction/browser/codeAction'; -import { CodeActionKind } from 'vs/editor/contrib/codeAction/browser/types'; +import { CodeActionKind, CodeActionTriggerSource } from 'vs/editor/contrib/codeAction/browser/types'; import { createTextModel } from 'vs/editor/test/common/testTextModel'; import { IMarkerData, MarkerSeverity } from 'vs/platform/markers/common/markers'; import { Progress } from 'vs/platform/progress/common/progress'; @@ -130,7 +130,7 @@ suite('CodeAction', () => { new CodeActionItem(testData.tsLint.abc, provider) ]; - const { validActions: actions } = await getCodeActions(registry, model, new Range(1, 1, 2, 1), { type: languages.CodeActionTriggerType.Invoke }, Progress.None, CancellationToken.None); + const { validActions: actions } = await getCodeActions(registry, model, new Range(1, 1, 2, 1), { type: languages.CodeActionTriggerType.Invoke, triggerAction: CodeActionTriggerSource.Default }, Progress.None, CancellationToken.None); assert.strictEqual(actions.length, 6); assert.deepStrictEqual(actions, expected); }); @@ -145,20 +145,20 @@ suite('CodeAction', () => { disposables.add(registry.register('fooLang', provider)); { - const { validActions: actions } = await getCodeActions(registry, model, new Range(1, 1, 2, 1), { type: languages.CodeActionTriggerType.Auto, filter: { include: new CodeActionKind('a') } }, Progress.None, CancellationToken.None); + const { validActions: actions } = await getCodeActions(registry, model, new Range(1, 1, 2, 1), { type: languages.CodeActionTriggerType.Auto, triggerAction: CodeActionTriggerSource.Default, filter: { include: new CodeActionKind('a') } }, Progress.None, CancellationToken.None); assert.strictEqual(actions.length, 2); assert.strictEqual(actions[0].action.title, 'a'); assert.strictEqual(actions[1].action.title, 'a.b'); } { - const { validActions: actions } = await getCodeActions(registry, model, new Range(1, 1, 2, 1), { type: languages.CodeActionTriggerType.Auto, filter: { include: new CodeActionKind('a.b') } }, Progress.None, CancellationToken.None); + const { validActions: actions } = await getCodeActions(registry, model, new Range(1, 1, 2, 1), { type: languages.CodeActionTriggerType.Auto, triggerAction: CodeActionTriggerSource.Default, filter: { include: new CodeActionKind('a.b') } }, Progress.None, CancellationToken.None); assert.strictEqual(actions.length, 1); assert.strictEqual(actions[0].action.title, 'a.b'); } { - const { validActions: actions } = await getCodeActions(registry, model, new Range(1, 1, 2, 1), { type: languages.CodeActionTriggerType.Auto, filter: { include: new CodeActionKind('a.b.c') } }, Progress.None, CancellationToken.None); + const { validActions: actions } = await getCodeActions(registry, model, new Range(1, 1, 2, 1), { type: languages.CodeActionTriggerType.Auto, triggerAction: CodeActionTriggerSource.Default, filter: { include: new CodeActionKind('a.b.c') } }, Progress.None, CancellationToken.None); assert.strictEqual(actions.length, 0); } }); @@ -177,7 +177,7 @@ suite('CodeAction', () => { disposables.add(registry.register('fooLang', provider)); - const { validActions: actions } = await getCodeActions(registry, model, new Range(1, 1, 2, 1), { type: languages.CodeActionTriggerType.Auto, filter: { include: new CodeActionKind('a') } }, Progress.None, CancellationToken.None); + const { validActions: actions } = await getCodeActions(registry, model, new Range(1, 1, 2, 1), { type: languages.CodeActionTriggerType.Auto, triggerAction: CodeActionTriggerSource.Default, filter: { include: new CodeActionKind('a') } }, Progress.None, CancellationToken.None); assert.strictEqual(actions.length, 1); assert.strictEqual(actions[0].action.title, 'a'); }); @@ -191,13 +191,13 @@ suite('CodeAction', () => { disposables.add(registry.register('fooLang', provider)); { - const { validActions: actions } = await getCodeActions(registry, model, new Range(1, 1, 2, 1), { type: languages.CodeActionTriggerType.Auto }, Progress.None, CancellationToken.None); + const { validActions: actions } = await getCodeActions(registry, model, new Range(1, 1, 2, 1), { type: languages.CodeActionTriggerType.Auto, triggerAction: CodeActionTriggerSource.SourceAction }, Progress.None, CancellationToken.None); assert.strictEqual(actions.length, 1); assert.strictEqual(actions[0].action.title, 'b'); } { - const { validActions: actions } = await getCodeActions(registry, model, new Range(1, 1, 2, 1), { type: languages.CodeActionTriggerType.Auto, filter: { include: CodeActionKind.Source, includeSourceActions: true } }, Progress.None, CancellationToken.None); + const { validActions: actions } = await getCodeActions(registry, model, new Range(1, 1, 2, 1), { type: languages.CodeActionTriggerType.Auto, triggerAction: CodeActionTriggerSource.Default, filter: { include: CodeActionKind.Source, includeSourceActions: true } }, Progress.None, CancellationToken.None); assert.strictEqual(actions.length, 1); assert.strictEqual(actions[0].action.title, 'a'); } @@ -214,7 +214,7 @@ suite('CodeAction', () => { { const { validActions: actions } = await getCodeActions(registry, model, new Range(1, 1, 2, 1), { - type: languages.CodeActionTriggerType.Auto, filter: { + type: languages.CodeActionTriggerType.Auto, triggerAction: CodeActionTriggerSource.SourceAction, filter: { include: CodeActionKind.Source.append('test'), excludes: [CodeActionKind.Source], includeSourceActions: true, @@ -251,7 +251,7 @@ suite('CodeAction', () => { { const { validActions: actions } = await getCodeActions(registry, model, new Range(1, 1, 2, 1), { - type: languages.CodeActionTriggerType.Auto, filter: { + type: languages.CodeActionTriggerType.Auto, triggerAction: CodeActionTriggerSource.Refactor, filter: { include: baseType, excludes: [subType], } @@ -276,7 +276,7 @@ suite('CodeAction', () => { disposables.add(registry.register('fooLang', provider)); const { validActions: actions } = await getCodeActions(registry, model, new Range(1, 1, 2, 1), { - type: languages.CodeActionTriggerType.Auto, + type: languages.CodeActionTriggerType.Auto, triggerAction: CodeActionTriggerSource.Refactor, filter: { include: CodeActionKind.QuickFix } diff --git a/src/vs/editor/contrib/hover/browser/markerHoverParticipant.ts b/src/vs/editor/contrib/hover/browser/markerHoverParticipant.ts index 8816b84f9e5..813dad2d6b6 100644 --- a/src/vs/editor/contrib/hover/browser/markerHoverParticipant.ts +++ b/src/vs/editor/contrib/hover/browser/markerHoverParticipant.ts @@ -17,7 +17,7 @@ import { CodeActionTriggerType } from 'vs/editor/common/languages'; import { IMarkerDecorationsService } from 'vs/editor/common/services/markerDecorations'; import { CodeActionSet, getCodeActions } from 'vs/editor/contrib/codeAction/browser/codeAction'; import { QuickFixAction, QuickFixController } from 'vs/editor/contrib/codeAction/browser/codeActionCommands'; -import { CodeActionKind, CodeActionTrigger } from 'vs/editor/contrib/codeAction/browser/types'; +import { CodeActionKind, CodeActionTrigger, CodeActionTriggerSource } from 'vs/editor/contrib/codeAction/browser/types'; import { MarkerController, NextMarkerAction } from 'vs/editor/contrib/gotoError/browser/gotoError'; import { HoverAnchor, HoverAnchorType, IEditorHoverParticipant, IEditorHoverRenderContext, IHoverPart } from 'vs/editor/contrib/hover/browser/hoverTypes'; import * as nls from 'vs/nls'; @@ -50,7 +50,8 @@ export class MarkerHover implements IHoverPart { const markerCodeActionTrigger: CodeActionTrigger = { type: CodeActionTriggerType.Invoke, - filter: { include: CodeActionKind.QuickFix } + filter: { include: CodeActionKind.QuickFix }, + triggerAction: CodeActionTriggerSource.QuickFixHover }; export class MarkerHoverParticipant implements IEditorHoverParticipant { diff --git a/src/vs/workbench/api/test/browser/extHostLanguageFeatures.test.ts b/src/vs/workbench/api/test/browser/extHostLanguageFeatures.test.ts index 7112f97075f..c0246ecc81a 100644 --- a/src/vs/workbench/api/test/browser/extHostLanguageFeatures.test.ts +++ b/src/vs/workbench/api/test/browser/extHostLanguageFeatures.test.ts @@ -53,6 +53,7 @@ import { URITransformerService } from 'vs/workbench/api/common/extHostUriTransfo import { OutlineModel } from 'vs/editor/contrib/documentSymbols/browser/outlineModel'; import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; import { LanguageFeaturesService } from 'vs/editor/common/services/languageFeaturesService'; +import { CodeActionTriggerSource } from 'vs/editor/contrib/codeAction/browser/types'; suite('ExtHostLanguageFeatures', function () { @@ -628,7 +629,7 @@ suite('ExtHostLanguageFeatures', function () { })); await rpcProtocol.sync(); - const { validActions: actions } = await getCodeActions(languageFeaturesService.codeActionProvider, model, model.getFullModelRange(), { type: languages.CodeActionTriggerType.Invoke }, Progress.None, CancellationToken.None); + const { validActions: actions } = await getCodeActions(languageFeaturesService.codeActionProvider, model, model.getFullModelRange(), { type: languages.CodeActionTriggerType.Invoke, triggerAction: CodeActionTriggerSource.QuickFix }, Progress.None, CancellationToken.None); assert.strictEqual(actions.length, 2); const [first, second] = actions; assert.strictEqual(first.action.title, 'Testing1'); @@ -652,7 +653,7 @@ suite('ExtHostLanguageFeatures', function () { })); await rpcProtocol.sync(); - const { validActions: actions } = await getCodeActions(languageFeaturesService.codeActionProvider, model, model.getFullModelRange(), { type: languages.CodeActionTriggerType.Invoke }, Progress.None, CancellationToken.None); + const { validActions: actions } = await getCodeActions(languageFeaturesService.codeActionProvider, model, model.getFullModelRange(), { type: languages.CodeActionTriggerType.Invoke, triggerAction: CodeActionTriggerSource.Default }, Progress.None, CancellationToken.None); assert.strictEqual(actions.length, 1); const [first] = actions; assert.strictEqual(first.action.title, 'Testing1'); @@ -675,7 +676,7 @@ suite('ExtHostLanguageFeatures', function () { })); await rpcProtocol.sync(); - const { validActions: actions } = await getCodeActions(languageFeaturesService.codeActionProvider, model, model.getFullModelRange(), { type: languages.CodeActionTriggerType.Invoke }, Progress.None, CancellationToken.None); + const { validActions: actions } = await getCodeActions(languageFeaturesService.codeActionProvider, model, model.getFullModelRange(), { type: languages.CodeActionTriggerType.Invoke, triggerAction: CodeActionTriggerSource.Default }, Progress.None, CancellationToken.None); assert.strictEqual(actions.length, 1); }); @@ -693,7 +694,7 @@ suite('ExtHostLanguageFeatures', function () { })); await rpcProtocol.sync(); - const { validActions: actions } = await getCodeActions(languageFeaturesService.codeActionProvider, model, model.getFullModelRange(), { type: languages.CodeActionTriggerType.Invoke }, Progress.None, CancellationToken.None); + const { validActions: actions } = await getCodeActions(languageFeaturesService.codeActionProvider, model, model.getFullModelRange(), { type: languages.CodeActionTriggerType.Invoke, triggerAction: CodeActionTriggerSource.QuickFix }, Progress.None, CancellationToken.None); assert.strictEqual(actions.length, 1); }); diff --git a/src/vs/workbench/contrib/codeEditor/browser/saveParticipants.ts b/src/vs/workbench/contrib/codeEditor/browser/saveParticipants.ts index 9041f5e39de..4b966ec9bd1 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/saveParticipants.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/saveParticipants.ts @@ -16,7 +16,7 @@ import { ITextModel } from 'vs/editor/common/model'; import { CodeActionTriggerType, CodeActionProvider } from 'vs/editor/common/languages'; import { getCodeActions } from 'vs/editor/contrib/codeAction/browser/codeAction'; import { applyCodeAction, ApplyCodeActionReason } from 'vs/editor/contrib/codeAction/browser/codeActionCommands'; -import { CodeActionKind } from 'vs/editor/contrib/codeAction/browser/types'; +import { CodeActionKind, CodeActionTriggerSource } from 'vs/editor/contrib/codeAction/browser/types'; import { formatDocumentRangesWithSelectedProvider, formatDocumentWithSelectedProvider, FormattingMode } from 'vs/editor/contrib/format/browser/format'; import { SnippetController2 } from 'vs/editor/contrib/snippet/browser/snippetController2'; import { localize } from 'vs/nls'; @@ -375,6 +375,7 @@ class CodeActionOnSaveParticipant implements ITextFileSaveParticipant { private getActionsToRun(model: ITextModel, codeActionKind: CodeActionKind, excludes: readonly CodeActionKind[], progress: IProgress, token: CancellationToken) { return getCodeActions(this.languageFeaturesService.codeActionProvider, model, model.getFullModelRange(), { type: CodeActionTriggerType.Auto, + triggerAction: CodeActionTriggerSource.OnSave, filter: { include: codeActionKind, excludes: excludes, includeSourceActions: true }, }, progress, token); } diff --git a/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts b/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts index e363e609335..1652365c097 100644 --- a/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts +++ b/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts @@ -34,7 +34,7 @@ import { CancelablePromise, createCancelablePromise, Delayer } from 'vs/base/com import { IModelService } from 'vs/editor/common/services/model'; import { Range } from 'vs/editor/common/core/range'; import { getCodeActions, CodeActionSet } from 'vs/editor/contrib/codeAction/browser/codeAction'; -import { CodeActionKind } from 'vs/editor/contrib/codeAction/browser/types'; +import { CodeActionKind, CodeActionTriggerSource } from 'vs/editor/contrib/codeAction/browser/types'; import { ITextModel } from 'vs/editor/common/model'; import { IEditorService, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { applyCodeAction, ApplyCodeActionReason } from 'vs/editor/contrib/codeAction/browser/codeActionCommands'; @@ -627,7 +627,7 @@ export class MarkerViewModel extends Disposable { if (!this.codeActionsPromise) { this.codeActionsPromise = createCancelablePromise(cancellationToken => { return getCodeActions(this.languageFeaturesService.codeActionProvider, model, new Range(this.marker.range.startLineNumber, this.marker.range.startColumn, this.marker.range.endLineNumber, this.marker.range.endColumn), { - type: CodeActionTriggerType.Invoke, filter: { include: CodeActionKind.QuickFix } + type: CodeActionTriggerType.Invoke, triggerAction: CodeActionTriggerSource.ProblemsView, filter: { include: CodeActionKind.QuickFix } }, Progress.None, cancellationToken).then(actions => { return this._register(actions); });