Path: blob/main/src/vs/workbench/contrib/codeActions/browser/codeActionsContribution.ts
3296 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { Emitter, Event } from '../../../../base/common/event.js';6import { HierarchicalKind } from '../../../../base/common/hierarchicalKind.js';7import { IJSONSchema, IJSONSchemaMap } from '../../../../base/common/jsonSchema.js';8import { Disposable } from '../../../../base/common/lifecycle.js';9import { editorConfigurationBaseNode } from '../../../../editor/common/config/editorConfigurationSchema.js';10import { ILanguageFeaturesService } from '../../../../editor/common/services/languageFeatures.js';11import { codeActionCommandId, refactorCommandId, sourceActionCommandId } from '../../../../editor/contrib/codeAction/browser/codeAction.js';12import { CodeActionKind } from '../../../../editor/contrib/codeAction/common/types.js';13import * as nls from '../../../../nls.js';14import { ConfigurationScope, Extensions, IConfigurationNode, IConfigurationPropertySchema, IConfigurationRegistry } from '../../../../platform/configuration/common/configurationRegistry.js';15import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js';16import { Registry } from '../../../../platform/registry/common/platform.js';17import { IWorkbenchContribution } from '../../../common/contributions.js';1819const createCodeActionsAutoSave = (description: string): IJSONSchema => {20return {21type: 'string',22enum: ['always', 'explicit', 'never', true, false],23enumDescriptions: [24nls.localize('alwaysSave', 'Triggers Code Actions on explicit saves and auto saves triggered by window or focus changes.'),25nls.localize('explicitSave', 'Triggers Code Actions only when explicitly saved'),26nls.localize('neverSave', 'Never triggers Code Actions on save'),27nls.localize('explicitSaveBoolean', 'Triggers Code Actions only when explicitly saved. This value will be deprecated in favor of "explicit".'),28nls.localize('neverSaveBoolean', 'Never triggers Code Actions on save. This value will be deprecated in favor of "never".')29],30default: 'explicit',31description: description32};33};3435const createNotebookCodeActionsAutoSave = (description: string): IJSONSchema => {36return {37type: ['string', 'boolean'],38enum: ['explicit', 'never', true, false],39enumDescriptions: [40nls.localize('explicit', 'Triggers Code Actions only when explicitly saved.'),41nls.localize('never', 'Never triggers Code Actions on save.'),42nls.localize('explicitBoolean', 'Triggers Code Actions only when explicitly saved. This value will be deprecated in favor of "explicit".'),43nls.localize('neverBoolean', 'Triggers Code Actions only when explicitly saved. This value will be deprecated in favor of "never".')44],45default: 'explicit',46description: description47};48};495051const codeActionsOnSaveSchema: IConfigurationPropertySchema = {52oneOf: [53{54type: 'object',55additionalProperties: {56type: 'string'57},58},59{60type: 'array',61items: { type: 'string' }62}63],64markdownDescription: nls.localize('editor.codeActionsOnSave', 'Run Code Actions for the editor on save. Code Actions must be specified and the editor must not be shutting down. When {0} is set to `afterDelay`, Code Actions will only be run when the file is saved explicitly. Example: `"source.organizeImports": "explicit" `', '`#files.autoSave#`'),65type: ['object', 'array'],66additionalProperties: {67type: 'string',68enum: ['always', 'explicit', 'never', true, false],69},70default: {},71scope: ConfigurationScope.LANGUAGE_OVERRIDABLE,72};7374export const editorConfiguration = Object.freeze<IConfigurationNode>({75...editorConfigurationBaseNode,76properties: {77'editor.codeActionsOnSave': codeActionsOnSaveSchema78}79});8081const notebookCodeActionsOnSaveSchema: IConfigurationPropertySchema = {82oneOf: [83{84type: 'object',85additionalProperties: {86type: 'string'87},88},89{90type: 'array',91items: { type: 'string' }92}93],94markdownDescription: nls.localize('notebook.codeActionsOnSave', 'Run a series of Code Actions for a notebook on save. Code Actions must be specified and the editor must not be shutting down. When {0} is set to `afterDelay`, Code Actions will only be run when the file is saved explicitly. Example: `"notebook.source.organizeImports": "explicit"`', '`#files.autoSave#`'),95type: 'object',96additionalProperties: {97type: ['string', 'boolean'],98enum: ['explicit', 'never', true, false],99// enum: ['explicit', 'always', 'never'], -- autosave support needs to be built first100// nls.localize('always', 'Always triggers Code Actions on save, including autosave, focus, and window change events.'),101},102default: {}103};104105export const notebookEditorConfiguration = Object.freeze<IConfigurationNode>({106...editorConfigurationBaseNode,107properties: {108'notebook.codeActionsOnSave': notebookCodeActionsOnSaveSchema109}110});111112export class CodeActionsContribution extends Disposable implements IWorkbenchContribution {113114private readonly _onDidChangeSchemaContributions = this._register(new Emitter<void>());115116private _allProvidedCodeActionKinds: HierarchicalKind[] = [];117118constructor(119@IKeybindingService keybindingService: IKeybindingService,120@ILanguageFeaturesService private readonly languageFeatures: ILanguageFeaturesService121) {122super();123124// TODO: @justschen caching of code actions based on extensions loaded: https://github.com/microsoft/vscode/issues/216019125this._register(126Event.runAndSubscribe(127Event.debounce(languageFeatures.codeActionProvider.onDidChange, () => { }, 1000),128() => {129this._allProvidedCodeActionKinds = this.getAllProvidedCodeActionKinds();130this.updateConfigurationSchema(this._allProvidedCodeActionKinds);131this._onDidChangeSchemaContributions.fire();132}));133134this._register(keybindingService.registerSchemaContribution({135getSchemaAdditions: () => this.getKeybindingSchemaAdditions(),136onDidChange: this._onDidChangeSchemaContributions.event,137}));138}139140private getAllProvidedCodeActionKinds(): Array<HierarchicalKind> {141const out = new Map<string, HierarchicalKind>();142for (const provider of this.languageFeatures.codeActionProvider.allNoModel()) {143for (const kind of provider.providedCodeActionKinds ?? []) {144out.set(kind, new HierarchicalKind(kind));145}146}147return Array.from(out.values());148}149150private updateConfigurationSchema(allProvidedKinds: Iterable<HierarchicalKind>): void {151const properties: IJSONSchemaMap = { ...codeActionsOnSaveSchema.properties };152const notebookProperties: IJSONSchemaMap = { ...notebookCodeActionsOnSaveSchema.properties };153for (const codeActionKind of allProvidedKinds) {154if (CodeActionKind.Source.contains(codeActionKind) && !properties[codeActionKind.value]) {155properties[codeActionKind.value] = createCodeActionsAutoSave(nls.localize('codeActionsOnSave.generic', "Controls whether '{0}' actions should be run on file save.", codeActionKind.value));156notebookProperties[codeActionKind.value] = createNotebookCodeActionsAutoSave(nls.localize('codeActionsOnSave.generic', "Controls whether '{0}' actions should be run on file save.", codeActionKind.value));157}158}159codeActionsOnSaveSchema.properties = properties;160notebookCodeActionsOnSaveSchema.properties = notebookProperties;161162Registry.as<IConfigurationRegistry>(Extensions.Configuration)163.notifyConfigurationSchemaUpdated(editorConfiguration);164}165166private getKeybindingSchemaAdditions(): IJSONSchema[] {167const conditionalSchema = (command: string, kinds: readonly string[]): IJSONSchema => {168return {169if: {170required: ['command'],171properties: {172'command': { const: command }173}174},175then: {176properties: {177'args': {178required: ['kind'],179properties: {180'kind': {181anyOf: [182{ enum: Array.from(kinds) },183{ type: 'string' },184]185}186}187}188}189}190};191};192193const filterProvidedKinds = (ofKind: HierarchicalKind): string[] => {194const out = new Set<string>();195for (const providedKind of this._allProvidedCodeActionKinds) {196if (ofKind.contains(providedKind)) {197out.add(providedKind.value);198}199}200return Array.from(out);201};202203return [204conditionalSchema(codeActionCommandId, filterProvidedKinds(HierarchicalKind.Empty)),205conditionalSchema(refactorCommandId, filterProvidedKinds(CodeActionKind.Refactor)),206conditionalSchema(sourceActionCommandId, filterProvidedKinds(CodeActionKind.Source)),207];208}209}210211212