Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use code editor service over dynamic editor registration #154352

Merged
merged 3 commits into from
Jul 8, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 19 additions & 3 deletions src/vs/editor/browser/services/abstractCodeEditorService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@

import * as dom from 'vs/base/browser/dom';
import { Emitter, Event } from 'vs/base/common/event';
import { IDisposable, DisposableStore, Disposable } from 'vs/base/common/lifecycle';
import { IDisposable, DisposableStore, Disposable, toDisposable } from 'vs/base/common/lifecycle';
import { LinkedList } from 'vs/base/common/linkedList';
import * as strings from 'vs/base/common/strings';
import { URI } from 'vs/base/common/uri';
import { ICodeEditor, IDiffEditor } from 'vs/editor/browser/editorBrowser';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import { ICodeEditorOpenHandler, ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import { IContentDecorationRenderOptions, IDecorationRenderOptions, IThemeDecorationRenderOptions, isThemeColor } from 'vs/editor/common/editorCommon';
import { IModelDecorationOptions, IModelDecorationOverviewRulerOptions, InjectedTextOptions, ITextModel, OverviewRulerLane, TrackedRangeStickiness } from 'vs/editor/common/model';
import { IResourceEditorInput } from 'vs/platform/editor/common/editor';
Expand Down Expand Up @@ -42,6 +43,7 @@ export abstract class AbstractCodeEditorService extends Disposable implements IC
protected _globalStyleSheet: GlobalStyleSheet | null;
private readonly _decorationOptionProviders = new Map<string, IModelDecorationOptionsProvider>();
private readonly _editorStyleSheets = new Map<string, RefCountedStyleSheet>();
private readonly _codeEditorOpenHandlers = new LinkedList<ICodeEditorOpenHandler>();

constructor(
@IThemeService private readonly _themeService: IThemeService,
Expand Down Expand Up @@ -247,7 +249,21 @@ export abstract class AbstractCodeEditorService extends Disposable implements IC
}

abstract getActiveCodeEditor(): ICodeEditor | null;
abstract openCodeEditor(input: IResourceEditorInput, source: ICodeEditor | null, sideBySide?: boolean): Promise<ICodeEditor | null>;

async openCodeEditor(input: IResourceEditorInput, source: ICodeEditor | null, sideBySide?: boolean): Promise<ICodeEditor | null> {
for (const handler of this._codeEditorOpenHandlers) {
const candidate = await handler(input, source, sideBySide);
if (candidate !== null) {
return candidate;
}
}
return null;
}

registerCodeEditorOpenHandler(handler: ICodeEditorOpenHandler): IDisposable {
const rm = this._codeEditorOpenHandlers.unshift(handler);
return toDisposable(rm);
}
}

export class ModelTransientSettingWatcher {
Expand Down
6 changes: 6 additions & 0 deletions src/vs/editor/browser/services/codeEditorService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { IModelDecorationOptions, ITextModel } from 'vs/editor/common/model';
import { ITextResourceEditorInput } from 'vs/platform/editor/common/editor';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { URI } from 'vs/base/common/uri';
import { IDisposable } from 'vs/base/common/lifecycle';

export const ICodeEditorService = createDecorator<ICodeEditorService>('codeEditorService');

Expand Down Expand Up @@ -53,4 +54,9 @@ export interface ICodeEditorService {

getActiveCodeEditor(): ICodeEditor | null;
openCodeEditor(input: ITextResourceEditorInput, source: ICodeEditor | null, sideBySide?: boolean): Promise<ICodeEditor | null>;
registerCodeEditorOpenHandler(handler: ICodeEditorOpenHandler): IDisposable;
}

export interface ICodeEditorOpenHandler {
(input: ITextResourceEditorInput, source: ICodeEditor | null, sideBySide?: boolean): Promise<ICodeEditor | null>;
}
16 changes: 8 additions & 8 deletions src/vs/editor/standalone/browser/standaloneCodeEditorService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { IRange } from 'vs/editor/common/core/range';
import { ScrollType } from 'vs/editor/common/editorCommon';
import { ITextModel } from 'vs/editor/common/model';
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IResourceEditorInput, ITextResourceEditorInput } from 'vs/platform/editor/common/editor';
import { ITextResourceEditorInput } from 'vs/platform/editor/common/editor';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { IThemeService } from 'vs/platform/theme/common/themeService';

