Skip to content

Commit 2a72088

Browse files
committed
Introduce a new way to manage editor options
1 parent 1930ce7 commit 2a72088

7 files changed

Lines changed: 246 additions & 16 deletions

File tree

src/vs/editor/browser/viewParts/lineNumbers/lineNumbers.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import 'vs/css!./lineNumbers';
77
import * as platform from 'vs/base/common/platform';
88
import { DynamicViewOverlay } from 'vs/editor/browser/view/dynamicViewOverlay';
9-
import { RenderLineNumbersType } from 'vs/editor/common/config/editorOptions';
9+
import { RenderLineNumbersType, EditorOptionId, EditorOption } from 'vs/editor/common/config/editorOptions';
1010
import { Position } from 'vs/editor/common/core/position';
1111
import { editorActiveLineNumber, editorLineNumbers } from 'vs/editor/common/view/editorColorRegistry';
1212
import { RenderingContext } from 'vs/editor/common/view/renderingContext';
@@ -45,7 +45,7 @@ export class LineNumbersOverlay extends DynamicViewOverlay {
4545
this._lineHeight = config.lineHeight;
4646
this._renderLineNumbers = config.viewInfo.renderLineNumbers;
4747
this._renderCustomLineNumbers = config.viewInfo.renderCustomLineNumbers;
48-
this._renderFinalNewline = config.viewInfo.renderFinalNewline;
48+
this._renderFinalNewline = this._context.configuration.options.get(EditorOptionId.RenderFinalNewline, EditorOption.RenderFinalNewline);
4949
this._lineNumbersLeft = config.layoutInfo.lineNumbersLeft;
5050
this._lineNumbersWidth = config.layoutInfo.lineNumbersWidth;
5151
}

src/vs/editor/common/config/commonEditorConfig.ts

