From f1024f82e2add3944db2ef610c1ad7e0a304d1ba Mon Sep 17 00:00:00 2001 From: Logan Ramos Date: Wed, 17 Feb 2021 09:26:11 -0500 Subject: [PATCH 01/18] Move editorOpenWith to the editorService --- src/vs/platform/editor/common/editor.ts | 3 +- .../api/browser/mainThreadNotebook.ts | 7 +- .../browser/parts/editor/editorActions.ts | 9 +- .../browser/parts/editor/editorCommands.ts | 3 +- src/vs/workbench/common/editor.ts | 26 +- .../customEditor/browser/customEditors.ts | 3 +- .../common/contributedCustomEditors.ts | 6 +- .../files/browser/editors/binaryFileEditor.ts | 7 +- .../contrib/files/browser/fileCommands.ts | 6 +- .../notebook/browser/notebook.contribution.ts | 3 +- .../notebook/browser/notebookServiceImpl.ts | 4 +- .../services/editor/browser/editorService.ts | 251 +++++++++++++- .../services/editor/common/editorOpenWith.ts | 309 ------------------ 13 files changed, 286 insertions(+), 351 deletions(-) delete mode 100644 src/vs/workbench/services/editor/common/editorOpenWith.ts diff --git a/src/vs/platform/editor/common/editor.ts b/src/vs/platform/editor/common/editor.ts index 585bef41bfb7e..add2d617c8343 100644 --- a/src/vs/platform/editor/common/editor.ts +++ b/src/vs/platform/editor/common/editor.ts @@ -206,8 +206,9 @@ export interface IEditorOptions { * - `undefined`: let the editor decide for itself * - `false`: disable overrides * - `string`: specific override by id + * - `null`: shows a picker for the user to decide */ - readonly override?: false | string; + readonly override?: false | string | null; /** * A optional hint to signal in which context the editor opens. diff --git a/src/vs/workbench/api/browser/mainThreadNotebook.ts b/src/vs/workbench/api/browser/mainThreadNotebook.ts index b9ba768326ddc..7be9e55a10cc2 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebook.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebook.ts @@ -16,7 +16,6 @@ import { URI, UriComponents } from 'vs/base/common/uri'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { EditorActivation, ITextEditorOptions } from 'vs/platform/editor/common/editor'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ILogService } from 'vs/platform/log/common/log'; import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; import { viewColumnToEditorGroup } from 'vs/workbench/common/editor'; @@ -28,7 +27,6 @@ import { ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, CellEditType, DisplayOrderKey, ICell import { INotebookEditorModelResolverService } from 'vs/workbench/contrib/notebook/common/notebookEditorModelResolverService'; import { IMainNotebookController, INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { IEditorGroup, IEditorGroupsService, preferredSideBySideGroupDirection } from 'vs/workbench/services/editor/common/editorGroupsService'; -import { openEditorWith } from 'vs/workbench/services/editor/common/editorOpenWith'; import { IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService'; @@ -132,8 +130,7 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo @ILogService private readonly _logService: ILogService, @INotebookCellStatusBarService private readonly _cellStatusBarService: INotebookCellStatusBarService, @INotebookEditorModelResolverService private readonly _notebookModelResolverService: INotebookEditorModelResolverService, - @IUriIdentityService private readonly _uriIdentityService: IUriIdentityService, - @IInstantiationService private readonly _instantiationService: IInstantiationService, + @IUriIdentityService private readonly _uriIdentityService: IUriIdentityService ) { super(); this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostNotebook); @@ -717,7 +714,7 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo const input = this._editorService.createEditorInput({ resource: URI.revive(resource), options: editorOptions }); // TODO: handle options.selection - const editorPane = await this._instantiationService.invokeFunction(openEditorWith, input, viewType, options, group); + const editorPane = await this._editorService.openEditor(input, { override: viewType, ...options }, group); const notebookEditor = (editorPane as unknown as { isNotebookEditor?: boolean })?.isNotebookEditor ? (editorPane!.getControl() as INotebookEditor) : undefined; if (notebookEditor) { diff --git a/src/vs/workbench/browser/parts/editor/editorActions.ts b/src/vs/workbench/browser/parts/editor/editorActions.ts index db48f41714888..5a5d60b656ce1 100644 --- a/src/vs/workbench/browser/parts/editor/editorActions.ts +++ b/src/vs/workbench/browser/parts/editor/editorActions.ts @@ -22,8 +22,6 @@ import { ItemActivation, IQuickInputService } from 'vs/platform/quickinput/commo import { AllEditorsByMostRecentlyUsedQuickAccess, ActiveGroupEditorsByMostRecentlyUsedQuickAccess, AllEditorsByAppearanceQuickAccess } from 'vs/workbench/browser/parts/editor/editorQuickAccess'; import { Codicon } from 'vs/base/common/codicons'; import { IFilesConfigurationService, AutoSaveMode } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; -import { openEditorWith, getAllAvailableEditors } from 'vs/workbench/services/editor/common/editorOpenWith'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; export class ExecuteCommandAction extends Action { @@ -1894,8 +1892,7 @@ export class ReopenResourcesAction extends Action { constructor( id: string, label: string, - @IEditorService private readonly editorService: IEditorService, - @IInstantiationService private readonly instantiationService: IInstantiationService, + @IEditorService private readonly editorService: IEditorService ) { super(id, label); } @@ -1913,7 +1910,7 @@ export class ReopenResourcesAction extends Action { const options = activeEditorPane.options; const group = activeEditorPane.group; - await this.instantiationService.invokeFunction(openEditorWith, activeInput, undefined, options, group); + await this.editorService.openEditor(activeInput, { override: null, ...options }, group); } } @@ -1944,7 +1941,7 @@ export class ToggleEditorTypeAction extends Action { const options = activeEditorPane.options; const group = activeEditorPane.group; - const overrides = getAllAvailableEditors(activeEditorResource, undefined, options, group, this.editorService); + const overrides = this.editorService.getEditorOverrides(activeEditorResource, options, group); const firstNonActiveOverride = overrides.find(([_, entry]) => !entry.active); if (!firstNonActiveOverride) { return; diff --git a/src/vs/workbench/browser/parts/editor/editorCommands.ts b/src/vs/workbench/browser/parts/editor/editorCommands.ts index f9fd2d5716473..4a1c222f1319c 100644 --- a/src/vs/workbench/browser/parts/editor/editorCommands.ts +++ b/src/vs/workbench/browser/parts/editor/editorCommands.ts @@ -25,7 +25,6 @@ import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; import { ActiveGroupEditorsByMostRecentlyUsedQuickAccess } from 'vs/workbench/browser/parts/editor/editorQuickAccess'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { ITextEditorOptions } from 'vs/platform/editor/common/editor'; -import { openEditorWith } from 'vs/workbench/services/editor/common/editorOpenWith'; export const CLOSE_SAVED_EDITORS_COMMAND_ID = 'workbench.action.closeUnmodifiedEditors'; export const CLOSE_EDITORS_IN_GROUP_COMMAND_ID = 'workbench.action.closeEditorsInGroup'; @@ -502,7 +501,7 @@ function registerOpenEditorAPICommands(): void { const textOptions: ITextEditorOptions = optionsArg ? { ...optionsArg, override: false } : { override: false }; const input = editorService.createEditorInput({ resource: URI.revive(resource) }); - return openEditorWith(accessor, input, id, textOptions, group); + return editorService.openEditor(input, { override: id, ...textOptions }, group); }); } diff --git a/src/vs/workbench/common/editor.ts b/src/vs/workbench/common/editor.ts index 5471bd95d6f92..9e2412787cef9 100644 --- a/src/vs/workbench/common/editor.ts +++ b/src/vs/workbench/common/editor.ts @@ -20,7 +20,7 @@ import { ActionRunner, IAction } from 'vs/base/common/actions'; import { IFileService } from 'vs/platform/files/common/files'; import { IPathData } from 'vs/platform/windows/common/windows'; import { coalesce, firstOrDefault } from 'vs/base/common/arrays'; -import { ACTIVE_GROUP, IResourceEditorInputType, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; +import { ACTIVE_GROUP, ICustomEditorInfo, IResourceEditorInputType, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { IRange } from 'vs/editor/common/core/range'; import { IExtUri } from 'vs/base/common/resources'; @@ -1009,7 +1009,7 @@ export class EditorOptions implements IEditorOptions { * - `false`: disable overrides * - `string`: specific override by id */ - override?: false | string; + override?: false | string | null; /** * A optional hint to signal in which context the editor opens. @@ -1067,7 +1067,7 @@ export class EditorOptions implements IEditorOptions { this.index = options.index; } - if (typeof options.override === 'string' || options.override === false) { + if (typeof options.override === 'string' || options.override === false || options.override === null) { this.override = options.override; } @@ -1637,3 +1637,23 @@ export function editorGroupToViewColumn(editorGroupService: IEditorGroupsService } //#endregion + +//#region Editor Open With +export const customEditorsAssociationsSettingId = 'workbench.editorAssociations'; + +export const builtinProviderDisplayName = localize('builtinProviderDisplayName', "Built-in"); + +export const DEFAULT_CUSTOM_EDITOR: ICustomEditorInfo = { + id: 'default', + displayName: localize('promptOpenWith.defaultEditor.displayName', "Text Editor"), + providerDisplayName: builtinProviderDisplayName +}; + +export type CustomEditorAssociation = { + readonly viewType: string; + readonly filenamePattern?: string; +}; + +export type CustomEditorsAssociations = readonly CustomEditorAssociation[]; + +////#endregion diff --git a/src/vs/workbench/contrib/customEditor/browser/customEditors.ts b/src/vs/workbench/contrib/customEditor/browser/customEditors.ts index 66de7e83c0c35..40f8ab9e70e2d 100644 --- a/src/vs/workbench/contrib/customEditor/browser/customEditors.ts +++ b/src/vs/workbench/contrib/customEditor/browser/customEditors.ts @@ -24,13 +24,12 @@ import { IStorageService } from 'vs/platform/storage/common/storage'; import * as colorRegistry from 'vs/platform/theme/common/colorRegistry'; import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; -import { EditorInput, EditorOptions, Extensions as EditorInputExtensions, GroupIdentifier, IEditorInput, IEditorInputFactoryRegistry, IEditorPane } from 'vs/workbench/common/editor'; +import { CustomEditorAssociation, CustomEditorsAssociations, customEditorsAssociationsSettingId, EditorInput, EditorOptions, Extensions as EditorInputExtensions, GroupIdentifier, IEditorInput, IEditorInputFactoryRegistry, IEditorPane } from 'vs/workbench/common/editor'; import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; import { CONTEXT_CUSTOM_EDITORS, CONTEXT_FOCUSED_CUSTOM_EDITOR_IS_EDITABLE, CustomEditorCapabilities, CustomEditorInfo, CustomEditorInfoCollection, CustomEditorPriority, ICustomEditorService } from 'vs/workbench/contrib/customEditor/common/customEditor'; import { CustomEditorModelManager } from 'vs/workbench/contrib/customEditor/common/customEditorModelManager'; import { IWebviewService } from 'vs/workbench/contrib/webview/browser/webview'; import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; -import { CustomEditorAssociation, CustomEditorsAssociations, customEditorsAssociationsSettingId } from 'vs/workbench/services/editor/common/editorOpenWith'; import { ICustomEditorInfo, ICustomEditorViewTypesHandler, IEditorService, IOpenEditorOverride, IOpenEditorOverrideEntry } from 'vs/workbench/services/editor/common/editorService'; import { ContributedCustomEditors, defaultCustomEditor } from '../common/contributedCustomEditors'; import { CustomEditorInput } from './customEditorInput'; diff --git a/src/vs/workbench/contrib/customEditor/common/contributedCustomEditors.ts b/src/vs/workbench/contrib/customEditor/common/contributedCustomEditors.ts index 98fcdb9b97474..a0139a2ae0234 100644 --- a/src/vs/workbench/contrib/customEditor/common/contributedCustomEditors.ts +++ b/src/vs/workbench/contrib/customEditor/common/contributedCustomEditors.ts @@ -9,16 +9,14 @@ import { URI } from 'vs/base/common/uri'; import * as nls from 'vs/nls'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; +import { builtinProviderDisplayName } from 'vs/workbench/common/editor'; import { Memento } from 'vs/workbench/common/memento'; import { CustomEditorDescriptor, CustomEditorInfo, CustomEditorPriority } from 'vs/workbench/contrib/customEditor/common/customEditor'; import { customEditorsExtensionPoint, ICustomEditorsExtensionPoint } from 'vs/workbench/contrib/customEditor/common/extensionPoint'; -import { DEFAULT_EDITOR_ID } from 'vs/workbench/services/editor/common/editorOpenWith'; import { IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry'; -const builtinProviderDisplayName = nls.localize('builtinProviderDisplayName', "Built-in"); - export const defaultCustomEditor = new CustomEditorInfo({ - id: DEFAULT_EDITOR_ID, + id: 'default', displayName: nls.localize('promptOpenWith.defaultEditor.displayName', "Text Editor"), providerDisplayName: builtinProviderDisplayName, selector: [ diff --git a/src/vs/workbench/contrib/files/browser/editors/binaryFileEditor.ts b/src/vs/workbench/contrib/files/browser/editors/binaryFileEditor.ts index 1cc203a01e339..855cbca8cc086 100644 --- a/src/vs/workbench/contrib/files/browser/editors/binaryFileEditor.ts +++ b/src/vs/workbench/contrib/files/browser/editors/binaryFileEditor.ts @@ -13,8 +13,7 @@ import { BINARY_FILE_EDITOR_ID } from 'vs/workbench/contrib/files/common/files'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IOpenerService } from 'vs/platform/opener/common/opener'; -import { openEditorWith } from 'vs/workbench/services/editor/common/editorOpenWith'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; /** * An implementation of editor for binary files that cannot be displayed. @@ -27,7 +26,7 @@ export class BinaryFileEditor extends BaseBinaryResourceEditor { @ITelemetryService telemetryService: ITelemetryService, @IThemeService themeService: IThemeService, @IOpenerService private readonly openerService: IOpenerService, - @IInstantiationService private readonly instantiationService: IInstantiationService, + @IEditorService private readonly editorService: IEditorService, @IStorageService storageService: IStorageService, @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService, ) { @@ -51,7 +50,7 @@ export class BinaryFileEditor extends BaseBinaryResourceEditor { input.setForceOpenAsText(); // If more editors are installed that can handle this input, show a picker - await this.instantiationService.invokeFunction(openEditorWith, input, undefined, options, this.group); + await this.editorService.openEditor(input, { override: null, ...options }, this.group); } } diff --git a/src/vs/workbench/contrib/files/browser/fileCommands.ts b/src/vs/workbench/contrib/files/browser/fileCommands.ts index d33543aaf0f5e..0fd6cd981c76d 100644 --- a/src/vs/workbench/contrib/files/browser/fileCommands.ts +++ b/src/vs/workbench/contrib/files/browser/fileCommands.ts @@ -41,7 +41,6 @@ import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/embeddedCodeEditorWidget'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; -import { openEditorWith } from 'vs/workbench/services/editor/common/editorOpenWith'; import { isPromiseCanceledError } from 'vs/base/common/errors'; import { toAction } from 'vs/base/common/actions'; @@ -359,7 +358,8 @@ CommandsRegistry.registerCommand({ const uri = getResourceForCommand(resource, accessor.get(IListService), accessor.get(IEditorService)); if (uri) { const input = editorService.createEditorInput({ resource: uri }); - openEditorWith(accessor, input, undefined, undefined, editorGroupsService.activeGroup); + editorService.openEditor(input, { override: null }, editorGroupsService.activeGroup); + //openEditorWith(accessor, input, undefined, undefined, editorGroupsService.activeGroup); } } }); @@ -680,7 +680,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ const textInput = editorService.createEditorInput({ options: { pinned: true } }); const group = editorGroupsService.activeGroup; - await openEditorWith(accessor, textInput, args.viewType, { pinned: true }, group); + await editorService.openEditor(textInput, { override: args.viewType, pinned: true }, group); } else { await editorService.openEditor({ options: { pinned: true } }); // untitled are always pinned } diff --git a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts index a5d6464c50c32..cc58e8c3e2619 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts @@ -24,7 +24,7 @@ import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle import { Registry } from 'vs/platform/registry/common/platform'; import { EditorDescriptor, Extensions as EditorExtensions, IEditorRegistry } from 'vs/workbench/browser/editor'; import { Extensions as WorkbenchExtensions, IWorkbenchContribution, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; -import { EditorInput, Extensions as EditorInputExtensions, ICustomEditorInputFactory, IEditorInput, IEditorInputFactory, IEditorInputFactoryRegistry } from 'vs/workbench/common/editor'; +import { CustomEditorsAssociations, customEditorsAssociationsSettingId, EditorInput, Extensions as EditorInputExtensions, ICustomEditorInputFactory, IEditorInput, IEditorInputFactory, IEditorInputFactoryRegistry } from 'vs/workbench/common/editor'; import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; import { NotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookEditor'; import { NotebookEditorInput } from 'vs/workbench/contrib/notebook/browser/notebookEditorInput'; @@ -34,7 +34,6 @@ import { CellKind, CellToolbarLocKey, CellUri, DisplayOrderKey, getCellUndoRedoC import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorService, IOpenEditorOverride } from 'vs/workbench/services/editor/common/editorService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { CustomEditorsAssociations, customEditorsAssociationsSettingId } from 'vs/workbench/services/editor/common/editorOpenWith'; import { CustomEditorInfo } from 'vs/workbench/contrib/customEditor/common/customEditor'; import { INotebookEditor, IN_NOTEBOOK_TEXT_DIFF_EDITOR, NotebookEditorOptions, NOTEBOOK_EDITOR_OPEN } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; diff --git a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts index 5049ff03ab4b3..f1cfb01b2a292 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts @@ -18,13 +18,13 @@ import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { BareFontInfo } from 'vs/editor/common/config/fontInfo'; import { CopyAction, CutAction, PasteAction } from 'vs/editor/contrib/clipboard/clipboard'; -import * as nls from 'vs/nls'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { NotebookExtensionDescription } from 'vs/workbench/api/common/extHost.protocol'; +import { builtinProviderDisplayName } from 'vs/workbench/common/editor'; import { Memento } from 'vs/workbench/common/memento'; import { INotebookEditorContribution, notebookMarkdownRendererExtensionPoint, notebookProviderExtensionPoint, notebookRendererExtensionPoint } from 'vs/workbench/contrib/notebook/browser/extensionPoint'; import { CellEditState, getActiveNotebookEditor, ICellViewModel, INotebookEditor, NotebookEditorOptions, updateEditorTopPadding } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; @@ -126,7 +126,7 @@ export class NotebookProviderInfoStore extends Disposable { priority: this._convertPriority(notebookContribution.priority), providerExtensionId: extension.description.identifier.value, providerDescription: extension.description.description, - providerDisplayName: extension.description.isBuiltin ? nls.localize('builtinProviderDisplayName', "Built-in") : extension.description.displayName || extension.description.identifier.value, + providerDisplayName: extension.description.isBuiltin ? builtinProviderDisplayName : extension.description.displayName || extension.description.identifier.value, providerExtensionLocation: extension.description.extensionLocation, dynamicContribution: false, exclusive: false diff --git a/src/vs/workbench/services/editor/browser/editorService.ts b/src/vs/workbench/services/editor/browser/editorService.ts index b99e476e2df03..26365952dc911 100644 --- a/src/vs/workbench/services/editor/browser/editorService.ts +++ b/src/vs/workbench/services/editor/browser/editorService.ts @@ -6,7 +6,7 @@ import * as nls from 'vs/nls'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IResourceEditorInput, ITextEditorOptions, IEditorOptions, EditorActivation } from 'vs/platform/editor/common/editor'; -import { SideBySideEditor, IEditorInput, IEditorPane, GroupIdentifier, IFileEditorInput, IUntitledTextResourceEditorInput, IResourceDiffEditorInput, IEditorInputFactoryRegistry, Extensions as EditorExtensions, EditorInput, SideBySideEditorInput, IEditorInputWithOptions, isEditorInputWithOptions, EditorOptions, TextEditorOptions, IEditorIdentifier, IEditorCloseEvent, ITextEditorPane, ITextDiffEditorPane, IRevertOptions, SaveReason, EditorsOrder, isTextEditorPane, IWorkbenchEditorConfiguration, EditorResourceAccessor, IVisibleEditorPane } from 'vs/workbench/common/editor'; +import { SideBySideEditor, IEditorInput, IEditorPane, GroupIdentifier, IFileEditorInput, IUntitledTextResourceEditorInput, IResourceDiffEditorInput, IEditorInputFactoryRegistry, Extensions as EditorExtensions, EditorInput, SideBySideEditorInput, IEditorInputWithOptions, isEditorInputWithOptions, EditorOptions, TextEditorOptions, IEditorIdentifier, IEditorCloseEvent, ITextEditorPane, ITextDiffEditorPane, IRevertOptions, SaveReason, EditorsOrder, isTextEditorPane, IWorkbenchEditorConfiguration, EditorResourceAccessor, IVisibleEditorPane, customEditorsAssociationsSettingId, builtinProviderDisplayName, DEFAULT_CUSTOM_EDITOR, CustomEditorAssociation, CustomEditorsAssociations } from 'vs/workbench/common/editor'; import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput'; import { Registry } from 'vs/platform/registry/common/platform'; import { ResourceMap } from 'vs/base/common/map'; @@ -15,13 +15,13 @@ import { IFileService, FileOperationEvent, FileOperation, FileChangesEvent, File import { Schemas } from 'vs/base/common/network'; import { Event, Emitter } from 'vs/base/common/event'; import { URI } from 'vs/base/common/uri'; -import { basename, joinPath, isEqual } from 'vs/base/common/resources'; +import { basename, joinPath, isEqual, extname } from 'vs/base/common/resources'; import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; import { IEditorGroupsService, IEditorGroup, GroupsOrder, IEditorReplacement, GroupChangeKind, preferredSideBySideGroupDirection, OpenEditorContext } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IResourceEditorInputType, SIDE_GROUP, IResourceEditorReplacement, IOpenEditorOverrideHandler, IEditorService, SIDE_GROUP_TYPE, ACTIVE_GROUP_TYPE, ISaveEditorsOptions, ISaveAllEditorsOptions, IRevertAllEditorsOptions, IBaseSaveRevertAllEditorOptions, IOpenEditorOverrideEntry, ICustomEditorViewTypesHandler, ICustomEditorInfo } from 'vs/workbench/services/editor/common/editorService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { Disposable, IDisposable, dispose, toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { coalesce, distinct, insert } from 'vs/base/common/arrays'; +import { coalesce, distinct, firstOrDefault, insert } from 'vs/base/common/arrays'; import { isCodeEditor, isDiffEditor, ICodeEditor, IDiffEditor, isCompositeEditor } from 'vs/editor/browser/editorBrowser'; import { IEditorGroupView, IEditorOpeningEvent, EditorServiceImpl } from 'vs/workbench/browser/parts/editor/editor'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; @@ -33,16 +33,62 @@ import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/u import { Promises, timeout } from 'vs/base/common/async'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { indexOfPath } from 'vs/base/common/extpath'; -import { DEFAULT_CUSTOM_EDITOR, updateViewTypeSchema, editorAssociationsConfigurationNode } from 'vs/workbench/services/editor/common/editorOpenWith'; -import { Extensions as ConfigurationExtensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; +import { Extensions as ConfigurationExtensions, IConfigurationNode, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService'; import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; import { IModelService } from 'vs/editor/common/services/modelService'; import { ILogService } from 'vs/platform/log/common/log'; +import { IKeyMods, IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; +import { Codicon } from 'vs/base/common/codicons'; +import { workbenchConfigurationNodeBase } from 'vs/workbench/common/configuration'; +import { IJSONSchema } from 'vs/base/common/jsonSchema'; type CachedEditorInput = ResourceEditorInput | IFileEditorInput | UntitledTextEditorInput; type OpenInEditorGroup = IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE; +/** + * Id of the default editor for open with. + */ +export const DEFAULT_EDITOR_ID = 'default'; + +const viewTypeSchemaAddition: IJSONSchema = { + type: 'string', + enum: [] +}; + +const editorAssociationsConfigurationNode: IConfigurationNode = { + ...workbenchConfigurationNodeBase, + properties: { + [customEditorsAssociationsSettingId]: { + type: 'array', + markdownDescription: nls.localize('editor.editorAssociations', "Configure which editor to use for specific file types."), + items: { + type: 'object', + defaultSnippets: [{ + body: { + 'viewType': '$1', + 'filenamePattern': '$2' + } + }], + properties: { + 'viewType': { + anyOf: [ + { + type: 'string', + description: nls.localize('editor.editorAssociations.viewType', "The unique id of the editor to use."), + }, + viewTypeSchemaAddition + ] + }, + 'filenamePattern': { + type: 'string', + description: nls.localize('editor.editorAssociations.filenamePattern', "Glob pattern specifying which files the editor should be used for."), + } + } + } + } + } +}; export class EditorService extends Disposable implements EditorServiceImpl { declare readonly _serviceBrand: undefined; @@ -77,7 +123,9 @@ export class EditorService extends Disposable implements EditorServiceImpl { @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, @IWorkingCopyService private readonly workingCopyService: IWorkingCopyService, @IUriIdentityService private readonly uriIdentityService: IUriIdentityService, - @ILogService private readonly logService: ILogService + @ILogService private readonly logService: ILogService, + @IQuickInputService private readonly quickInputService: IQuickInputService, + @IEditorGroupsService private readonly editorGroupsService: IEditorGroupsService ) { super(); @@ -497,7 +545,13 @@ export class EditorService extends Disposable implements EditorServiceImpl { } getEditorOverrides(resource: URI, options: IEditorOptions | undefined, group: IEditorGroup | undefined): [IOpenEditorOverrideHandler, IOpenEditorOverrideEntry][] { - const overrides = []; + const overrides: [IOpenEditorOverrideHandler, IOpenEditorOverrideEntry][] = []; + const fileEditorInputFactory = Registry.as(EditorExtensions.EditorInputFactories).getFileEditorInputFactory(); + const defaultEditorOverrideEntry = Object.freeze({ + id: DEFAULT_EDITOR_ID, + label: nls.localize('promptOpenWith.defaultEditor.displayName', "Text Editor"), + detail: builtinProviderDisplayName + }); for (const handler of this.openEditorHandlers) { if (typeof handler.getEditorOverrides === 'function') { try { @@ -507,7 +561,39 @@ export class EditorService extends Disposable implements EditorServiceImpl { } } } + if (!overrides.some(([_, entry]) => entry.id === DEFAULT_EDITOR_ID)) { + overrides.unshift([ + { + open: (input: IEditorInput, options: IEditorOptions | ITextEditorOptions | undefined, group: IEditorGroup) => { + const resource = EditorResourceAccessor.getOriginalUri(input); + if (!resource) { + return; + } + const fileEditorInput = this.createEditorInput({ resource, forceFile: true }); + const textOptions: IEditorOptions | ITextEditorOptions = options ? { ...options, override: false } : { override: false }; + return { + override: (async () => { + // Try to replace existing editors for resource + const existingEditor = firstOrDefault(this.findEditors(resource, group)); + if (existingEditor && !fileEditorInput.matches(existingEditor)) { + await this.replaceEditors([{ + editor: existingEditor, + replacement: fileEditorInput, + options: options ? EditorOptions.create(options) : undefined, + }], group); + } + + return this.openEditor(fileEditorInput, textOptions, group); + })() + }; + } + }, + { + ...defaultEditorOverrideEntry, + active: fileEditorInputFactory.isFileEditorInput(this.activeEditor) && isEqual(this.activeEditor.resource, resource), + }]); + } return overrides; } @@ -538,6 +624,11 @@ export class EditorService extends Disposable implements EditorServiceImpl { if (result) { const [resolvedGroup, resolvedEditor, resolvedOptions] = result; + // If the override option is provided we want to open that specific editor or show a picker + if (resolvedOptions && (resolvedOptions.override === null || typeof resolvedOptions.override === 'string')) { + return await this.openEditorWith(resolvedEditor, withNullAsUndefined(resolvedOptions.override), resolvedOptions, resolvedGroup); + } + return withNullAsUndefined(await resolvedGroup.openEditor(resolvedEditor, resolvedOptions)); } @@ -598,6 +689,139 @@ export class EditorService extends Disposable implements EditorServiceImpl { return undefined; } + private async openEditorWith(editor: IEditorInput, editorID: string | undefined, editorOptions: IEditorOptions | undefined, group: IEditorGroup): Promise { + const resource = editor.resource; + if (!resource) { + return; + } + + + const allEditorOverrides = this.getEditorOverrides(resource, undefined, undefined); + if (!allEditorOverrides.length) { + return; + } + + // Function which handles the quirks of opening from a pciker such as keymods + const openSelectedEditor = (picked: PickedResult) => { + let targetGroup = group; + if (picked.keyMods?.alt || picked.keyMods?.ctrlCmd) { + const direction = preferredSideBySideGroupDirection(this.configurationService); + targetGroup = this.editorGroupsService.findGroup({ direction }, group.id); + targetGroup = targetGroup ?? this.editorGroupsService.addGroup(group, direction); + } + const openOptions: IEditorOptions = { + ...editorOptions, + override: picked.item.id, + preserveFocus: picked.openInBackground || editorOptions?.preserveFocus, + }; + return picked.item.handler.open(editor, openOptions, targetGroup, OpenEditorContext.NEW_EDITOR)?.override; + }; + + let overrideToUse: [IOpenEditorOverrideHandler, IOpenEditorOverrideEntry] | undefined; + if (typeof editorID === 'string') { + overrideToUse = allEditorOverrides.find(([_, entry]) => entry.id === editorID); + } else if (allEditorOverrides.length === 1) { + overrideToUse = allEditorOverrides[0]; + } + if (overrideToUse) { + openSelectedEditor({ + item: { handler: overrideToUse[0], ...overrideToUse[1] }, + openInBackground: false + }); + } + + type QuickPickItem = IQuickPickItem & { + readonly handler: IOpenEditorOverrideHandler; + }; + + type PickedResult = { + readonly item: QuickPickItem; + readonly keyMods?: IKeyMods; + readonly openInBackground: boolean; + }; + + // Prompt the user to select an override + const originalResource = EditorResourceAccessor.getOriginalUri(editor) || resource; + const resourceExt = extname(originalResource); + + const items: (IQuickPickItem & { handler: IOpenEditorOverrideHandler })[] = allEditorOverrides.map(([handler, entry]) => { + return { + handler: handler, + id: entry.id, + label: entry.label, + description: entry.active ? nls.localize('promptOpenWith.currentlyActive', 'Currently Active') : undefined, + detail: entry.detail, + buttons: resourceExt ? [{ + iconClass: Codicon.gear.classNames, + tooltip: nls.localize('promptOpenWith.setDefaultTooltip', "Set as default editor for '{0}' files", resourceExt) + }] : undefined + }; + }); + + const picker = this.quickInputService.createQuickPick(); + picker.items = items; + if (items.length) { + picker.selectedItems = [items[0]]; + } + picker.placeholder = nls.localize('promptOpenWith.placeHolder', "Select editor for '{0}'", basename(originalResource)); + picker.canAcceptInBackground = true; + + let picked: PickedResult | undefined; + try { + picked = await new Promise(resolve => { + picker.onDidAccept(e => { + if (picker.selectedItems.length === 1) { + const result: PickedResult = { + item: picker.selectedItems[0], + keyMods: picker.keyMods, + openInBackground: e.inBackground + }; + resolve(result); + } else { + resolve(undefined); + } + }); + + picker.onDidTriggerItemButton(e => { + const pick = e.item; + const id = pick.id; + resolve({ item: pick, openInBackground: false }); // open the view + picker.dispose(); + + // And persist the setting + if (pick && id) { + const newAssociation: CustomEditorAssociation = { viewType: id, filenamePattern: '*' + resourceExt }; + const currentAssociations = [...this.configurationService.getValue(customEditorsAssociationsSettingId)]; + + // First try updating existing association + for (let i = 0; i < currentAssociations.length; ++i) { + const existing = currentAssociations[i]; + if (existing.filenamePattern === newAssociation.filenamePattern) { + currentAssociations.splice(i, 1, newAssociation); + this.configurationService.updateValue(customEditorsAssociationsSettingId, currentAssociations); + return; + } + } + + // Otherwise, create a new one + currentAssociations.unshift(newAssociation); + this.configurationService.updateValue(customEditorsAssociationsSettingId, currentAssociations); + } + }); + + picker.show(); + }); + } finally { + picker.dispose(); + } + + if (!picked) { + return undefined; + } + + return openSelectedEditor(picked); + } + private findTargetGroup(input: IEditorInput, options?: IEditorOptions, group?: OpenInEditorGroup): IEditorGroup { let targetGroup: IEditorGroup | undefined; @@ -897,6 +1121,13 @@ export class EditorService extends Disposable implements EditorServiceImpl { return this.createOrGetCached(canonicalResource, () => { + //const customFactory = Registry.as(EditorExtensions.EditorInputFactories).getCustomEditorInputFactory(canonicalResource.scheme); + + // Custom Editor + // if (customFactory) { + // return customFactory.createCustomEditorInput(canonicalResource, this.instantiationService); + // } + // File if (resourceEditorInput.forceFile || this.fileService.canHandleResource(canonicalResource)) { return this.fileEditorInputFactory.createFileEditorInput(canonicalResource, preferredResource, resourceEditorInput.label, resourceEditorInput.description, resourceEditorInput.encoding, resourceEditorInput.mode, this.instantiationService); @@ -1185,7 +1416,11 @@ export class EditorService extends Disposable implements EditorServiceImpl { enumDescriptions.push(nls.localize('editorAssociations.viewType.sourceDescription', "Source: {0}", info.providerDisplayName)); }); - updateViewTypeSchema(enumValues, enumDescriptions); + viewTypeSchemaAddition.enum = enumValues; + viewTypeSchemaAddition.enumDescriptions = enumDescriptions; + + Registry.as(ConfigurationExtensions.Configuration) + .notifyConfigurationSchemaUpdated(editorAssociationsConfigurationNode); } //#endregion diff --git a/src/vs/workbench/services/editor/common/editorOpenWith.ts b/src/vs/workbench/services/editor/common/editorOpenWith.ts deleted file mode 100644 index 7dc8cdfa3663e..0000000000000 --- a/src/vs/workbench/services/editor/common/editorOpenWith.ts +++ /dev/null @@ -1,309 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as nls from 'vs/nls'; -import { IJSONSchema } from 'vs/base/common/jsonSchema'; -import { IConfigurationNode, IConfigurationRegistry, Extensions } from 'vs/platform/configuration/common/configurationRegistry'; -import { workbenchConfigurationNodeBase } from 'vs/workbench/common/configuration'; -import { Registry } from 'vs/platform/registry/common/platform'; -import { ICustomEditorInfo, IEditorService, IOpenEditorOverrideHandler, IOpenEditorOverrideEntry } from 'vs/workbench/services/editor/common/editorService'; -import { IEditorInput, IEditorPane, IEditorInputFactoryRegistry, Extensions as EditorExtensions, EditorResourceAccessor, EditorOptions } from 'vs/workbench/common/editor'; -import { ITextEditorOptions, IEditorOptions } from 'vs/platform/editor/common/editor'; -import { IEditorGroup, IEditorGroupsService, OpenEditorContext, preferredSideBySideGroupDirection } from 'vs/workbench/services/editor/common/editorGroupsService'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IKeyMods, IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; -import { URI } from 'vs/base/common/uri'; -import { extname, basename, isEqual } from 'vs/base/common/resources'; -import { Codicon } from 'vs/base/common/codicons'; -import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -import { firstOrDefault } from 'vs/base/common/arrays'; - -/** - * Id of the default editor for open with. - */ -export const DEFAULT_EDITOR_ID = 'default'; - -/** - * Try to open an resource with a given editor. - * - * @param input Resource to open. - * @param id Id of the editor to use. If not provided, the user is prompted for which editor to use. - */ -export async function openEditorWith( - accessor: ServicesAccessor, - input: IEditorInput, - id: string | undefined, - options: IEditorOptions | ITextEditorOptions | undefined, - group: IEditorGroup, -): Promise { - const editorService = accessor.get(IEditorService); - const editorGroupsService = accessor.get(IEditorGroupsService); - const configurationService = accessor.get(IConfigurationService); - const quickInputService = accessor.get(IQuickInputService); - - const resource = input.resource; - if (!resource) { - return; - } - - const overrideOptions = { ...options, override: id }; - - const allEditorOverrides = getAllAvailableEditors(resource, id, overrideOptions, group, editorService); - if (!allEditorOverrides.length) { - return; - } - - let overrideToUse; - if (typeof id === 'string') { - overrideToUse = allEditorOverrides.find(([_, entry]) => entry.id === id); - } else if (allEditorOverrides.length === 1) { - overrideToUse = allEditorOverrides[0]; - } - if (overrideToUse) { - return overrideToUse[0].open(input, overrideOptions, group, OpenEditorContext.NEW_EDITOR)?.override; - } - - // Prompt - const originalResource = EditorResourceAccessor.getOriginalUri(input) || resource; - const resourceExt = extname(originalResource); - - const items: (IQuickPickItem & { handler: IOpenEditorOverrideHandler })[] = allEditorOverrides.map(([handler, entry]) => { - return { - handler: handler, - id: entry.id, - label: entry.label, - description: entry.active ? nls.localize('promptOpenWith.currentlyActive', 'Currently Active') : undefined, - detail: entry.detail, - buttons: resourceExt ? [{ - iconClass: Codicon.gear.classNames, - tooltip: nls.localize('promptOpenWith.setDefaultTooltip', "Set as default editor for '{0}' files", resourceExt) - }] : undefined - }; - }); - type QuickPickItem = IQuickPickItem & { - readonly handler: IOpenEditorOverrideHandler; - }; - - const picker = quickInputService.createQuickPick(); - picker.items = items; - if (items.length) { - picker.selectedItems = [items[0]]; - } - picker.placeholder = nls.localize('promptOpenWith.placeHolder', "Select editor for '{0}'", basename(originalResource)); - picker.canAcceptInBackground = true; - - type PickedResult = { - readonly item: QuickPickItem; - readonly keyMods?: IKeyMods; - readonly openInBackground: boolean; - }; - - function openEditor(picked: PickedResult) { - const targetGroup = getTargetGroup(group, picked.keyMods, configurationService, editorGroupsService); - - const openOptions: IEditorOptions = { - ...options, - override: picked.item.id, - preserveFocus: picked.openInBackground || options?.preserveFocus, - }; - return picked.item.handler.open(input, openOptions, targetGroup, OpenEditorContext.NEW_EDITOR)?.override; - } - - let picked: PickedResult | undefined; - try { - picked = await new Promise(resolve => { - picker.onDidAccept(e => { - if (picker.selectedItems.length === 1) { - const result: PickedResult = { - item: picker.selectedItems[0], - keyMods: picker.keyMods, - openInBackground: e.inBackground - }; - - if (e.inBackground) { - openEditor(result); - } else { - resolve(result); - } - } else { - resolve(undefined); - } - }); - - picker.onDidTriggerItemButton(e => { - const pick = e.item; - const id = pick.id; - resolve({ item: pick, openInBackground: false }); // open the view - picker.dispose(); - - // And persist the setting - if (pick && id) { - const newAssociation: CustomEditorAssociation = { viewType: id, filenamePattern: '*' + resourceExt }; - const currentAssociations = [...configurationService.getValue(customEditorsAssociationsSettingId)]; - - // First try updating existing association - for (let i = 0; i < currentAssociations.length; ++i) { - const existing = currentAssociations[i]; - if (existing.filenamePattern === newAssociation.filenamePattern) { - currentAssociations.splice(i, 1, newAssociation); - configurationService.updateValue(customEditorsAssociationsSettingId, currentAssociations); - return; - } - } - - // Otherwise, create a new one - currentAssociations.unshift(newAssociation); - configurationService.updateValue(customEditorsAssociationsSettingId, currentAssociations); - } - }); - - picker.show(); - }); - } finally { - picker.dispose(); - } - - if (!picked) { - return undefined; - } - - return openEditor(picked); -} - -const builtinProviderDisplayName = nls.localize('builtinProviderDisplayName', "Built-in"); - -export const defaultEditorOverrideEntry = Object.freeze({ - id: DEFAULT_EDITOR_ID, - label: nls.localize('promptOpenWith.defaultEditor.displayName', "Text Editor"), - detail: builtinProviderDisplayName -}); - -/** - * Get the group to open the editor in by looking at the pressed keys from the picker. - */ -function getTargetGroup( - startingGroup: IEditorGroup, - keyMods: IKeyMods | undefined, - configurationService: IConfigurationService, - editorGroupsService: IEditorGroupsService, -) { - if (keyMods?.alt || keyMods?.ctrlCmd) { - const direction = preferredSideBySideGroupDirection(configurationService); - const targetGroup = editorGroupsService.findGroup({ direction }, startingGroup.id); - return targetGroup ?? editorGroupsService.addGroup(startingGroup, direction); - } - return startingGroup; -} - -/** - * Get a list of all available editors, including the default text editor. - */ -export function getAllAvailableEditors( - resource: URI, - id: string | undefined, - options: IEditorOptions | ITextEditorOptions | undefined, - group: IEditorGroup, - editorService: IEditorService -): Array<[IOpenEditorOverrideHandler, IOpenEditorOverrideEntry]> { - const fileEditorInputFactory = Registry.as(EditorExtensions.EditorInputFactories).getFileEditorInputFactory(); - const overrides = editorService.getEditorOverrides(resource, options, group); - if (!overrides.some(([_, entry]) => entry.id === DEFAULT_EDITOR_ID)) { - overrides.unshift([ - { - open: (input: IEditorInput, options: IEditorOptions | ITextEditorOptions | undefined, group: IEditorGroup) => { - const resource = EditorResourceAccessor.getOriginalUri(input); - if (!resource) { - return; - } - - const fileEditorInput = editorService.createEditorInput({ resource, forceFile: true }); - const textOptions: IEditorOptions | ITextEditorOptions = options ? { ...options, override: false } : { override: false }; - return { - override: (async () => { - // Try to replace existing editors for resource - const existingEditor = firstOrDefault(editorService.findEditors(resource, group)); - if (existingEditor && !fileEditorInput.matches(existingEditor)) { - await editorService.replaceEditors([{ - editor: existingEditor, - replacement: fileEditorInput, - options: options ? EditorOptions.create(options) : undefined, - }], group); - } - - return editorService.openEditor(fileEditorInput, textOptions, group); - })() - }; - } - }, - { - ...defaultEditorOverrideEntry, - active: fileEditorInputFactory.isFileEditorInput(editorService.activeEditor) && isEqual(editorService.activeEditor.resource, resource), - }]); - } - - return overrides; -} - -export const customEditorsAssociationsSettingId = 'workbench.editorAssociations'; - -export const viewTypeSchemaAddition: IJSONSchema = { - type: 'string', - enum: [] -}; - -export type CustomEditorAssociation = { - readonly viewType: string; - readonly filenamePattern?: string; -}; - -export type CustomEditorsAssociations = readonly CustomEditorAssociation[]; - -export const editorAssociationsConfigurationNode: IConfigurationNode = { - ...workbenchConfigurationNodeBase, - properties: { - [customEditorsAssociationsSettingId]: { - type: 'array', - markdownDescription: nls.localize('editor.editorAssociations', "Configure which editor to use for specific file types."), - items: { - type: 'object', - defaultSnippets: [{ - body: { - 'viewType': '$1', - 'filenamePattern': '$2' - } - }], - properties: { - 'viewType': { - anyOf: [ - { - type: 'string', - description: nls.localize('editor.editorAssociations.viewType', "The unique id of the editor to use."), - }, - viewTypeSchemaAddition - ] - }, - 'filenamePattern': { - type: 'string', - description: nls.localize('editor.editorAssociations.filenamePattern', "Glob pattern specifying which files the editor should be used for."), - } - } - } - } - } -}; - -export const DEFAULT_CUSTOM_EDITOR: ICustomEditorInfo = { - id: 'default', - displayName: nls.localize('promptOpenWith.defaultEditor.displayName', "Text Editor"), - providerDisplayName: builtinProviderDisplayName -}; - -export function updateViewTypeSchema(enumValues: string[], enumDescriptions: string[]): void { - viewTypeSchemaAddition.enum = enumValues; - viewTypeSchemaAddition.enumDescriptions = enumDescriptions; - - Registry.as(Extensions.Configuration) - .notifyConfigurationSchemaUpdated(editorAssociationsConfigurationNode); -} From 773cb0f6b30c1050b48744ed848471622e26d392 Mon Sep 17 00:00:00 2001 From: Logan Ramos Date: Wed, 17 Feb 2021 09:59:33 -0500 Subject: [PATCH 02/18] Remove custom editor input handling --- src/vs/workbench/services/editor/browser/editorService.ts | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/vs/workbench/services/editor/browser/editorService.ts b/src/vs/workbench/services/editor/browser/editorService.ts index 26365952dc911..05f58cd10921a 100644 --- a/src/vs/workbench/services/editor/browser/editorService.ts +++ b/src/vs/workbench/services/editor/browser/editorService.ts @@ -1121,13 +1121,6 @@ export class EditorService extends Disposable implements EditorServiceImpl { return this.createOrGetCached(canonicalResource, () => { - //const customFactory = Registry.as(EditorExtensions.EditorInputFactories).getCustomEditorInputFactory(canonicalResource.scheme); - - // Custom Editor - // if (customFactory) { - // return customFactory.createCustomEditorInput(canonicalResource, this.instantiationService); - // } - // File if (resourceEditorInput.forceFile || this.fileService.canHandleResource(canonicalResource)) { return this.fileEditorInputFactory.createFileEditorInput(canonicalResource, preferredResource, resourceEditorInput.label, resourceEditorInput.description, resourceEditorInput.encoding, resourceEditorInput.mode, this.instantiationService); From 60889c5d43a63a730341aec0ed9672f3cca2bac1 Mon Sep 17 00:00:00 2001 From: Logan Ramos Date: Wed, 17 Feb 2021 18:19:37 -0500 Subject: [PATCH 03/18] Fix open with API command --- src/vs/workbench/browser/parts/editor/editorCommands.ts | 2 +- src/vs/workbench/services/editor/browser/editorService.ts | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/editorCommands.ts b/src/vs/workbench/browser/parts/editor/editorCommands.ts index 4a1c222f1319c..c5743f5cc5fc3 100644 --- a/src/vs/workbench/browser/parts/editor/editorCommands.ts +++ b/src/vs/workbench/browser/parts/editor/editorCommands.ts @@ -498,7 +498,7 @@ function registerOpenEditorAPICommands(): void { group = editorGroupsService.getGroup(viewColumnToEditorGroup(editorGroupsService, columnArg)) ?? editorGroupsService.activeGroup; } - const textOptions: ITextEditorOptions = optionsArg ? { ...optionsArg, override: false } : { override: false }; + const textOptions: ITextEditorOptions = optionsArg ? optionsArg : {}; const input = editorService.createEditorInput({ resource: URI.revive(resource) }); return editorService.openEditor(input, { override: id, ...textOptions }, group); diff --git a/src/vs/workbench/services/editor/browser/editorService.ts b/src/vs/workbench/services/editor/browser/editorService.ts index 05f58cd10921a..ee8c38337e06a 100644 --- a/src/vs/workbench/services/editor/browser/editorService.ts +++ b/src/vs/workbench/services/editor/browser/editorService.ts @@ -724,7 +724,7 @@ export class EditorService extends Disposable implements EditorServiceImpl { overrideToUse = allEditorOverrides[0]; } if (overrideToUse) { - openSelectedEditor({ + return openSelectedEditor({ item: { handler: overrideToUse[0], ...overrideToUse[1] }, openInBackground: false }); @@ -1518,6 +1518,10 @@ export class DelegatingEditorService implements IEditorService { if (result) { const [resolvedGroup, resolvedEditor, resolvedOptions] = result; + // If the override option is provided we want to open that specific editor or show a picker + if (resolvedOptions && (resolvedOptions.override === null || typeof resolvedOptions.override === 'string')) { + return await this.editorService.openEditor(resolvedEditor, { ...resolvedOptions, override: withNullAsUndefined(resolvedOptions.override) }, resolvedGroup); + } // Pass on to editor open handler const editorPane = await this.editorOpenHandler( (group: IEditorGroup, editor: IEditorInput, options?: IEditorOptions) => group.openEditor(editor, options), From 6138c8648124ab0a42a215c2952534ae2e88c41e Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 18 Feb 2021 14:39:33 +0100 Subject: [PATCH 04/18] adopt master properly --- .../services/editor/browser/editorService.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/services/editor/browser/editorService.ts b/src/vs/workbench/services/editor/browser/editorService.ts index 106929b6fe3bd..0cad3feaf67bb 100644 --- a/src/vs/workbench/services/editor/browser/editorService.ts +++ b/src/vs/workbench/services/editor/browser/editorService.ts @@ -61,7 +61,7 @@ const editorAssociationsConfigurationNode: IConfigurationNode = { properties: { [customEditorsAssociationsSettingId]: { type: 'array', - markdownDescription: nls.localize('editor.editorAssociations', "Configure which editor to use for specific file types."), + markdownDescription: localize('editor.editorAssociations', "Configure which editor to use for specific file types."), items: { type: 'object', defaultSnippets: [{ @@ -75,14 +75,14 @@ const editorAssociationsConfigurationNode: IConfigurationNode = { anyOf: [ { type: 'string', - description: nls.localize('editor.editorAssociations.viewType', "The unique id of the editor to use."), + description: localize('editor.editorAssociations.viewType', "The unique id of the editor to use."), }, viewTypeSchemaAddition ] }, 'filenamePattern': { type: 'string', - description: nls.localize('editor.editorAssociations.filenamePattern', "Glob pattern specifying which files the editor should be used for."), + description: localize('editor.editorAssociations.filenamePattern', "Glob pattern specifying which files the editor should be used for."), } } } @@ -549,7 +549,7 @@ export class EditorService extends Disposable implements EditorServiceImpl { const fileEditorInputFactory = Registry.as(EditorExtensions.EditorInputFactories).getFileEditorInputFactory(); const defaultEditorOverrideEntry = Object.freeze({ id: DEFAULT_EDITOR_ID, - label: nls.localize('promptOpenWith.defaultEditor.displayName', "Text Editor"), + label: localize('promptOpenWith.defaultEditor.displayName', "Text Editor"), detail: builtinProviderDisplayName }); for (const handler of this.openEditorHandlers) { @@ -749,11 +749,11 @@ export class EditorService extends Disposable implements EditorServiceImpl { handler: handler, id: entry.id, label: entry.label, - description: entry.active ? nls.localize('promptOpenWith.currentlyActive', 'Currently Active') : undefined, + description: entry.active ? localize('promptOpenWith.currentlyActive', 'Currently Active') : undefined, detail: entry.detail, buttons: resourceExt ? [{ iconClass: Codicon.gear.classNames, - tooltip: nls.localize('promptOpenWith.setDefaultTooltip', "Set as default editor for '{0}' files", resourceExt) + tooltip: localize('promptOpenWith.setDefaultTooltip', "Set as default editor for '{0}' files", resourceExt) }] : undefined }; }); @@ -763,7 +763,7 @@ export class EditorService extends Disposable implements EditorServiceImpl { if (items.length) { picker.selectedItems = [items[0]]; } - picker.placeholder = nls.localize('promptOpenWith.placeHolder', "Select editor for '{0}'", basename(originalResource)); + picker.placeholder = localize('promptOpenWith.placeHolder', "Select editor for '{0}'", basename(originalResource)); picker.canAcceptInBackground = true; let picked: PickedResult | undefined; From 399b72e88db9da81acc02e68ffd77f4b869f8360 Mon Sep 17 00:00:00 2001 From: Logan Ramos Date: Thu, 18 Feb 2021 10:18:01 -0500 Subject: [PATCH 05/18] Remove dnagling comment --- src/vs/workbench/contrib/files/browser/fileCommands.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vs/workbench/contrib/files/browser/fileCommands.ts b/src/vs/workbench/contrib/files/browser/fileCommands.ts index 0fd6cd981c76d..694c061116afd 100644 --- a/src/vs/workbench/contrib/files/browser/fileCommands.ts +++ b/src/vs/workbench/contrib/files/browser/fileCommands.ts @@ -359,7 +359,6 @@ CommandsRegistry.registerCommand({ if (uri) { const input = editorService.createEditorInput({ resource: uri }); editorService.openEditor(input, { override: null }, editorGroupsService.activeGroup); - //openEditorWith(accessor, input, undefined, undefined, editorGroupsService.activeGroup); } } }); From bf0199909f6603d4379baed92a4efe94bcfc63e8 Mon Sep 17 00:00:00 2001 From: Logan Ramos Date: Thu, 18 Feb 2021 10:46:21 -0500 Subject: [PATCH 06/18] Address some feedback --- src/vs/workbench/contrib/files/browser/fileCommands.ts | 4 ++-- src/vs/workbench/services/editor/browser/editorService.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/files/browser/fileCommands.ts b/src/vs/workbench/contrib/files/browser/fileCommands.ts index 694c061116afd..a2af7f956476a 100644 --- a/src/vs/workbench/contrib/files/browser/fileCommands.ts +++ b/src/vs/workbench/contrib/files/browser/fileCommands.ts @@ -353,13 +353,13 @@ CommandsRegistry.registerCommand({ id: OPEN_WITH_EXPLORER_COMMAND_ID, handler: async (accessor, resource: URI | object) => { const editorService = accessor.get(IEditorService); - const editorGroupsService = accessor.get(IEditorGroupsService); const uri = getResourceForCommand(resource, accessor.get(IListService), accessor.get(IEditorService)); if (uri) { const input = editorService.createEditorInput({ resource: uri }); - editorService.openEditor(input, { override: null }, editorGroupsService.activeGroup); + return editorService.openEditor(input, { override: null }); } + return; } }); diff --git a/src/vs/workbench/services/editor/browser/editorService.ts b/src/vs/workbench/services/editor/browser/editorService.ts index 0cad3feaf67bb..9590c330e3b87 100644 --- a/src/vs/workbench/services/editor/browser/editorService.ts +++ b/src/vs/workbench/services/editor/browser/editorService.ts @@ -626,7 +626,7 @@ export class EditorService extends Disposable implements EditorServiceImpl { // If the override option is provided we want to open that specific editor or show a picker if (resolvedOptions && (resolvedOptions.override === null || typeof resolvedOptions.override === 'string')) { - return await this.openEditorWith(resolvedEditor, withNullAsUndefined(resolvedOptions.override), resolvedOptions, resolvedGroup); + return this.openEditorWith(resolvedEditor, withNullAsUndefined(resolvedOptions.override), resolvedOptions, resolvedGroup); } return withNullAsUndefined(await resolvedGroup.openEditor(resolvedEditor, resolvedOptions)); From 5b0dc61937623a462ab897d7caf2053ac41c6f37 Mon Sep 17 00:00:00 2001 From: Logan Ramos Date: Thu, 18 Feb 2021 10:48:34 -0500 Subject: [PATCH 07/18] Cleanup region comment. Co-authored-by: Benjamin Pasero --- src/vs/workbench/common/editor.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/common/editor.ts b/src/vs/workbench/common/editor.ts index 9977fef72e911..0804b33e42eb4 100644 --- a/src/vs/workbench/common/editor.ts +++ b/src/vs/workbench/common/editor.ts @@ -1639,6 +1639,7 @@ export function editorGroupToViewColumn(editorGroupService: IEditorGroupsService //#endregion //#region Editor Open With + export const customEditorsAssociationsSettingId = 'workbench.editorAssociations'; export const builtinProviderDisplayName = localize('builtinProviderDisplayName', "Built-in"); From 1cdc4767087b8a0618ea20bdaa408135892a0d02 Mon Sep 17 00:00:00 2001 From: Logan Ramos Date: Thu, 18 Feb 2021 10:48:53 -0500 Subject: [PATCH 08/18] Cleanup region comment. Co-authored-by: Benjamin Pasero --- src/vs/workbench/common/editor.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/common/editor.ts b/src/vs/workbench/common/editor.ts index 0804b33e42eb4..b1ef193578ccb 100644 --- a/src/vs/workbench/common/editor.ts +++ b/src/vs/workbench/common/editor.ts @@ -1657,4 +1657,4 @@ export type CustomEditorAssociation = { export type CustomEditorsAssociations = readonly CustomEditorAssociation[]; -////#endregion +//#endregion From 3cc1560ac76230e6bee2cf9d5c8ece245f5bb6d1 Mon Sep 17 00:00:00 2001 From: Logan Ramos Date: Thu, 18 Feb 2021 12:12:39 -0500 Subject: [PATCH 09/18] Update override typings --- src/vs/platform/editor/common/editor.ts | 16 +++++++++++++--- .../workbench/api/browser/mainThreadEditors.ts | 4 ++-- .../workbench/api/browser/mainThreadNotebook.ts | 4 ++-- .../api/common/extHostTypeConverters.ts | 4 ++-- .../browser/parts/editor/editorActions.ts | 3 ++- .../browser/parts/editor/editorDropTarget.ts | 3 ++- src/vs/workbench/common/editor.ts | 8 ++++---- .../customEditor/browser/customEditors.ts | 14 +++++++------- .../files/browser/editors/binaryFileEditor.ts | 3 ++- .../contrib/files/browser/fileActions.ts | 5 +++-- .../contrib/files/browser/fileCommands.ts | 3 ++- .../notebook/browser/notebook.contribution.ts | 12 ++++++------ .../contrib/notebook/browser/notebookEditor.ts | 4 ++-- .../browser/searchEditor.contribution.ts | 3 ++- .../browser/editor/editorWalkThrough.ts | 3 ++- .../services/editor/browser/editorService.ts | 10 +++++----- 16 files changed, 58 insertions(+), 41 deletions(-) diff --git a/src/vs/platform/editor/common/editor.ts b/src/vs/platform/editor/common/editor.ts index add2d617c8343..35c03481ddec9 100644 --- a/src/vs/platform/editor/common/editor.ts +++ b/src/vs/platform/editor/common/editor.ts @@ -111,6 +111,17 @@ export enum EditorActivation { PRESERVE } +export enum OverrideOptions { + /** + * Displays a picker and allows the user to decide which editor to use + */ + PICKER, + /** + * Disables overrides + */ + DISABLED, +} + export enum EditorOpenContext { /** @@ -204,11 +215,10 @@ export interface IEditorOptions { /** * Allows to override the editor that should be used to display the input: * - `undefined`: let the editor decide for itself - * - `false`: disable overrides * - `string`: specific override by id - * - `null`: shows a picker for the user to decide + * - `OverrideOptions`: Various options which can be given to dictate how overrides are handled */ - readonly override?: false | string | null; + readonly override?: string | OverrideOptions; /** * A optional hint to signal in which context the editor opens. diff --git a/src/vs/workbench/api/browser/mainThreadEditors.ts b/src/vs/workbench/api/browser/mainThreadEditors.ts index 0f52d9af149fc..c83c5bb52cc96 100644 --- a/src/vs/workbench/api/browser/mainThreadEditors.ts +++ b/src/vs/workbench/api/browser/mainThreadEditors.ts @@ -14,7 +14,7 @@ import { ISelection } from 'vs/editor/common/core/selection'; import { IDecorationOptions, IDecorationRenderOptions, ILineChange } from 'vs/editor/common/editorCommon'; import { ISingleEditOperation } from 'vs/editor/common/model'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; -import { ITextEditorOptions, IResourceEditorInput, EditorActivation } from 'vs/platform/editor/common/editor'; +import { ITextEditorOptions, IResourceEditorInput, EditorActivation, OverrideOptions } from 'vs/platform/editor/common/editor'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { MainThreadDocumentsAndEditors } from 'vs/workbench/api/browser/mainThreadDocumentsAndEditors'; import { MainThreadTextEditor } from 'vs/workbench/api/browser/mainThreadEditor'; @@ -142,7 +142,7 @@ export class MainThreadTextEditors implements MainThreadTextEditorsShape { // preserve pre 1.38 behaviour to not make group active when preserveFocus: true // but make sure to restore the editor to fix https://github.com/microsoft/vscode/issues/79633 activation: options.preserveFocus ? EditorActivation.RESTORE : undefined, - override: false + override: OverrideOptions.DISABLED }; const input: IResourceEditorInput = { diff --git a/src/vs/workbench/api/browser/mainThreadNotebook.ts b/src/vs/workbench/api/browser/mainThreadNotebook.ts index c0f8844788ef3..6e22c020de867 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebook.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebook.ts @@ -15,7 +15,7 @@ import { IExtUri, isEqual } from 'vs/base/common/resources'; import { URI, UriComponents } from 'vs/base/common/uri'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { EditorActivation, ITextEditorOptions } from 'vs/platform/editor/common/editor'; +import { EditorActivation, ITextEditorOptions, OverrideOptions } from 'vs/platform/editor/common/editor'; import { ILogService } from 'vs/platform/log/common/log'; import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; import { viewColumnToEditorGroup } from 'vs/workbench/common/editor'; @@ -691,7 +691,7 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo // preserve pre 1.38 behaviour to not make group active when preserveFocus: true // but make sure to restore the editor to fix https://github.com/microsoft/vscode/issues/79633 activation: options.preserveFocus ? EditorActivation.RESTORE : undefined, - override: false, + override: OverrideOptions.DISABLED, }; const columnArg = viewColumnToEditorGroup(this._editorGroupsService, options.position); diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index bc0db6649e536..8a29f073c6c83 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -6,7 +6,7 @@ import * as modes from 'vs/editor/common/modes'; import * as types from './extHostTypes'; import * as search from 'vs/workbench/contrib/search/common/search'; -import { ITextEditorOptions } from 'vs/platform/editor/common/editor'; +import { ITextEditorOptions, OverrideOptions } from 'vs/platform/editor/common/editor'; import { IDecorationOptions, IThemeDecorationRenderOptions, IDecorationRenderOptions, IContentDecorationRenderOptions } from 'vs/editor/common/editorCommon'; import { EndOfLineSequence, TrackedRangeStickiness } from 'vs/editor/common/model'; import type * as vscode from 'vscode'; @@ -1344,7 +1344,7 @@ export namespace TextEditorOpenOptions { inactive: options.background, preserveFocus: options.preserveFocus, selection: typeof options.selection === 'object' ? Range.from(options.selection) : undefined, - override: typeof options.override === 'boolean' ? false : undefined + override: typeof options.override === 'boolean' ? OverrideOptions.DISABLED : undefined }; } diff --git a/src/vs/workbench/browser/parts/editor/editorActions.ts b/src/vs/workbench/browser/parts/editor/editorActions.ts index bb30f6b6f0c3d..8d4e196ede2a9 100644 --- a/src/vs/workbench/browser/parts/editor/editorActions.ts +++ b/src/vs/workbench/browser/parts/editor/editorActions.ts @@ -22,6 +22,7 @@ import { ItemActivation, IQuickInputService } from 'vs/platform/quickinput/commo import { AllEditorsByMostRecentlyUsedQuickAccess, ActiveGroupEditorsByMostRecentlyUsedQuickAccess, AllEditorsByAppearanceQuickAccess } from 'vs/workbench/browser/parts/editor/editorQuickAccess'; import { Codicon } from 'vs/base/common/codicons'; import { IFilesConfigurationService, AutoSaveMode } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; +import { OverrideOptions } from 'vs/platform/editor/common/editor'; export class ExecuteCommandAction extends Action { @@ -1910,7 +1911,7 @@ export class ReopenResourcesAction extends Action { const options = activeEditorPane.options; const group = activeEditorPane.group; - await this.editorService.openEditor(activeInput, { override: null, ...options }, group); + await this.editorService.openEditor(activeInput, { override: OverrideOptions.PICKER, ...options }, group); } } diff --git a/src/vs/workbench/browser/parts/editor/editorDropTarget.ts b/src/vs/workbench/browser/parts/editor/editorDropTarget.ts index d3d2258f572fd..e96006fac9f39 100644 --- a/src/vs/workbench/browser/parts/editor/editorDropTarget.ts +++ b/src/vs/workbench/browser/parts/editor/editorDropTarget.ts @@ -26,6 +26,7 @@ import { assertIsDefined, assertAllDefined } from 'vs/base/common/types'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { localize } from 'vs/nls'; import { ByteSize } from 'vs/platform/files/common/files'; +import { OverrideOptions } from 'vs/platform/editor/common/editor'; interface IDropOperation { splitDirection?: GroupDirection; @@ -284,7 +285,7 @@ class DropOverlay extends Themable { const options = getActiveTextEditorOptions(sourceGroup, draggedEditor.editor, EditorOptions.create({ pinned: true, // always pin dropped editor sticky: sourceGroup.isSticky(draggedEditor.editor), // preserve sticky state - override: false, // Use `draggedEditor.editor` as is. If it is already a custom editor, it will stay so. + override: OverrideOptions.DISABLED, // Use `draggedEditor.editor` as is. If it is already a custom editor, it will stay so. })); const copyEditor = this.isCopyOperation(event, draggedEditor); targetGroup.openEditor(draggedEditor.editor, options, copyEditor ? OpenEditorContext.COPY_EDITOR : OpenEditorContext.MOVE_EDITOR); diff --git a/src/vs/workbench/common/editor.ts b/src/vs/workbench/common/editor.ts index b1ef193578ccb..6a923f7a0b824 100644 --- a/src/vs/workbench/common/editor.ts +++ b/src/vs/workbench/common/editor.ts @@ -9,7 +9,7 @@ import { withNullAsUndefined, assertIsDefined } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import { IDisposable, Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { IEditor, IEditorViewState, ScrollType, IDiffEditor } from 'vs/editor/common/editorCommon'; -import { IEditorModel, IEditorOptions, ITextEditorOptions, IBaseResourceEditorInput, IResourceEditorInput, EditorActivation, EditorOpenContext, ITextEditorSelection, TextEditorSelectionRevealType } from 'vs/platform/editor/common/editor'; +import { IEditorModel, IEditorOptions, ITextEditorOptions, IBaseResourceEditorInput, IResourceEditorInput, EditorActivation, EditorOpenContext, ITextEditorSelection, TextEditorSelectionRevealType, OverrideOptions } from 'vs/platform/editor/common/editor'; import { IInstantiationService, IConstructorSignature0, ServicesAccessor, BrandedService } from 'vs/platform/instantiation/common/instantiation'; import { IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -1006,10 +1006,10 @@ export class EditorOptions implements IEditorOptions { /** * Allows to override the editor that should be used to display the input: * - `undefined`: let the editor decide for itself - * - `false`: disable overrides * - `string`: specific override by id + * - `OverrideOptions`: Various options which can be given to dictate how overrides are handled */ - override?: false | string | null; + override?: string | OverrideOptions; /** * A optional hint to signal in which context the editor opens. @@ -1067,7 +1067,7 @@ export class EditorOptions implements IEditorOptions { this.index = options.index; } - if (typeof options.override === 'string' || options.override === false || options.override === null) { + if (options.override !== undefined) { this.override = options.override; } diff --git a/src/vs/workbench/contrib/customEditor/browser/customEditors.ts b/src/vs/workbench/contrib/customEditor/browser/customEditors.ts index 40f8ab9e70e2d..a45415a561686 100644 --- a/src/vs/workbench/contrib/customEditor/browser/customEditors.ts +++ b/src/vs/workbench/contrib/customEditor/browser/customEditors.ts @@ -15,7 +15,7 @@ import { RedoCommand, UndoCommand } from 'vs/editor/browser/editorExtensions'; import * as nls from 'vs/nls'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { EditorActivation, IEditorOptions, ITextEditorOptions } from 'vs/platform/editor/common/editor'; +import { EditorActivation, IEditorOptions, ITextEditorOptions, OverrideOptions } from 'vs/platform/editor/common/editor'; import { FileOperation, IFileService } from 'vs/platform/files/common/files'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; @@ -217,7 +217,7 @@ export class CustomEditorService extends Disposable implements ICustomEditorServ ): Promise { if (viewType === defaultCustomEditor.id) { const fileEditorInput = this.editorService.createEditorInput({ resource, forceFile: true }); - return this.openEditorForResource(resource, fileEditorInput, { ...options, override: false }, group); + return this.openEditorForResource(resource, fileEditorInput, { ...options, override: OverrideOptions.DISABLED }, group); } if (!this._contributedEditors.get(viewType)) { @@ -402,7 +402,7 @@ export class CustomEditorService extends Disposable implements ICustomEditorServ } const targetGroup = group || this.editorGroupService.activeGroup; - const newEditor = await this.openEditorForResource(resource, editorToUse.editor, { ...options, override: false }, targetGroup); + const newEditor = await this.openEditorForResource(resource, editorToUse.editor, { ...options, override: OverrideOptions.DISABLED }, targetGroup); if (targetGroup.id !== editorToUse.group.id) { editorToUse.group.closeEditor(editorToUse.editor); } @@ -507,7 +507,7 @@ export class CustomEditorContribution extends Disposable implements IWorkbenchCo if (id) { return { - override: this.customEditorService.openWith(resource, id, { ...options, override: false }, group) + override: this.customEditorService.openWith(resource, id, { ...options, override: OverrideOptions.DISABLED }, group) }; } @@ -544,7 +544,7 @@ export class CustomEditorContribution extends Disposable implements IWorkbenchCo return { override: this.editorService.openEditor(existingEditorForResource, { ...options, - override: false, + override: OverrideOptions.DISABLED, activation: options?.preserveFocus ? EditorActivation.RESTORE : undefined, }, group) }; @@ -575,7 +575,7 @@ export class CustomEditorContribution extends Disposable implements IWorkbenchCo // Open VS Code's standard editor but prompt user to see if they wish to use a custom one instead return { override: (async () => { - const standardEditor = await this.editorService.openEditor(editor, { ...options, override: false }, group); + const standardEditor = await this.editorService.openEditor(editor, { ...options, override: OverrideOptions.DISABLED }, group); // Give a moment to make sure the editor is showing. // Otherwise the focus shift can cause the prompt to be dismissed right away. await new Promise(resolve => setTimeout(resolve, 20)); @@ -643,7 +643,7 @@ export class CustomEditorContribution extends Disposable implements IWorkbenchCo return { override: (async () => { const input = this.instantiationService.createInstance(DiffEditorInput, editor.getName(), editor.getDescription(), originalOverride || editor.originalInput, modifiedOverride || editor.modifiedInput, true); - return this.editorService.openEditor(input, { ...options, override: false }, group); + return this.editorService.openEditor(input, { ...options, override: OverrideOptions.DISABLED }, group); })(), }; } diff --git a/src/vs/workbench/contrib/files/browser/editors/binaryFileEditor.ts b/src/vs/workbench/contrib/files/browser/editors/binaryFileEditor.ts index 855cbca8cc086..47ef92228e8c7 100644 --- a/src/vs/workbench/contrib/files/browser/editors/binaryFileEditor.ts +++ b/src/vs/workbench/contrib/files/browser/editors/binaryFileEditor.ts @@ -14,6 +14,7 @@ import { IStorageService } from 'vs/platform/storage/common/storage'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { OverrideOptions } from 'vs/platform/editor/common/editor'; /** * An implementation of editor for binary files that cannot be displayed. @@ -50,7 +51,7 @@ export class BinaryFileEditor extends BaseBinaryResourceEditor { input.setForceOpenAsText(); // If more editors are installed that can handle this input, show a picker - await this.editorService.openEditor(input, { override: null, ...options }, this.group); + await this.editorService.openEditor(input, { override: OverrideOptions.PICKER, ...options }, this.group); } } diff --git a/src/vs/workbench/contrib/files/browser/fileActions.ts b/src/vs/workbench/contrib/files/browser/fileActions.ts index 61b0b7f33f951..57106573c9e6a 100644 --- a/src/vs/workbench/contrib/files/browser/fileActions.ts +++ b/src/vs/workbench/contrib/files/browser/fileActions.ts @@ -54,6 +54,7 @@ import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/ur import { ResourceFileEdit } from 'vs/editor/browser/services/bulkEditService'; import { IExplorerService } from 'vs/workbench/contrib/files/browser/files'; import { listenStream } from 'vs/base/common/stream'; +import { OverrideOptions } from 'vs/platform/editor/common/editor'; export const NEW_FILE_COMMAND_ID = 'explorer.newFile'; export const NEW_FILE_LABEL = nls.localize('newFile', "New File"); @@ -466,7 +467,7 @@ export class GlobalCompareResourcesAction extends Action { override: this.editorService.openEditor({ leftResource: activeResource, rightResource: resource, - options: { override: false, pinned: true } + options: { override: OverrideOptions.DISABLED, pinned: true } }) }; } @@ -476,7 +477,7 @@ export class GlobalCompareResourcesAction extends Action { return { override: this.editorService.openEditor({ resource: activeResource, - options: { override: false, pinned: true } + options: { override: OverrideOptions.DISABLED, pinned: true } }) }; } diff --git a/src/vs/workbench/contrib/files/browser/fileCommands.ts b/src/vs/workbench/contrib/files/browser/fileCommands.ts index a2af7f956476a..e533203b7f8ad 100644 --- a/src/vs/workbench/contrib/files/browser/fileCommands.ts +++ b/src/vs/workbench/contrib/files/browser/fileCommands.ts @@ -43,6 +43,7 @@ import { ITextFileService } from 'vs/workbench/services/textfile/common/textfile import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; import { isPromiseCanceledError } from 'vs/base/common/errors'; import { toAction } from 'vs/base/common/actions'; +import { OverrideOptions } from 'vs/platform/editor/common/editor'; // Commands @@ -357,7 +358,7 @@ CommandsRegistry.registerCommand({ const uri = getResourceForCommand(resource, accessor.get(IListService), accessor.get(IEditorService)); if (uri) { const input = editorService.createEditorInput({ resource: uri }); - return editorService.openEditor(input, { override: null }); + return editorService.openEditor(input, { override: OverrideOptions.PICKER }); } return; } diff --git a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts index cc58e8c3e2619..7b17f52d625a5 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts @@ -16,7 +16,7 @@ import { IModeService } from 'vs/editor/common/services/modeService'; import { ITextModelContentProvider, ITextModelService } from 'vs/editor/common/services/resolverService'; import * as nls from 'vs/nls'; import { Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; -import { IEditorOptions, ITextEditorOptions, IResourceEditorInput } from 'vs/platform/editor/common/editor'; +import { IEditorOptions, ITextEditorOptions, IResourceEditorInput, OverrideOptions } from 'vs/platform/editor/common/editor'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -351,7 +351,7 @@ export class NotebookContribution extends Disposable implements IWorkbenchContri await group.closeEditor(originalInput); originalInput.dispose(); - const newEditor = await group.openEditor(notebookInput, { ...options, index: originalEditorIndex, override: false }); + const newEditor = await group.openEditor(notebookInput, { ...options, index: originalEditorIndex, override: OverrideOptions.DISABLED }); if (newEditor) { return newEditor; } else { @@ -368,7 +368,7 @@ export class NotebookContribution extends Disposable implements IWorkbenchContri // there are notebook editors with the same resource if (existingEditors.find(editor => editor.viewType === id)) { - return { override: this.editorService.openEditor(existingEditors.find(editor => editor.viewType === id)!, { ...options, override: false }, group) }; + return { override: this.editorService.openEditor(existingEditors.find(editor => editor.viewType === id)!, { ...options, override: OverrideOptions.DISABLED }, group) }; } else { return { override: (async () => { @@ -378,7 +378,7 @@ export class NotebookContribution extends Disposable implements IWorkbenchContri await group.closeEditor(firstEditor); firstEditor.dispose(); const notebookInput = NotebookEditorInput.create(this.instantiationService, originalInput.resource!, originalInput.getName(), id); - const newEditor = await group.openEditor(notebookInput, { ...options, index: originalEditorIndex, override: false }); + const newEditor = await group.openEditor(notebookInput, { ...options, index: originalEditorIndex, override: OverrideOptions.DISABLED }); if (newEditor) { return newEditor; @@ -473,7 +473,7 @@ export class NotebookContribution extends Disposable implements IWorkbenchContri } const notebookInput = NotebookEditorInput.create(this.instantiationService, notebookUri, originalInput.getName(), info.id); - const notebookOptions = new NotebookEditorOptions({ ...options, cellOptions, override: false, index }); + const notebookOptions = new NotebookEditorOptions({ ...options, cellOptions, override: OverrideOptions.DISABLED, index }); return { override: this.editorService.openEditor(notebookInput, notebookOptions, group) }; } @@ -516,7 +516,7 @@ export class NotebookContribution extends Disposable implements IWorkbenchContri const info = associatedEditors[0]; const notebookInput = NotebookDiffEditorInput.create(this.instantiationService, notebookUri, modifiedInput.getName(), originalNotebookUri, originalInput.getName(), diffEditorInput.getName(), info.id); - const notebookOptions = new NotebookEditorOptions({ ...options, override: false }); + const notebookOptions = new NotebookEditorOptions({ ...options, override: OverrideOptions.DISABLED }); return { override: this.editorService.openEditor(notebookInput, notebookOptions, group) }; } } diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts index 1025ccaf67b2e..03a5c8eee2390 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts @@ -10,7 +10,7 @@ import { DisposableStore } from 'vs/base/common/lifecycle'; import 'vs/css!./media/notebook'; import { localize } from 'vs/nls'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { IEditorOptions, ITextEditorOptions } from 'vs/platform/editor/common/editor'; +import { IEditorOptions, ITextEditorOptions, OverrideOptions } from 'vs/platform/editor/common/editor'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { IStorageService } from 'vs/platform/storage/common/storage'; @@ -173,7 +173,7 @@ export class NotebookEditor extends EditorPane { label: localize('fail.reOpen', "Reopen file with VS Code standard text editor"), run: async () => { const fileEditorInput = this._editorService.createEditorInput({ resource: input.resource, forceFile: true }); - const textOptions: IEditorOptions | ITextEditorOptions = options ? { ...options, override: false } : { override: false }; + const textOptions: IEditorOptions | ITextEditorOptions = options ? { ...options, override: OverrideOptions.DISABLED } : { override: OverrideOptions.DISABLED }; await this._editorService.openEditor(fileEditorInput, textOptions); } }] diff --git a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.contribution.ts b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.contribution.ts index e16b8167ce7e0..4e3a04008b42a 100644 --- a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.contribution.ts +++ b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.contribution.ts @@ -33,6 +33,7 @@ import { getOrMakeSearchEditorInput, SearchConfiguration, SearchEditorInput, SEA import { parseSavedSearchEditor } from 'vs/workbench/contrib/searchEditor/browser/searchEditorSerialization'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { VIEW_ID } from 'vs/workbench/services/search/common/search'; +import { OverrideOptions } from 'vs/platform/editor/common/editor'; const OpenInEditorCommandId = 'search.action.openInEditor'; @@ -92,7 +93,7 @@ class SearchEditorContribution implements IWorkbenchContribution { override: (async () => { const { config } = await instantiationService.invokeFunction(parseSavedSearchEditor, resource); const input = instantiationService.invokeFunction(getOrMakeSearchEditorInput, { backingUri: resource, config }); - return editorService.openEditor(input, { ...options, override: false }, group); + return editorService.openEditor(input, { ...options, override: OverrideOptions.DISABLED }, group); })() }; } diff --git a/src/vs/workbench/contrib/welcome/walkThrough/browser/editor/editorWalkThrough.ts b/src/vs/workbench/contrib/welcome/walkThrough/browser/editor/editorWalkThrough.ts index 7db8ceec1752b..0042a8655a7fb 100644 --- a/src/vs/workbench/contrib/welcome/walkThrough/browser/editor/editorWalkThrough.ts +++ b/src/vs/workbench/contrib/welcome/walkThrough/browser/editor/editorWalkThrough.ts @@ -11,6 +11,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { WalkThroughInput, WalkThroughInputOptions } from 'vs/workbench/contrib/welcome/walkThrough/browser/walkThroughInput'; import { FileAccess, Schemas } from 'vs/base/common/network'; import { IEditorInputFactory, EditorInput } from 'vs/workbench/common/editor'; +import { OverrideOptions } from 'vs/platform/editor/common/editor'; const typeId = 'workbench.editors.walkThroughInput'; const inputOptions: WalkThroughInputOptions = { @@ -40,7 +41,7 @@ export class EditorWalkThroughAction extends Action { public run(): Promise { const input = this.instantiationService.createInstance(WalkThroughInput, inputOptions); - return this.editorService.openEditor(input, { pinned: true, override: false }) + return this.editorService.openEditor(input, { pinned: true, override: OverrideOptions.DISABLED }) .then(() => void (0)); } } diff --git a/src/vs/workbench/services/editor/browser/editorService.ts b/src/vs/workbench/services/editor/browser/editorService.ts index 9590c330e3b87..096ced736eccf 100644 --- a/src/vs/workbench/services/editor/browser/editorService.ts +++ b/src/vs/workbench/services/editor/browser/editorService.ts @@ -5,7 +5,7 @@ import { localize } from 'vs/nls'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IResourceEditorInput, ITextEditorOptions, IEditorOptions, EditorActivation } from 'vs/platform/editor/common/editor'; +import { IResourceEditorInput, ITextEditorOptions, IEditorOptions, EditorActivation, OverrideOptions } from 'vs/platform/editor/common/editor'; import { SideBySideEditor, IEditorInput, IEditorPane, GroupIdentifier, IFileEditorInput, IUntitledTextResourceEditorInput, IResourceDiffEditorInput, IEditorInputFactoryRegistry, Extensions as EditorExtensions, EditorInput, SideBySideEditorInput, IEditorInputWithOptions, isEditorInputWithOptions, EditorOptions, TextEditorOptions, IEditorIdentifier, IEditorCloseEvent, ITextEditorPane, ITextDiffEditorPane, IRevertOptions, SaveReason, EditorsOrder, isTextEditorPane, IWorkbenchEditorConfiguration, EditorResourceAccessor, IVisibleEditorPane, customEditorsAssociationsSettingId, builtinProviderDisplayName, DEFAULT_CUSTOM_EDITOR, CustomEditorAssociation, CustomEditorsAssociations } from 'vs/workbench/common/editor'; import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -571,7 +571,7 @@ export class EditorService extends Disposable implements EditorServiceImpl { } const fileEditorInput = this.createEditorInput({ resource, forceFile: true }); - const textOptions: IEditorOptions | ITextEditorOptions = options ? { ...options, override: false } : { override: false }; + const textOptions: IEditorOptions | ITextEditorOptions = options ? { ...options, override: OverrideOptions.DISABLED } : { override: OverrideOptions.DISABLED }; return { override: (async () => { // Try to replace existing editors for resource @@ -598,7 +598,7 @@ export class EditorService extends Disposable implements EditorServiceImpl { } private onGroupWillOpenEditor(group: IEditorGroup, event: IEditorOpeningEvent): void { - if (event.options?.override === false) { + if (event.options?.override === OverrideOptions.DISABLED) { return; // return early when overrides are explicitly disabled } @@ -625,8 +625,8 @@ export class EditorService extends Disposable implements EditorServiceImpl { const [resolvedGroup, resolvedEditor, resolvedOptions] = result; // If the override option is provided we want to open that specific editor or show a picker - if (resolvedOptions && (resolvedOptions.override === null || typeof resolvedOptions.override === 'string')) { - return this.openEditorWith(resolvedEditor, withNullAsUndefined(resolvedOptions.override), resolvedOptions, resolvedGroup); + if (resolvedOptions && (resolvedOptions.override === OverrideOptions.PICKER || typeof resolvedOptions.override === 'string')) { + return this.openEditorWith(resolvedEditor, resolvedOptions.override === OverrideOptions.PICKER ? undefined : resolvedOptions.override, resolvedOptions, resolvedGroup); } return withNullAsUndefined(await resolvedGroup.openEditor(resolvedEditor, resolvedOptions)); From 88bd0324f9f5e36bb99dc81a77b3939785123d7c Mon Sep 17 00:00:00 2001 From: Logan Ramos Date: Thu, 18 Feb 2021 13:39:52 -0500 Subject: [PATCH 10/18] Fix missing picker --- src/vs/workbench/services/editor/browser/editorService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/services/editor/browser/editorService.ts b/src/vs/workbench/services/editor/browser/editorService.ts index 096ced736eccf..6a9f46a7512c8 100644 --- a/src/vs/workbench/services/editor/browser/editorService.ts +++ b/src/vs/workbench/services/editor/browser/editorService.ts @@ -1519,7 +1519,7 @@ export class DelegatingEditorService implements IEditorService { const [resolvedGroup, resolvedEditor, resolvedOptions] = result; // If the override option is provided we want to open that specific editor or show a picker - if (resolvedOptions && (resolvedOptions.override === null || typeof resolvedOptions.override === 'string')) { + if (resolvedOptions && (resolvedOptions.override === OverrideOptions.PICKER || typeof resolvedOptions.override === 'string')) { return await this.editorService.openEditor(resolvedEditor, { ...resolvedOptions, override: withNullAsUndefined(resolvedOptions.override) }, resolvedGroup); } // Pass on to editor open handler From e3cbe87573e3b206f2ce4cd9be2c7cb76faeee19 Mon Sep 17 00:00:00 2001 From: Logan Ramos Date: Thu, 18 Feb 2021 14:14:38 -0500 Subject: [PATCH 11/18] Fix bug with reopen with --- src/vs/workbench/browser/parts/editor/editorActions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/parts/editor/editorActions.ts b/src/vs/workbench/browser/parts/editor/editorActions.ts index 8d4e196ede2a9..882ee3e827a48 100644 --- a/src/vs/workbench/browser/parts/editor/editorActions.ts +++ b/src/vs/workbench/browser/parts/editor/editorActions.ts @@ -1911,7 +1911,7 @@ export class ReopenResourcesAction extends Action { const options = activeEditorPane.options; const group = activeEditorPane.group; - await this.editorService.openEditor(activeInput, { override: OverrideOptions.PICKER, ...options }, group); + await this.editorService.openEditor(activeInput, { ...options, override: OverrideOptions.PICKER }, group); } } From bea6f088a6e9c62e2d9c3e78fd28349f403ab74b Mon Sep 17 00:00:00 2001 From: Logan Ramos Date: Thu, 18 Feb 2021 15:45:44 -0500 Subject: [PATCH 12/18] Remove duplicate import --- src/vs/workbench/services/editor/browser/editorService.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/services/editor/browser/editorService.ts b/src/vs/workbench/services/editor/browser/editorService.ts index 6a9f46a7512c8..46e072d883d14 100644 --- a/src/vs/workbench/services/editor/browser/editorService.ts +++ b/src/vs/workbench/services/editor/browser/editorService.ts @@ -124,8 +124,7 @@ export class EditorService extends Disposable implements EditorServiceImpl { @IWorkingCopyService private readonly workingCopyService: IWorkingCopyService, @IUriIdentityService private readonly uriIdentityService: IUriIdentityService, @ILogService private readonly logService: ILogService, - @IQuickInputService private readonly quickInputService: IQuickInputService, - @IEditorGroupsService private readonly editorGroupsService: IEditorGroupsService + @IQuickInputService private readonly quickInputService: IQuickInputService ) { super(); @@ -706,8 +705,8 @@ export class EditorService extends Disposable implements EditorServiceImpl { let targetGroup = group; if (picked.keyMods?.alt || picked.keyMods?.ctrlCmd) { const direction = preferredSideBySideGroupDirection(this.configurationService); - targetGroup = this.editorGroupsService.findGroup({ direction }, group.id); - targetGroup = targetGroup ?? this.editorGroupsService.addGroup(group, direction); + targetGroup = this.editorGroupService.findGroup({ direction }, group.id); + targetGroup = targetGroup ?? this.editorGroupService.addGroup(group, direction); } const openOptions: IEditorOptions = { ...editorOptions, From e8b820b63ccf1a1bfb9e1f14c294a0615f11c2dc Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Sat, 20 Feb 2021 14:40:55 +0100 Subject: [PATCH 13/18] First round of feedback via changes - OverrideOptions => EditorOverride - It is safe to destructure (...) undefined - Ensure when destructuring, override is always winning - Not a big fan of shared builtinProviderDisplayName import --- src/vs/platform/editor/common/editor.ts | 12 +++++++----- .../workbench/api/browser/mainThreadEditors.ts | 4 ++-- .../workbench/api/browser/mainThreadNotebook.ts | 6 +++--- .../api/common/extHostTypeConverters.ts | 4 ++-- .../browser/parts/editor/editorActions.ts | 4 ++-- .../browser/parts/editor/editorCommands.ts | 4 +--- .../browser/parts/editor/editorDropTarget.ts | 4 ++-- src/vs/workbench/common/editor.ts | 10 ++++------ .../customEditor/browser/customEditors.ts | 14 +++++++------- .../common/contributedCustomEditors.ts | 5 ++--- .../files/browser/editors/binaryFileEditor.ts | 4 ++-- .../contrib/files/browser/fileActions.ts | 6 +++--- .../contrib/files/browser/fileCommands.ts | 8 +++++--- .../notebook/browser/notebook.contribution.ts | 12 ++++++------ .../contrib/notebook/browser/notebookEditor.ts | 4 ++-- .../notebook/browser/notebookServiceImpl.ts | 4 ++-- .../browser/searchEditor.contribution.ts | 4 ++-- .../browser/editor/editorWalkThrough.ts | 4 ++-- .../services/editor/browser/editorService.ts | 16 ++++++++-------- 19 files changed, 64 insertions(+), 65 deletions(-) diff --git a/src/vs/platform/editor/common/editor.ts b/src/vs/platform/editor/common/editor.ts index 35c03481ddec9..d190315824beb 100644 --- a/src/vs/platform/editor/common/editor.ts +++ b/src/vs/platform/editor/common/editor.ts @@ -111,15 +111,17 @@ export enum EditorActivation { PRESERVE } -export enum OverrideOptions { +export enum EditorOverride { + /** * Displays a picker and allows the user to decide which editor to use */ - PICKER, + PICK = 1, + /** * Disables overrides */ - DISABLED, + DISABLED } export enum EditorOpenContext { @@ -216,9 +218,9 @@ export interface IEditorOptions { * Allows to override the editor that should be used to display the input: * - `undefined`: let the editor decide for itself * - `string`: specific override by id - * - `OverrideOptions`: Various options which can be given to dictate how overrides are handled + * - `EditorOverride`: specific override handling */ - readonly override?: string | OverrideOptions; + readonly override?: string | EditorOverride; /** * A optional hint to signal in which context the editor opens. diff --git a/src/vs/workbench/api/browser/mainThreadEditors.ts b/src/vs/workbench/api/browser/mainThreadEditors.ts index c83c5bb52cc96..5587adddb267c 100644 --- a/src/vs/workbench/api/browser/mainThreadEditors.ts +++ b/src/vs/workbench/api/browser/mainThreadEditors.ts @@ -14,7 +14,7 @@ import { ISelection } from 'vs/editor/common/core/selection'; import { IDecorationOptions, IDecorationRenderOptions, ILineChange } from 'vs/editor/common/editorCommon'; import { ISingleEditOperation } from 'vs/editor/common/model'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; -import { ITextEditorOptions, IResourceEditorInput, EditorActivation, OverrideOptions } from 'vs/platform/editor/common/editor'; +import { ITextEditorOptions, IResourceEditorInput, EditorActivation, EditorOverride } from 'vs/platform/editor/common/editor'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { MainThreadDocumentsAndEditors } from 'vs/workbench/api/browser/mainThreadDocumentsAndEditors'; import { MainThreadTextEditor } from 'vs/workbench/api/browser/mainThreadEditor'; @@ -142,7 +142,7 @@ export class MainThreadTextEditors implements MainThreadTextEditorsShape { // preserve pre 1.38 behaviour to not make group active when preserveFocus: true // but make sure to restore the editor to fix https://github.com/microsoft/vscode/issues/79633 activation: options.preserveFocus ? EditorActivation.RESTORE : undefined, - override: OverrideOptions.DISABLED + override: EditorOverride.DISABLED }; const input: IResourceEditorInput = { diff --git a/src/vs/workbench/api/browser/mainThreadNotebook.ts b/src/vs/workbench/api/browser/mainThreadNotebook.ts index 42da2077b32f8..2a0a4ce3d939d 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebook.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebook.ts @@ -13,7 +13,7 @@ import { Schemas } from 'vs/base/common/network'; import { isEqual } from 'vs/base/common/resources'; import { URI, UriComponents } from 'vs/base/common/uri'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { EditorActivation, ITextEditorOptions, OverrideOptions } from 'vs/platform/editor/common/editor'; +import { EditorActivation, ITextEditorOptions, EditorOverride } from 'vs/platform/editor/common/editor'; import { ILogService } from 'vs/platform/log/common/log'; import { BoundModelReferenceCollection } from 'vs/workbench/api/browser/mainThreadDocuments'; import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; @@ -660,7 +660,7 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo // preserve pre 1.38 behaviour to not make group active when preserveFocus: true // but make sure to restore the editor to fix https://github.com/microsoft/vscode/issues/79633 activation: options.preserveFocus ? EditorActivation.RESTORE : undefined, - override: OverrideOptions.DISABLED, + override: EditorOverride.DISABLED, }; const columnArg = viewColumnToEditorGroup(this._editorGroupsService, options.position); @@ -682,7 +682,7 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo const input = this._editorService.createEditorInput({ resource: URI.revive(resource), options: editorOptions }); // TODO: handle options.selection - const editorPane = await this._editorService.openEditor(input, { override: viewType, ...options }, group); + const editorPane = await this._editorService.openEditor(input, { ...options, override: viewType }, group); const notebookEditor = (editorPane as unknown as { isNotebookEditor?: boolean })?.isNotebookEditor ? (editorPane!.getControl() as INotebookEditor) : undefined; if (notebookEditor) { diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index ba1d41ee96859..ae84d8eb41abd 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -6,7 +6,7 @@ import * as modes from 'vs/editor/common/modes'; import * as types from './extHostTypes'; import * as search from 'vs/workbench/contrib/search/common/search'; -import { ITextEditorOptions, OverrideOptions } from 'vs/platform/editor/common/editor'; +import { ITextEditorOptions, EditorOverride } from 'vs/platform/editor/common/editor'; import { IDecorationOptions, IThemeDecorationRenderOptions, IDecorationRenderOptions, IContentDecorationRenderOptions } from 'vs/editor/common/editorCommon'; import { EndOfLineSequence, TrackedRangeStickiness } from 'vs/editor/common/model'; import type * as vscode from 'vscode'; @@ -1344,7 +1344,7 @@ export namespace TextEditorOpenOptions { inactive: options.background, preserveFocus: options.preserveFocus, selection: typeof options.selection === 'object' ? Range.from(options.selection) : undefined, - override: typeof options.override === 'boolean' ? OverrideOptions.DISABLED : undefined + override: typeof options.override === 'boolean' ? EditorOverride.DISABLED : undefined }; } diff --git a/src/vs/workbench/browser/parts/editor/editorActions.ts b/src/vs/workbench/browser/parts/editor/editorActions.ts index 882ee3e827a48..a4ae28f8e42ec 100644 --- a/src/vs/workbench/browser/parts/editor/editorActions.ts +++ b/src/vs/workbench/browser/parts/editor/editorActions.ts @@ -22,7 +22,7 @@ import { ItemActivation, IQuickInputService } from 'vs/platform/quickinput/commo import { AllEditorsByMostRecentlyUsedQuickAccess, ActiveGroupEditorsByMostRecentlyUsedQuickAccess, AllEditorsByAppearanceQuickAccess } from 'vs/workbench/browser/parts/editor/editorQuickAccess'; import { Codicon } from 'vs/base/common/codicons'; import { IFilesConfigurationService, AutoSaveMode } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; -import { OverrideOptions } from 'vs/platform/editor/common/editor'; +import { EditorOverride } from 'vs/platform/editor/common/editor'; export class ExecuteCommandAction extends Action { @@ -1911,7 +1911,7 @@ export class ReopenResourcesAction extends Action { const options = activeEditorPane.options; const group = activeEditorPane.group; - await this.editorService.openEditor(activeInput, { ...options, override: OverrideOptions.PICKER }, group); + await this.editorService.openEditor(activeInput, { ...options, override: EditorOverride.PICK }, group); } } diff --git a/src/vs/workbench/browser/parts/editor/editorCommands.ts b/src/vs/workbench/browser/parts/editor/editorCommands.ts index 0c44e27f2dffb..c832f4015f71c 100644 --- a/src/vs/workbench/browser/parts/editor/editorCommands.ts +++ b/src/vs/workbench/browser/parts/editor/editorCommands.ts @@ -498,10 +498,8 @@ function registerOpenEditorAPICommands(): void { group = editorGroupsService.getGroup(viewColumnToEditorGroup(editorGroupsService, columnArg)) ?? editorGroupsService.activeGroup; } - const textOptions: ITextEditorOptions = optionsArg ? optionsArg : {}; - const input = editorService.createEditorInput({ resource: URI.revive(resource) }); - return editorService.openEditor(input, { override: id, ...textOptions }, group); + return editorService.openEditor(input, { ...optionsArg, override: id }, group); }); } diff --git a/src/vs/workbench/browser/parts/editor/editorDropTarget.ts b/src/vs/workbench/browser/parts/editor/editorDropTarget.ts index e2e708438480b..d2531b9d193ce 100644 --- a/src/vs/workbench/browser/parts/editor/editorDropTarget.ts +++ b/src/vs/workbench/browser/parts/editor/editorDropTarget.ts @@ -26,7 +26,7 @@ import { assertIsDefined, assertAllDefined } from 'vs/base/common/types'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { localize } from 'vs/nls'; import { ByteSize } from 'vs/platform/files/common/files'; -import { OverrideOptions } from 'vs/platform/editor/common/editor'; +import { EditorOverride } from 'vs/platform/editor/common/editor'; interface IDropOperation { splitDirection?: GroupDirection; @@ -285,7 +285,7 @@ class DropOverlay extends Themable { const options = getActiveTextEditorOptions(sourceGroup, draggedEditor.editor, EditorOptions.create({ pinned: true, // always pin dropped editor sticky: sourceGroup.isSticky(draggedEditor.editor), // preserve sticky state - override: OverrideOptions.DISABLED, // Use `draggedEditor.editor` as is. If it is already a custom editor, it will stay so. + override: EditorOverride.DISABLED // preserve editor type })); const copyEditor = this.isCopyOperation(event, draggedEditor); targetGroup.openEditor(draggedEditor.editor, options, copyEditor ? OpenEditorContext.COPY_EDITOR : OpenEditorContext.MOVE_EDITOR); diff --git a/src/vs/workbench/common/editor.ts b/src/vs/workbench/common/editor.ts index 6a923f7a0b824..13eb9710b5f78 100644 --- a/src/vs/workbench/common/editor.ts +++ b/src/vs/workbench/common/editor.ts @@ -9,7 +9,7 @@ import { withNullAsUndefined, assertIsDefined } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import { IDisposable, Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { IEditor, IEditorViewState, ScrollType, IDiffEditor } from 'vs/editor/common/editorCommon'; -import { IEditorModel, IEditorOptions, ITextEditorOptions, IBaseResourceEditorInput, IResourceEditorInput, EditorActivation, EditorOpenContext, ITextEditorSelection, TextEditorSelectionRevealType, OverrideOptions } from 'vs/platform/editor/common/editor'; +import { IEditorModel, IEditorOptions, ITextEditorOptions, IBaseResourceEditorInput, IResourceEditorInput, EditorActivation, EditorOpenContext, ITextEditorSelection, TextEditorSelectionRevealType, EditorOverride } from 'vs/platform/editor/common/editor'; import { IInstantiationService, IConstructorSignature0, ServicesAccessor, BrandedService } from 'vs/platform/instantiation/common/instantiation'; import { IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -1007,9 +1007,9 @@ export class EditorOptions implements IEditorOptions { * Allows to override the editor that should be used to display the input: * - `undefined`: let the editor decide for itself * - `string`: specific override by id - * - `OverrideOptions`: Various options which can be given to dictate how overrides are handled + * - `EditorOverride`: specific override handling */ - override?: string | OverrideOptions; + override: string | EditorOverride | undefined; /** * A optional hint to signal in which context the editor opens. @@ -1642,12 +1642,10 @@ export function editorGroupToViewColumn(editorGroupService: IEditorGroupsService export const customEditorsAssociationsSettingId = 'workbench.editorAssociations'; -export const builtinProviderDisplayName = localize('builtinProviderDisplayName', "Built-in"); - export const DEFAULT_CUSTOM_EDITOR: ICustomEditorInfo = { id: 'default', displayName: localize('promptOpenWith.defaultEditor.displayName', "Text Editor"), - providerDisplayName: builtinProviderDisplayName + providerDisplayName: localize('builtinProviderDisplayName', "Built-in") }; export type CustomEditorAssociation = { diff --git a/src/vs/workbench/contrib/customEditor/browser/customEditors.ts b/src/vs/workbench/contrib/customEditor/browser/customEditors.ts index a45415a561686..b4ef915414bd3 100644 --- a/src/vs/workbench/contrib/customEditor/browser/customEditors.ts +++ b/src/vs/workbench/contrib/customEditor/browser/customEditors.ts @@ -15,7 +15,7 @@ import { RedoCommand, UndoCommand } from 'vs/editor/browser/editorExtensions'; import * as nls from 'vs/nls'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { EditorActivation, IEditorOptions, ITextEditorOptions, OverrideOptions } from 'vs/platform/editor/common/editor'; +import { EditorActivation, IEditorOptions, ITextEditorOptions, EditorOverride } from 'vs/platform/editor/common/editor'; import { FileOperation, IFileService } from 'vs/platform/files/common/files'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; @@ -217,7 +217,7 @@ export class CustomEditorService extends Disposable implements ICustomEditorServ ): Promise { if (viewType === defaultCustomEditor.id) { const fileEditorInput = this.editorService.createEditorInput({ resource, forceFile: true }); - return this.openEditorForResource(resource, fileEditorInput, { ...options, override: OverrideOptions.DISABLED }, group); + return this.openEditorForResource(resource, fileEditorInput, { ...options, override: EditorOverride.DISABLED }, group); } if (!this._contributedEditors.get(viewType)) { @@ -402,7 +402,7 @@ export class CustomEditorService extends Disposable implements ICustomEditorServ } const targetGroup = group || this.editorGroupService.activeGroup; - const newEditor = await this.openEditorForResource(resource, editorToUse.editor, { ...options, override: OverrideOptions.DISABLED }, targetGroup); + const newEditor = await this.openEditorForResource(resource, editorToUse.editor, { ...options, override: EditorOverride.DISABLED }, targetGroup); if (targetGroup.id !== editorToUse.group.id) { editorToUse.group.closeEditor(editorToUse.editor); } @@ -507,7 +507,7 @@ export class CustomEditorContribution extends Disposable implements IWorkbenchCo if (id) { return { - override: this.customEditorService.openWith(resource, id, { ...options, override: OverrideOptions.DISABLED }, group) + override: this.customEditorService.openWith(resource, id, { ...options, override: EditorOverride.DISABLED }, group) }; } @@ -544,7 +544,7 @@ export class CustomEditorContribution extends Disposable implements IWorkbenchCo return { override: this.editorService.openEditor(existingEditorForResource, { ...options, - override: OverrideOptions.DISABLED, + override: EditorOverride.DISABLED, activation: options?.preserveFocus ? EditorActivation.RESTORE : undefined, }, group) }; @@ -575,7 +575,7 @@ export class CustomEditorContribution extends Disposable implements IWorkbenchCo // Open VS Code's standard editor but prompt user to see if they wish to use a custom one instead return { override: (async () => { - const standardEditor = await this.editorService.openEditor(editor, { ...options, override: OverrideOptions.DISABLED }, group); + const standardEditor = await this.editorService.openEditor(editor, { ...options, override: EditorOverride.DISABLED }, group); // Give a moment to make sure the editor is showing. // Otherwise the focus shift can cause the prompt to be dismissed right away. await new Promise(resolve => setTimeout(resolve, 20)); @@ -643,7 +643,7 @@ export class CustomEditorContribution extends Disposable implements IWorkbenchCo return { override: (async () => { const input = this.instantiationService.createInstance(DiffEditorInput, editor.getName(), editor.getDescription(), originalOverride || editor.originalInput, modifiedOverride || editor.modifiedInput, true); - return this.editorService.openEditor(input, { ...options, override: OverrideOptions.DISABLED }, group); + return this.editorService.openEditor(input, { ...options, override: EditorOverride.DISABLED }, group); })(), }; } diff --git a/src/vs/workbench/contrib/customEditor/common/contributedCustomEditors.ts b/src/vs/workbench/contrib/customEditor/common/contributedCustomEditors.ts index a0139a2ae0234..8af7b66b6570f 100644 --- a/src/vs/workbench/contrib/customEditor/common/contributedCustomEditors.ts +++ b/src/vs/workbench/contrib/customEditor/common/contributedCustomEditors.ts @@ -9,7 +9,6 @@ import { URI } from 'vs/base/common/uri'; import * as nls from 'vs/nls'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; -import { builtinProviderDisplayName } from 'vs/workbench/common/editor'; import { Memento } from 'vs/workbench/common/memento'; import { CustomEditorDescriptor, CustomEditorInfo, CustomEditorPriority } from 'vs/workbench/contrib/customEditor/common/customEditor'; import { customEditorsExtensionPoint, ICustomEditorsExtensionPoint } from 'vs/workbench/contrib/customEditor/common/extensionPoint'; @@ -18,7 +17,7 @@ import { IExtensionPointUser } from 'vs/workbench/services/extensions/common/ext export const defaultCustomEditor = new CustomEditorInfo({ id: 'default', displayName: nls.localize('promptOpenWith.defaultEditor.displayName', "Text Editor"), - providerDisplayName: builtinProviderDisplayName, + providerDisplayName: nls.localize('builtinProviderDisplayName', "Built-in"), selector: [ { filenamePattern: '*' } ], @@ -59,7 +58,7 @@ export class ContributedCustomEditors extends Disposable { this.add(new CustomEditorInfo({ id: webviewEditorContribution.viewType, displayName: webviewEditorContribution.displayName, - providerDisplayName: extension.description.isBuiltin ? builtinProviderDisplayName : extension.description.displayName || extension.description.identifier.value, + providerDisplayName: extension.description.isBuiltin ? nls.localize('builtinProviderDisplayName', "Built-in") : extension.description.displayName || extension.description.identifier.value, selector: webviewEditorContribution.selector || [], priority: getPriorityFromContribution(webviewEditorContribution, extension.description), })); diff --git a/src/vs/workbench/contrib/files/browser/editors/binaryFileEditor.ts b/src/vs/workbench/contrib/files/browser/editors/binaryFileEditor.ts index 47ef92228e8c7..59334b13ead60 100644 --- a/src/vs/workbench/contrib/files/browser/editors/binaryFileEditor.ts +++ b/src/vs/workbench/contrib/files/browser/editors/binaryFileEditor.ts @@ -14,7 +14,7 @@ import { IStorageService } from 'vs/platform/storage/common/storage'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { OverrideOptions } from 'vs/platform/editor/common/editor'; +import { EditorOverride } from 'vs/platform/editor/common/editor'; /** * An implementation of editor for binary files that cannot be displayed. @@ -51,7 +51,7 @@ export class BinaryFileEditor extends BaseBinaryResourceEditor { input.setForceOpenAsText(); // If more editors are installed that can handle this input, show a picker - await this.editorService.openEditor(input, { override: OverrideOptions.PICKER, ...options }, this.group); + await this.editorService.openEditor(input, { ...options, override: EditorOverride.PICK, }, this.group); } } diff --git a/src/vs/workbench/contrib/files/browser/fileActions.ts b/src/vs/workbench/contrib/files/browser/fileActions.ts index 57106573c9e6a..908574c09f387 100644 --- a/src/vs/workbench/contrib/files/browser/fileActions.ts +++ b/src/vs/workbench/contrib/files/browser/fileActions.ts @@ -54,7 +54,7 @@ import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/ur import { ResourceFileEdit } from 'vs/editor/browser/services/bulkEditService'; import { IExplorerService } from 'vs/workbench/contrib/files/browser/files'; import { listenStream } from 'vs/base/common/stream'; -import { OverrideOptions } from 'vs/platform/editor/common/editor'; +import { EditorOverride } from 'vs/platform/editor/common/editor'; export const NEW_FILE_COMMAND_ID = 'explorer.newFile'; export const NEW_FILE_LABEL = nls.localize('newFile', "New File"); @@ -467,7 +467,7 @@ export class GlobalCompareResourcesAction extends Action { override: this.editorService.openEditor({ leftResource: activeResource, rightResource: resource, - options: { override: OverrideOptions.DISABLED, pinned: true } + options: { override: EditorOverride.DISABLED, pinned: true } }) }; } @@ -477,7 +477,7 @@ export class GlobalCompareResourcesAction extends Action { return { override: this.editorService.openEditor({ resource: activeResource, - options: { override: OverrideOptions.DISABLED, pinned: true } + options: { override: EditorOverride.DISABLED, pinned: true } }) }; } diff --git a/src/vs/workbench/contrib/files/browser/fileCommands.ts b/src/vs/workbench/contrib/files/browser/fileCommands.ts index 54dca85021641..73bc376a36234 100644 --- a/src/vs/workbench/contrib/files/browser/fileCommands.ts +++ b/src/vs/workbench/contrib/files/browser/fileCommands.ts @@ -43,7 +43,7 @@ import { ITextFileService } from 'vs/workbench/services/textfile/common/textfile import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; import { isPromiseCanceledError } from 'vs/base/common/errors'; import { toAction } from 'vs/base/common/actions'; -import { OverrideOptions } from 'vs/platform/editor/common/editor'; +import { EditorOverride } from 'vs/platform/editor/common/editor'; // Commands @@ -358,9 +358,11 @@ CommandsRegistry.registerCommand({ const uri = getResourceForCommand(resource, accessor.get(IListService), accessor.get(IEditorService)); if (uri) { const input = editorService.createEditorInput({ resource: uri }); - return editorService.openEditor(input, { override: OverrideOptions.PICKER }); + + return editorService.openEditor(input, { override: EditorOverride.PICK }); } - return; + + return undefined; } }); diff --git a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts index 7b17f52d625a5..49e7997fe4969 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts @@ -16,7 +16,7 @@ import { IModeService } from 'vs/editor/common/services/modeService'; import { ITextModelContentProvider, ITextModelService } from 'vs/editor/common/services/resolverService'; import * as nls from 'vs/nls'; import { Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; -import { IEditorOptions, ITextEditorOptions, IResourceEditorInput, OverrideOptions } from 'vs/platform/editor/common/editor'; +import { IEditorOptions, ITextEditorOptions, IResourceEditorInput, EditorOverride } from 'vs/platform/editor/common/editor'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -351,7 +351,7 @@ export class NotebookContribution extends Disposable implements IWorkbenchContri await group.closeEditor(originalInput); originalInput.dispose(); - const newEditor = await group.openEditor(notebookInput, { ...options, index: originalEditorIndex, override: OverrideOptions.DISABLED }); + const newEditor = await group.openEditor(notebookInput, { ...options, index: originalEditorIndex, override: EditorOverride.DISABLED }); if (newEditor) { return newEditor; } else { @@ -368,7 +368,7 @@ export class NotebookContribution extends Disposable implements IWorkbenchContri // there are notebook editors with the same resource if (existingEditors.find(editor => editor.viewType === id)) { - return { override: this.editorService.openEditor(existingEditors.find(editor => editor.viewType === id)!, { ...options, override: OverrideOptions.DISABLED }, group) }; + return { override: this.editorService.openEditor(existingEditors.find(editor => editor.viewType === id)!, { ...options, override: EditorOverride.DISABLED }, group) }; } else { return { override: (async () => { @@ -378,7 +378,7 @@ export class NotebookContribution extends Disposable implements IWorkbenchContri await group.closeEditor(firstEditor); firstEditor.dispose(); const notebookInput = NotebookEditorInput.create(this.instantiationService, originalInput.resource!, originalInput.getName(), id); - const newEditor = await group.openEditor(notebookInput, { ...options, index: originalEditorIndex, override: OverrideOptions.DISABLED }); + const newEditor = await group.openEditor(notebookInput, { ...options, index: originalEditorIndex, override: EditorOverride.DISABLED }); if (newEditor) { return newEditor; @@ -473,7 +473,7 @@ export class NotebookContribution extends Disposable implements IWorkbenchContri } const notebookInput = NotebookEditorInput.create(this.instantiationService, notebookUri, originalInput.getName(), info.id); - const notebookOptions = new NotebookEditorOptions({ ...options, cellOptions, override: OverrideOptions.DISABLED, index }); + const notebookOptions = new NotebookEditorOptions({ ...options, cellOptions, override: EditorOverride.DISABLED, index }); return { override: this.editorService.openEditor(notebookInput, notebookOptions, group) }; } @@ -516,7 +516,7 @@ export class NotebookContribution extends Disposable implements IWorkbenchContri const info = associatedEditors[0]; const notebookInput = NotebookDiffEditorInput.create(this.instantiationService, notebookUri, modifiedInput.getName(), originalNotebookUri, originalInput.getName(), diffEditorInput.getName(), info.id); - const notebookOptions = new NotebookEditorOptions({ ...options, override: OverrideOptions.DISABLED }); + const notebookOptions = new NotebookEditorOptions({ ...options, override: EditorOverride.DISABLED }); return { override: this.editorService.openEditor(notebookInput, notebookOptions, group) }; } } diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts index 03a5c8eee2390..df46d1d7a8e9e 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts @@ -10,7 +10,7 @@ import { DisposableStore } from 'vs/base/common/lifecycle'; import 'vs/css!./media/notebook'; import { localize } from 'vs/nls'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { IEditorOptions, ITextEditorOptions, OverrideOptions } from 'vs/platform/editor/common/editor'; +import { IEditorOptions, ITextEditorOptions, EditorOverride } from 'vs/platform/editor/common/editor'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { IStorageService } from 'vs/platform/storage/common/storage'; @@ -173,7 +173,7 @@ export class NotebookEditor extends EditorPane { label: localize('fail.reOpen', "Reopen file with VS Code standard text editor"), run: async () => { const fileEditorInput = this._editorService.createEditorInput({ resource: input.resource, forceFile: true }); - const textOptions: IEditorOptions | ITextEditorOptions = options ? { ...options, override: OverrideOptions.DISABLED } : { override: OverrideOptions.DISABLED }; + const textOptions: IEditorOptions | ITextEditorOptions = { ...options, override: EditorOverride.DISABLED }; await this._editorService.openEditor(fileEditorInput, textOptions); } }] diff --git a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts index e86d009c9f11e..7e4af9b819880 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { localize } from 'vs/nls'; import { getPixelRatio, getZoomLevel } from 'vs/base/browser/browser'; import { flatten } from 'vs/base/common/arrays'; import { CancellationToken } from 'vs/base/common/cancellation'; @@ -23,7 +24,6 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { NotebookExtensionDescription } from 'vs/workbench/api/common/extHost.protocol'; -import { builtinProviderDisplayName } from 'vs/workbench/common/editor'; import { Memento } from 'vs/workbench/common/memento'; import { INotebookEditorContribution, notebookMarkdownRendererExtensionPoint, notebookProviderExtensionPoint, notebookRendererExtensionPoint } from 'vs/workbench/contrib/notebook/browser/extensionPoint'; import { CellEditState, getActiveNotebookEditor, ICellViewModel, INotebookEditor, NotebookEditorOptions, updateEditorTopPadding } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; @@ -125,7 +125,7 @@ export class NotebookProviderInfoStore extends Disposable { priority: this._convertPriority(notebookContribution.priority), providerExtensionId: extension.description.identifier.value, providerDescription: extension.description.description, - providerDisplayName: extension.description.isBuiltin ? builtinProviderDisplayName : extension.description.displayName || extension.description.identifier.value, + providerDisplayName: extension.description.isBuiltin ? localize('builtinProviderDisplayName', "Built-in") : extension.description.displayName || extension.description.identifier.value, providerExtensionLocation: extension.description.extensionLocation, dynamicContribution: false, exclusive: false diff --git a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.contribution.ts b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.contribution.ts index 4e3a04008b42a..6a4919885359b 100644 --- a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.contribution.ts +++ b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.contribution.ts @@ -33,7 +33,7 @@ import { getOrMakeSearchEditorInput, SearchConfiguration, SearchEditorInput, SEA import { parseSavedSearchEditor } from 'vs/workbench/contrib/searchEditor/browser/searchEditorSerialization'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { VIEW_ID } from 'vs/workbench/services/search/common/search'; -import { OverrideOptions } from 'vs/platform/editor/common/editor'; +import { EditorOverride } from 'vs/platform/editor/common/editor'; const OpenInEditorCommandId = 'search.action.openInEditor'; @@ -93,7 +93,7 @@ class SearchEditorContribution implements IWorkbenchContribution { override: (async () => { const { config } = await instantiationService.invokeFunction(parseSavedSearchEditor, resource); const input = instantiationService.invokeFunction(getOrMakeSearchEditorInput, { backingUri: resource, config }); - return editorService.openEditor(input, { ...options, override: OverrideOptions.DISABLED }, group); + return editorService.openEditor(input, { ...options, override: EditorOverride.DISABLED }, group); })() }; } diff --git a/src/vs/workbench/contrib/welcome/walkThrough/browser/editor/editorWalkThrough.ts b/src/vs/workbench/contrib/welcome/walkThrough/browser/editor/editorWalkThrough.ts index 0042a8655a7fb..6e46aba0d6b2b 100644 --- a/src/vs/workbench/contrib/welcome/walkThrough/browser/editor/editorWalkThrough.ts +++ b/src/vs/workbench/contrib/welcome/walkThrough/browser/editor/editorWalkThrough.ts @@ -11,7 +11,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { WalkThroughInput, WalkThroughInputOptions } from 'vs/workbench/contrib/welcome/walkThrough/browser/walkThroughInput'; import { FileAccess, Schemas } from 'vs/base/common/network'; import { IEditorInputFactory, EditorInput } from 'vs/workbench/common/editor'; -import { OverrideOptions } from 'vs/platform/editor/common/editor'; +import { EditorOverride } from 'vs/platform/editor/common/editor'; const typeId = 'workbench.editors.walkThroughInput'; const inputOptions: WalkThroughInputOptions = { @@ -41,7 +41,7 @@ export class EditorWalkThroughAction extends Action { public run(): Promise { const input = this.instantiationService.createInstance(WalkThroughInput, inputOptions); - return this.editorService.openEditor(input, { pinned: true, override: OverrideOptions.DISABLED }) + return this.editorService.openEditor(input, { pinned: true, override: EditorOverride.DISABLED }) .then(() => void (0)); } } diff --git a/src/vs/workbench/services/editor/browser/editorService.ts b/src/vs/workbench/services/editor/browser/editorService.ts index 46e072d883d14..264ae6c29ec22 100644 --- a/src/vs/workbench/services/editor/browser/editorService.ts +++ b/src/vs/workbench/services/editor/browser/editorService.ts @@ -5,8 +5,8 @@ import { localize } from 'vs/nls'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IResourceEditorInput, ITextEditorOptions, IEditorOptions, EditorActivation, OverrideOptions } from 'vs/platform/editor/common/editor'; -import { SideBySideEditor, IEditorInput, IEditorPane, GroupIdentifier, IFileEditorInput, IUntitledTextResourceEditorInput, IResourceDiffEditorInput, IEditorInputFactoryRegistry, Extensions as EditorExtensions, EditorInput, SideBySideEditorInput, IEditorInputWithOptions, isEditorInputWithOptions, EditorOptions, TextEditorOptions, IEditorIdentifier, IEditorCloseEvent, ITextEditorPane, ITextDiffEditorPane, IRevertOptions, SaveReason, EditorsOrder, isTextEditorPane, IWorkbenchEditorConfiguration, EditorResourceAccessor, IVisibleEditorPane, customEditorsAssociationsSettingId, builtinProviderDisplayName, DEFAULT_CUSTOM_EDITOR, CustomEditorAssociation, CustomEditorsAssociations } from 'vs/workbench/common/editor'; +import { IResourceEditorInput, ITextEditorOptions, IEditorOptions, EditorActivation, EditorOverride } from 'vs/platform/editor/common/editor'; +import { SideBySideEditor, IEditorInput, IEditorPane, GroupIdentifier, IFileEditorInput, IUntitledTextResourceEditorInput, IResourceDiffEditorInput, IEditorInputFactoryRegistry, Extensions as EditorExtensions, EditorInput, SideBySideEditorInput, IEditorInputWithOptions, isEditorInputWithOptions, EditorOptions, TextEditorOptions, IEditorIdentifier, IEditorCloseEvent, ITextEditorPane, ITextDiffEditorPane, IRevertOptions, SaveReason, EditorsOrder, isTextEditorPane, IWorkbenchEditorConfiguration, EditorResourceAccessor, IVisibleEditorPane, customEditorsAssociationsSettingId, DEFAULT_CUSTOM_EDITOR, CustomEditorAssociation, CustomEditorsAssociations } from 'vs/workbench/common/editor'; import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput'; import { Registry } from 'vs/platform/registry/common/platform'; import { ResourceMap } from 'vs/base/common/map'; @@ -549,7 +549,7 @@ export class EditorService extends Disposable implements EditorServiceImpl { const defaultEditorOverrideEntry = Object.freeze({ id: DEFAULT_EDITOR_ID, label: localize('promptOpenWith.defaultEditor.displayName', "Text Editor"), - detail: builtinProviderDisplayName + detail: localize('builtinProviderDisplayName', "Built-in") }); for (const handler of this.openEditorHandlers) { if (typeof handler.getEditorOverrides === 'function') { @@ -570,7 +570,7 @@ export class EditorService extends Disposable implements EditorServiceImpl { } const fileEditorInput = this.createEditorInput({ resource, forceFile: true }); - const textOptions: IEditorOptions | ITextEditorOptions = options ? { ...options, override: OverrideOptions.DISABLED } : { override: OverrideOptions.DISABLED }; + const textOptions: IEditorOptions | ITextEditorOptions = { ...options, override: EditorOverride.DISABLED }; return { override: (async () => { // Try to replace existing editors for resource @@ -597,7 +597,7 @@ export class EditorService extends Disposable implements EditorServiceImpl { } private onGroupWillOpenEditor(group: IEditorGroup, event: IEditorOpeningEvent): void { - if (event.options?.override === OverrideOptions.DISABLED) { + if (event.options?.override === EditorOverride.DISABLED) { return; // return early when overrides are explicitly disabled } @@ -624,8 +624,8 @@ export class EditorService extends Disposable implements EditorServiceImpl { const [resolvedGroup, resolvedEditor, resolvedOptions] = result; // If the override option is provided we want to open that specific editor or show a picker - if (resolvedOptions && (resolvedOptions.override === OverrideOptions.PICKER || typeof resolvedOptions.override === 'string')) { - return this.openEditorWith(resolvedEditor, resolvedOptions.override === OverrideOptions.PICKER ? undefined : resolvedOptions.override, resolvedOptions, resolvedGroup); + if (resolvedOptions && (resolvedOptions.override === EditorOverride.PICK || typeof resolvedOptions.override === 'string')) { + return this.openEditorWith(resolvedEditor, resolvedOptions.override === EditorOverride.PICK ? undefined : resolvedOptions.override, resolvedOptions, resolvedGroup); } return withNullAsUndefined(await resolvedGroup.openEditor(resolvedEditor, resolvedOptions)); @@ -1518,7 +1518,7 @@ export class DelegatingEditorService implements IEditorService { const [resolvedGroup, resolvedEditor, resolvedOptions] = result; // If the override option is provided we want to open that specific editor or show a picker - if (resolvedOptions && (resolvedOptions.override === OverrideOptions.PICKER || typeof resolvedOptions.override === 'string')) { + if (resolvedOptions && (resolvedOptions.override === EditorOverride.PICK || typeof resolvedOptions.override === 'string')) { return await this.editorService.openEditor(resolvedEditor, { ...resolvedOptions, override: withNullAsUndefined(resolvedOptions.override) }, resolvedGroup); } // Pass on to editor open handler From 32360ff4714310d66430f7ef72c7bc176f3acce6 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Sun, 21 Feb 2021 08:50:49 +0100 Subject: [PATCH 14/18] editorservice - static DEFAULT_EDITOR_OVERRIDE_ID --- .../services/editor/browser/editorService.ts | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/services/editor/browser/editorService.ts b/src/vs/workbench/services/editor/browser/editorService.ts index 264ae6c29ec22..2a3c8803f7c40 100644 --- a/src/vs/workbench/services/editor/browser/editorService.ts +++ b/src/vs/workbench/services/editor/browser/editorService.ts @@ -46,11 +46,6 @@ import { IJSONSchema } from 'vs/base/common/jsonSchema'; type CachedEditorInput = ResourceEditorInput | IFileEditorInput | UntitledTextEditorInput; type OpenInEditorGroup = IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE; -/** - * Id of the default editor for open with. - */ -export const DEFAULT_EDITOR_ID = 'default'; - const viewTypeSchemaAddition: IJSONSchema = { type: 'string', enum: [] @@ -535,6 +530,8 @@ export class EditorService extends Disposable implements EditorServiceImpl { //#region editor overrides + private static readonly DEFAULT_EDITOR_OVERRIDE_ID = 'default'; + private readonly openEditorHandlers: IOpenEditorOverrideHandler[] = []; overrideOpenEditor(handler: IOpenEditorOverrideHandler): IDisposable { @@ -547,7 +544,7 @@ export class EditorService extends Disposable implements EditorServiceImpl { const overrides: [IOpenEditorOverrideHandler, IOpenEditorOverrideEntry][] = []; const fileEditorInputFactory = Registry.as(EditorExtensions.EditorInputFactories).getFileEditorInputFactory(); const defaultEditorOverrideEntry = Object.freeze({ - id: DEFAULT_EDITOR_ID, + id: EditorService.DEFAULT_EDITOR_OVERRIDE_ID, label: localize('promptOpenWith.defaultEditor.displayName', "Text Editor"), detail: localize('builtinProviderDisplayName', "Built-in") }); @@ -560,7 +557,7 @@ export class EditorService extends Disposable implements EditorServiceImpl { } } } - if (!overrides.some(([_, entry]) => entry.id === DEFAULT_EDITOR_ID)) { + if (!overrides.some(([_, entry]) => entry.id === EditorService.DEFAULT_EDITOR_OVERRIDE_ID)) { overrides.unshift([ { open: (input: IEditorInput, options: IEditorOptions | ITextEditorOptions | undefined, group: IEditorGroup) => { From 293664bd658ea81287345a8a741901b0fc61d309 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Sun, 21 Feb 2021 09:16:20 +0100 Subject: [PATCH 15/18] editors - introduce a editor associations registry This moves the relevant code out of the editor service. --- src/vs/workbench/browser/editor.ts | 155 +++++++++++++++++- src/vs/workbench/common/editor.ts | 21 +-- .../customEditor/browser/customEditors.ts | 29 ++-- .../notebook/browser/notebook.contribution.ts | 14 +- .../notebook/browser/notebookServiceImpl.ts | 26 +-- .../services/editor/browser/editorService.ts | 107 +----------- .../services/editor/common/editorService.ts | 19 --- .../test/browser/workbenchTestServices.ts | 5 +- 8 files changed, 197 insertions(+), 179 deletions(-) diff --git a/src/vs/workbench/browser/editor.ts b/src/vs/workbench/browser/editor.ts index fd865714eecd1..f789de7045898 100644 --- a/src/vs/workbench/browser/editor.ts +++ b/src/vs/workbench/browser/editor.ts @@ -3,6 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { localize } from 'vs/nls'; +import { Event } from 'vs/base/common/event'; import { EditorInput } from 'vs/workbench/common/editor'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -10,9 +12,27 @@ import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane'; import { IConstructorSignature0, IInstantiationService, BrandedService } from 'vs/platform/instantiation/common/instantiation'; import { insert } from 'vs/base/common/arrays'; import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { IJSONSchema } from 'vs/base/common/jsonSchema'; +import { workbenchConfigurationNodeBase } from 'vs/workbench/common/configuration'; +import { Extensions as ConfigurationExtensions, IConfigurationNode, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; + +export const Extensions = { + Editors: 'workbench.contributions.editors', + Associations: 'workbench.editors.associations' +}; + +//#region Editors Registry export interface IEditorDescriptor { + + /** + * The unique identifier of the editor + */ getId(): string; + + /** + * The display name of the editor + */ getName(): string; instantiate(instantiationService: IInstantiationService): EditorPane; @@ -174,8 +194,137 @@ class EditorRegistry implements IEditorRegistry { } } -export const Extensions = { - Editors: 'workbench.contributions.editors' +Registry.add(Extensions.Editors, new EditorRegistry()); + +//#endregion + + +//#region Editor Associations + +export const editorsAssociationsSettingId = 'workbench.editorAssociations'; + +export const DEFAULT_EDITOR_ASSOCIATION: IEditorType = { + id: 'default', + displayName: localize('promptOpenWith.defaultEditor.displayName', "Text Editor"), + providerDisplayName: localize('builtinProviderDisplayName', "Built-in") }; -Registry.add(Extensions.Editors, new EditorRegistry()); +export type EditorAssociation = { + readonly editorType: string; + readonly filenamePattern?: string; +}; + +export type EditorsAssociations = readonly EditorAssociation[]; + +const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); + +const editorTypeSchemaAddition: IJSONSchema = { + type: 'string', + enum: [] +}; + +const editorAssociationsConfigurationNode: IConfigurationNode = { + ...workbenchConfigurationNodeBase, + properties: { + 'workbench.editorAssociations': { + type: 'array', + markdownDescription: localize('editor.editorAssociations', "Configure which editor to use for specific file types."), + items: { + type: 'object', + defaultSnippets: [{ + body: { + 'viewType': '$1', + 'filenamePattern': '$2' + } + }], + properties: { + 'viewType': { + anyOf: [ + { + type: 'string', + description: localize('editor.editorAssociations.viewType', "The unique id of the editor to use."), + }, + editorTypeSchemaAddition + ] + }, + 'filenamePattern': { + type: 'string', + description: localize('editor.editorAssociations.filenamePattern', "Glob pattern specifying which files the editor should be used for."), + } + } + } + } + } +}; + +export interface IEditorType { + readonly id: string; + readonly displayName: string; + readonly providerDisplayName: string; +} + +export interface IEditorTypesHandler { + readonly onDidChangeEditorTypes: Event; + + getEditorTypes(): IEditorType[]; +} + +export interface IEditorAssociationsRegistry { + + /** + * Register handlers for editor types + */ + registerEditorTypesHandler(source: string, handler: IEditorTypesHandler): IDisposable; +} + +class EditorAssociationsRegistry implements IEditorAssociationsRegistry { + + private readonly editorTypesHandlers = new Map(); + + registerEditorTypesHandler(source: string, handler: IEditorTypesHandler): IDisposable { + if (this.editorTypesHandlers.has(source)) { + throw new Error(`Use a different name for the editor component, ${source} is already occupied.`); + } + + this.editorTypesHandlers.set(source, handler); + this.updateSchema(); + + const editorTypeChangeEvent = handler.onDidChangeEditorTypes(() => { + this.updateSchema(); + }); + + return { + dispose: () => { + editorTypeChangeEvent.dispose(); + this.editorTypesHandlers.delete(source); + this.updateSchema(); + } + }; + } + + private updateSchema() { + const enumValues: string[] = []; + const enumDescriptions: string[] = []; + + const infos: IEditorType[] = [DEFAULT_EDITOR_ASSOCIATION]; + + for (const [, handler] of this.editorTypesHandlers) { + infos.push(...handler.getEditorTypes()); + } + + infos.forEach(info => { + enumValues.push(info.id); + enumDescriptions.push(localize('editorAssociations.editorType.sourceDescription', "Source: {0}", info.providerDisplayName)); + }); + + editorTypeSchemaAddition.enum = enumValues; + editorTypeSchemaAddition.enumDescriptions = enumDescriptions; + + configurationRegistry.notifyConfigurationSchemaUpdated(editorAssociationsConfigurationNode); + } +} + +Registry.add(Extensions.Associations, new EditorAssociationsRegistry()); +configurationRegistry.registerConfiguration(editorAssociationsConfigurationNode); + +//#endregion diff --git a/src/vs/workbench/common/editor.ts b/src/vs/workbench/common/editor.ts index 13eb9710b5f78..37a27432c0e55 100644 --- a/src/vs/workbench/common/editor.ts +++ b/src/vs/workbench/common/editor.ts @@ -20,7 +20,7 @@ import { ActionRunner, IAction } from 'vs/base/common/actions'; import { IFileService } from 'vs/platform/files/common/files'; import { IPathData } from 'vs/platform/windows/common/windows'; import { coalesce, firstOrDefault } from 'vs/base/common/arrays'; -import { ACTIVE_GROUP, ICustomEditorInfo, IResourceEditorInputType, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; +import { ACTIVE_GROUP, IResourceEditorInputType, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { IRange } from 'vs/editor/common/core/range'; import { IExtUri } from 'vs/base/common/resources'; @@ -1637,22 +1637,3 @@ export function editorGroupToViewColumn(editorGroupService: IEditorGroupsService } //#endregion - -//#region Editor Open With - -export const customEditorsAssociationsSettingId = 'workbench.editorAssociations'; - -export const DEFAULT_CUSTOM_EDITOR: ICustomEditorInfo = { - id: 'default', - displayName: localize('promptOpenWith.defaultEditor.displayName', "Text Editor"), - providerDisplayName: localize('builtinProviderDisplayName', "Built-in") -}; - -export type CustomEditorAssociation = { - readonly viewType: string; - readonly filenamePattern?: string; -}; - -export type CustomEditorsAssociations = readonly CustomEditorAssociation[]; - -//#endregion diff --git a/src/vs/workbench/contrib/customEditor/browser/customEditors.ts b/src/vs/workbench/contrib/customEditor/browser/customEditors.ts index b4ef915414bd3..9b334a3e8ed62 100644 --- a/src/vs/workbench/contrib/customEditor/browser/customEditors.ts +++ b/src/vs/workbench/contrib/customEditor/browser/customEditors.ts @@ -23,18 +23,19 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { IStorageService } from 'vs/platform/storage/common/storage'; import * as colorRegistry from 'vs/platform/theme/common/colorRegistry'; import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { Extensions as EditorExtensions, IEditorTypesHandler, IEditorType, IEditorAssociationsRegistry, EditorsAssociations, editorsAssociationsSettingId, EditorAssociation } from 'vs/workbench/browser/editor'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; -import { CustomEditorAssociation, CustomEditorsAssociations, customEditorsAssociationsSettingId, EditorInput, EditorOptions, Extensions as EditorInputExtensions, GroupIdentifier, IEditorInput, IEditorInputFactoryRegistry, IEditorPane } from 'vs/workbench/common/editor'; +import { EditorInput, EditorOptions, Extensions as EditorInputExtensions, GroupIdentifier, IEditorInput, IEditorInputFactoryRegistry, IEditorPane } from 'vs/workbench/common/editor'; import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; import { CONTEXT_CUSTOM_EDITORS, CONTEXT_FOCUSED_CUSTOM_EDITOR_IS_EDITABLE, CustomEditorCapabilities, CustomEditorInfo, CustomEditorInfoCollection, CustomEditorPriority, ICustomEditorService } from 'vs/workbench/contrib/customEditor/common/customEditor'; import { CustomEditorModelManager } from 'vs/workbench/contrib/customEditor/common/customEditorModelManager'; import { IWebviewService } from 'vs/workbench/contrib/webview/browser/webview'; import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; -import { ICustomEditorInfo, ICustomEditorViewTypesHandler, IEditorService, IOpenEditorOverride, IOpenEditorOverrideEntry } from 'vs/workbench/services/editor/common/editorService'; +import { IEditorService, IOpenEditorOverride, IOpenEditorOverrideEntry } from 'vs/workbench/services/editor/common/editorService'; import { ContributedCustomEditors, defaultCustomEditor } from '../common/contributedCustomEditors'; import { CustomEditorInput } from './customEditorInput'; -export class CustomEditorService extends Disposable implements ICustomEditorService, ICustomEditorViewTypesHandler { +export class CustomEditorService extends Disposable implements ICustomEditorService, IEditorTypesHandler { _serviceBrand: any; private readonly _contributedEditors: ContributedCustomEditors; @@ -44,8 +45,8 @@ export class CustomEditorService extends Disposable implements ICustomEditorServ private readonly _customEditorContextKey: IContextKey; private readonly _focusedCustomEditorIsEditable: IContextKey; - private readonly _onDidChangeViewTypes = new Emitter(); - onDidChangeViewTypes: Event = this._onDidChangeViewTypes.event; + private readonly _onDidChangeEditorTypes = this._register(new Emitter()); + onDidChangeEditorTypes: Event = this._onDidChangeEditorTypes.event; private readonly _fileEditorInputFactory = Registry.as(EditorInputExtensions.EditorInputFactories).getFileEditorInputFactory(); @@ -68,9 +69,9 @@ export class CustomEditorService extends Disposable implements ICustomEditorServ this._contributedEditors = this._register(new ContributedCustomEditors(storageService)); this._register(this._contributedEditors.onChange(() => { this.updateContexts(); - this._onDidChangeViewTypes.fire(); + this._onDidChangeEditorTypes.fire(); })); - this._register(this.editorService.registerCustomEditorViewTypesHandler('Custom Editor', this)); + this._register(Registry.as(EditorExtensions.Associations).registerEditorTypesHandler('Custom Editor', this)); this._register(this.editorService.onDidActiveEditorChange(() => this.updateContexts())); this._register(fileService.onDidRunOperation(e => { @@ -90,7 +91,7 @@ export class CustomEditorService extends Disposable implements ICustomEditorServ this.updateContexts(); } - getViewTypes(): ICustomEditorInfo[] { + getEditorTypes(): IEditorType[] { return [...this._contributedEditors]; } @@ -117,11 +118,11 @@ export class CustomEditorService extends Disposable implements ICustomEditorServ } public getUserConfiguredCustomEditors(resource: URI): CustomEditorInfoCollection { - const rawAssociations = this.configurationService.getValue(customEditorsAssociationsSettingId) || []; + const rawAssociations = this.configurationService.getValue(editorsAssociationsSettingId) || []; return new CustomEditorInfoCollection( coalesce(rawAssociations .filter(association => CustomEditorInfo.selectorMatches(association, resource)) - .map(association => this._contributedEditors.get(association.viewType)))); + .map(association => this._contributedEditors.get(association.editorType)))); } public getAllCustomEditors(resource: URI): CustomEditorInfoCollection { @@ -187,22 +188,22 @@ export class CustomEditorService extends Disposable implements ICustomEditorServ // And persist the setting if (pick) { - const newAssociation: CustomEditorAssociation = { viewType: pick, filenamePattern: '*' + resourceExt }; - const currentAssociations = [...this.configurationService.getValue(customEditorsAssociationsSettingId)]; + const newAssociation: EditorAssociation = { editorType: pick, filenamePattern: '*' + resourceExt }; + const currentAssociations = [...this.configurationService.getValue(editorsAssociationsSettingId)]; // First try updating existing association for (let i = 0; i < currentAssociations.length; ++i) { const existing = currentAssociations[i]; if (existing.filenamePattern === newAssociation.filenamePattern) { currentAssociations.splice(i, 1, newAssociation); - this.configurationService.updateValue(customEditorsAssociationsSettingId, currentAssociations); + this.configurationService.updateValue(editorsAssociationsSettingId, currentAssociations); return; } } // Otherwise, create a new one currentAssociations.unshift(newAssociation); - this.configurationService.updateValue(customEditorsAssociationsSettingId, currentAssociations); + this.configurationService.updateValue(editorsAssociationsSettingId, currentAssociations); } }); picker.show(); diff --git a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts index 49e7997fe4969..4d001e37956a2 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts @@ -22,9 +22,9 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { Registry } from 'vs/platform/registry/common/platform'; -import { EditorDescriptor, Extensions as EditorExtensions, IEditorRegistry } from 'vs/workbench/browser/editor'; +import { EditorDescriptor, EditorsAssociations, editorsAssociationsSettingId, Extensions as EditorExtensions, IEditorRegistry } from 'vs/workbench/browser/editor'; import { Extensions as WorkbenchExtensions, IWorkbenchContribution, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; -import { CustomEditorsAssociations, customEditorsAssociationsSettingId, EditorInput, Extensions as EditorInputExtensions, ICustomEditorInputFactory, IEditorInput, IEditorInputFactory, IEditorInputFactoryRegistry } from 'vs/workbench/common/editor'; +import { EditorInput, Extensions as EditorInputExtensions, ICustomEditorInputFactory, IEditorInput, IEditorInputFactory, IEditorInputFactoryRegistry } from 'vs/workbench/common/editor'; import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; import { NotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookEditor'; import { NotebookEditorInput } from 'vs/workbench/contrib/notebook/browser/notebookEditorInput'; @@ -303,18 +303,18 @@ export class NotebookContribution extends Disposable implements IWorkbenchContri } getUserAssociatedEditors(resource: URI) { - const rawAssociations = this.configurationService.getValue(customEditorsAssociationsSettingId) || []; + const rawAssociations = this.configurationService.getValue(editorsAssociationsSettingId) || []; return coalesce(rawAssociations .filter(association => CustomEditorInfo.selectorMatches(association, resource))); } getUserAssociatedNotebookEditors(resource: URI) { - const rawAssociations = this.configurationService.getValue(customEditorsAssociationsSettingId) || []; + const rawAssociations = this.configurationService.getValue(editorsAssociationsSettingId) || []; return coalesce(rawAssociations .filter(association => CustomEditorInfo.selectorMatches(association, resource)) - .map(association => this.notebookService.getContributedNotebookProvider(association.viewType))); + .map(association => this.notebookService.getContributedNotebookProvider(association.editorType))); } getContributedEditors(resource: URI) { @@ -431,7 +431,7 @@ export class NotebookContribution extends Disposable implements IWorkbenchContri } const userAssociatedEditors = this.getUserAssociatedEditors(notebookUri); - const notebookEditor = userAssociatedEditors.filter(association => this.notebookService.getContributedNotebookProvider(association.viewType)); + const notebookEditor = userAssociatedEditors.filter(association => this.notebookService.getContributedNotebookProvider(association.editorType)); if (userAssociatedEditors.length && !notebookEditor.length) { // user pick a non-notebook editor for this resource @@ -494,7 +494,7 @@ export class NotebookContribution extends Disposable implements IWorkbenchContri } const userAssociatedEditors = this.getUserAssociatedEditors(notebookUri); - const notebookEditor = userAssociatedEditors.filter(association => this.notebookService.getContributedNotebookProvider(association.viewType)); + const notebookEditor = userAssociatedEditors.filter(association => this.notebookService.getContributedNotebookProvider(association.editorType)); if (userAssociatedEditors.length && !notebookEditor.length) { // user pick a non-notebook editor for this resource diff --git a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts index 7e4af9b819880..9c40d441000e4 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts @@ -36,9 +36,11 @@ import { NotebookMarkdownRendererInfo } from 'vs/workbench/contrib/notebook/comm import { NotebookOutputRendererInfo } from 'vs/workbench/contrib/notebook/common/notebookOutputRenderer'; import { NotebookEditorDescriptor, NotebookProviderInfo } from 'vs/workbench/contrib/notebook/common/notebookProvider'; import { IMainNotebookController, INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; -import { ICustomEditorInfo, ICustomEditorViewTypesHandler, IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry'; +import { Extensions as EditorExtensions, IEditorTypesHandler, IEditorType, IEditorAssociationsRegistry } from 'vs/workbench/browser/editor'; +import { Registry } from 'vs/platform/registry/common/platform'; export class NotebookKernelProviderInfoStore { private readonly _notebookKernelProviders: INotebookKernelProvider[] = []; @@ -236,7 +238,7 @@ class ModelData implements IDisposable { } } -export class NotebookService extends Disposable implements INotebookService, ICustomEditorViewTypesHandler { +export class NotebookService extends Disposable implements INotebookService, IEditorTypesHandler { declare readonly _serviceBrand: undefined; private readonly _notebookProviders = new Map(); notebookProviderInfoStore: NotebookProviderInfoStore; @@ -244,10 +246,10 @@ export class NotebookService extends Disposable implements INotebookService, ICu private readonly markdownRenderersInfos = new Set(); notebookKernelProviderInfoStore: NotebookKernelProviderInfoStore = new NotebookKernelProviderInfoStore(); private readonly _models = new ResourceMap(); - private _onDidChangeActiveEditor = new Emitter(); + private _onDidChangeActiveEditor = this._register(new Emitter()); onDidChangeActiveEditor: Event = this._onDidChangeActiveEditor.event; private _activeEditorDisposables = new DisposableStore(); - private _onDidChangeVisibleEditors = new Emitter(); + private _onDidChangeVisibleEditors = this._register(new Emitter()); onDidChangeVisibleEditors: Event = this._onDidChangeVisibleEditors.event; private readonly _onNotebookEditorAdd: Emitter = this._register(new Emitter()); public readonly onNotebookEditorAdd: Event = this._onNotebookEditorAdd.event; @@ -263,12 +265,12 @@ export class NotebookService extends Disposable implements INotebookService, ICu public readonly onNotebookDocumentSaved: Event = this._onNotebookDocumentSaved.event; private readonly _notebookEditors = new Map(); - private readonly _onDidChangeViewTypes = new Emitter(); - onDidChangeViewTypes: Event = this._onDidChangeViewTypes.event; + private readonly _onDidChangeEditorTypes = this._register(new Emitter()); + onDidChangeEditorTypes: Event = this._onDidChangeEditorTypes.event; - private readonly _onDidChangeKernels = new Emitter(); + private readonly _onDidChangeKernels = this._register(new Emitter()); onDidChangeKernels: Event = this._onDidChangeKernels.event; - private readonly _onDidChangeNotebookActiveKernel = new Emitter<{ uri: URI, providerHandle: number | undefined, kernelFriendlyId: string | undefined; }>(); + private readonly _onDidChangeNotebookActiveKernel = this._register(new Emitter<{ uri: URI, providerHandle: number | undefined, kernelFriendlyId: string | undefined; }>()); onDidChangeNotebookActiveKernel: Event<{ uri: URI, providerHandle: number | undefined, kernelFriendlyId: string | undefined; }> = this._onDidChangeNotebookActiveKernel.event; private cutItems: NotebookCellTextModel[] | undefined; private _lastClipboardIsCopy: boolean = true; @@ -353,7 +355,7 @@ export class NotebookService extends Disposable implements INotebookService, ICu } }); - this._editorService.registerCustomEditorViewTypesHandler('Notebook', this); + this._register(Registry.as(EditorExtensions.Associations).registerEditorTypesHandler('Notebook', this)); const updateOrder = () => { const userOrder = this._configurationService.getValue(DisplayOrderKey); @@ -633,7 +635,7 @@ export class NotebookService extends Disposable implements INotebookService, ICu return this._decorationOptionProviders.get(key); } - getViewTypes(): ICustomEditorInfo[] { + getEditorTypes(): IEditorType[] { return [...this.notebookProviderInfoStore].map(info => ({ id: info.id, displayName: info.displayName, @@ -687,10 +689,10 @@ export class NotebookService extends Disposable implements INotebookService, ICu this.notebookProviderInfoStore.get(viewType)?.update({ options: controller.options }); - this._onDidChangeViewTypes.fire(); + this._onDidChangeEditorTypes.fire(); return toDisposable(() => { this._notebookProviders.delete(viewType); - this._onDidChangeViewTypes.fire(); + this._onDidChangeEditorTypes.fire(); }); } diff --git a/src/vs/workbench/services/editor/browser/editorService.ts b/src/vs/workbench/services/editor/browser/editorService.ts index 2a3c8803f7c40..724d521b21194 100644 --- a/src/vs/workbench/services/editor/browser/editorService.ts +++ b/src/vs/workbench/services/editor/browser/editorService.ts @@ -6,7 +6,8 @@ import { localize } from 'vs/nls'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IResourceEditorInput, ITextEditorOptions, IEditorOptions, EditorActivation, EditorOverride } from 'vs/platform/editor/common/editor'; -import { SideBySideEditor, IEditorInput, IEditorPane, GroupIdentifier, IFileEditorInput, IUntitledTextResourceEditorInput, IResourceDiffEditorInput, IEditorInputFactoryRegistry, Extensions as EditorExtensions, EditorInput, SideBySideEditorInput, IEditorInputWithOptions, isEditorInputWithOptions, EditorOptions, TextEditorOptions, IEditorIdentifier, IEditorCloseEvent, ITextEditorPane, ITextDiffEditorPane, IRevertOptions, SaveReason, EditorsOrder, isTextEditorPane, IWorkbenchEditorConfiguration, EditorResourceAccessor, IVisibleEditorPane, customEditorsAssociationsSettingId, DEFAULT_CUSTOM_EDITOR, CustomEditorAssociation, CustomEditorsAssociations } from 'vs/workbench/common/editor'; +import { SideBySideEditor, IEditorInput, IEditorPane, GroupIdentifier, IFileEditorInput, IUntitledTextResourceEditorInput, IResourceDiffEditorInput, IEditorInputFactoryRegistry, Extensions as EditorExtensions, EditorInput, SideBySideEditorInput, IEditorInputWithOptions, isEditorInputWithOptions, EditorOptions, TextEditorOptions, IEditorIdentifier, IEditorCloseEvent, ITextEditorPane, ITextDiffEditorPane, IRevertOptions, SaveReason, EditorsOrder, isTextEditorPane, IWorkbenchEditorConfiguration, EditorResourceAccessor, IVisibleEditorPane } from 'vs/workbench/common/editor'; +import { EditorAssociation, EditorsAssociations, editorsAssociationsSettingId } from 'vs/workbench/browser/editor'; import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput'; import { Registry } from 'vs/platform/registry/common/platform'; import { ResourceMap } from 'vs/base/common/map'; @@ -18,7 +19,7 @@ import { URI } from 'vs/base/common/uri'; import { basename, joinPath, isEqual, extname } from 'vs/base/common/resources'; import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; import { IEditorGroupsService, IEditorGroup, GroupsOrder, IEditorReplacement, GroupChangeKind, preferredSideBySideGroupDirection, OpenEditorContext } from 'vs/workbench/services/editor/common/editorGroupsService'; -import { IResourceEditorInputType, SIDE_GROUP, IResourceEditorReplacement, IOpenEditorOverrideHandler, IEditorService, SIDE_GROUP_TYPE, ACTIVE_GROUP_TYPE, ISaveEditorsOptions, ISaveAllEditorsOptions, IRevertAllEditorsOptions, IBaseSaveRevertAllEditorOptions, IOpenEditorOverrideEntry, ICustomEditorViewTypesHandler, ICustomEditorInfo } from 'vs/workbench/services/editor/common/editorService'; +import { IResourceEditorInputType, SIDE_GROUP, IResourceEditorReplacement, IOpenEditorOverrideHandler, IEditorService, SIDE_GROUP_TYPE, ACTIVE_GROUP_TYPE, ISaveEditorsOptions, ISaveAllEditorsOptions, IRevertAllEditorsOptions, IBaseSaveRevertAllEditorOptions, IOpenEditorOverrideEntry } from 'vs/workbench/services/editor/common/editorService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { Disposable, IDisposable, dispose, toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { coalesce, distinct, firstOrDefault, insert } from 'vs/base/common/arrays'; @@ -33,57 +34,16 @@ import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/u import { Promises, timeout } from 'vs/base/common/async'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { indexOfPath } from 'vs/base/common/extpath'; -import { Extensions as ConfigurationExtensions, IConfigurationNode, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService'; import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; import { IModelService } from 'vs/editor/common/services/modelService'; import { ILogService } from 'vs/platform/log/common/log'; import { IKeyMods, IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; import { Codicon } from 'vs/base/common/codicons'; -import { workbenchConfigurationNodeBase } from 'vs/workbench/common/configuration'; -import { IJSONSchema } from 'vs/base/common/jsonSchema'; type CachedEditorInput = ResourceEditorInput | IFileEditorInput | UntitledTextEditorInput; type OpenInEditorGroup = IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE; -const viewTypeSchemaAddition: IJSONSchema = { - type: 'string', - enum: [] -}; - -const editorAssociationsConfigurationNode: IConfigurationNode = { - ...workbenchConfigurationNodeBase, - properties: { - [customEditorsAssociationsSettingId]: { - type: 'array', - markdownDescription: localize('editor.editorAssociations', "Configure which editor to use for specific file types."), - items: { - type: 'object', - defaultSnippets: [{ - body: { - 'viewType': '$1', - 'filenamePattern': '$2' - } - }], - properties: { - 'viewType': { - anyOf: [ - { - type: 'string', - description: localize('editor.editorAssociations.viewType', "The unique id of the editor to use."), - }, - viewTypeSchemaAddition - ] - }, - 'filenamePattern': { - type: 'string', - description: localize('editor.editorAssociations.filenamePattern', "Glob pattern specifying which files the editor should be used for."), - } - } - } - } - } -}; export class EditorService extends Disposable implements EditorServiceImpl { declare readonly _serviceBrand: undefined; @@ -786,22 +746,22 @@ export class EditorService extends Disposable implements EditorServiceImpl { // And persist the setting if (pick && id) { - const newAssociation: CustomEditorAssociation = { viewType: id, filenamePattern: '*' + resourceExt }; - const currentAssociations = [...this.configurationService.getValue(customEditorsAssociationsSettingId)]; + const newAssociation: EditorAssociation = { editorType: id, filenamePattern: '*' + resourceExt }; + const currentAssociations = [...this.configurationService.getValue(editorsAssociationsSettingId)]; // First try updating existing association for (let i = 0; i < currentAssociations.length; ++i) { const existing = currentAssociations[i]; if (existing.filenamePattern === newAssociation.filenamePattern) { currentAssociations.splice(i, 1, newAssociation); - this.configurationService.updateValue(customEditorsAssociationsSettingId, currentAssociations); + this.configurationService.updateValue(editorsAssociationsSettingId, currentAssociations); return; } } // Otherwise, create a new one currentAssociations.unshift(newAssociation); - this.configurationService.updateValue(customEditorsAssociationsSettingId, currentAssociations); + this.configurationService.updateValue(editorsAssociationsSettingId, currentAssociations); } }); @@ -1365,54 +1325,6 @@ export class EditorService extends Disposable implements EditorServiceImpl { //#endregion - //#region Custom View Type - - private readonly customEditorViewTypesHandlers = new Map(); - - registerCustomEditorViewTypesHandler(source: string, handler: ICustomEditorViewTypesHandler): IDisposable { - if (this.customEditorViewTypesHandlers.has(source)) { - throw new Error(`Use a different name for the custom editor component, ${source} is already occupied.`); - } - - this.customEditorViewTypesHandlers.set(source, handler); - this.updateSchema(); - - const viewTypeChangeEvent = handler.onDidChangeViewTypes(() => { - this.updateSchema(); - }); - - return { - dispose: () => { - viewTypeChangeEvent.dispose(); - this.customEditorViewTypesHandlers.delete(source); - this.updateSchema(); - } - }; - } - - private updateSchema() { - const enumValues: string[] = []; - const enumDescriptions: string[] = []; - - const infos: ICustomEditorInfo[] = [DEFAULT_CUSTOM_EDITOR]; - - for (const [, handler] of this.customEditorViewTypesHandlers) { - infos.push(...handler.getViewTypes()); - } - - infos.forEach(info => { - enumValues.push(info.id); - enumDescriptions.push(localize('editorAssociations.viewType.sourceDescription', "Source: {0}", info.providerDisplayName)); - }); - - viewTypeSchemaAddition.enum = enumValues; - viewTypeSchemaAddition.enumDescriptions = enumDescriptions; - - Registry.as(ConfigurationExtensions.Configuration) - .notifyConfigurationSchemaUpdated(editorAssociationsConfigurationNode); - } - - //#endregion //#region Editor Tracking @@ -1583,14 +1495,9 @@ export class DelegatingEditorService implements IEditorService { revert(editors: IEditorIdentifier | IEditorIdentifier[], options?: IRevertOptions): Promise { return this.editorService.revert(editors, options); } revertAll(options?: IRevertAllEditorsOptions): Promise { return this.editorService.revertAll(options); } - registerCustomEditorViewTypesHandler(source: string, handler: ICustomEditorViewTypesHandler): IDisposable { return this.editorService.registerCustomEditorViewTypesHandler(source, handler); } - whenClosed(editors: IResourceEditorInput[]): Promise { return this.editorService.whenClosed(editors); } //#endregion } registerSingleton(IEditorService, EditorService); - -Registry.as(ConfigurationExtensions.Configuration) - .registerConfiguration(editorAssociationsConfigurationNode); diff --git a/src/vs/workbench/services/editor/common/editorService.ts b/src/vs/workbench/services/editor/common/editorService.ts index fc0f40a1cda74..ce8ab56137301 100644 --- a/src/vs/workbench/services/editor/common/editorService.ts +++ b/src/vs/workbench/services/editor/common/editorService.ts @@ -73,18 +73,6 @@ export interface ISaveAllEditorsOptions extends ISaveEditorsOptions, IBaseSaveRe export interface IRevertAllEditorsOptions extends IRevertOptions, IBaseSaveRevertAllEditorOptions { } -export interface ICustomEditorInfo { - readonly id: string; - readonly displayName: string; - readonly providerDisplayName: string; -} - -export interface ICustomEditorViewTypesHandler { - readonly onDidChangeViewTypes: Event; - - getViewTypes(): ICustomEditorInfo[]; -} - export interface IEditorService { readonly _serviceBrand: undefined; @@ -250,13 +238,6 @@ export interface IEditorService { */ overrideOpenEditor(handler: IOpenEditorOverrideHandler): IDisposable; - /** - * Register handlers for custom editor view types. - * The handler will provide all available custom editors registered - * and also notify the editor service when a custom editor view type is registered/unregistered. - */ - registerCustomEditorViewTypesHandler(source: string, handler: ICustomEditorViewTypesHandler): IDisposable; - /** * Converts a lightweight input to a workbench editor input. */ diff --git a/src/vs/workbench/test/browser/workbenchTestServices.ts b/src/vs/workbench/test/browser/workbenchTestServices.ts index 619081b57b0de..46e85aafc85dc 100644 --- a/src/vs/workbench/test/browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/browser/workbenchTestServices.ts @@ -51,7 +51,7 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IDecorationsService, IResourceDecorationChangeEvent, IDecoration, IDecorationData, IDecorationsProvider } from 'vs/workbench/services/decorations/browser/decorations'; import { IDisposable, toDisposable, Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { IEditorGroupsService, IEditorGroup, GroupsOrder, GroupsArrangement, GroupDirection, IAddGroupOptions, IMergeGroupOptions, IMoveEditorOptions, ICopyEditorOptions, IEditorReplacement, IGroupChangeEvent, IFindGroupScope, EditorGroupLayout, ICloseEditorOptions, GroupOrientation, ICloseAllEditorsOptions, ICloseEditorsFilter } from 'vs/workbench/services/editor/common/editorGroupsService'; -import { IEditorService, IOpenEditorOverrideHandler, ISaveEditorsOptions, IRevertAllEditorsOptions, IResourceEditorInputType, SIDE_GROUP_TYPE, ACTIVE_GROUP_TYPE, IOpenEditorOverrideEntry, ICustomEditorViewTypesHandler } from 'vs/workbench/services/editor/common/editorService'; +import { IEditorService, IOpenEditorOverrideHandler, ISaveEditorsOptions, IRevertAllEditorsOptions, IResourceEditorInputType, SIDE_GROUP_TYPE, ACTIVE_GROUP_TYPE, IOpenEditorOverrideEntry } from 'vs/workbench/services/editor/common/editorService'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { IEditorRegistry, EditorDescriptor, Extensions } from 'vs/workbench/browser/editor'; import { EditorGroup } from 'vs/workbench/common/editor/editorGroup'; @@ -747,9 +747,6 @@ export class TestEditorService implements EditorServiceImpl { findEditors() { return []; } getEditorOverrides(resource: URI, options: IEditorOptions | undefined, group: IEditorGroup | undefined): [IOpenEditorOverrideHandler, IOpenEditorOverrideEntry][] { return []; } overrideOpenEditor(_handler: IOpenEditorOverrideHandler): IDisposable { return toDisposable(() => undefined); } - registerCustomEditorViewTypesHandler(source: string, handler: ICustomEditorViewTypesHandler): IDisposable { - throw new Error('Method not implemented.'); - } openEditor(editor: IEditorInput, options?: IEditorOptions | ITextEditorOptions, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise; openEditor(editor: IResourceEditorInput | IUntitledTextResourceEditorInput, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise; openEditor(editor: IResourceDiffEditorInput, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise; From f7b7f94cb347f8cdd529f7929ff4a2d0213b087f Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Sun, 21 Feb 2021 11:09:08 +0100 Subject: [PATCH 16/18] cleanup editor picking --- .../services/editor/browser/editorService.ts | 259 ++++++++++-------- 1 file changed, 140 insertions(+), 119 deletions(-) diff --git a/src/vs/workbench/services/editor/browser/editorService.ts b/src/vs/workbench/services/editor/browser/editorService.ts index 724d521b21194..648d83b5da202 100644 --- a/src/vs/workbench/services/editor/browser/editorService.ts +++ b/src/vs/workbench/services/editor/browser/editorService.ts @@ -7,7 +7,7 @@ import { localize } from 'vs/nls'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IResourceEditorInput, ITextEditorOptions, IEditorOptions, EditorActivation, EditorOverride } from 'vs/platform/editor/common/editor'; import { SideBySideEditor, IEditorInput, IEditorPane, GroupIdentifier, IFileEditorInput, IUntitledTextResourceEditorInput, IResourceDiffEditorInput, IEditorInputFactoryRegistry, Extensions as EditorExtensions, EditorInput, SideBySideEditorInput, IEditorInputWithOptions, isEditorInputWithOptions, EditorOptions, TextEditorOptions, IEditorIdentifier, IEditorCloseEvent, ITextEditorPane, ITextDiffEditorPane, IRevertOptions, SaveReason, EditorsOrder, isTextEditorPane, IWorkbenchEditorConfiguration, EditorResourceAccessor, IVisibleEditorPane } from 'vs/workbench/common/editor'; -import { EditorAssociation, EditorsAssociations, editorsAssociationsSettingId } from 'vs/workbench/browser/editor'; +import { DEFAULT_EDITOR_ASSOCIATION, EditorAssociation, EditorsAssociations, editorsAssociationsSettingId } from 'vs/workbench/browser/editor'; import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput'; import { Registry } from 'vs/platform/registry/common/platform'; import { ResourceMap } from 'vs/base/common/map'; @@ -490,8 +490,6 @@ export class EditorService extends Disposable implements EditorServiceImpl { //#region editor overrides - private static readonly DEFAULT_EDITOR_OVERRIDE_ID = 'default'; - private readonly openEditorHandlers: IOpenEditorOverrideHandler[] = []; overrideOpenEditor(handler: IOpenEditorOverrideHandler): IDisposable { @@ -502,12 +500,8 @@ export class EditorService extends Disposable implements EditorServiceImpl { getEditorOverrides(resource: URI, options: IEditorOptions | undefined, group: IEditorGroup | undefined): [IOpenEditorOverrideHandler, IOpenEditorOverrideEntry][] { const overrides: [IOpenEditorOverrideHandler, IOpenEditorOverrideEntry][] = []; - const fileEditorInputFactory = Registry.as(EditorExtensions.EditorInputFactories).getFileEditorInputFactory(); - const defaultEditorOverrideEntry = Object.freeze({ - id: EditorService.DEFAULT_EDITOR_OVERRIDE_ID, - label: localize('promptOpenWith.defaultEditor.displayName', "Text Editor"), - detail: localize('builtinProviderDisplayName', "Built-in") - }); + + // Collect contributed editor open overrides for (const handler of this.openEditorHandlers) { if (typeof handler.getEditorOverrides === 'function') { try { @@ -517,11 +511,13 @@ export class EditorService extends Disposable implements EditorServiceImpl { } } } - if (!overrides.some(([_, entry]) => entry.id === EditorService.DEFAULT_EDITOR_OVERRIDE_ID)) { + + // Ensure the default one is always present + if (!overrides.some(([, entry]) => entry.id === DEFAULT_EDITOR_ASSOCIATION.id)) { overrides.unshift([ { - open: (input: IEditorInput, options: IEditorOptions | ITextEditorOptions | undefined, group: IEditorGroup) => { - const resource = EditorResourceAccessor.getOriginalUri(input); + open: (editor: IEditorInput, options: IEditorOptions | ITextEditorOptions | undefined, group: IEditorGroup) => { + const resource = EditorResourceAccessor.getOriginalUri(editor); if (!resource) { return; } @@ -530,6 +526,7 @@ export class EditorService extends Disposable implements EditorServiceImpl { const textOptions: IEditorOptions | ITextEditorOptions = { ...options, override: EditorOverride.DISABLED }; return { override: (async () => { + // Try to replace existing editors for resource const existingEditor = firstOrDefault(this.findEditors(resource, group)); if (existingEditor && !fileEditorInput.matches(existingEditor)) { @@ -546,10 +543,14 @@ export class EditorService extends Disposable implements EditorServiceImpl { } }, { - ...defaultEditorOverrideEntry, - active: fileEditorInputFactory.isFileEditorInput(this.activeEditor) && isEqual(this.activeEditor.resource, resource), - }]); + id: DEFAULT_EDITOR_ASSOCIATION.id, + label: DEFAULT_EDITOR_ASSOCIATION.displayName, + detail: DEFAULT_EDITOR_ASSOCIATION.providerDisplayName, + active: this.fileEditorInputFactory.isFileEditorInput(this.activeEditor) && isEqual(this.activeEditor.resource, resource), + } + ]); } + return overrides; } @@ -581,10 +582,11 @@ export class EditorService extends Disposable implements EditorServiceImpl { const [resolvedGroup, resolvedEditor, resolvedOptions] = result; // If the override option is provided we want to open that specific editor or show a picker - if (resolvedOptions && (resolvedOptions.override === EditorOverride.PICK || typeof resolvedOptions.override === 'string')) { - return this.openEditorWith(resolvedEditor, resolvedOptions.override === EditorOverride.PICK ? undefined : resolvedOptions.override, resolvedOptions, resolvedGroup); + if (resolvedOptions?.override === EditorOverride.PICK || typeof resolvedOptions?.override === 'string') { + return this.openEditorWith(resolvedOptions.override, resolvedEditor, resolvedOptions, resolvedGroup); } + // Otherwise proceed to open normally return withNullAsUndefined(await resolvedGroup.openEditor(resolvedEditor, resolvedOptions)); } @@ -645,140 +647,159 @@ export class EditorService extends Disposable implements EditorServiceImpl { return undefined; } - private async openEditorWith(editor: IEditorInput, editorID: string | undefined, editorOptions: IEditorOptions | undefined, group: IEditorGroup): Promise { + private async openEditorWith(override: EditorOverride.PICK | string, editor: IEditorInput, options: IEditorOptions | undefined, group: IEditorGroup): Promise { + const editorOverride = await this.findEditorOverride(override, editor, options, group); + if (!editorOverride) { + return undefined; + } + + const [editorOverrideHandler, , targetOptions, targetGroup] = editorOverride; + + return editorOverrideHandler.open(editor, targetOptions ?? options, targetGroup ?? group, OpenEditorContext.NEW_EDITOR)?.override; + } + + private async findEditorOverride(override: EditorOverride.PICK | string, editor: IEditorInput, options: IEditorOptions | undefined, group: IEditorGroup): Promise<[IOpenEditorOverrideHandler, IOpenEditorOverrideEntry, IEditorOptions?, IEditorGroup?] | undefined> { + + // We need a resource at least const resource = editor.resource; if (!resource) { - return; + return undefined; } - + // Collect all overrides for resource const allEditorOverrides = this.getEditorOverrides(resource, undefined, undefined); if (!allEditorOverrides.length) { - return; + return undefined; } - // Function which handles the quirks of opening from a pciker such as keymods - const openSelectedEditor = (picked: PickedResult) => { - let targetGroup = group; - if (picked.keyMods?.alt || picked.keyMods?.ctrlCmd) { - const direction = preferredSideBySideGroupDirection(this.configurationService); - targetGroup = this.editorGroupService.findGroup({ direction }, group.id); - targetGroup = targetGroup ?? this.editorGroupService.addGroup(group, direction); + // Return early for a specific override or we have just 1 in total + if (typeof override === 'string') { + const overrideToUse = allEditorOverrides.find(([, entry]) => entry.id === override); + if (overrideToUse) { + return overrideToUse; } - const openOptions: IEditorOptions = { - ...editorOptions, - override: picked.item.id, - preserveFocus: picked.openInBackground || editorOptions?.preserveFocus, - }; - return picked.item.handler.open(editor, openOptions, targetGroup, OpenEditorContext.NEW_EDITOR)?.override; - }; - - let overrideToUse: [IOpenEditorOverrideHandler, IOpenEditorOverrideEntry] | undefined; - if (typeof editorID === 'string') { - overrideToUse = allEditorOverrides.find(([_, entry]) => entry.id === editorID); } else if (allEditorOverrides.length === 1) { - overrideToUse = allEditorOverrides[0]; - } - if (overrideToUse) { - return openSelectedEditor({ - item: { handler: overrideToUse[0], ...overrideToUse[1] }, - openInBackground: false - }); + return allEditorOverrides[0]; } - type QuickPickItem = IQuickPickItem & { - readonly handler: IOpenEditorOverrideHandler; + // Otherwise find via picker + return this.doPickEditorOverride(allEditorOverrides, editor, options, group); + } + + private async doPickEditorOverride(allEditorOverrides: [IOpenEditorOverrideHandler, IOpenEditorOverrideEntry][], editor: IEditorInput, options: IEditorOptions | undefined, group: IEditorGroup): Promise<[IOpenEditorOverrideHandler, IOpenEditorOverrideEntry, IEditorOptions?, IEditorGroup?] | undefined> { + + type EditorOverrideQuickPickItem = IQuickPickItem & { + readonly overrideHandler: IOpenEditorOverrideHandler; + readonly overrideEntry: IOpenEditorOverrideEntry; }; - type PickedResult = { - readonly item: QuickPickItem; + type EditorOverridePick = { + readonly item: EditorOverrideQuickPickItem; readonly keyMods?: IKeyMods; readonly openInBackground: boolean; }; - // Prompt the user to select an override - const originalResource = EditorResourceAccessor.getOriginalUri(editor) || resource; - const resourceExt = extname(originalResource); + const resource = EditorResourceAccessor.getOriginalUri(editor); - const items: (IQuickPickItem & { handler: IOpenEditorOverrideHandler })[] = allEditorOverrides.map(([handler, entry]) => { + const editorOverridePicks = allEditorOverrides.map(([overrideHandler, overrideEntry]) => { return { - handler: handler, - id: entry.id, - label: entry.label, - description: entry.active ? localize('promptOpenWith.currentlyActive', 'Currently Active') : undefined, - detail: entry.detail, - buttons: resourceExt ? [{ + id: overrideEntry.id, + label: overrideEntry.label, + description: overrideEntry.active ? localize('promptOpenWith.currentlyActive', "Currently Active") : undefined, + detail: overrideEntry.detail, + buttons: resource && extname(resource) ? [{ iconClass: Codicon.gear.classNames, - tooltip: localize('promptOpenWith.setDefaultTooltip', "Set as default editor for '{0}' files", resourceExt) - }] : undefined + tooltip: localize('promptOpenWith.setDefaultTooltip', "Set as default editor for '{0}' files", extname(resource)) + }] : undefined, + overrideHandler, + overrideEntry }; }); - const picker = this.quickInputService.createQuickPick(); - picker.items = items; - if (items.length) { - picker.selectedItems = [items[0]]; + // Create editor override picker + const editorOverridePicker = this.quickInputService.createQuickPick(); + editorOverridePicker.placeholder = resource ? localize('promptOpenWith.placeHolder', "Select editor for '{0}'", basename(resource)) : localize('promptOpenWith.placeHolderGeneric', "Select editor"); + editorOverridePicker.canAcceptInBackground = true; + editorOverridePicker.items = editorOverridePicks; + if (editorOverridePicks.length) { + editorOverridePicker.selectedItems = [editorOverridePicks[0]]; } - picker.placeholder = localize('promptOpenWith.placeHolder', "Select editor for '{0}'", basename(originalResource)); - picker.canAcceptInBackground = true; - - let picked: PickedResult | undefined; - try { - picked = await new Promise(resolve => { - picker.onDidAccept(e => { - if (picker.selectedItems.length === 1) { - const result: PickedResult = { - item: picker.selectedItems[0], - keyMods: picker.keyMods, - openInBackground: e.inBackground - }; - resolve(result); - } else { - resolve(undefined); - } - }); - picker.onDidTriggerItemButton(e => { - const pick = e.item; - const id = pick.id; - resolve({ item: pick, openInBackground: false }); // open the view - picker.dispose(); - - // And persist the setting - if (pick && id) { - const newAssociation: EditorAssociation = { editorType: id, filenamePattern: '*' + resourceExt }; - const currentAssociations = [...this.configurationService.getValue(editorsAssociationsSettingId)]; - - // First try updating existing association - for (let i = 0; i < currentAssociations.length; ++i) { - const existing = currentAssociations[i]; - if (existing.filenamePattern === newAssociation.filenamePattern) { - currentAssociations.splice(i, 1, newAssociation); - this.configurationService.updateValue(editorsAssociationsSettingId, currentAssociations); - return; - } - } + // Prompt the user to select an override + const picked: EditorOverridePick | undefined = await new Promise(resolve => { + editorOverridePicker.onDidAccept(e => { + let result: EditorOverridePick | undefined = undefined; + + if (editorOverridePicker.selectedItems.length === 1) { + result = { + item: editorOverridePicker.selectedItems[0], + keyMods: editorOverridePicker.keyMods, + openInBackground: e.inBackground + }; + } + + resolve(result); + }); + + editorOverridePicker.onDidTriggerItemButton(e => { + + // Trigger opening and close picker + resolve({ item: e.item, openInBackground: false }); + + // Persist setting + if (resource && e.item && e.item.id) { + const newAssociation: EditorAssociation = { editorType: e.item.id, filenamePattern: `*${extname(resource)}` }; + const currentAssociations = [...this.configurationService.getValue(editorsAssociationsSettingId)]; - // Otherwise, create a new one - currentAssociations.unshift(newAssociation); - this.configurationService.updateValue(editorsAssociationsSettingId, currentAssociations); + // First try updating existing association + for (let i = 0; i < currentAssociations.length; ++i) { + const existing = currentAssociations[i]; + if (existing.filenamePattern === newAssociation.filenamePattern) { + currentAssociations.splice(i, 1, newAssociation); + this.configurationService.updateValue(editorsAssociationsSettingId, currentAssociations); + return; + } } - }); - picker.show(); + // Otherwise, create a new one + currentAssociations.unshift(newAssociation); + this.configurationService.updateValue(editorsAssociationsSettingId, currentAssociations); + } }); - } finally { - picker.dispose(); - } - if (!picked) { - return undefined; + editorOverridePicker.show(); + }); + + // Close picker + editorOverridePicker.dispose(); + + // If the user picked an override, look at how the picker was + // used (e.g. modifier keys, open in background) and create the + // options and group to use accordingly + if (picked) { + + // Figure out target group + let targetGroup: IEditorGroup | undefined; + if (picked.keyMods?.alt || picked.keyMods?.ctrlCmd) { + const direction = preferredSideBySideGroupDirection(this.configurationService); + targetGroup = this.editorGroupService.findGroup({ direction }, group.id); + targetGroup = targetGroup ?? this.editorGroupService.addGroup(group, direction); + } + + // Figure out options + const targetOptions: IEditorOptions = { + ...options, + override: picked.item.overrideEntry.id, + preserveFocus: picked.openInBackground || options?.preserveFocus, + }; + + return [picked.item.overrideHandler, picked.item.overrideEntry, targetOptions, targetGroup]; } - return openSelectedEditor(picked); + return undefined; } - private findTargetGroup(input: IEditorInput, options?: IEditorOptions, group?: OpenInEditorGroup): IEditorGroup { + private findTargetGroup(editor: IEditorInput, options?: IEditorOptions, group?: OpenInEditorGroup): IEditorGroup { let targetGroup: IEditorGroup | undefined; // Group: Instance of Group @@ -803,7 +824,7 @@ export class EditorService extends Disposable implements EditorServiceImpl { // Respect option to reveal an editor if it is already visible in any group if (options?.revealIfVisible) { for (const group of groupsByLastActive) { - if (group.isActive(input)) { + if (group.isActive(editor)) { targetGroup = group; break; } @@ -818,12 +839,12 @@ export class EditorService extends Disposable implements EditorServiceImpl { let groupWithInputOpened: IEditorGroup | undefined = undefined; for (const group of groupsByLastActive) { - if (group.isOpened(input)) { + if (group.isOpened(editor)) { if (!groupWithInputOpened) { groupWithInputOpened = group; } - if (!groupWithInputActive && group.isActive(input)) { + if (!groupWithInputActive && group.isActive(editor)) { groupWithInputActive = group; } } From 8da2cec66bc29261ebf604642b9b429e676473f4 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Sun, 21 Feb 2021 11:28:34 +0100 Subject: [PATCH 17/18] cleanup editor delegate --- .../contrib/files/browser/explorerViewlet.ts | 6 ++--- .../services/editor/browser/editorService.ts | 24 +++---------------- .../editor/test/browser/editorService.test.ts | 4 ++-- 3 files changed, 8 insertions(+), 26 deletions(-) diff --git a/src/vs/workbench/contrib/files/browser/explorerViewlet.ts b/src/vs/workbench/contrib/files/browser/explorerViewlet.ts index 6a164f9abf2ef..dbe99c77bf36b 100644 --- a/src/vs/workbench/contrib/files/browser/explorerViewlet.ts +++ b/src/vs/workbench/contrib/files/browser/explorerViewlet.ts @@ -204,7 +204,7 @@ export class ExplorerViewPaneContainer extends ViewPaneContainer { // without causing the animation in the opened editors view to kick in and change scroll position. // We try to be smart and only use the delay if we recognize that the user action is likely to cause // a new entry in the opened editors view. - const delegatingEditorService = this.instantiationService.createInstance(DelegatingEditorService, async (delegate, group, editor, options): Promise => { + const delegatingEditorService = this.instantiationService.createInstance(DelegatingEditorService, async (group, delegate): Promise => { let openEditorsView = this.getOpenEditorsView(); if (openEditorsView) { let delay = 0; @@ -221,9 +221,9 @@ export class ExplorerViewPaneContainer extends ViewPaneContainer { } try { - return await delegate(group, editor, options); + return await delegate(); } catch (error) { - return null; // ignore + return undefined; // ignore } finally { if (openEditorsView) { openEditorsView.setStructuralRefreshDelay(0); diff --git a/src/vs/workbench/services/editor/browser/editorService.ts b/src/vs/workbench/services/editor/browser/editorService.ts index 648d83b5da202..c014931e38fd4 100644 --- a/src/vs/workbench/services/editor/browser/editorService.ts +++ b/src/vs/workbench/services/editor/browser/editorService.ts @@ -1418,11 +1418,9 @@ export class EditorService extends Disposable implements EditorServiceImpl { export interface IEditorOpenHandler { ( - delegate: (group: IEditorGroup, editor: IEditorInput, options?: IEditorOptions) => Promise, group: IEditorGroup, - editor: IEditorInput, - options?: IEditorOptions | ITextEditorOptions - ): Promise; + delegate: () => Promise, + ): Promise; } /** @@ -1447,23 +1445,7 @@ export class DelegatingEditorService implements IEditorService { if (result) { const [resolvedGroup, resolvedEditor, resolvedOptions] = result; - // If the override option is provided we want to open that specific editor or show a picker - if (resolvedOptions && (resolvedOptions.override === EditorOverride.PICK || typeof resolvedOptions.override === 'string')) { - return await this.editorService.openEditor(resolvedEditor, { ...resolvedOptions, override: withNullAsUndefined(resolvedOptions.override) }, resolvedGroup); - } - // Pass on to editor open handler - const editorPane = await this.editorOpenHandler( - (group: IEditorGroup, editor: IEditorInput, options?: IEditorOptions) => group.openEditor(editor, options), - resolvedGroup, - resolvedEditor, - resolvedOptions - ); - - if (editorPane) { - return editorPane; // the opening was handled, so return early - } - - return withNullAsUndefined(await resolvedGroup.openEditor(resolvedEditor, resolvedOptions)); + return this.editorOpenHandler(resolvedGroup, () => this.editorService.openEditor(resolvedEditor, resolvedOptions, resolvedGroup)); } return undefined; diff --git a/src/vs/workbench/services/editor/test/browser/editorService.test.ts b/src/vs/workbench/services/editor/test/browser/editorService.test.ts index f9cd0be30c812..2215f11e1f91d 100644 --- a/src/vs/workbench/services/editor/test/browser/editorService.test.ts +++ b/src/vs/workbench/services/editor/test/browser/editorService.test.ts @@ -403,8 +403,8 @@ suite('EditorService', () => { const ed = instantiationService.createInstance(MyEditor, 'my.editor'); const inp = instantiationService.createInstance(ResourceEditorInput, URI.parse('my://resource-delegate'), 'name', 'description', undefined); - const delegate = instantiationService.createInstance(DelegatingEditorService, async (delegate, group, input) => { - assert.strictEqual(input, inp); + const delegate = instantiationService.createInstance(DelegatingEditorService, async (group, delegate) => { + assert.ok(group); done(); From 8694848086b614db35fe2be235b4c2711e353e39 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Sun, 21 Feb 2021 11:37:34 +0100 Subject: [PATCH 18/18] final :lipstick: --- src/vs/workbench/browser/editor.ts | 32 ++++---- .../services/editor/browser/editorService.ts | 74 ++++++++++--------- 2 files changed, 55 insertions(+), 51 deletions(-) diff --git a/src/vs/workbench/browser/editor.ts b/src/vs/workbench/browser/editor.ts index f789de7045898..3a5607bcc714f 100644 --- a/src/vs/workbench/browser/editor.ts +++ b/src/vs/workbench/browser/editor.ts @@ -274,48 +274,48 @@ export interface IEditorAssociationsRegistry { /** * Register handlers for editor types */ - registerEditorTypesHandler(source: string, handler: IEditorTypesHandler): IDisposable; + registerEditorTypesHandler(id: string, handler: IEditorTypesHandler): IDisposable; } class EditorAssociationsRegistry implements IEditorAssociationsRegistry { private readonly editorTypesHandlers = new Map(); - registerEditorTypesHandler(source: string, handler: IEditorTypesHandler): IDisposable { - if (this.editorTypesHandlers.has(source)) { - throw new Error(`Use a different name for the editor component, ${source} is already occupied.`); + registerEditorTypesHandler(id: string, handler: IEditorTypesHandler): IDisposable { + if (this.editorTypesHandlers.has(id)) { + throw new Error(`An editor type handler with ${id} was already registered.`); } - this.editorTypesHandlers.set(source, handler); - this.updateSchema(); + this.editorTypesHandlers.set(id, handler); + this.updateEditorAssociationsSchema(); const editorTypeChangeEvent = handler.onDidChangeEditorTypes(() => { - this.updateSchema(); + this.updateEditorAssociationsSchema(); }); return { dispose: () => { editorTypeChangeEvent.dispose(); - this.editorTypesHandlers.delete(source); - this.updateSchema(); + this.editorTypesHandlers.delete(id); + this.updateEditorAssociationsSchema(); } }; } - private updateSchema() { + private updateEditorAssociationsSchema() { const enumValues: string[] = []; const enumDescriptions: string[] = []; - const infos: IEditorType[] = [DEFAULT_EDITOR_ASSOCIATION]; + const editorTypes: IEditorType[] = [DEFAULT_EDITOR_ASSOCIATION]; for (const [, handler] of this.editorTypesHandlers) { - infos.push(...handler.getEditorTypes()); + editorTypes.push(...handler.getEditorTypes()); } - infos.forEach(info => { - enumValues.push(info.id); - enumDescriptions.push(localize('editorAssociations.editorType.sourceDescription', "Source: {0}", info.providerDisplayName)); - }); + for (const { id, providerDisplayName } of editorTypes) { + enumValues.push(id); + enumDescriptions.push(localize('editorAssociations.editorType.sourceDescription', "Source: {0}", providerDisplayName)); + } editorTypeSchemaAddition.enum = enumValues; editorTypeSchemaAddition.enumDescriptions = enumDescriptions; diff --git a/src/vs/workbench/services/editor/browser/editorService.ts b/src/vs/workbench/services/editor/browser/editorService.ts index c014931e38fd4..981a5404437ab 100644 --- a/src/vs/workbench/services/editor/browser/editorService.ts +++ b/src/vs/workbench/services/editor/browser/editorService.ts @@ -514,46 +514,50 @@ export class EditorService extends Disposable implements EditorServiceImpl { // Ensure the default one is always present if (!overrides.some(([, entry]) => entry.id === DEFAULT_EDITOR_ASSOCIATION.id)) { - overrides.unshift([ - { - open: (editor: IEditorInput, options: IEditorOptions | ITextEditorOptions | undefined, group: IEditorGroup) => { - const resource = EditorResourceAccessor.getOriginalUri(editor); - if (!resource) { - return; - } - - const fileEditorInput = this.createEditorInput({ resource, forceFile: true }); - const textOptions: IEditorOptions | ITextEditorOptions = { ...options, override: EditorOverride.DISABLED }; - return { - override: (async () => { - - // Try to replace existing editors for resource - const existingEditor = firstOrDefault(this.findEditors(resource, group)); - if (existingEditor && !fileEditorInput.matches(existingEditor)) { - await this.replaceEditors([{ - editor: existingEditor, - replacement: fileEditorInput, - options: options ? EditorOptions.create(options) : undefined, - }], group); - } - - return this.openEditor(fileEditorInput, textOptions, group); - })() - }; - } - }, - { - id: DEFAULT_EDITOR_ASSOCIATION.id, - label: DEFAULT_EDITOR_ASSOCIATION.displayName, - detail: DEFAULT_EDITOR_ASSOCIATION.providerDisplayName, - active: this.fileEditorInputFactory.isFileEditorInput(this.activeEditor) && isEqual(this.activeEditor.resource, resource), - } - ]); + overrides.unshift(this.getDefaultEditorOverride(resource)); } return overrides; } + private getDefaultEditorOverride(resource: URI): [IOpenEditorOverrideHandler, IOpenEditorOverrideEntry] { + return [ + { + open: (editor: IEditorInput, options: IEditorOptions | ITextEditorOptions | undefined, group: IEditorGroup) => { + const resource = EditorResourceAccessor.getOriginalUri(editor); + if (!resource) { + return; + } + + const fileEditorInput = this.createEditorInput({ resource, forceFile: true }); + const textOptions: IEditorOptions | ITextEditorOptions = { ...options, override: EditorOverride.DISABLED }; + return { + override: (async () => { + + // Try to replace existing editors for resource + const existingEditor = firstOrDefault(this.findEditors(resource, group)); + if (existingEditor && !fileEditorInput.matches(existingEditor)) { + await this.replaceEditors([{ + editor: existingEditor, + replacement: fileEditorInput, + options: options ? EditorOptions.create(options) : undefined, + }], group); + } + + return this.openEditor(fileEditorInput, textOptions, group); + })() + }; + } + }, + { + id: DEFAULT_EDITOR_ASSOCIATION.id, + label: DEFAULT_EDITOR_ASSOCIATION.displayName, + detail: DEFAULT_EDITOR_ASSOCIATION.providerDisplayName, + active: this.fileEditorInputFactory.isFileEditorInput(this.activeEditor) && isEqual(this.activeEditor.resource, resource), + } + ]; + } + private onGroupWillOpenEditor(group: IEditorGroup, event: IEditorOpeningEvent): void { if (event.options?.override === EditorOverride.DISABLED) { return; // return early when overrides are explicitly disabled