Skip to content

Commit 31a22e7

Browse files
authored
Merge pull request microsoft#154352 from microsoft/joh/issue153110
2 parents ef14540 + 0b9de0b commit 31a22e7

File tree

8 files changed

+91
-65
lines changed

8 files changed

+91
-65
lines changed

src/vs/editor/browser/services/abstractCodeEditorService.ts

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,12 @@
55

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

4648
constructor(
4749
@IThemeService private readonly _themeService: IThemeService,
@@ -247,7 +249,21 @@ export abstract class AbstractCodeEditorService extends Disposable implements IC
247249
}
248250

249251
abstract getActiveCodeEditor(): ICodeEditor | null;
250-
abstract openCodeEditor(input: IResourceEditorInput, source: ICodeEditor | null, sideBySide?: boolean): Promise<ICodeEditor | null>;
252+
253+
async openCodeEditor(input: IResourceEditorInput, source: ICodeEditor | null, sideBySide?: boolean): Promise<ICodeEditor | null> {
254+
for (const handler of this._codeEditorOpenHandlers) {
255+
const candidate = await handler(input, source, sideBySide);
256+
if (candidate !== null) {
257+
return candidate;
258+
}
259+
}
260+
return null;
261+
}
262+
263+
registerCodeEditorOpenHandler(handler: ICodeEditorOpenHandler): IDisposable {
264+
const rm = this._codeEditorOpenHandlers.unshift(handler);
265+
return toDisposable(rm);
266+
}
251267
}
252268

