From 56375704d9012a0c65f295ef26fc3152b9e0edbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Mangeonjean?= Date: Fri, 1 Sep 2023 17:18:53 +0200 Subject: [PATCH] feat: simulate an editor group for each standalone editor --- src/missing-services.ts | 76 +++-- src/service-override/editor.ts | 106 ++---- src/service-override/tools/editor.ts | 466 ++++++++++++++++++++++++++- src/service-override/views.ts | 152 +++++---- 4 files changed, 624 insertions(+), 176 deletions(-) diff --git a/src/missing-services.ts b/src/missing-services.ts index 5d4faeba..324feaec 100644 --- a/src/missing-services.ts +++ b/src/missing-services.ts @@ -165,6 +165,7 @@ import { ITestResultService } from 'vs/workbench/contrib/testing/common/testResu import { IDiagnosticsService, NullDiagnosticsService } from 'vs/platform/diagnostics/common/diagnostics' import { INotebookSearchService } from 'vs/workbench/contrib/search/browser/notebookSearch' import { ResourceSet } from 'vs/base/common/map' +import { IEditorGroupView } from 'vs/workbench/browser/parts/editor/editor' import { unsupported } from './tools' class NullLoggerService extends AbstractLoggerService { @@ -268,7 +269,32 @@ registerSingleton(IFileService, class FileService implements IFileService { } }, InstantiationType.Eager) -class EmptyEditorGroup implements IEditorGroup { +class EmptyEditorGroup implements IEditorGroup, IEditorGroupView { + onDidFocus = Event.None + onDidOpenEditorFail = Event.None + whenRestored = Promise.resolve() + get titleHeight () { + return unsupported() + } + + disposed = false + setActive = unsupported + notifyIndexChanged = unsupported + relayout = unsupported + dispose = unsupported + toJSON = unsupported + preferredWidth?: number | undefined + preferredHeight?: number | undefined + get element () { + return unsupported() + } + + minimumWidth = 0 + maximumWidth = Number.POSITIVE_INFINITY + minimumHeight = 0 + maximumHeight = Number.POSITIVE_INFINITY + onDidChange = Event.None + layout = unsupported onDidModelChange = Event.None onWillDispose = Event.None onDidActiveEditorChange = Event.None @@ -288,17 +314,17 @@ class EmptyEditorGroup implements IEditorGroup { isLocked = false stickyCount = 0 editors = [] - get scopedContextKeyService () { return StandaloneServices.get(IContextKeyService) } - getEditors = unsupported - findEditors = unsupported - getEditorByIndex = unsupported + get scopedContextKeyService (): IContextKeyService { return StandaloneServices.get(IContextKeyService) } + getEditors = () => [] + findEditors = () => [] + getEditorByIndex = () => undefined getIndexOfEditor = unsupported openEditor = unsupported openEditors = unsupported - isPinned = unsupported - isSticky = unsupported - isActive = unsupported - contains = unsupported + isPinned = () => false + isSticky = () => false + isActive = () => false + contains = () => false moveEditor = unsupported moveEditors = unsupported copyEditor = unsupported @@ -315,12 +341,12 @@ class EmptyEditorGroup implements IEditorGroup { // ignore } - isFirst = () => true - isLast = () => true + isFirst = unsupported + isLast = unsupported } const fakeActiveGroup = new EmptyEditorGroup() -registerSingleton(IEditorGroupsService, class EditorGroupsService implements IEditorGroupsService { +export class EmptyEditorGroupsService implements IEditorGroupsService { readonly _serviceBrand = undefined getLayout = unsupported onDidChangeActiveGroup = Event.None @@ -332,9 +358,9 @@ registerSingleton(IEditorGroupsService, class EditorGroupsService implements IEd onDidScroll = Event.None onDidChangeGroupIndex = Event.None onDidChangeGroupLocked = Event.None - get contentDimension () { return unsupported() } + get contentDimension (): never { return unsupported() } activeGroup = fakeActiveGroup - get sideGroup () { return unsupported() } + get sideGroup (): never { return unsupported() } groups = [fakeActiveGroup] count = 0 orientation = GroupOrientation.HORIZONTAL @@ -342,27 +368,29 @@ registerSingleton(IEditorGroupsService, class EditorGroupsService implements IEd whenReady = Promise.resolve() whenRestored = Promise.resolve() hasRestorableState = false - getGroups = () => [] - getGroup = () => undefined + getGroups = (): never[] => [] + getGroup = (): undefined => undefined activateGroup = unsupported getSize = unsupported setSize = unsupported arrangeGroups = unsupported applyLayout = unsupported centerLayout = unsupported - isLayoutCentered = () => false + isLayoutCentered = (): boolean => false setGroupOrientation = unsupported - findGroup = () => undefined + findGroup = (): undefined => undefined addGroup = unsupported removeGroup = unsupported moveGroup = unsupported mergeGroup = unsupported mergeAllGroups = unsupported copyGroup = unsupported - get partOptions () { return unsupported() } + partOptions = {} onDidChangeEditorPartOptions = Event.None enforcePartOptions = unsupported -}, InstantiationType.Eager) +} + +registerSingleton(IEditorGroupsService, EmptyEditorGroupsService, InstantiationType.Eager) registerSingleton(IWorkingCopyFileService, WorkingCopyFileService, InstantiationType.Eager) registerSingleton(IPathService, BrowserPathService, InstantiationType.Delayed) @@ -1137,8 +1165,12 @@ registerSingleton(IUpdateService, class UpdateService implements IUpdateService registerSingleton(IStatusbarService, class StatusbarService implements IStatusbarService { _serviceBrand: undefined onDidChangeEntryVisibility = Event.None - addEntry = unsupported - isEntryVisible = unsupported + addEntry = () => ({ + dispose: () => {}, + update: () => {} + }) + + isEntryVisible = () => false updateEntryVisibility = unsupported focus = unsupported focusNextEntry = unsupported diff --git a/src/service-override/editor.ts b/src/service-override/editor.ts index 6a86134b..d823dfc5 100644 --- a/src/service-override/editor.ts +++ b/src/service-override/editor.ts @@ -1,96 +1,29 @@ -import '../missing-services' -import { IEditorOverrideServices, StandaloneServices } from 'vs/editor/standalone/browser/standaloneServices' -import { IResolvedTextEditorModel, ITextModelService } from 'vs/editor/common/services/resolverService' +import { IEditorOverrideServices } from 'vs/editor/standalone/browser/standaloneServices' +import { IResolvedTextEditorModel } from 'vs/editor/common/services/resolverService' import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService' import { CodeEditorService } from 'vs/workbench/services/editor/browser/codeEditorService' -import { IEditorService, IEditorsChangeEvent, IOpenEditorsOptions, IRevertAllEditorsOptions, ISaveAllEditorsOptions, ISaveEditorsOptions, ISaveEditorsResult, IUntypedEditorReplacement, PreferredGroup } from 'vs/workbench/services/editor/common/editorService' -import { EditorExtensions, EditorInputWithOptions, EditorsOrder, GroupIdentifier, IEditorCloseEvent, IEditorIdentifier, IFindEditorOptions, IRevertOptions, IVisibleEditorPane } from 'vs/workbench/common/editor' -import type { IEditorFactoryRegistry, IEditorPane, IFileEditorInput, IResourceDiffEditorInput, ITextDiffEditorPane, IUntitledTextResourceEditorInput, IUntypedEditorInput } from 'vs/workbench/common/editor' -import { Emitter, Event } from 'vs/base/common/event' -import { EditorInput } from 'vs/workbench/common/editor/editorInput' -import { IEditorOptions, IResourceEditorInput, IResourceEditorInputIdentifier, ITextResourceEditorInput } from 'vs/platform/editor/common/editor' -import { IEditor } from 'vs/editor/common/editorCommon' +import { IEditorService } from 'vs/workbench/services/editor/common/editorService' +import { EditorExtensions, IEditorFactoryRegistry, IFileEditorInput } from 'vs/workbench/common/editor' +import { IEditorOptions } from 'vs/platform/editor/common/editor' import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors' -import { Disposable, IReference } from 'vs/base/common/lifecycle' -import { ICodeEditor, IDiffEditor } from 'vs/editor/browser/editorBrowser' +import { IReference } from 'vs/base/common/lifecycle' import { ITextEditorService, TextEditorService } from 'vs/workbench/services/textfile/common/textEditorService' -import { ICloseEditorOptions, IEditorGroup, IEditorReplacement } from 'vs/workbench/services/editor/common/editorGroupsService' -import { URI } from 'vs/base/common/uri' import { Registry } from 'vs/platform/registry/common/platform' import { FILE_EDITOR_INPUT_ID } from 'vs/workbench/contrib/files/common/files' -import { OpenEditor, wrapOpenEditor } from './tools/editor' +import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService' +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation' +import { MonacoDelegateEditorGroupsService, MonacoEditorService, OpenEditor } from './tools/editor' import { unsupported } from '../tools' +import { EmptyEditorGroupsService } from '../missing-services' import 'vs/workbench/browser/parts/editor/editor.contribution' -class SimpleEditorService extends Disposable implements IEditorService { - public activeTextEditorControl: IEditor | undefined - private _onDidActiveEditorChange = this._register(new Emitter()) - - constructor ( - _openEditor: OpenEditor, - @ITextModelService textModelService: ITextModelService - ) { - super() - - setTimeout(() => { - const codeEditorService = StandaloneServices.get(ICodeEditorService) - this.activeTextEditorControl = codeEditorService.getFocusedCodeEditor() ?? undefined - const handleCodeEditor = (editor: ICodeEditor) => { - const onEditorFocused = () => { - const newFocusedEditor = codeEditorService.getFocusedCodeEditor() - if (newFocusedEditor !== this.activeTextEditorControl) { - this.activeTextEditorControl = newFocusedEditor ?? undefined - this._onDidActiveEditorChange.fire() - } - } - editor.onDidFocusEditorText(onEditorFocused) - editor.onDidFocusEditorWidget(onEditorFocused) - } - this._register(codeEditorService.onCodeEditorAdd(handleCodeEditor)) - codeEditorService.listCodeEditors().forEach(handleCodeEditor) - }) - - this.openEditor = wrapOpenEditor(textModelService, this.openEditor.bind(this), _openEditor) +class MonacoEditorGroupsService extends MonacoDelegateEditorGroupsService { + constructor (@IInstantiationService instantiationService: IInstantiationService) { + super( + instantiationService.createInstance(EmptyEditorGroupsService), + instantiationService + ) } - - readonly _serviceBrand: undefined - onDidActiveEditorChange: Event = this._onDidActiveEditorChange.event - onDidVisibleEditorsChange: Event = Event.None - onDidEditorsChange: Event = Event.None - onDidCloseEditor: Event = Event.None - activeEditorPane: IVisibleEditorPane | undefined - activeEditor: EditorInput | undefined - activeTextEditorLanguageId: string | undefined - visibleEditorPanes: IVisibleEditorPane[] = [] - visibleEditors: EditorInput[] = [] - visibleTextEditorControls: Array = [] - editors: EditorInput[] = [] - count: number = 0 - getEditors: (order: EditorsOrder, options?: { excludeSticky?: boolean }) => readonly IEditorIdentifier[] = () => [] - - openEditor(editor: EditorInput, options?: IEditorOptions, group?: PreferredGroup): Promise - openEditor(editor: IUntypedEditorInput, group?: PreferredGroup): Promise - openEditor(editor: IResourceEditorInput, group?: PreferredGroup): Promise - openEditor(editor: ITextResourceEditorInput | IUntitledTextResourceEditorInput, group?: PreferredGroup): Promise - openEditor(editor: IResourceDiffEditorInput, group?: PreferredGroup): Promise - openEditor(editor: EditorInput | IUntypedEditorInput, optionsOrPreferredGroup?: IEditorOptions | PreferredGroup, preferredGroup?: PreferredGroup): Promise - async openEditor (_editor: EditorInput | IUntypedEditorInput, _optionsOrPreferredGroup?: IEditorOptions | PreferredGroup, _preferredGroup?: PreferredGroup): Promise { - return undefined - } - - openEditors: (editors: Array, preferredGroup?: PreferredGroup, options?: IOpenEditorsOptions) => Promise = unsupported - replaceEditors: (replacements: Array, group: IEditorGroup | GroupIdentifier) => Promise = unsupported - isOpened: (editor: IResourceEditorInputIdentifier) => boolean = () => false - isVisible: (editor: EditorInput) => boolean = () => false - findEditors(resource: URI, options?: IFindEditorOptions): readonly IEditorIdentifier[] - findEditors(editor: IResourceEditorInputIdentifier, options?: IFindEditorOptions): readonly IEditorIdentifier[] - findEditors (): readonly IEditorIdentifier[] { return [] } - save: (editors: IEditorIdentifier | IEditorIdentifier[], options?: ISaveEditorsOptions) => Promise = async () => ({ success: true, editors: [] }) - saveAll: (options?: ISaveAllEditorsOptions) => Promise = async () => ({ success: true, editors: [] }) - revert: (editors: IEditorIdentifier | IEditorIdentifier[], options?: IRevertOptions) => Promise = unsupported - revertAll: (options?: IRevertAllEditorsOptions) => Promise = unsupported - closeEditor: (editor: IEditorIdentifier, options?: ICloseEditorOptions) => Promise = unsupported - closeEditors: (editors: readonly IEditorIdentifier[], options?: ICloseEditorOptions) => Promise = unsupported } /** @@ -106,8 +39,9 @@ Registry.as(EditorExtensions.EditorFactory).registerFile export default function getServiceOverride (openEditor: OpenEditor): IEditorOverrideServices { return { [ICodeEditorService.toString()]: new SyncDescriptor(CodeEditorService, undefined, true), - [IEditorService.toString()]: new SyncDescriptor(SimpleEditorService, [openEditor], true), - [ITextEditorService.toString()]: new SyncDescriptor(TextEditorService, [], false) + [IEditorService.toString()]: new SyncDescriptor(MonacoEditorService, [openEditor, () => false], true), + [ITextEditorService.toString()]: new SyncDescriptor(TextEditorService, [], false), + [IEditorGroupsService.toString()]: new SyncDescriptor(MonacoEditorGroupsService) } } @@ -116,5 +50,5 @@ export { IEditorOptions, IResolvedTextEditorModel, IReference, - SimpleEditorService + MonacoEditorService } diff --git a/src/service-override/tools/editor.ts b/src/service-override/tools/editor.ts index 5e6fd504..17b9d27a 100644 --- a/src/service-override/tools/editor.ts +++ b/src/service-override/tools/editor.ts @@ -1,17 +1,33 @@ import { StandaloneServices } from 'vs/editor/standalone/browser/standaloneServices' import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService' import { IEditorService, isPreferredGroup, PreferredGroup, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService' -import { IEditorControl, IEditorPane, IResourceDiffEditorInput, isEditorInput, isResourceEditorInput, ITextDiffEditorPane, IUntitledTextResourceEditorInput, IUntypedEditorInput } from 'vs/workbench/common/editor' +import { EditorCloseContext, GroupModelChangeKind, IActiveEditorChangeEvent, IEditorCloseEvent, IEditorControl, IEditorPane, IEditorWillOpenEvent, IResourceDiffEditorInput, isEditorInput, isResourceEditorInput, ITextDiffEditorPane, IUntitledTextResourceEditorInput, IUntypedEditorInput, IVisibleEditorPane } from 'vs/workbench/common/editor' import { EditorInput } from 'vs/workbench/common/editor/editorInput' import { IEditorOptions, IResourceEditorInput, ITextResourceEditorInput } from 'vs/platform/editor/common/editor' import { applyTextEditorOptions } from 'vs/workbench/common/editor/editorOptions' import { ScrollType } from 'vs/editor/common/editorCommon' -import { ICodeEditor } from 'vs/editor/browser/editorBrowser' -import { Event } from 'vs/base/common/event' -import { DEFAULT_EDITOR_MAX_DIMENSIONS, DEFAULT_EDITOR_MIN_DIMENSIONS } from 'vs/workbench/browser/parts/editor/editor' +import { ICodeEditor, IDiffEditor } from 'vs/editor/browser/editorBrowser' +import { IEditorGroupView, DEFAULT_EDITOR_MAX_DIMENSIONS, DEFAULT_EDITOR_MIN_DIMENSIONS } from 'vs/workbench/browser/parts/editor/editor' import { IResolvedTextEditorModel, ITextModelService } from 'vs/editor/common/services/resolverService' -import { StandaloneEditor } from 'vs/editor/standalone/browser/standaloneCodeEditor' -import { IReference } from 'vs/base/common/lifecycle' +import { IStandaloneCodeEditor, StandaloneCodeEditor, StandaloneEditor } from 'vs/editor/standalone/browser/standaloneCodeEditor' +import { Disposable, IReference } from 'vs/base/common/lifecycle' +import { EditorService } from 'vs/workbench/services/editor/browser/editorService' +import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService' +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation' +import { IConfigurationService } from 'vs/platform/configuration/common/configuration' +import { IWorkspaceTrustRequestService } from 'vs/platform/workspace/common/workspaceTrust' +import { IEditorResolverService } from 'vs/workbench/services/editor/common/editorResolverService' +import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity' +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace' +import { IFileService } from 'vs/platform/files/common/files' +import { ITextEditorService } from 'vs/workbench/services/textfile/common/textEditorService' +import { IHostService } from 'vs/workbench/services/host/browser/host' +import { Emitter, Event } from 'vs/base/common/event' +import { TextResourceEditorInput } from 'vs/workbench/common/editor/textResourceEditorInput' +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey' +import { URI } from 'vs/base/common/uri' +import { IGroupModelChangeEvent } from 'vs/workbench/common/editor/editorGroupModel' +import { EditorLayoutInfo } from 'vs/editor/common/config/editorOptions' import { unsupported } from '../../tools' export type OpenEditor = (modelRef: IReference, options: IEditorOptions | undefined, sideBySide?: boolean) => Promise @@ -104,3 +120,441 @@ export function wrapOpenEditor (textModelService: ITextModelService, defaultBeha return openEditor } + +export class MonacoEditorService extends EditorService { + constructor ( + _openEditorFallback: OpenEditor | undefined, + private _isEditorPartVisible: () => boolean, + @IEditorGroupsService _editorGroupService: IEditorGroupsService, + @IInstantiationService instantiationService: IInstantiationService, + @IFileService fileService: IFileService, + @IConfigurationService configurationService: IConfigurationService, + @IWorkspaceContextService contextService: IWorkspaceContextService, + @IUriIdentityService uriIdentityService: IUriIdentityService, + @IEditorResolverService editorResolverService: IEditorResolverService, + @IWorkspaceTrustRequestService workspaceTrustRequestService: IWorkspaceTrustRequestService, + @IHostService hostService: IHostService, + @ITextEditorService textEditorService: ITextEditorService, + @ITextModelService textModelService: ITextModelService + ) { + super( + _editorGroupService, + instantiationService, + fileService, + configurationService, + contextService, + uriIdentityService, + editorResolverService, + workspaceTrustRequestService, + hostService, + textEditorService + ) + + this.openEditor = wrapOpenEditor(textModelService, this.openEditor.bind(this), _openEditorFallback) + } + + override get activeTextEditorControl (): ICodeEditor | IDiffEditor | undefined { + // By default, only the editor inside the EditorPart can be "active" here, hack it so the active editor is now the focused editor if it exists + // It is required for the editor.addAction to be able to add an entry in the editor action menu + const focusedCodeEditor = StandaloneServices.get(ICodeEditorService).getFocusedCodeEditor() + if (focusedCodeEditor != null && focusedCodeEditor instanceof StandaloneCodeEditor) { + return focusedCodeEditor + } + + return super.activeTextEditorControl + } + + // Override openEditor to fallback on user function is the EditorPart is not visible + override openEditor(editor: EditorInput, options?: IEditorOptions, group?: PreferredGroup): Promise + override openEditor(editor: IUntypedEditorInput, group?: PreferredGroup): Promise + override openEditor(editor: IResourceEditorInput, group?: PreferredGroup): Promise + override openEditor(editor: ITextResourceEditorInput | IUntitledTextResourceEditorInput, group?: PreferredGroup): Promise + override openEditor(editor: IResourceDiffEditorInput, group?: PreferredGroup): Promise + override openEditor(editor: EditorInput | IUntypedEditorInput, optionsOrPreferredGroup?: IEditorOptions | PreferredGroup, preferredGroup?: PreferredGroup): Promise + override async openEditor (editor: EditorInput | IUntypedEditorInput, optionsOrPreferredGroup?: IEditorOptions | PreferredGroup, preferredGroup?: PreferredGroup): Promise { + // Do not try to open the file if the editor part is not displayed, let the fallback happen + if (!this._isEditorPartVisible()) { + return undefined + } + + return super.openEditor(editor, optionsOrPreferredGroup, preferredGroup) + } +} + +class StandaloneEditorPane implements IVisibleEditorPane { + constructor (private editor: IStandaloneCodeEditor, public input: TextResourceEditorInput, public group: IEditorGroup) { + } + + onDidChangeControl = Event.None + + options = undefined + minimumWidth = 0 + maximumWidth = Number.POSITIVE_INFINITY + minimumHeight = 0 + maximumHeight = Number.POSITIVE_INFINITY + onDidChangeSizeConstraints = Event.None + scopedContextKeyService = undefined + getControl (): IEditorControl | undefined { + return this.editor + } + + getViewState (): object | undefined { + return undefined + } + + isVisible (): boolean { + return true + } + + onDidFocus = this.editor.onDidFocusEditorWidget + onDidBlur = this.editor.onDidBlurEditorWidget + hasFocus (): boolean { + return this.editor.hasWidgetFocus() + } + + getId (): string { + return this.editor.getId() + } + + getTitle (): string | undefined { + return undefined + } + + focus (): void { + this.editor.focus() + } +} + +class StandaloneEditorGroup extends Disposable implements IEditorGroup, IEditorGroupView { + private static idCounter = 0 + + private pane: StandaloneEditorPane | undefined + public active: boolean = false + constructor ( + public editor: IStandaloneCodeEditor, + @IInstantiationService instantiationService: IInstantiationService, + @IContextKeyService public scopedContextKeyService: IContextKeyService + ) { + super() + const onNewModel = (uri: URI) => { + const editorInput = instantiationService.createInstance(TextResourceEditorInput, uri, undefined, undefined, undefined, undefined) + + this._onWillOpenEditor.fire({ + editor: editorInput, + groupId: this.id + }) + + this.pane = new StandaloneEditorPane(editor, editorInput, this) + + this._onDidModelChange.fire({ + kind: GroupModelChangeKind.EDITOR_OPEN, + editor: editorInput, + editorIndex: 0 + }) + + this._onDidActiveEditorChange.fire({ + editor: editorInput + }) + } + const onRemovedModel = (uri: URI) => { + if (this.pane != null && this.pane.input.resource.toString() === uri.toString()) { + const pane = this.pane + this.pane = undefined + this._onDidModelChange.fire({ + kind: GroupModelChangeKind.EDITOR_CLOSE, + editorIndex: 0 + }) + + this._onDidActiveEditorChange.fire({ + editor: undefined + }) + + this._onDidCloseEditor.fire({ + context: EditorCloseContext.UNKNOWN, + editor: pane.input, + groupId: this.id, + index: 0, + sticky: false + }) + } + } + + editor.onDidChangeModel((e) => { + if (e.oldModelUrl != null) { + onRemovedModel(e.oldModelUrl) + } + if (e.newModelUrl != null) { + onNewModel(e.newModelUrl) + } + }) + this._register({ + dispose: () => { + const model = editor.getModel() + if (model != null) { + onRemovedModel(model.uri) + } + } + }) + const currentModel = editor.getModel() + if (currentModel != null) { + const editorInput = instantiationService.createInstance(TextResourceEditorInput, currentModel.uri, undefined, undefined, undefined, undefined) + this.pane = new StandaloneEditorPane(editor, editorInput, this) + } + } + + onDidFocus = this.editor.onDidFocusEditorWidget + onDidOpenEditorFail = Event.None + whenRestored = Promise.resolve() + get titleHeight () { return unsupported() } + disposed = false + setActive (isActive: boolean) { + this.active = isActive + } + + notifyIndexChanged = unsupported + relayout = unsupported + toJSON = unsupported + get element () { return unsupported() } + minimumWidth = 0 + maximumWidth = Number.POSITIVE_INFINITY + minimumHeight = 0 + maximumHeight = Number.POSITIVE_INFINITY + onDidChange: Event = this.editor.onDidLayoutChange + layout = () => this.editor.layout() + + _onDidModelChange = new Emitter() + onDidModelChange = this._onDidModelChange.event + + onWillDispose = this.editor.onDidDispose + _onDidActiveEditorChange = new Emitter() + onDidActiveEditorChange = this._onDidActiveEditorChange.event + + onWillCloseEditor = Event.None + + _onDidCloseEditor = new Emitter() + onDidCloseEditor = this._onDidCloseEditor.event + + onWillMoveEditor = Event.None + + _onWillOpenEditor = new Emitter() + onWillOpenEditor = this._onWillOpenEditor.event + + readonly id = --StandaloneEditorGroup.idCounter + index = -1 + label = `standalone editor ${this.editor.getId()}` + ariaLabel = `standalone editor ${this.editor.getId()}` + get activeEditorPane () { + return this.pane + } + + get activeEditor () { + return this.pane?.input ?? null + } + + previewEditor = null + get count () { + return this.pane != null ? 1 : 0 + } + + get isEmpty () { + return this.pane == null + } + + isLocked = true + stickyCount = 0 + get editors () { + return this.pane != null ? [this.pane.input] : [] + } + + getEditors = () => this.editors + findEditors = (resource: URI) => this.pane != null && resource.toString() === this.pane.input.resource.toString() ? [this.pane.input] : [] + getEditorByIndex = (index: number) => this.pane != null && index === 0 ? this.pane.input : undefined + getIndexOfEditor = (editorInput: EditorInput) => this.pane != null && this.pane.input === editorInput ? 0 : -1 + openEditor = unsupported + openEditors = unsupported + isPinned = () => false + isSticky = () => false + isActive = () => this.editor.hasWidgetFocus() + contains = (candidate: EditorInput | IUntypedEditorInput) => { + return this.pane != null && this.pane.input === candidate + } + + moveEditor = unsupported + moveEditors = unsupported + copyEditor = unsupported + copyEditors = unsupported + closeEditor = unsupported + closeEditors = unsupported + closeAllEditors = unsupported + replaceEditors = unsupported + pinEditor = unsupported + stickEditor = unsupported + unstickEditor = unsupported + lock = unsupported + focus (): void { + this.editor.focus() + } + + isFirst = unsupported + isLast = unsupported +} + +export class MonacoDelegateEditorGroupsService extends Disposable implements IEditorGroupsService { + readonly _serviceBrand = undefined + + public additionalGroups: StandaloneEditorGroup[] = [] + public activeGroupOverride: StandaloneEditorGroup | undefined = undefined + + constructor (protected delegate: D, @IInstantiationService instantiationService: IInstantiationService) { + super() + setTimeout(() => { + const codeEditorService = StandaloneServices.get(ICodeEditorService) + + const handleCodeEditor = (editor: ICodeEditor) => { + if (editor instanceof StandaloneEditor) { + const onEditorFocused = () => { + this.activeGroupOverride = this.additionalGroups.find(group => group.editor === editor) + this._onDidChangeActiveGroup.fire(this.activeGroup) + } + editor.onDidFocusEditorText(onEditorFocused) + editor.onDidFocusEditorWidget(onEditorFocused) + if (editor.hasWidgetFocus()) { + onEditorFocused() + } + + const newGroup = instantiationService.createInstance(StandaloneEditorGroup, editor) + this.additionalGroups.push(newGroup) + this._onDidAddGroup.fire(newGroup) + } + } + const handleCodeEditorRemoved = (editor: ICodeEditor) => { + if (editor instanceof StandaloneEditor) { + const removedGroup = this.additionalGroups.find(group => group.editor === editor) + if (removedGroup != null) { + removedGroup.dispose() + if (this.activeGroupOverride === removedGroup) { + this.activeGroupOverride = undefined + this._onDidChangeActiveGroup.fire(this.activeGroup) + } + this.additionalGroups = this.additionalGroups.filter(group => group !== removedGroup) + this._onDidRemoveGroup.fire(removedGroup) + } + } + } + this._register(codeEditorService.onCodeEditorAdd(handleCodeEditor)) + this._register(codeEditorService.onCodeEditorRemove(handleCodeEditorRemoved)) + codeEditorService.listCodeEditors().forEach(handleCodeEditor) + }) + } + + public get groups (): IEditorGroup[] { + return [...this.additionalGroups, ...this.delegate.groups] + } + + public get activeGroup (): IEditorGroup { + return this.activeGroupOverride ?? this.delegate.activeGroup + } + + _onDidChangeActiveGroup = new Emitter() + onDidChangeActiveGroup = Event.any(this._onDidChangeActiveGroup.event, this.delegate.onDidChangeActiveGroup) + + _onDidAddGroup = new Emitter() + onDidAddGroup = Event.any(this._onDidAddGroup.event, this.delegate.onDidAddGroup) + + _onDidRemoveGroup = new Emitter() + onDidRemoveGroup = Event.any(this._onDidRemoveGroup.event, this.delegate.onDidRemoveGroup) + + onDidMoveGroup = this.delegate.onDidMoveGroup + onDidActivateGroup = this.delegate.onDidActivateGroup + onDidLayout = this.delegate.onDidLayout + onDidScroll = this.delegate.onDidScroll + onDidChangeGroupIndex = this.delegate.onDidChangeGroupIndex + onDidChangeGroupLocked = this.delegate.onDidChangeGroupLocked + get contentDimension (): IEditorGroupsService['contentDimension'] { return this.delegate.contentDimension } + get sideGroup (): IEditorGroupsService['sideGroup'] { return this.delegate.sideGroup } + get count (): IEditorGroupsService['count'] { return this.delegate.count + this.additionalGroups.length } + get orientation (): IEditorGroupsService['orientation'] { return this.delegate.orientation } + get isReady (): IEditorGroupsService['isReady'] { return this.delegate.isReady } + get whenReady (): IEditorGroupsService['whenReady'] { return this.delegate.whenReady } + get whenRestored (): IEditorGroupsService['whenRestored'] { return this.delegate.whenRestored } + get hasRestorableState (): IEditorGroupsService['hasRestorableState'] { return this.delegate.hasRestorableState } + get partOptions (): IEditorGroupsService['partOptions'] { return this.delegate.partOptions } + + getLayout: IEditorGroupsService['getLayout'] = () => { + return this.delegate.getLayout() + } + + getGroups: IEditorGroupsService['getGroups'] = (order) => { + return [...this.additionalGroups, ...this.delegate.getGroups(order)] + } + + getGroup: IEditorGroupsService['getGroup'] = (identifier) => { + return this.delegate.getGroup(identifier) ?? this.additionalGroups.find(group => group.id === identifier) + } + + activateGroup: IEditorGroupsService['activateGroup'] = (...args) => { + return this.delegate.activateGroup(...args) + } + + getSize: IEditorGroupsService['getSize'] = (...args) => { + return this.delegate.getSize(...args) + } + + setSize: IEditorGroupsService['setSize'] = (...args) => { + return this.delegate.setSize(...args) + } + + arrangeGroups: IEditorGroupsService['arrangeGroups'] = (...args) => { + return this.delegate.arrangeGroups(...args) + } + + applyLayout: IEditorGroupsService['applyLayout'] = (...args) => { + return this.delegate.applyLayout(...args) + } + + centerLayout: IEditorGroupsService['centerLayout'] = (...args) => { + return this.delegate.centerLayout(...args) + } + + isLayoutCentered: IEditorGroupsService['isLayoutCentered'] = (...args) => { + return this.delegate.isLayoutCentered(...args) + } + + setGroupOrientation: IEditorGroupsService['setGroupOrientation'] = (...args) => { + return this.delegate.setGroupOrientation(...args) + } + + findGroup: IEditorGroupsService['findGroup'] = (...args) => { + return this.delegate.findGroup(...args) + } + + addGroup: IEditorGroupsService['addGroup'] = (...args) => { + return this.delegate.addGroup(...args) + } + + removeGroup: IEditorGroupsService['removeGroup'] = (...args) => { + return this.delegate.removeGroup(...args) + } + + moveGroup: IEditorGroupsService['moveGroup'] = (...args) => { + return this.delegate.moveGroup(...args) + } + + mergeGroup: IEditorGroupsService['mergeGroup'] = (...args) => { + return this.delegate.mergeGroup(...args) + } + + mergeAllGroups: IEditorGroupsService['mergeAllGroups'] = (...args) => { + return this.delegate.mergeAllGroups(...args) + } + + copyGroup: IEditorGroupsService['copyGroup'] = (...args) => { + return this.delegate.copyGroup(...args) + } + + enforcePartOptions: IEditorGroupsService['enforcePartOptions'] = (...args) => { + return this.delegate.enforcePartOptions(...args) + } + + onDidChangeEditorPartOptions = this.delegate.onDidChangeEditorPartOptions +} diff --git a/src/service-override/views.ts b/src/service-override/views.ts index bbbde817..aa5a336c 100644 --- a/src/service-override/views.ts +++ b/src/service-override/views.ts @@ -46,9 +46,8 @@ import 'vs/workbench/contrib/files/browser/files.contribution.js?exclude=registe import { Codicon } from 'vs/base/common/codicons' import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService' import { IEditorDropService } from 'vs/workbench/services/editor/browser/editorDropService' -import { EditorService } from 'vs/workbench/services/editor/browser/editorService' import { IEditorDropTargetDelegate } from 'vs/workbench/browser/parts/editor/editorDropTarget' -import { IEditorService, PreferredGroup } from 'vs/workbench/services/editor/common/editorService' +import { IEditorService } from 'vs/workbench/services/editor/common/editorService' import { IEditorResolverService } from 'vs/workbench/services/editor/common/editorResolverService' import { EditorResolverService } from 'vs/workbench/services/editor/browser/editorResolverService' import { BreadcrumbsService, IBreadcrumbsService } from 'vs/workbench/browser/parts/editor/breadcrumbs' @@ -56,15 +55,9 @@ import { IContextViewService } from 'vs/platform/contextview/browser/contextView import { ContextViewService } from 'vs/platform/contextview/browser/contextViewService' import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService' import { EditorInput, IEditorCloseHandler } from 'vs/workbench/common/editor/editorInput' -import { EditorExtensions, IEditorPane, IResourceDiffEditorInput, ITextDiffEditorPane, IUntitledTextResourceEditorInput, IUntypedEditorInput, Verbosity } from 'vs/workbench/common/editor' -import { IEditorOptions, IResourceEditorInput, ITextResourceEditorInput } from 'vs/platform/editor/common/editor' -import { ITextModelService, IResolvedTextEditorModel } from 'vs/editor/common/services/resolverService' -import { IFileService } from 'vs/platform/files/common/files' -import { IConfigurationService } from 'vs/platform/configuration/common/configuration' -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace' -import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity' -import { IWorkspaceTrustRequestService } from 'vs/platform/workspace/common/workspaceTrust' -import { IHostService } from 'vs/workbench/services/host/browser/host' +import { EditorExtensions, Verbosity } from 'vs/workbench/common/editor' +import { IEditorOptions } from 'vs/platform/editor/common/editor' +import { IResolvedTextEditorModel } from 'vs/editor/common/services/resolverService' import { ITextEditorService, TextEditorService } from 'vs/workbench/services/textfile/common/textEditorService' import { CodeEditorService } from 'vs/workbench/services/editor/browser/codeEditorService' import { IUntitledTextEditorService, UntitledTextEditorService } from 'vs/workbench/services/untitled/common/untitledTextEditorService' @@ -96,8 +89,7 @@ import { IStorageService } from 'vs/platform/storage/common/storage' import { IThemeService } from 'vs/platform/theme/common/themeService' import { ConfirmResult } from 'vs/platform/dialogs/common/dialogs' import { ILayoutService } from 'vs/platform/layout/browser/layoutService' -import { StandaloneCodeEditor } from 'vs/editor/standalone/browser/standaloneCodeEditor' -import { OpenEditor, wrapOpenEditor } from './tools/editor' +import { MonacoDelegateEditorGroupsService, MonacoEditorService, OpenEditor } from './tools/editor' import getBulkEditServiceOverride from './bulkEdit' import getLayoutServiceOverride, { LayoutService } from './layout' import getQuickAccessOverride from './quickaccess' @@ -468,62 +460,98 @@ function isEditorPartVisible (): boolean { return container != null && isElementVisible(container) } -class MonacoEditorService extends EditorService { - constructor ( - _openEditorFallback: OpenEditor | undefined, - @IEditorGroupsService _editorGroupService: IEditorGroupsService, - @IInstantiationService instantiationService: IInstantiationService, - @IFileService fileService: IFileService, - @IConfigurationService configurationService: IConfigurationService, - @IWorkspaceContextService contextService: IWorkspaceContextService, - @IUriIdentityService uriIdentityService: IUriIdentityService, - @IEditorResolverService editorResolverService: IEditorResolverService, - @IWorkspaceTrustRequestService workspaceTrustRequestService: IWorkspaceTrustRequestService, - @IHostService hostService: IHostService, - @ITextEditorService textEditorService: ITextEditorService, - @ITextModelService textModelService: ITextModelService - ) { +type PublicInterface = Pick + +class MonacoEditorPart extends MonacoDelegateEditorGroupsService implements Omit, keyof IEditorGroupsService> { + constructor (@IInstantiationService instantiationService: IInstantiationService) { super( - _editorGroupService, - instantiationService, - fileService, - configurationService, - contextService, - uriIdentityService, - editorResolverService, - workspaceTrustRequestService, - hostService, - textEditorService + instantiationService.createInstance(EditorPart), + instantiationService ) + } + + onDidChangeSizeConstraints = this.delegate.onDidChangeSizeConstraints - this.openEditor = wrapOpenEditor(textModelService, this.openEditor.bind(this), _openEditorFallback) + restoreGroup: EditorPart['restoreGroup'] = (...args) => { + return this.delegate.restoreGroup(...args) } - override get activeTextEditorControl () { - // By default, only the editor inside the EditorPart can be "active" here, hack it so the active editor is now the focused editor if it exists - // It is required for the editor.addAction to be able to add an entry in the editor action menu - const focusedCodeEditor = StandaloneServices.get(ICodeEditorService).getFocusedCodeEditor() - if (focusedCodeEditor != null && focusedCodeEditor instanceof StandaloneCodeEditor) { - return focusedCodeEditor - } + isGroupMaximized: EditorPart['isGroupMaximized'] = (...args) => { + return this.delegate.isGroupMaximized(...args) + } - return super.activeTextEditorControl + createEditorDropTarget: EditorPart['createEditorDropTarget'] = (...args) => { + return this.delegate.createEditorDropTarget(...args) } - // Override openEditor to fallback on user function is the EditorPart is not visible - override openEditor(editor: EditorInput, options?: IEditorOptions, group?: PreferredGroup): Promise - override openEditor(editor: IUntypedEditorInput, group?: PreferredGroup): Promise - override openEditor(editor: IResourceEditorInput, group?: PreferredGroup): Promise - override openEditor(editor: ITextResourceEditorInput | IUntitledTextResourceEditorInput, group?: PreferredGroup): Promise - override openEditor(editor: IResourceDiffEditorInput, group?: PreferredGroup): Promise - override openEditor(editor: EditorInput | IUntypedEditorInput, optionsOrPreferredGroup?: IEditorOptions | PreferredGroup, preferredGroup?: PreferredGroup): Promise - override async openEditor (editor: EditorInput | IUntypedEditorInput, optionsOrPreferredGroup?: IEditorOptions | PreferredGroup, preferredGroup?: PreferredGroup): Promise { - // Do not try to open the file if the editor part is not displayed, let the fallback happen - if (!isEditorPartVisible()) { - return undefined - } + get minimumWidth (): number { + return this.delegate.minimumWidth + } + + get maximumWidth (): number { + return this.delegate.maximumWidth + } + + get minimumHeight (): number { + return this.delegate.minimumHeight + } + + get maximumHeight (): number { + return this.delegate.maximumHeight + } + + get snap (): boolean { + return this.delegate.snap + } + + get onDidChange () { + return this.delegate.onDidChange + } + + get priority () { + return this.delegate.priority + } + + updateStyles: EditorPart['updateStyles'] = (...args) => { + return this.delegate.updateStyles(...args) + } + + setBoundarySashes: EditorPart['setBoundarySashes'] = (...args) => { + return this.delegate.setBoundarySashes(...args) + } + + layout: EditorPart['layout'] = (...args) => { + return this.delegate.layout(...args) + } + + toJSON: EditorPart['toJSON'] = (...args) => { + return this.delegate.toJSON(...args) + } + + get dimension () { + return this.delegate.dimension + } + + onDidVisibilityChange = this.delegate.onDidVisibilityChange + + create: EditorPart['create'] = (...args) => { + return this.delegate.create(...args) + } + + getContainer: EditorPart['getContainer'] = (...args) => { + return this.delegate.getContainer(...args) + } + + setVisible: EditorPart['setVisible'] = (...args) => { + return this.delegate.setVisible(...args) + } + + getId: EditorPart['getId'] = (...args) => { + return this.delegate.getId(...args) + } - return super.openEditor(editor, optionsOrPreferredGroup, preferredGroup) + get element () { + return this.delegate.element } } @@ -593,10 +621,10 @@ export default function getServiceOverride (openEditorFallback?: OpenEditor, _we [ICodeEditorService.toString()]: new SyncDescriptor(CodeEditorService, [], true), [ITextEditorService.toString()]: new SyncDescriptor(TextEditorService, [], false), - [IEditorGroupsService.toString()]: new SyncDescriptor(EditorPart, [], false), + [IEditorGroupsService.toString()]: new SyncDescriptor(MonacoEditorPart, [], false), [IStatusbarService.toString()]: new SyncDescriptor(StatusbarPart, [], false), [IEditorDropService.toString()]: new SyncDescriptor(EditorDropService, [], true), - [IEditorService.toString()]: new SyncDescriptor(MonacoEditorService, [openEditorFallback], false), + [IEditorService.toString()]: new SyncDescriptor(MonacoEditorService, [openEditorFallback, isEditorPartVisible], false), [IEditorResolverService.toString()]: new SyncDescriptor(EditorResolverService, [], false), [IBreadcrumbsService.toString()]: new SyncDescriptor(BreadcrumbsService, [], true), [IContextViewService.toString()]: new SyncDescriptor(ContextViewService, [], true),