Lines changed: 76 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,58 @@ export interface IEnvConfiguration {
6363

6464
const hasOwnProperty = Object.hasOwnProperty;
6565

66+
export class EditorConfiguration2 {
67+
public static readOptions(options: editorOptions.IEditorOptions): editorOptions.RawEditorOptions {
68+
// console.log(`parseOptions`, options);
69+
const result = new editorOptions.RawEditorOptions();
70+
for (const editorOption of editorOptions.editorOptionsRegistry) {
71+
result._write(editorOption.id, editorOption.read(options));
72+
}
73+
return result;
74+
}
75+
76+
public static mixOptions(a: editorOptions.RawEditorOptions, b: editorOptions.IEditorOptions): editorOptions.RawEditorOptions {
77+
// console.log(`mixOptions`, a, b);
78+
const result = new editorOptions.RawEditorOptions();
79+
for (const editorOption of editorOptions.editorOptionsRegistry) {
80+
result._write(editorOption.id, editorOption.mix(a._read(editorOption.id), editorOption.read(b)));
81+
}
82+
return result;
83+
}
84+
85+
public static validateOptions(options: editorOptions.RawEditorOptions): editorOptions.ValidatedEditorOptions {
86+
// console.log(`validateOptions`, options);
87+
const result = new editorOptions.ValidatedEditorOptions();
88+
for (const editorOption of editorOptions.editorOptionsRegistry) {
89+
result._write(editorOption.id, editorOption.validate(options._read(editorOption.id)));
90+
}
91+
return result;
92+
}
93+
94+
public static computeOptions(options: editorOptions.ValidatedEditorOptions, env: editorOptions.IEnvironmentalOptions): editorOptions.ComputedEditorOptions {
95+
// console.log(`computeOptions`, options, env);
96+
const result = new editorOptions.ComputedEditorOptions();
97+
for (const editorOption of editorOptions.editorOptionsRegistry) {
98+
result._write(editorOption.id, editorOption.compute(options._read(editorOption.id)));
99+
}
100+
return result;
101+
}
102+
103+
public static checkEquals(a: editorOptions.ComputedEditorOptions, b: editorOptions.ComputedEditorOptions): editorOptions.ChangedEditorOptions | null {
104+
// console.log(`equals`, a, b);
105+
const result = new editorOptions.ChangedEditorOptions();
106+
let somethingChanged = false;
107+
for (const editorOption of editorOptions.editorOptionsRegistry) {
108+
const equals = editorOption.equals(a._read(editorOption.id), b._read(editorOption.id));
109+
result._write(editorOption.id, equals);
110+
if (!equals) {
111+
somethingChanged = true;
112+
}
113+
}
114+
return (somethingChanged ? result : null);
115+
}
116+
}
117+
66118
export abstract class CommonEditorConfiguration extends Disposable implements editorCommon.IConfiguration {
67119

68120
public readonly isSimpleWidget: boolean;
@@ -75,6 +127,10 @@ export abstract class CommonEditorConfiguration extends Disposable implements ed
75127
private _onDidChange = this._register(new Emitter<editorOptions.IConfigurationChangedEvent>());
76128
public readonly onDidChange: Event<editorOptions.IConfigurationChangedEvent> = this._onDidChange.event;
77129

130+
private _rawOptions2: editorOptions.RawEditorOptions;
131+
private _validatedOptions2: editorOptions.ValidatedEditorOptions;
132+
public options!: editorOptions.ComputedEditorOptions;
133+
78134
constructor(isSimpleWidget: boolean, options: editorOptions.IEditorOptions) {
79135
super();
80136

@@ -89,6 +145,10 @@ export abstract class CommonEditorConfiguration extends Disposable implements ed
89145
this._rawOptions.parameterHints = objects.mixin({}, this._rawOptions.parameterHints || {});
90146

91147
this._validatedOptions = editorOptions.EditorOptionsValidator.validate(this._rawOptions, EDITOR_DEFAULTS);
148+
149+
this._rawOptions2 = EditorConfiguration2.readOptions(options);
150+
this._validatedOptions2 = EditorConfiguration2.validateOptions(this._rawOptions2);
151+
92152
this._isDominatedByLongLines = false;
93153
this._lineNumbersDigitCount = 1;
94154

@@ -105,24 +165,28 @@ export abstract class CommonEditorConfiguration extends Disposable implements ed
105165

106166
protected _recomputeOptions(): void {
107167
const oldOptions = this.editor;
108-
const newOptions = this._computeInternalOptions();
168+
const oldOptions2 = this.options;
169+
const [newOptions, newOptions2] = this._computeInternalOptions();
170+
171+
const changeEvent = (oldOptions2 ? EditorConfiguration2.checkEquals(oldOptions2, newOptions2) : null);
109172

110-
if (oldOptions && oldOptions.equals(newOptions)) {
173+
if (oldOptions && oldOptions.equals(newOptions) && oldOptions2 && changeEvent === null) {
111174
return;
112175
}
113176

114177
this.editor = newOptions;
178+
this.options = newOptions2;
115179

116180
if (oldOptions) {
117-
this._onDidChange.fire(oldOptions.createChangeEvent(newOptions));
181+
this._onDidChange.fire(oldOptions.createChangeEvent(newOptions, changeEvent));
118182
}
119183
}
120184

121185
public getRawOptions(): editorOptions.IEditorOptions {
122186
return this._rawOptions;
123187
}
124188

125-
private _computeInternalOptions(): editorOptions.InternalEditorOptions {
189+
private _computeInternalOptions(): [editorOptions.InternalEditorOptions, editorOptions.ComputedEditorOptions] {
126190
const opts = this._validatedOptions;
127191
const partialEnv = this._getEnvConfiguration();
128192
const bareFontInfo = BareFontInfo.createFromRawSettings(this._rawOptions, partialEnv.zoomLevel, this.isSimpleWidget);
@@ -138,7 +202,9 @@ export abstract class CommonEditorConfiguration extends Disposable implements ed
138202
tabFocusMode: TabFocus.getTabFocusMode(),
139203
accessibilitySupport: partialEnv.accessibilitySupport
140204
};
141-
return editorOptions.InternalEditorOptionsFactory.createInternalEditorOptions(env, opts);
205+
const r = editorOptions.InternalEditorOptionsFactory.createInternalEditorOptions(env, opts);
206+
const r2 = EditorConfiguration2.computeOptions(this._validatedOptions2, env);
207+
return [r, r2];
142208
}
143209

144210
private static _primitiveArrayEquals(a: any[], b: any[]): boolean {
@@ -189,7 +255,11 @@ export abstract class CommonEditorConfiguration extends Disposable implements ed
189255
return;
190256
}
191257
this._rawOptions = objects.mixin(this._rawOptions, newOptions || {});
258+
this._rawOptions2 = EditorConfiguration2.mixOptions(this._rawOptions2, newOptions);
259+
192260
this._validatedOptions = editorOptions.EditorOptionsValidator.validate(this._rawOptions, EDITOR_DEFAULTS);
261+
this._validatedOptions2 = EditorConfiguration2.validateOptions(this._rawOptions2);
262+
193263
this._recomputeOptions();
194264
}
195265

@@ -275,7 +345,7 @@ const editorConfiguration: IConfigurationNode = {
275345
},
276346
'editor.renderFinalNewline': {
277347
'type': 'boolean',
278-
'default': EDITOR_DEFAULTS.viewInfo.renderFinalNewline,
348+
'default': editorOptions.EditorOption.RenderFinalNewline.defaultValue,
279349
'description': nls.localize('renderFinalNewline', "Render last line number when the file ends with a newline.")
280350
},
281351
'editor.rulers': {

src/vs/editor/common/config/editorOptions.ts

Lines changed: 132 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -997,7 +997,6 @@ export interface InternalEditorViewOptions {
997997
readonly renderLineNumbers: RenderLineNumbersType;
998998
readonly renderCustomLineNumbers: ((lineNumber: number) => string) | null;
999999
readonly cursorSurroundingLines: number;
1000-
readonly renderFinalNewline: boolean;
10011000
readonly selectOnLineNumbers: boolean;
10021001
readonly glyphMargin: boolean;
10031002
readonly revealHorizontalRightPadding: number;
@@ -1230,8 +1229,14 @@ export class InternalEditorOptions {
12301229
/**
12311230
* @internal
12321231
*/
1233-
public createChangeEvent(newOpts: InternalEditorOptions): IConfigurationChangedEvent {
1232+
public createChangeEvent(newOpts: InternalEditorOptions, changeEvent: ChangedEditorOptions | null): IConfigurationChangedEvent {
12341233
return {
1234+
hasChanged: (id: EditorOptionId) => {
1235+
if (!changeEvent) {
1236+
return false;
1237+
}
1238+
return changeEvent.get(id);
1239+
},
12351240
canUseLayerHinting: (this.canUseLayerHinting !== newOpts.canUseLayerHinting),
12361241
pixelRatio: (this.pixelRatio !== newOpts.pixelRatio),
12371242
editorClassName: (this.editorClassName !== newOpts.editorClassName),
@@ -1312,7 +1317,6 @@ export class InternalEditorOptions {
13121317
&& a.renderLineNumbers === b.renderLineNumbers
13131318
&& a.renderCustomLineNumbers === b.renderCustomLineNumbers
13141319
&& a.cursorSurroundingLines === b.cursorSurroundingLines
1315-
&& a.renderFinalNewline === b.renderFinalNewline
13161320
&& a.selectOnLineNumbers === b.selectOnLineNumbers
13171321
&& a.glyphMargin === b.glyphMargin
13181322
&& a.revealHorizontalRightPadding === b.revealHorizontalRightPadding
@@ -1638,6 +1642,7 @@ export interface EditorLayoutInfo {
16381642
* An event describing that the configuration of the editor has changed.
16391643
*/
16401644
export interface IConfigurationChangedEvent {
1645+
hasChanged(id: EditorOptionId): boolean;
16411646
readonly canUseLayerHinting: boolean;
16421647
readonly pixelRatio: boolean;
16431648
readonly editorClassName: boolean;
@@ -2076,7 +2081,6 @@ export class EditorOptionsValidator {
20762081
cursorSurroundingLines: _clampedInt(opts.cursorSurroundingLines, defaults.cursorWidth, 0, Number.MAX_VALUE),
20772082
renderLineNumbers: renderLineNumbers,
20782083
renderCustomLineNumbers: renderCustomLineNumbers,
2079-
renderFinalNewline: _boolean(opts.renderFinalNewline, defaults.renderFinalNewline),
20802084
selectOnLineNumbers: _boolean(opts.selectOnLineNumbers, defaults.selectOnLineNumbers),
20812085
glyphMargin: _boolean(opts.glyphMargin, defaults.glyphMargin),
20822086
revealHorizontalRightPadding: _clampedInt(opts.revealHorizontalRightPadding, defaults.revealHorizontalRightPadding, 0, 1000),
@@ -2198,7 +2202,6 @@ export class InternalEditorOptionsFactory {
21982202
renderLineNumbers: opts.viewInfo.renderLineNumbers,
21992203
renderCustomLineNumbers: opts.viewInfo.renderCustomLineNumbers,
22002204
cursorSurroundingLines: opts.viewInfo.cursorSurroundingLines,
2201-
renderFinalNewline: opts.viewInfo.renderFinalNewline,
22022205
selectOnLineNumbers: opts.viewInfo.selectOnLineNumbers,
22032206
glyphMargin: opts.viewInfo.glyphMargin,
22042207
revealHorizontalRightPadding: opts.viewInfo.revealHorizontalRightPadding,
@@ -2665,7 +2668,6 @@ export const EDITOR_DEFAULTS: IValidatedEditorOptions = {
26652668
renderLineNumbers: RenderLineNumbersType.On,
26662669
renderCustomLineNumbers: null,
26672670
cursorSurroundingLines: 0,
2668-
renderFinalNewline: true,
26692671
selectOnLineNumbers: true,
26702672
glyphMargin: true,
26712673
revealHorizontalRightPadding: 30,
@@ -2770,3 +2772,127 @@ export const EDITOR_DEFAULTS: IValidatedEditorOptions = {
27702772
codeActionsOnSaveTimeout: 750
27712773
},
27722774
};
2775+
2776+
export interface IRawEditorOptionsBag {
2777+
[key: string]: any;
2778+
}
2779+
2780+
/**
2781+
* @internal
2782+
*/
2783+
export class RawEditorOptions {
2784+
private readonly _values: any[] = [];
2785+
public _read<T>(id: EditorOptionId): T | undefined {
2786+
return this._values[id];
2787+
}
2788+
public _write<T>(id: EditorOptionId, value: T | undefined): void {
2789+
this._values[id] = value;
2790+
}
2791+
}
2792+
2793+
/**
2794+
* @internal
2795+
*/
2796+
export class ValidatedEditorOptions {
2797+
private readonly _values: any[] = [];
2798+
public _read<T>(option: EditorOptionId): T {
2799+
return this._values[option];
2800+
}
2801+
public _write<T>(option: EditorOptionId, value: T): void {
2802+
this._values[option] = value;
2803+
}
2804+
}
2805+
2806+
export interface IComputedEditorOptions {
2807+
get<T1, T2, T3>(id: EditorOptionId, option: IEditorOption<T1, T2, T3>): T3;
2808+
}
2809+
2810+
/**
2811+
* @internal
2812+
*/
2813+
export class ComputedEditorOptions {
2814+
private readonly _values: any[] = [];
2815+
public _read<T>(id: EditorOptionId): T {
2816+
return this._values[id];
2817+
}
2818+
public get<T1, T2, T3>(id: EditorOptionId, option: IEditorOption<T1, T2, T3>): T3 {
2819+
return this._values[id];
2820+
}
2821+
public _write<T>(id: EditorOptionId, value: T): void {
2822+
this._values[id] = value;
2823+
}
2824+
}
2825+
2826+
/**
2827+
* @internal
2828+
*/
2829+
export class ChangedEditorOptions {
2830+
private readonly _values: boolean[] = [];
2831+
public get(id: EditorOptionId): boolean {
2832+
return this._values[id];
2833+
}
2834+
public _write(id: EditorOptionId, value: boolean): void {
2835+
this._values[id] = value;
2836+
}
2837+
}
2838+
2839+
interface IEditorOption<T1, T2 = T1, T3 = T2> {
2840+
readonly id: EditorOptionId;
2841+
readonly name: string;
2842+
readonly defaultValue: T1;
2843+
read(options: IRawEditorOptionsBag): T1 | undefined;
2844+
mix(a: T1 | undefined, b: T1 | undefined): T1 | undefined;
2845+
validate(input: T1 | undefined): T2;
2846+
compute(value: T2): T3;
2847+
equals(a: T3, b: T3): boolean;
2848+
}
2849+
2850+
class BooleanEditorOption implements IEditorOption<boolean> {
2851+
public readonly id: EditorOptionId;
2852+
public readonly name: string;
2853+
public readonly defaultValue: boolean;
2854+
2855+
constructor(id: EditorOptionId, name: string, defaultValue: boolean) {
2856+
this.id = id;
2857+
this.name = name;
2858+
this.defaultValue = defaultValue;
2859+
}
2860+
2861+
public read(options: IRawEditorOptionsBag): boolean | undefined {
2862+
return options[this.name];
2863+
}
2864+
2865+
public mix(a: boolean | undefined, b: boolean | undefined): boolean | undefined {
2866+
return (typeof b !== 'undefined' ? b : a);
2867+
}
2868+
2869+
public validate(input: boolean | undefined): boolean {
2870+
return _boolean(input, this.defaultValue);
2871+
}
2872+
2873+
public compute(value: boolean): boolean {
2874+
return value;
2875+
}
2876+
2877+
public equals(a: boolean, b: boolean): boolean {
2878+
return (a === b);
2879+
}
2880+
}
2881+
2882+
/**
2883+
* @internal
2884+
*/
2885+
export const editorOptionsRegistry: IEditorOption<any>[] = [];
2886+
2887+
function registerEditorOption<T1, T2, T3>(option: IEditorOption<T1, T2, T3>): IEditorOption<T1, T2, T3> {
2888+
editorOptionsRegistry[option.id] = option;
2889+
return option;
2890+
}
2891+
2892+
export const enum EditorOptionId {
2893+
RenderFinalNewline,
2894+
}
2895+
2896+
export const EditorOption = {
2897+
RenderFinalNewline: registerEditorOption(new BooleanEditorOption(EditorOptionId.RenderFinalNewline, 'renderFinalNewline', true))
2898+
};

src/vs/editor/common/editorCommon.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ export interface IConfiguration extends IDisposable {
152152
onDidChange(listener: (e: editorOptions.IConfigurationChangedEvent) => void): IDisposable;
153153

154154
readonly editor: editorOptions.InternalEditorOptions;
155+
readonly options: editorOptions.IComputedEditorOptions;
155156

156157
setMaxLineNumber(maxLineNumber: number): void;
157158
updateOptions(newOptions: editorOptions.IEditorOptions): void;

src/vs/editor/common/standalone/standaloneEnums.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,10 @@ export enum RenderLineNumbersType {
430430
Custom = 4
431431
}
432432

433+
export enum EditorOptionId {
434+
RenderFinalNewline = 0
435+
}
436+
433437
/**
434438
* A positioning preference for rendering content widgets.
435439
*/

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,7 @@ export function createMonacoEditorAPI(): typeof monaco.editor {
369369
RenderMinimap: standaloneEnums.RenderMinimap,
370370
ScrollType: standaloneEnums.ScrollType,
371371
RenderLineNumbersType: standaloneEnums.RenderLineNumbersType,
372+
EditorOptionId: standaloneEnums.EditorOptionId,
372373

373374
// classes
374375
InternalEditorOptions: <any>editorOptions.InternalEditorOptions,
@@ -378,7 +379,8 @@ export function createMonacoEditorAPI(): typeof monaco.editor {
378379
FindMatch: <any>FindMatch,
379380

380381
// vars
381-
EditorType: editorCommon.EditorType
382+
EditorType: editorCommon.EditorType,
383+
EditorOption: <any>editorOptions.EditorOption,
382384

383385
};
384386
}

0 commit comments

Comments
 (0)