253269
export class ModelTransientSettingWatcher {

src/vs/editor/browser/services/codeEditorService.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { IModelDecorationOptions, ITextModel } from 'vs/editor/common/model';
1010
import { ITextResourceEditorInput } from 'vs/platform/editor/common/editor';
1111
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
1212
import { URI } from 'vs/base/common/uri';
13+
import { IDisposable } from 'vs/base/common/lifecycle';
1314

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

@@ -53,4 +54,9 @@ export interface ICodeEditorService {
5354

5455
getActiveCodeEditor(): ICodeEditor | null;
5556
openCodeEditor(input: ITextResourceEditorInput, source: ICodeEditor | null, sideBySide?: boolean): Promise<ICodeEditor | null>;
57+
registerCodeEditorOpenHandler(handler: ICodeEditorOpenHandler): IDisposable;
58+
}
59+
60+
export interface ICodeEditorOpenHandler {
61+
(input: ITextResourceEditorInput, source: ICodeEditor | null, sideBySide?: boolean): Promise<ICodeEditor | null>;
5662
}

src/vs/editor/standalone/browser/standaloneCodeEditorService.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { IRange } from 'vs/editor/common/core/range';
1313
import { ScrollType } from 'vs/editor/common/editorCommon';
1414
import { ITextModel } from 'vs/editor/common/model';
1515
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
16-
import { IResourceEditorInput, ITextResourceEditorInput } from 'vs/platform/editor/common/editor';
16+
import { ITextResourceEditorInput } from 'vs/platform/editor/common/editor';
1717
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
1818
import { IThemeService } from 'vs/platform/theme/common/themeService';
1919

@@ -31,6 +31,13 @@ export class StandaloneCodeEditorService extends AbstractCodeEditorService {
3131
this.onCodeEditorRemove(() => this._checkContextKey());
3232
this._editorIsOpen = contextKeyService.createKey('editorIsOpen', false);
3333
this._activeCodeEditor = null;
34+
35+
this.registerCodeEditorOpenHandler(async (input, source, sideBySide) => {
36+
if (!source) {
37+
return null;
38+
}
39+
return this.doOpenEditor(source, input);
40+
});
3441
}
3542

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

55-
public openCodeEditor(input: IResourceEditorInput, source: ICodeEditor | null, sideBySide?: boolean): Promise<ICodeEditor | null> {
56-
if (!source) {
57-
return Promise.resolve(null);
58-
}
59-
60-
return Promise.resolve(this.doOpenEditor(source, input));
61-
}
6262

6363
private doOpenEditor(editor: ICodeEditor, input: ITextResourceEditorInput): ICodeEditor | null {
6464
const model = this.findModel(editor, input.resource);

src/vs/editor/test/browser/editorTestServices.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export class TestCodeEditorService extends AbstractCodeEditorService {
2222
return null;
2323
}
2424
public lastInput?: IResourceEditorInput;
25-
openCodeEditor(input: IResourceEditorInput, source: ICodeEditor | null, sideBySide?: boolean): Promise<ICodeEditor | null> {
25+
override openCodeEditor(input: IResourceEditorInput, source: ICodeEditor | null, sideBySide?: boolean): Promise<ICodeEditor | null> {
2626
this.lastInput = input;
2727
return Promise.resolve(null);
2828
}

src/vs/workbench/contrib/codeEditor/browser/outline/documentSymbolsOutline.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -403,8 +403,7 @@ class DocumentSymbolsOutlineCreator implements IOutlineCreator<IEditorPane, Docu
403403
readonly dispose: () => void;
404404

405405
constructor(
406-
@IOutlineService outlineService: IOutlineService,
407-
@IInstantiationService private readonly _instantiationService: IInstantiationService,
406+
@IOutlineService outlineService: IOutlineService
408407
) {
409408
const reg = outlineService.registerOutlineCreator(this);
410409
this.dispose = () => reg.dispose();
@@ -427,7 +426,7 @@ class DocumentSymbolsOutlineCreator implements IOutlineCreator<IEditorPane, Docu
427426
return undefined;
428427
}
429428
const firstLoadBarrier = new Barrier();
430-
const result = this._instantiationService.createInstance(DocumentSymbolsOutline, editor, target, firstLoadBarrier);
429+
const result = editor.invokeWithinContext(accessor => accessor.get(IInstantiationService).createInstance(DocumentSymbolsOutline, editor!, target, firstLoadBarrier));
431430
await firstLoadBarrier.wait();
432431
return result;
433432
}

src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.contribution.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,13 @@ import { registerAction2 } from 'vs/platform/actions/common/actions';
88
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
99
import { Registry } from 'vs/platform/registry/common/platform';
1010
import { EditorPaneDescriptor, IEditorPaneRegistry } from 'vs/workbench/browser/editor';
11+
import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions';
1112
import { EditorExtensions, IEditorFactoryRegistry } from 'vs/workbench/common/editor';
12-
import { CompareInput1WithBaseCommand, CompareInput2WithBaseCommand, GoToNextConflict, GoToPreviousConflict, OpenMergeEditor, ToggleActiveConflictInput1, ToggleActiveConflictInput2, SetColumnLayout, SetMixedLayout, OpenBaseFile } from 'vs/workbench/contrib/mergeEditor/browser/commands/commands';
13+
import { CompareInput1WithBaseCommand, CompareInput2WithBaseCommand, GoToNextConflict, GoToPreviousConflict, OpenBaseFile, OpenMergeEditor, SetColumnLayout, SetMixedLayout, ToggleActiveConflictInput1, ToggleActiveConflictInput2 } from 'vs/workbench/contrib/mergeEditor/browser/commands/commands';
1314
import { MergeEditorCopyContentsToJSON, MergeEditorOpenContents } from 'vs/workbench/contrib/mergeEditor/browser/commands/devCommands';
1415
import { MergeEditorInput } from 'vs/workbench/contrib/mergeEditor/browser/mergeEditorInput';
15-
import { MergeEditor } from 'vs/workbench/contrib/mergeEditor/browser/view/mergeEditor';
16+
import { MergeEditor, MergeEditorOpenHandlerContribution } from 'vs/workbench/contrib/mergeEditor/browser/view/mergeEditor';
17+
import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle';
1618
import { MergeEditorSerializer } from './mergeEditorSerializer';
1719

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

4850
registerAction2(CompareInput1WithBaseCommand);
4951
registerAction2(CompareInput2WithBaseCommand);
52+
53+
54+
Registry
55+
.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench)
56+
.registerWorkbenchContribution(MergeEditorOpenHandlerContribution, LifecyclePhase.Restored);

src/vs/workbench/contrib/mergeEditor/browser/view/mergeEditor.ts

Lines changed: 40 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,13 @@ import { CancellationToken } from 'vs/base/common/cancellation';
1111
import { Color } from 'vs/base/common/color';
1212
import { BugIndicatingError } from 'vs/base/common/errors';
1313
import { Emitter, Event } from 'vs/base/common/event';
14-
import { DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle';
14+
import { Disposable, DisposableStore } from 'vs/base/common/lifecycle';
1515
import { autorunWithStore, IObservable } from 'vs/base/common/observable';
1616
import { isEqual } from 'vs/base/common/resources';
1717
import { URI } from 'vs/base/common/uri';
1818
import 'vs/css!./media/mergeEditor';
1919
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
20+
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
2021
import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget';
2122
import { IEditorOptions as ICodeEditorOptions } from 'vs/editor/common/config/editorOptions';
2223
import { ICodeEditorViewState, ScrollType } from 'vs/editor/common/editorCommon';
@@ -26,7 +27,7 @@ import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/men
2627
import { IMenuService, MenuId } from 'vs/platform/actions/common/actions';
2728
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
2829
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
29-
import { IEditorOptions, ITextEditorOptions } from 'vs/platform/editor/common/editor';
30+
import { IEditorOptions, ITextEditorOptions, ITextResourceEditorInput } from 'vs/platform/editor/common/editor';
3031
import { IFileService } from 'vs/platform/files/common/files';
3132
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
3233
import { ILabelService } from 'vs/platform/label/common/label';
@@ -35,7 +36,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
3536
import { IThemeService } from 'vs/platform/theme/common/themeService';
3637
import { FloatingClickWidget } from 'vs/workbench/browser/codeeditor';
3738
import { AbstractTextEditor } from 'vs/workbench/browser/parts/editor/textEditor';
38-
import { EditorInputWithOptions, EditorResourceAccessor, IEditorOpenContext } from 'vs/workbench/common/editor';
39+
import { IEditorOpenContext } from 'vs/workbench/common/editor';
3940
import { EditorInput } from 'vs/workbench/common/editor/editorInput';
4041
import { applyTextEditorOptions } from 'vs/workbench/common/editor/editorOptions';
4142
import { MergeEditorInput } from 'vs/workbench/contrib/mergeEditor/browser/mergeEditorInput';
@@ -46,7 +47,6 @@ import { MergeEditorViewModel } from 'vs/workbench/contrib/mergeEditor/browser/v
4647
import { ctxBaseResourceScheme, ctxIsMergeEditor, ctxMergeEditorLayout, MergeEditorLayoutTypes } from 'vs/workbench/contrib/mergeEditor/common/mergeEditor';
4748
import { settingsSashBorder } from 'vs/workbench/contrib/preferences/common/settingsEditorColorRegistry';
4849
import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
49-
import { IEditorResolverService, RegisteredEditorPriority } from 'vs/workbench/services/editor/common/editorResolverService';
5050
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
5151
import './colors';
5252
import { InputCodeEditorView } from './editors/inputCodeEditorView';
@@ -86,9 +86,9 @@ export class MergeEditor extends AbstractTextEditor<IMergeEditorViewState> {
8686
private readonly _sessionDisposables = new DisposableStore();
8787

8888
private _grid!: Grid<IView>;
89-
private readonly input1View = this._register(this.instantiation.createInstance(InputCodeEditorView, 1));
90-
private readonly input2View = this._register(this.instantiation.createInstance(InputCodeEditorView, 2));
91-
private readonly inputResultView = this._register(this.instantiation.createInstance(ResultCodeEditorView));
89+
private readonly input1View = this._register(this.instantiationService.createInstance(InputCodeEditorView, 1));
90+
private readonly input2View = this._register(this.instantiationService.createInstance(InputCodeEditorView, 2));
91+
private readonly inputResultView = this._register(this.instantiationService.createInstance(ResultCodeEditorView));
9292

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

105105
constructor(
106-
@IInstantiationService private readonly instantiation: IInstantiationService,
106+
@IInstantiationService instantiation: IInstantiationService,
107107
@ILabelService private readonly _labelService: ILabelService,
108108
@IMenuService private readonly _menuService: IMenuService,
109109
@IContextKeyService private readonly _contextKeyService: IContextKeyService,
@@ -115,7 +115,6 @@ export class MergeEditor extends AbstractTextEditor<IMergeEditorViewState> {
115115
@IEditorService editorService: IEditorService,
116116
@IEditorGroupsService editorGroupService: IEditorGroupsService,
117117
@IFileService fileService: IFileService,
118-
@IEditorResolverService private readonly _editorResolverService: IEditorResolverService,
119118
) {
120119
super(MergeEditor.ID, telemetryService, instantiation, storageService, textResourceConfigurationService, themeService, editorService, editorGroupService, fileService);
121120

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

298297
this._sessionDisposables.clear();
299-
this._toggleEditorOverwrite(true);
300298

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

375373
this._sessionDisposables.clear();
376-
this._toggleEditorOverwrite(false);
377374

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

407404
this._ctxIsMergeEditor.set(visible);
408-
this._toggleEditorOverwrite(visible);
409-
}
410-
411-
private readonly _editorOverrideHandle = this._store.add(new MutableDisposable());
412-
413-
private _toggleEditorOverwrite(haveIt: boolean) {
414-
if (!haveIt) {
415-
this._editorOverrideHandle.clear();
416-
return;
417-
}
418-
// this is RATHER UGLY. I dynamically register an editor for THIS (editor,input) so that
419-
// navigating within the merge editor works, e.g navigating from the outline or breakcrumps
420-
// or revealing a definition, reference etc
421-
// TODO@jrieken @bpasero @lramos15
422-
const input = this.input;
423-
if (input instanceof MergeEditorInput) {
424-
this._editorOverrideHandle.value = this._editorResolverService.registerEditor(
425-
`${input.result.scheme}:${input.result.fsPath}`,
426-
{
427-
id: `${this.getId()}/fake`,
428-
label: this.input?.getName()!,
429-
priority: RegisteredEditorPriority.exclusive
430-
},
431-
{},
432-
(candidate): EditorInputWithOptions => {
433-
const resource = EditorResourceAccessor.getCanonicalUri(candidate);
434-
if (!isEqual(resource, this.model?.result.uri)) {
435-
throw new Error(`Expected to be called WITH ${input.result.toString()}`);
436-
}
437-
return { editor: input };
438-
}
439-
);
440-
}
441405
}
442406

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

473+
export class MergeEditorOpenHandlerContribution extends Disposable {
474+
475+
constructor(
476+
@IEditorService private readonly _editorService: IEditorService,
477+
@ICodeEditorService codeEditorService: ICodeEditorService,
478+
) {
479+
super();
480+
this._store.add(codeEditorService.registerCodeEditorOpenHandler(this.openCodeEditorFromMergeEditor.bind(this)));
481+
}
482+
483+
private async openCodeEditorFromMergeEditor(input: ITextResourceEditorInput, _source: ICodeEditor | null, sideBySide?: boolean | undefined): Promise<ICodeEditor | null> {
484+
const activePane = this._editorService.activeEditorPane;
485+
if (!sideBySide
486+
&& input.options
487+
&& activePane instanceof MergeEditor
488+
&& activePane.getControl()
489+
&& activePane.input instanceof MergeEditorInput
490+
&& isEqual(input.resource, activePane.input.result)
491+
) {
492+
// Special: stay inside the merge editor when it is active and when the input
493+
// targets the result editor of the merge editor.
494+
const targetEditor = <ICodeEditor>activePane.getControl()!;
495+
applyTextEditorOptions(input.options, targetEditor, ScrollType.Smooth);
496+
return targetEditor;
497+
}
498+
499+
// cannot handle this
500+
return null;
501+
}
502+
}
503+
509504
type IMergeEditorViewState = ICodeEditorViewState & {
510505
readonly input1State?: ICodeEditorViewState;
511506
readonly input2State?: ICodeEditorViewState;

src/vs/workbench/services/editor/browser/codeEditorService.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ export class CodeEditorService extends AbstractCodeEditorService {
2424
@IConfigurationService private readonly configurationService: IConfigurationService,
2525
) {
2626
super(themeService);
27+
28+
this.registerCodeEditorOpenHandler(this.doOpenCodeEditor.bind(this));
29+
this.registerCodeEditorOpenHandler(this.doOpenCodeEditorFromDiff.bind(this));
2730
}
2831

2932
getActiveCodeEditor(): ICodeEditor | null {
@@ -44,7 +47,7 @@ export class CodeEditorService extends AbstractCodeEditorService {
4447
return null;
4548
}
4649

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

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

69-
// Open using our normal editor service
70-
return this.doOpenCodeEditor(input, source, sideBySide);
72+
return null;
7173
}
7274

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

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

0 commit comments

Comments
 (0)