Skip to content

Commit 8a894eb

Browse files
authored
Adopt idle instantiation in editor contributions and use explicit syntax in the ones which need to be eager (microsoft#166966)
* Adopt idle instantiation in editor contributions and use explicit syntax in the ones which need to be eager * Make sure all editor contributions are eventually instantiated * Introduce multiple values for `EditorContributionInstantiation` and adopt them
1 parent c7bdf0a commit 8a894eb

File tree

64 files changed

+391
-228
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

64 files changed

+391
-228
lines changed

src/vs/editor/browser/editorExtensions.ts

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,18 +30,37 @@ export type ServicesAccessor = InstantiationServicesAccessor;
3030
export type IEditorContributionCtor = IConstructorSignature<IEditorContribution, [ICodeEditor]>;
3131
export type IDiffEditorContributionCtor = IConstructorSignature<IDiffEditorContribution, [IDiffEditor]>;
3232

33-
export enum EditorContributionInstantiation {
33+
export const enum EditorContributionInstantiation {
3434
/**
3535
* The contribution is created eagerly when the {@linkcode ICodeEditor} is instantiated.
36+
* Only Eager contributions can participate in saving or restoring of view state.
3637
*/
3738
Eager,
3839

3940
/**
40-
* The contribution is created on idle (or when explicitly requested).
41-
*
42-
* Idle contributions cannot participate in saving or restoring of view states.
41+
* The contribution is created at the latest 50ms after the first render after attaching a text model.
42+
* If the contribution is explicitly requested via `getContribution`, it will be instantiated sooner.
43+
* If there is idle time available, it will be instantiated sooner.
4344
*/
44-
Idle,
45+
AfterFirstRender,
46+
47+
/**
48+
* The contribution is created before the editor emits events produced by user interaction (mouse events, keyboard events).
49+
* If the contribution is explicitly requested via `getContribution`, it will be instantiated sooner.
50+
* If there is idle time available, it will be instantiated sooner.
51+
*/
52+
BeforeFirstInteraction,
53+
54+
/**
55+
* The contribution is created when there is idle time available, at the latest 5000ms after the editor creation.
56+
* If the contribution is explicitly requested via `getContribution`, it will be instantiated sooner.
57+
*/
58+
Eventually,
59+
60+
/**
61+
* The contribution is created only when explicitly requested via `getContribution`.
62+
*/
63+
Lazy,
4564
}
4665

4766
export interface IEditorContributionDescription {

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
*--------------------------------------------------------------------------------------------*/
55

66
import { IMarkerDecorationsService } from 'vs/editor/common/services/markerDecorations';
7-
import { registerEditorContribution } from 'vs/editor/browser/editorExtensions';
7+
import { EditorContributionInstantiation, registerEditorContribution } from 'vs/editor/browser/editorExtensions';
88
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
99
import { IEditorContribution } from 'vs/editor/common/editorCommon';
1010

@@ -23,4 +23,4 @@ export class MarkerDecorationsContribution implements IEditorContribution {
2323
}
2424
}
2525

26-
registerEditorContribution(MarkerDecorationsContribution.ID, MarkerDecorationsContribution);
26+
registerEditorContribution(MarkerDecorationsContribution.ID, MarkerDecorationsContribution, EditorContributionInstantiation.Eager); // eager because it instantiates IMarkerDecorationsService which is responsible for rendering squiggles
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
import { runWhenIdle } from 'vs/base/common/async';
7+
import { onUnexpectedError } from 'vs/base/common/errors';
8+
import { Disposable, DisposableMap } from 'vs/base/common/lifecycle';
9+
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
10+
import { EditorContributionInstantiation, IEditorContributionDescription } from 'vs/editor/browser/editorExtensions';
11+
import { IEditorContribution } from 'vs/editor/common/editorCommon';
12+
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
13+
14+
export class CodeEditorContributions extends Disposable {
15+
16+
private _editor: ICodeEditor | null = null;
17+
private _instantiationService: IInstantiationService | null = null;
18+
19+
/**
20+
* Contains all instantiated contributions.
21+
*/
22+
private readonly _instances = this._register(new DisposableMap<string, IEditorContribution>());
23+
/**
24+
* Contains contributions which are not yet instantiated.
25+
*/
26+
private readonly _pending = new Map<string, IEditorContributionDescription>();
27+
/**
28+
* Tracks which instantiation kinds are still left in `_pending`.
29+
*/
30+
private readonly _finishedInstantiation: boolean[] = [];
31+
32+
constructor(
33+
34+
) {
35+
super();
36+
37+
this._finishedInstantiation[EditorContributionInstantiation.Eager] = false;
38+
this._finishedInstantiation[EditorContributionInstantiation.AfterFirstRender] = false;
39+
this._finishedInstantiation[EditorContributionInstantiation.BeforeFirstInteraction] = false;
40+
this._finishedInstantiation[EditorContributionInstantiation.Eventually] = false;
41+
}
42+
43+
public initialize(editor: ICodeEditor, contributions: IEditorContributionDescription[], instantiationService: IInstantiationService) {
44+
this._editor = editor;
45+
this._instantiationService = instantiationService;
46+
47+
for (const desc of contributions) {
48+
if (this._pending.has(desc.id)) {
49+
onUnexpectedError(new Error(`Cannot have two contributions with the same id ${desc.id}`));
50+
continue;
51+
}
52+
this._pending.set(desc.id, desc);
53+
}
54+
55+
this._instantiateSome(EditorContributionInstantiation.Eager);
56+
57+
// AfterFirstRender
58+
// - these extensions will be instantiated at the latest 50ms after the first render.
59+
// - but if there is idle time, we will instantiate them sooner.
60+
this._register(runWhenIdle(() => {
61+
this._instantiateSome(EditorContributionInstantiation.AfterFirstRender);
62+
}));
63+
64+
// BeforeFirstInteraction
65+
// - these extensions will be instantiated at the latest before a mouse or a keyboard event.
66+
// - but if there is idle time, we will instantiate them sooner.
67+
this._register(runWhenIdle(() => {
68+
this._instantiateSome(EditorContributionInstantiation.BeforeFirstInteraction);
69+
}));
70+
71+
// Eventually
72+
// - these extensions will only be instantiated when there is idle time.
73+
// - since there is no guarantee that there will ever be idle time, we set a timeout of 5s here.
74+
this._register(runWhenIdle(() => {
75+
this._instantiateSome(EditorContributionInstantiation.Eventually);
76+
}, 5000));
77+
}
78+
79+
public saveViewState(): { [key: string]: any } {
80+
const contributionsState: { [key: string]: any } = {};
81+
for (const [id, contribution] of this._instances) {
82+
if (typeof contribution.saveViewState === 'function') {
83+
contributionsState[id] = contribution.saveViewState();
84+
}
85+
}
86+
return contributionsState;
87+
}
88+
89+
public restoreViewState(contributionsState: { [key: string]: any }): void {
90+
for (const [id, contribution] of this._instances) {
91+
if (typeof contribution.restoreViewState === 'function') {
92+
contribution.restoreViewState(contributionsState[id]);
93+
}
94+
}
95+
}
96+
97+
public get(id: string): IEditorContribution | null {
98+
this._instantiateById(id);
99+
return this._instances.get(id) || null;
100+
}
101+
102+
/**
103+
* used by tests
104+
*/
105+
public set(id: string, value: IEditorContribution) {
106+
this._instances.set(id, value);
107+
}
108+
109+
public onBeforeInteractionEvent(): void {
110+
// this method is called very often by the editor!
111+
this._instantiateSome(EditorContributionInstantiation.BeforeFirstInteraction);
112+
}
113+
114+
public onAfterModelAttached(): void {
115+
this._register(runWhenIdle(() => {
116+
this._instantiateSome(EditorContributionInstantiation.AfterFirstRender);
117+
}, 50));
118+
}
119+
120+
private _instantiateSome(instantiation: EditorContributionInstantiation): void {
121+
if (this._finishedInstantiation[instantiation]) {
122+
// already done with this instantiation!
123+
return;
124+
}
125+
this._finishedInstantiation[instantiation] = true;
126+
127+
const contribs = this._findPendingContributionsByInstantiation(instantiation);
128+
for (const contrib of contribs) {
129+
this._instantiateById(contrib.id);
130+
}
131+
}
132+
133+
private _findPendingContributionsByInstantiation(instantiation: EditorContributionInstantiation): readonly IEditorContributionDescription[] {
134+
const result: IEditorContributionDescription[] = [];
135+
for (const [, desc] of this._pending) {
136+
if (desc.instantiation === instantiation) {
137+
result.push(desc);
138+
}
139+
}
140+
return result;
141+
}
142+
143+
private _instantiateById(id: string): void {
144+
const desc = this._pending.get(id);
145+
if (!desc) {
146+
return;
147+
}
148+
149+
this._pending.delete(id);
150+
151+
if (!this._instantiationService || !this._editor) {
152+
throw new Error(`Cannot instantiate contributions before being initialized!`);
153+
}
154+
155+
try {
156+
const instance = this._instantiationService.createInstance(desc.ctor, this._editor);
157+
this._instances.set(desc.id, instance);
158+
if (typeof instance.restoreViewState === 'function' && desc.instantiation !== EditorContributionInstantiation.Eager) {
159+
console.warn(`Editor contribution '${desc.id}' should be eager instantiated because it uses saveViewState / restoreViewState.`);
160+
}
161+
} catch (err) {
162+
onUnexpectedError(err);
163+
}
164+
}
165+
}

0 commit comments

Comments
 (0)