diff --git a/src/vs/workbench/contrib/customEditor/browser/commands.ts b/src/vs/workbench/contrib/customEditor/browser/commands.ts index b6189996d7550..d96619b257a6b 100644 --- a/src/vs/workbench/contrib/customEditor/browser/commands.ts +++ b/src/vs/workbench/contrib/customEditor/browser/commands.ts @@ -10,8 +10,7 @@ import { InputFocusedContextKey } from 'vs/platform/contextkey/common/contextkey import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { CustomEditorInput } from 'vs/workbench/contrib/customEditor/browser/customEditorInput'; -import { defaultCustomEditor } from 'vs/workbench/contrib/customEditor/common/contributedCustomEditors'; -import { CONTEXT_CUSTOM_EDITORS, CONTEXT_FOCUSED_CUSTOM_EDITOR_IS_EDITABLE, ICustomEditorService } from 'vs/workbench/contrib/customEditor/common/customEditor'; +import { CONTEXT_FOCUSED_CUSTOM_EDITOR_IS_EDITABLE } from 'vs/workbench/contrib/customEditor/common/customEditor'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; @@ -67,51 +66,3 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic } }).register(); -(new class ToggleCustomEditorCommand extends Command { - public static readonly ID = 'editor.action.customEditor.toggle'; - - constructor() { - super({ - id: ToggleCustomEditorCommand.ID, - precondition: CONTEXT_CUSTOM_EDITORS, - }); - } - - public runCommand(accessor: ServicesAccessor): void { - const editorService = accessor.get(IEditorService); - const activeEditorPane = editorService.activeEditorPane; - if (!activeEditorPane) { - return; - } - - const activeGroup = activeEditorPane.group; - const activeEditor = activeEditorPane.input; - const targetResource = activeEditor.resource; - - if (!targetResource) { - return; - } - - const customEditorService = accessor.get(ICustomEditorService); - - let toggleView = defaultCustomEditor.id; - if (!(activeEditor instanceof CustomEditorInput)) { - const bestAvailableEditor = customEditorService.getContributedCustomEditors(targetResource).bestAvailableEditor; - if (bestAvailableEditor) { - toggleView = bestAvailableEditor.id; - } else { - return; - } - } - - const newEditorInput = customEditorService.createInput(targetResource, toggleView, activeGroup.id); - - editorService.replaceEditors([{ - editor: activeEditor, - replacement: newEditorInput, - options: { - ignoreOverrides: true, - } - }], activeGroup); - } -}).register(); diff --git a/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts b/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts index f2051f2a71899..4ef2a0dd098c1 100644 --- a/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts +++ b/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts @@ -5,7 +5,7 @@ import * as nls from 'vs/nls'; import { Registry } from 'vs/platform/registry/common/platform'; -import { ToggleAutoSaveAction, GlobalNewUntitledFileAction, FocusFilesExplorer, GlobalCompareResourcesAction, SaveAllAction, ShowActiveFileInExplorer, CollapseExplorerView, RefreshExplorerView, CompareWithClipboardAction, NEW_FILE_COMMAND_ID, NEW_FILE_LABEL, NEW_FOLDER_COMMAND_ID, NEW_FOLDER_LABEL, TRIGGER_RENAME_LABEL, MOVE_FILE_TO_TRASH_LABEL, COPY_FILE_LABEL, PASTE_FILE_LABEL, FileCopiedContext, renameHandler, moveFileToTrashHandler, copyFileHandler, pasteFileHandler, deleteFileHandler, cutFileHandler, DOWNLOAD_COMMAND_ID, openFilePreserveFocusHandler, DOWNLOAD_LABEL, ShowOpenedFileInNewWindow, ReopenResourcesAction } from 'vs/workbench/contrib/files/browser/fileActions'; +import { ToggleAutoSaveAction, GlobalNewUntitledFileAction, FocusFilesExplorer, GlobalCompareResourcesAction, SaveAllAction, ShowActiveFileInExplorer, CollapseExplorerView, RefreshExplorerView, CompareWithClipboardAction, NEW_FILE_COMMAND_ID, NEW_FILE_LABEL, NEW_FOLDER_COMMAND_ID, NEW_FOLDER_LABEL, TRIGGER_RENAME_LABEL, MOVE_FILE_TO_TRASH_LABEL, COPY_FILE_LABEL, PASTE_FILE_LABEL, FileCopiedContext, renameHandler, moveFileToTrashHandler, copyFileHandler, pasteFileHandler, deleteFileHandler, cutFileHandler, DOWNLOAD_COMMAND_ID, openFilePreserveFocusHandler, DOWNLOAD_LABEL, ShowOpenedFileInNewWindow, ReopenResourcesAction, ToggleEditorTypeCommand as ToggleEditorTypeAction } from 'vs/workbench/contrib/files/browser/fileActions'; import { revertLocalChangesCommand, acceptLocalChangesCommand, CONFLICT_RESOLUTION_CONTEXT } from 'vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler'; import { SyncActionDescriptor, MenuId, MenuRegistry, ILocalizedString } from 'vs/platform/actions/common/actions'; import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; @@ -37,6 +37,7 @@ const registry = Registry.as(ActionExtensions.Workbenc registry.registerWorkbenchAction(SyncActionDescriptor.from(SaveAllAction, { primary: undefined, mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_S }, win: { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_S) } }), 'File: Save All', category.value); registry.registerWorkbenchAction(SyncActionDescriptor.from(GlobalCompareResourcesAction), 'File: Compare Active File With...', category.value); registry.registerWorkbenchAction(SyncActionDescriptor.from(ReopenResourcesAction), 'File: Reopen With...', category.value, ActiveEditorAvailableEditorIdsContext); +registry.registerWorkbenchAction(SyncActionDescriptor.from(ToggleEditorTypeAction), 'File: Toggle Editor Type', category.value, ActiveEditorAvailableEditorIdsContext); registry.registerWorkbenchAction(SyncActionDescriptor.from(FocusFilesExplorer), 'File: Focus on Files Explorer', category.value); registry.registerWorkbenchAction(SyncActionDescriptor.from(ShowActiveFileInExplorer), 'File: Reveal Active File in Side Bar', category.value); registry.registerWorkbenchAction(SyncActionDescriptor.from(CollapseExplorerView), 'File: Collapse Folders in Explorer', category.value); diff --git a/src/vs/workbench/contrib/files/browser/fileActions.ts b/src/vs/workbench/contrib/files/browser/fileActions.ts index 154344ce1359f..84ed5a66d0d3b 100644 --- a/src/vs/workbench/contrib/files/browser/fileActions.ts +++ b/src/vs/workbench/contrib/files/browser/fileActions.ts @@ -50,8 +50,8 @@ import { once } from 'vs/base/common/functional'; import { IEditorOptions } from 'vs/platform/editor/common/editor'; import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService'; import { Codicon } from 'vs/base/common/codicons'; -import { openEditorWith } from 'vs/workbench/contrib/files/common/openWith'; import { IViewsService } from 'vs/workbench/common/views'; +import { openEditorWith, getAllAvailableEditors } from 'vs/workbench/contrib/files/common/openWith'; export const NEW_FILE_COMMAND_ID = 'explorer.newFile'; export const NEW_FILE_LABEL = nls.localize('newFile', "New File"); @@ -539,7 +539,44 @@ export class ReopenResourcesAction extends Action { const options = activeEditorPane.options; const group = activeEditorPane.group; - return openEditorWith(activeInput, undefined, options, group, this.editorService, this.configurationService, this.quickInputService); + await openEditorWith(activeInput, undefined, options, group, this.editorService, this.configurationService, this.quickInputService); + } +} + +export class ToggleEditorTypeCommand extends Action { + + static readonly ID = 'workbench.files.action.toggleEditorType'; + static readonly LABEL = nls.localize('workbench.files.action.toggleEditorType', "Toggle Editor Type"); + + constructor( + id: string, + label: string, + @IEditorService private readonly editorService: IEditorService, + ) { + super(id, label); + } + + async run(): Promise { + const activeEditorPane = this.editorService.activeEditorPane; + if (!activeEditorPane) { + return; + } + + const input = activeEditorPane.input; + if (!input.resource) { + return; + } + + const options = activeEditorPane.options; + const group = activeEditorPane.group; + + const overrides = getAllAvailableEditors(input, input.resource, options, group, this.editorService); + const firstNonActiveOverride = overrides.find(([_, entry]) => !entry.active); + if (!firstNonActiveOverride) { + return; + } + + await firstNonActiveOverride[0].open(input, options, group, firstNonActiveOverride[1].id)?.override; } } diff --git a/src/vs/workbench/contrib/files/common/openWith.ts b/src/vs/workbench/contrib/files/common/openWith.ts index e902026eb371d..081121ce4a33c 100644 --- a/src/vs/workbench/contrib/files/common/openWith.ts +++ b/src/vs/workbench/contrib/files/common/openWith.ts @@ -4,16 +4,17 @@ *--------------------------------------------------------------------------------------------*/ import { basename, extname, isEqual } from 'vs/base/common/resources'; +import { URI } from 'vs/base/common/uri'; import * as nls from 'vs/nls'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IEditorOptions, ITextEditorOptions } from 'vs/platform/editor/common/editor'; import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; -import { IEditorInput } from 'vs/workbench/common/editor'; +import { IEditorInput, IEditorPane } from 'vs/workbench/common/editor'; import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput'; import { DEFAULT_EDITOR_ID } from 'vs/workbench/contrib/files/common/files'; import { CustomEditorAssociation, CustomEditorsAssociations, customEditorsAssociationsSettingId } from 'vs/workbench/services/editor/common/editorAssociationsSetting'; import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService'; -import { IEditorService, IOpenEditorOverrideHandler } from 'vs/workbench/services/editor/common/editorService'; +import { IEditorService, IOpenEditorOverrideEntry, IOpenEditorOverrideHandler } from 'vs/workbench/services/editor/common/editorService'; const builtinProviderDisplayName = nls.localize('builtinProviderDisplayName', "Built-in"); @@ -31,23 +32,26 @@ export async function openEditorWith( editorService: IEditorService, configurationService: IConfigurationService, quickInputService: IQuickInputService, -): Promise { +): Promise { const resource = input.resource; if (!resource) { return; } - const resourceExt = extname(resource); - const overrides = editorService.getEditorOverrides(input, options, group); + const allEditorOverrides = getAllAvailableEditors(input, resource, options, group, editorService); + if (!allEditorOverrides.length) { + return; + } - const overrideToUse = overrides.find(([_, entry]) => entry.id === id); + const overrideToUse = typeof id === 'string' && allEditorOverrides.find(([_, entry]) => entry.id === id); if (overrideToUse) { - overrideToUse[0].open(input, options, group, id); - return; + return overrideToUse[0].open(input, options, group, id)?.override; } // Prompt - const items: (IQuickPickItem & { handler?: IOpenEditorOverrideHandler })[] = overrides.map((override) => { + const resourceExt = extname(resource); + + const items: (IQuickPickItem & { handler: IOpenEditorOverrideHandler })[] = allEditorOverrides.map((override) => { return { handler: override[0], id: override[1].id, @@ -61,31 +65,14 @@ export async function openEditorWith( }; }); - if (!items.length) { - return; - } - - if (!items.find(item => item.id === DEFAULT_EDITOR_ID)) { - items.unshift({ - id: DEFAULT_EDITOR_ID, - label: nls.localize('promptOpenWith.defaultEditor.displayName', "Text Editor"), - description: editorService.activeEditor instanceof FileEditorInput && isEqual(editorService.activeEditor.resource, resource) ? nls.localize('promptOpenWith.currentlyActive', 'Currently Active') : undefined, - detail: builtinProviderDisplayName, - buttons: resourceExt ? [{ - iconClass: 'codicon-settings-gear', - tooltip: nls.localize('promptOpenWith.setDefaultTooltip', "Set as default editor for '{0}' files", resourceExt) - }] : undefined - }); - } - - const picker = quickInputService.createQuickPick<(IQuickPickItem & { handler?: IOpenEditorOverrideHandler })>(); + const picker = quickInputService.createQuickPick<(IQuickPickItem & { handler: IOpenEditorOverrideHandler })>(); picker.items = items; if (items.length) { picker.selectedItems = [items[0]]; } picker.placeholder = nls.localize('promptOpenWith.placeHolder', "Select editor for '{0}'", basename(resource)); - const pickedItem = await new Promise<(IQuickPickItem & { handler?: IOpenEditorOverrideHandler }) | undefined>(resolve => { + const pickedItem = await new Promise<(IQuickPickItem & { handler: IOpenEditorOverrideHandler }) | undefined>(resolve => { picker.onDidAccept(() => { resolve(picker.selectedItems.length === 1 ? picker.selectedItems[0] : undefined); picker.dispose(); @@ -121,18 +108,40 @@ export async function openEditorWith( picker.show(); }); - if (!pickedItem) { - return; - } + return pickedItem?.handler.open(input!, options, group, pickedItem.id)?.override; +} - if (pickedItem.id === DEFAULT_EDITOR_ID) { - const fileEditorInput = editorService.createEditorInput({ resource: resource!, forceFile: true }); - const textOptions = options ? { ...options, ignoreOverrides: true } : { ignoreOverrides: true }; +/** + * Get a list of all available editors, including the default text editor. + */ +export function getAllAvailableEditors( + input: IEditorInput, + resource: URI, + options: IEditorOptions | ITextEditorOptions | undefined, + group: IEditorGroup, + editorService: IEditorService, +): Array<[IOpenEditorOverrideHandler, IOpenEditorOverrideEntry]> { + const overrides = editorService.getEditorOverrides(input, options, group); + if (!overrides.some(([_, entry]) => entry.id === DEFAULT_EDITOR_ID)) { + overrides.unshift([ + { + open: (input: IEditorInput, options: IEditorOptions | ITextEditorOptions | undefined, group: IEditorGroup) => { + if (!input.resource) { + return; + } - await editorService.openEditor(fileEditorInput, textOptions, group); - return; + const fileEditorInput = editorService.createEditorInput({ resource: input.resource, forceFile: true }); + const textOptions = options ? { ...options, ignoreOverrides: true } : { ignoreOverrides: true }; + return { override: editorService.openEditor(fileEditorInput, textOptions, group) }; + } + }, + { + id: DEFAULT_EDITOR_ID, + active: editorService.activeEditor instanceof FileEditorInput && isEqual(editorService.activeEditor.resource, resource), + label: nls.localize('promptOpenWith.defaultEditor.displayName', "Text Editor"), + detail: builtinProviderDisplayName, + }]); } - - pickedItem.handler!.open(input!, options, group, pickedItem.id); - return; + return overrides; } +