Expand All @@ -31,6 +31,13 @@ export class StandaloneCodeEditorService extends AbstractCodeEditorService {
this.onCodeEditorRemove(() => this._checkContextKey());
this._editorIsOpen = contextKeyService.createKey('editorIsOpen', false);
this._activeCodeEditor = null;

this.registerCodeEditorOpenHandler(async (input, source, sideBySide) => {
if (!source) {
return null;
}
return this.doOpenEditor(source, input);
});
}

private _checkContextKey(): void {
Expand All @@ -52,13 +59,6 @@ export class StandaloneCodeEditorService extends AbstractCodeEditorService {
return this._activeCodeEditor;
}

public openCodeEditor(input: IResourceEditorInput, source: ICodeEditor | null, sideBySide?: boolean): Promise<ICodeEditor | null> {
if (!source) {
return Promise.resolve(null);
}

return Promise.resolve(this.doOpenEditor(source, input));
}

private doOpenEditor(editor: ICodeEditor, input: ITextResourceEditorInput): ICodeEditor | null {
const model = this.findModel(editor, input.resource);
Expand Down
2 changes: 1 addition & 1 deletion src/vs/editor/test/browser/editorTestServices.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export class TestCodeEditorService extends AbstractCodeEditorService {
return null;
}
public lastInput?: IResourceEditorInput;
openCodeEditor(input: IResourceEditorInput, source: ICodeEditor | null, sideBySide?: boolean): Promise<ICodeEditor | null> {
override openCodeEditor(input: IResourceEditorInput, source: ICodeEditor | null, sideBySide?: boolean): Promise<ICodeEditor | null> {
this.lastInput = input;
return Promise.resolve(null);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -403,8 +403,7 @@ class DocumentSymbolsOutlineCreator implements IOutlineCreator<IEditorPane, Docu
readonly dispose: () => void;

constructor(
@IOutlineService outlineService: IOutlineService,
@IInstantiationService private readonly _instantiationService: IInstantiationService,
@IOutlineService outlineService: IOutlineService
) {
const reg = outlineService.registerOutlineCreator(this);
this.dispose = () => reg.dispose();
Expand All @@ -427,7 +426,7 @@ class DocumentSymbolsOutlineCreator implements IOutlineCreator<IEditorPane, Docu
return undefined;
}
const firstLoadBarrier = new Barrier();
const result = this._instantiationService.createInstance(DocumentSymbolsOutline, editor, target, firstLoadBarrier);
const result = editor.invokeWithinContext(accessor => accessor.get(IInstantiationService).createInstance(DocumentSymbolsOutline, editor!, target, firstLoadBarrier));
await firstLoadBarrier.wait();
return result;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@ import { registerAction2 } from 'vs/platform/actions/common/actions';
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { Registry } from 'vs/platform/registry/common/platform';
import { EditorPaneDescriptor, IEditorPaneRegistry } from 'vs/workbench/browser/editor';
import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions';
import { EditorExtensions, IEditorFactoryRegistry } from 'vs/workbench/common/editor';
import { CompareInput1WithBaseCommand, CompareInput2WithBaseCommand, GoToNextConflict, GoToPreviousConflict, OpenMergeEditor, ToggleActiveConflictInput1, ToggleActiveConflictInput2, SetColumnLayout, SetMixedLayout, OpenBaseFile } from 'vs/workbench/contrib/mergeEditor/browser/commands/commands';
import { CompareInput1WithBaseCommand, CompareInput2WithBaseCommand, GoToNextConflict, GoToPreviousConflict, OpenBaseFile, OpenMergeEditor, SetColumnLayout, SetMixedLayout, ToggleActiveConflictInput1, ToggleActiveConflictInput2 } from 'vs/workbench/contrib/mergeEditor/browser/commands/commands';
import { MergeEditorCopyContentsToJSON, MergeEditorOpenContents } from 'vs/workbench/contrib/mergeEditor/browser/commands/devCommands';
import { MergeEditorInput } from 'vs/workbench/contrib/mergeEditor/browser/mergeEditorInput';
import { MergeEditor } from 'vs/workbench/contrib/mergeEditor/browser/view/mergeEditor';
import { MergeEditor, MergeEditorOpenHandlerContribution } from 'vs/workbench/contrib/mergeEditor/browser/view/mergeEditor';
import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle';
import { MergeEditorSerializer } from './mergeEditorSerializer';

Registry.as<IEditorPaneRegistry>(EditorExtensions.EditorPane).registerEditorPane(
Expand Down Expand Up @@ -47,3 +49,8 @@ registerAction2(ToggleActiveConflictInput2);

registerAction2(CompareInput1WithBaseCommand);
registerAction2(CompareInput2WithBaseCommand);


Registry
.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench)
.registerWorkbenchContribution(MergeEditorOpenHandlerContribution, LifecyclePhase.Restored);
85 changes: 40 additions & 45 deletions src/vs/workbench/contrib/mergeEditor/browser/view/mergeEditor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,13 @@ import { CancellationToken } from 'vs/base/common/cancellation';
import { Color } from 'vs/base/common/color';
import { BugIndicatingError } from 'vs/base/common/errors';
import { Emitter, Event } from 'vs/base/common/event';
import { DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle';
import { Disposable, DisposableStore } from 'vs/base/common/lifecycle';
import { autorunWithStore, IObservable } from 'vs/base/common/observable';
import { isEqual } from 'vs/base/common/resources';
import { URI } from 'vs/base/common/uri';
import 'vs/css!./media/mergeEditor';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget';
import { IEditorOptions as ICodeEditorOptions } from 'vs/editor/common/config/editorOptions';
import { ICodeEditorViewState, ScrollType } from 'vs/editor/common/editorCommon';
Expand All @@ -26,7 +27,7 @@ import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/men
import { IMenuService, MenuId } from 'vs/platform/actions/common/actions';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IEditorOptions, ITextEditorOptions } from 'vs/platform/editor/common/editor';
import { IEditorOptions, ITextEditorOptions, ITextResourceEditorInput } from 'vs/platform/editor/common/editor';
import { IFileService } from 'vs/platform/files/common/files';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ILabelService } from 'vs/platform/label/common/label';
Expand All @@ -35,7 +36,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { FloatingClickWidget } from 'vs/workbench/browser/codeeditor';
import { AbstractTextEditor } from 'vs/workbench/browser/parts/editor/textEditor';
import { EditorInputWithOptions, EditorResourceAccessor, IEditorOpenContext } from 'vs/workbench/common/editor';
import { IEditorOpenContext } from 'vs/workbench/common/editor';
import { EditorInput } from 'vs/workbench/common/editor/editorInput';
import { applyTextEditorOptions } from 'vs/workbench/common/editor/editorOptions';
import { MergeEditorInput } from 'vs/workbench/contrib/mergeEditor/browser/mergeEditorInput';
Expand All @@ -46,7 +47,6 @@ import { MergeEditorViewModel } from 'vs/workbench/contrib/mergeEditor/browser/v
import { ctxBaseResourceScheme, ctxIsMergeEditor, ctxMergeEditorLayout, MergeEditorLayoutTypes } from 'vs/workbench/contrib/mergeEditor/common/mergeEditor';
import { settingsSashBorder } from 'vs/workbench/contrib/preferences/common/settingsEditorColorRegistry';
import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import { IEditorResolverService, RegisteredEditorPriority } from 'vs/workbench/services/editor/common/editorResolverService';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import './colors';
import { InputCodeEditorView } from './editors/inputCodeEditorView';
Expand Down Expand Up @@ -86,9 +86,9 @@ export class MergeEditor extends AbstractTextEditor<IMergeEditorViewState> {
private readonly _sessionDisposables = new DisposableStore();

private _grid!: Grid<IView>;
private readonly input1View = this._register(this.instantiation.createInstance(InputCodeEditorView, 1));
private readonly input2View = this._register(this.instantiation.createInstance(InputCodeEditorView, 2));
private readonly inputResultView = this._register(this.instantiation.createInstance(ResultCodeEditorView));
private readonly input1View = this._register(this.instantiationService.createInstance(InputCodeEditorView, 1));
private readonly input2View = this._register(this.instantiationService.createInstance(InputCodeEditorView, 2));
private readonly inputResultView = this._register(this.instantiationService.createInstance(ResultCodeEditorView));

private readonly _layoutMode: MergeEditorLayout;
private readonly _ctxIsMergeEditor: IContextKey<boolean>;
Expand All @@ -103,7 +103,7 @@ export class MergeEditor extends AbstractTextEditor<IMergeEditorViewState> {
}

constructor(
@IInstantiationService private readonly instantiation: IInstantiationService,
@IInstantiationService instantiation: IInstantiationService,
@ILabelService private readonly _labelService: ILabelService,
@IMenuService private readonly _menuService: IMenuService,
@IContextKeyService private readonly _contextKeyService: IContextKeyService,
Expand All @@ -115,7 +115,6 @@ export class MergeEditor extends AbstractTextEditor<IMergeEditorViewState> {
@IEditorService editorService: IEditorService,
@IEditorGroupsService editorGroupService: IEditorGroupsService,
@IFileService fileService: IFileService,
@IEditorResolverService private readonly _editorResolverService: IEditorResolverService,
) {
super(MergeEditor.ID, telemetryService, instantiation, storageService, textResourceConfigurationService, themeService, editorService, editorGroupService, fileService);

Expand Down Expand Up @@ -186,7 +185,7 @@ export class MergeEditor extends AbstractTextEditor<IMergeEditorViewState> {
createAndFillInActionBarActions(toolbarMenu, { renderShortTitle: true, shouldForwardArgs: true }, actions);
if (actions.length > 0) {
const [first] = actions;
const acceptBtn = this.instantiation.createInstance(FloatingClickWidget, this.inputResultView.editor, first.label, first.id);
const acceptBtn = this.instantiationService.createInstance(FloatingClickWidget, this.inputResultView.editor, first.label, first.id);
toolbarMenuDisposables.add(acceptBtn.onClick(() => first.run(this.inputResultView.editor.getModel()?.uri)));
toolbarMenuDisposables.add(acceptBtn);
acceptBtn.render();
Expand Down Expand Up @@ -296,7 +295,6 @@ export class MergeEditor extends AbstractTextEditor<IMergeEditorViewState> {
await super.setInput(input, options, context, token);

this._sessionDisposables.clear();
this._toggleEditorOverwrite(true);

const model = await input.resolve();
this._model = model;
Expand Down Expand Up @@ -373,7 +371,6 @@ export class MergeEditor extends AbstractTextEditor<IMergeEditorViewState> {
super.clearInput();

this._sessionDisposables.clear();
this._toggleEditorOverwrite(false);

for (const { editor } of [this.input1View, this.input2View, this.inputResultView]) {
editor.setModel(null);
Expand Down Expand Up @@ -405,39 +402,6 @@ export class MergeEditor extends AbstractTextEditor<IMergeEditorViewState> {
}

this._ctxIsMergeEditor.set(visible);
this._toggleEditorOverwrite(visible);
}

private readonly _editorOverrideHandle = this._store.add(new MutableDisposable());

private _toggleEditorOverwrite(haveIt: boolean) {
if (!haveIt) {
this._editorOverrideHandle.clear();
return;
}
// this is RATHER UGLY. I dynamically register an editor for THIS (editor,input) so that
// navigating within the merge editor works, e.g navigating from the outline or breakcrumps
// or revealing a definition, reference etc
// TODO@jrieken @bpasero @lramos15
const input = this.input;
if (input instanceof MergeEditorInput) {
this._editorOverrideHandle.value = this._editorResolverService.registerEditor(
`${input.result.scheme}:${input.result.fsPath}`,
{
id: `${this.getId()}/fake`,
label: this.input?.getName()!,
priority: RegisteredEditorPriority.exclusive
},
{},
(candidate): EditorInputWithOptions => {
const resource = EditorResourceAccessor.getCanonicalUri(candidate);
if (!isEqual(resource, this.model?.result.uri)) {
throw new Error(`Expected to be called WITH ${input.result.toString()}`);
}
return { editor: input };
}
);
}
}

// ---- interact with "outside world" via`getControl`, `scopedContextKeyService`: we only expose the result-editor keep the others internal
Expand Down Expand Up @@ -506,6 +470,37 @@ export class MergeEditor extends AbstractTextEditor<IMergeEditorViewState> {
}
}

export class MergeEditorOpenHandlerContribution extends Disposable {

constructor(
@IEditorService private readonly _editorService: IEditorService,
@ICodeEditorService codeEditorService: ICodeEditorService,
) {
super();
this._store.add(codeEditorService.registerCodeEditorOpenHandler(this.openCodeEditorFromMergeEditor.bind(this)));
}

private async openCodeEditorFromMergeEditor(input: ITextResourceEditorInput, _source: ICodeEditor | null, sideBySide?: boolean | undefined): Promise<ICodeEditor | null> {
const activePane = this._editorService.activeEditorPane;
if (!sideBySide
&& input.options
&& activePane instanceof MergeEditor
&& activePane.getControl()
&& activePane.input instanceof MergeEditorInput
&& isEqual(input.resource, activePane.input.result)
) {
// Special: stay inside the merge editor when it is active and when the input
// targets the result editor of the merge editor.
const targetEditor = <ICodeEditor>activePane.getControl()!;
applyTextEditorOptions(input.options, targetEditor, ScrollType.Smooth);
return targetEditor;
}

// cannot handle this
return null;
}
}

type IMergeEditorViewState = ICodeEditorViewState & {
readonly input1State?: ICodeEditorViewState;
readonly input2State?: ICodeEditorViewState;
Expand Down
9 changes: 6 additions & 3 deletions src/vs/workbench/services/editor/browser/codeEditorService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ export class CodeEditorService extends AbstractCodeEditorService {
@IConfigurationService private readonly configurationService: IConfigurationService,
) {
super(themeService);

this.registerCodeEditorOpenHandler(this.doOpenCodeEditor.bind(this));
this.registerCodeEditorOpenHandler(this.doOpenCodeEditorFromDiff.bind(this));
}

getActiveCodeEditor(): ICodeEditor | null {
Expand All @@ -44,7 +47,7 @@ export class CodeEditorService extends AbstractCodeEditorService {
return null;
}

async openCodeEditor(input: IResourceEditorInput, source: ICodeEditor | null, sideBySide?: boolean): Promise<ICodeEditor | null> {
private async doOpenCodeEditorFromDiff(input: IResourceEditorInput, source: ICodeEditor | null, sideBySide?: boolean): Promise<ICodeEditor | null> {

// Special case: If the active editor is a diff editor and the request to open originates and
// targets the modified side of it, we just apply the request there to prevent opening the modified
Expand All @@ -66,10 +69,10 @@ export class CodeEditorService extends AbstractCodeEditorService {
return targetEditor;
}

// Open using our normal editor service
return this.doOpenCodeEditor(input, source, sideBySide);
return null;
}

// Open using our normal editor service
private async doOpenCodeEditor(input: IResourceEditorInput, source: ICodeEditor | null, sideBySide?: boolean): Promise<ICodeEditor | null> {

// Special case: we want to detect the request to open an editor that
Expand Down