commonEditorConfig.ts 33.4 KB
Newer Older
E
Erich Gamma 已提交
1 2 3 4 5 6
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/
'use strict';

A
Alex Dima 已提交
7
import * as nls from 'vs/nls';
J
Johannes Rieken 已提交
8 9
import Event, { Emitter } from 'vs/base/common/event';
import { Disposable } from 'vs/base/common/lifecycle';
A
Alex Dima 已提交
10
import * as objects from 'vs/base/common/objects';
A
Alex Dima 已提交
11
import * as platform from 'vs/base/common/platform';
J
Johannes Rieken 已提交
12 13 14
import { Extensions, IConfigurationRegistry, IConfigurationNode } from 'vs/platform/configuration/common/configurationRegistry';
import { Registry } from 'vs/platform/platform';
import { DefaultConfig, DEFAULT_INDENTATION, DEFAULT_TRIM_AUTO_WHITESPACE, GOLDEN_LINE_HEIGHT_RATIO } from 'vs/editor/common/config/defaultConfig';
A
Alex Dima 已提交
15
import * as editorCommon from 'vs/editor/common/editorCommon';
J
Johannes Rieken 已提交
16 17
import { EditorLayoutProvider } from 'vs/editor/common/viewLayout/editorLayoutProvider';
import { ScrollbarVisibility } from 'vs/base/common/scrollable';
E
Erich Gamma 已提交
18

19 20 21 22 23 24 25 26 27 28 29
// TODO@Alex: investigate if it is better to stick to 31 bits (see smi = SMall Integer)
// See https://thibaultlaurens.github.io/javascript/2013/04/29/how-the-v8-engine-works/#tagged-values
/**
 * MAX_INT that fits in 32 bits
 */
const MAX_SAFE_INT = 0x7fffffff;
/**
 * MIN_INT that fits in 32 bits
 */
const MIN_SAFE_INT = -0x80000000;

30
export interface IEditorZoom {
J
Johannes Rieken 已提交
31
	onDidChangeZoomLevel: Event<number>;
32
	getZoomLevel(): number;
J
Johannes Rieken 已提交
33
	setZoomLevel(zoomLevel: number): void;
34 35 36 37 38 39 40
}

export const EditorZoom: IEditorZoom = new class {

	private _zoomLevel: number = 0;

	private _onDidChangeZoomLevel: Emitter<number> = new Emitter<number>();
J
Johannes Rieken 已提交
41
	public onDidChangeZoomLevel: Event<number> = this._onDidChangeZoomLevel.event;
42 43 44 45 46

	public getZoomLevel(): number {
		return this._zoomLevel;
	}

J
Johannes Rieken 已提交
47
	public setZoomLevel(zoomLevel: number): void {
48 49 50 51 52 53 54 55 56
		zoomLevel = Math.min(Math.max(-9, zoomLevel), 9);
		if (this._zoomLevel === zoomLevel) {
			return;
		}

		this._zoomLevel = zoomLevel;
		this._onDidChangeZoomLevel.fire(this._zoomLevel);
	}
};
E
Erich Gamma 已提交
57

58 59 60 61 62 63 64
/**
 * Control what pressing Tab does.
 * If it is false, pressing Tab or Shift-Tab will be handled by the editor.
 * If it is true, pressing Tab or Shift-Tab will move the browser focus.
 * Defaults to false.
 */
export interface ITabFocus {
J
Johannes Rieken 已提交
65
	onDidChangeTabFocus: Event<boolean>;
66
	getTabFocusMode(): boolean;
J
Johannes Rieken 已提交
67
	setTabFocusMode(tabFocusMode: boolean): void;
68 69 70 71 72 73
}

export const TabFocus: ITabFocus = new class {
	private _tabFocus: boolean = false;

	private _onDidChangeTabFocus: Emitter<boolean> = new Emitter<boolean>();
J
Johannes Rieken 已提交
74
	public onDidChangeTabFocus: Event<boolean> = this._onDidChangeTabFocus.event;
75 76 77 78 79

	public getTabFocusMode(): boolean {
		return this._tabFocus;
	}

J
Johannes Rieken 已提交
80
	public setTabFocusMode(tabFocusMode: boolean): void {
81 82 83 84 85 86 87 88 89
		if (this._tabFocus === tabFocusMode) {
			return;
		}

		this._tabFocus = tabFocusMode;
		this._onDidChangeTabFocus.fire(this._tabFocus);
	}
};

90 91 92 93 94 95 96 97 98 99 100 101 102
/**
 * Experimental screen reader support toggle
 */
export class GlobalScreenReaderNVDA {

	private static _value = false;
	private static _onChange = new Emitter<boolean>();
	public static onChange: Event<boolean> = GlobalScreenReaderNVDA._onChange.event;

	public static getValue(): boolean {
		return this._value;
	}

J
Johannes Rieken 已提交
103
	public static setValue(value: boolean): void {
104 105 106 107 108 109 110 111
		if (this._value === value) {
			return;
		}
		this._value = value;
		this._onChange.fire(this._value);
	}
}

E
Erich Gamma 已提交
112 113
export class ConfigurationWithDefaults {

J
Johannes Rieken 已提交
114
	private _editor: editorCommon.IEditorOptions;
E
Erich Gamma 已提交
115

J
Johannes Rieken 已提交
116
	constructor(options: editorCommon.IEditorOptions) {
A
Alex Dima 已提交
117
		this._editor = <editorCommon.IEditorOptions>objects.clone(DefaultConfig.editor);
E
Erich Gamma 已提交
118 119 120 121

		this._mergeOptionsIn(options);
	}

A
Alex Dima 已提交
122
	public getEditorOptions(): editorCommon.IEditorOptions {
E
Erich Gamma 已提交
123 124 125
		return this._editor;
	}

J
Johannes Rieken 已提交
126
	private _mergeOptionsIn(newOptions: editorCommon.IEditorOptions): void {
A
Alex Dima 已提交
127
		this._editor = objects.mixin(this._editor, newOptions || {});
E
Erich Gamma 已提交
128 129
	}

J
Johannes Rieken 已提交
130
	public updateOptions(newOptions: editorCommon.IEditorOptions): void {
E
Erich Gamma 已提交
131 132 133 134 135 136 137 138 139 140 141
		// Apply new options
		this._mergeOptionsIn(newOptions);
	}
}

class InternalEditorOptionsHelper {

	constructor() {
	}

	public static createInternalEditorOptions(
J
Johannes Rieken 已提交
142 143
		outerWidth: number, outerHeight: number,
		opts: editorCommon.IEditorOptions,
144
		fontInfo: editorCommon.FontInfo,
J
Johannes Rieken 已提交
145 146
		editorClassName: string,
		isDominatedByLongLines: boolean,
147
		maxLineNumber: number,
148
		canUseTranslate3d: boolean
149
	): editorCommon.InternalEditorOptions {
E
Erich Gamma 已提交
150 151

		let wrappingColumn = toInteger(opts.wrappingColumn, -1);
152
		let wordWrap = toBoolean(opts.wordWrap);
E
Erich Gamma 已提交
153

J
Johannes Rieken 已提交
154
		let stopRenderingLineAfter: number;
E
Erich Gamma 已提交
155 156 157 158 159 160 161 162 163 164 165 166 167 168
		if (typeof opts.stopRenderingLineAfter !== 'undefined') {
			stopRenderingLineAfter = toInteger(opts.stopRenderingLineAfter, -1);
		} else if (wrappingColumn >= 0) {
			stopRenderingLineAfter = -1;
		} else {
			stopRenderingLineAfter = 10000;
		}

		let mouseWheelScrollSensitivity = toFloat(opts.mouseWheelScrollSensitivity, 1);
		let scrollbar = this._sanitizeScrollbarOpts(opts.scrollbar, mouseWheelScrollSensitivity);

		let glyphMargin = toBoolean(opts.glyphMargin);
		let lineNumbers = opts.lineNumbers;
		let lineNumbersMinChars = toInteger(opts.lineNumbersMinChars, 1);
169 170 171 172 173 174 175 176

		let lineDecorationsWidth: number;
		if (typeof opts.lineDecorationsWidth === 'string' && /^\d+(\.\d+)?ch$/.test(opts.lineDecorationsWidth)) {
			let multiple = parseFloat(opts.lineDecorationsWidth.substr(0, opts.lineDecorationsWidth.length - 2));
			lineDecorationsWidth = multiple * fontInfo.typicalHalfwidthCharacterWidth;
		} else {
			lineDecorationsWidth = toInteger(opts.lineDecorationsWidth, 0);
		}
M
Martin Aeschlimann 已提交
177
		if (opts.folding) {
178
			lineDecorationsWidth += 16;
M
Martin Aeschlimann 已提交
179
		}
180
		let renderLineNumbers: boolean;
J
Johannes Rieken 已提交
181
		let renderCustomLineNumbers: (lineNumber: number) => string;
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208
		let renderRelativeLineNumbers: boolean;

		// Compatibility with old true or false values
		if (<any>lineNumbers === true) {
			lineNumbers = 'on';
		} else if (<any>lineNumbers === false) {
			lineNumbers = 'off';
		}

		if (typeof lineNumbers === 'function') {
			renderLineNumbers = true;
			renderCustomLineNumbers = lineNumbers;
			renderRelativeLineNumbers = false;
		} else if (lineNumbers === 'relative') {
			renderLineNumbers = true;
			renderCustomLineNumbers = null;
			renderRelativeLineNumbers = true;
		} else if (lineNumbers === 'on') {
			renderLineNumbers = true;
			renderCustomLineNumbers = null;
			renderRelativeLineNumbers = false;
		} else {
			renderLineNumbers = false;
			renderCustomLineNumbers = null;
			renderRelativeLineNumbers = false;
		}

E
Erich Gamma 已提交
209 210 211 212
		let layoutInfo = EditorLayoutProvider.compute({
			outerWidth: outerWidth,
			outerHeight: outerHeight,
			showGlyphMargin: glyphMargin,
213
			lineHeight: fontInfo.lineHeight,
214
			showLineNumbers: renderLineNumbers,
E
Erich Gamma 已提交
215 216
			lineNumbersMinChars: lineNumbersMinChars,
			lineDecorationsWidth: lineDecorationsWidth,
217
			maxDigitWidth: fontInfo.maxDigitWidth,
218
			maxLineNumber: maxLineNumber,
E
Erich Gamma 已提交
219 220 221 222 223 224 225 226 227 228 229
			verticalScrollbarWidth: scrollbar.verticalScrollbarSize,
			horizontalScrollbarHeight: scrollbar.horizontalScrollbarSize,
			scrollbarArrowSize: scrollbar.arrowSize,
			verticalScrollbarHasArrows: scrollbar.verticalHasArrows
		});

		if (isDominatedByLongLines && wrappingColumn > 0) {
			// Force viewport width wrapping if model is dominated by long lines
			wrappingColumn = 0;
		}

230
		let bareWrappingInfo: { isViewportWrapping: boolean; wrappingColumn: number; };
E
Erich Gamma 已提交
231 232
		if (wrappingColumn === 0) {
			// If viewport width wrapping is enabled
233
			bareWrappingInfo = {
E
Erich Gamma 已提交
234
				isViewportWrapping: true,
235
				wrappingColumn: Math.max(1, Math.floor((layoutInfo.contentWidth - layoutInfo.verticalScrollbarWidth) / fontInfo.typicalHalfwidthCharacterWidth))
E
Erich Gamma 已提交
236
			};
237
		} else if (wrappingColumn > 0 && wordWrap === true) {
238 239 240 241 242
			// Enable smart viewport wrapping
			bareWrappingInfo = {
				isViewportWrapping: true,
				wrappingColumn: Math.min(wrappingColumn, Math.floor((layoutInfo.contentWidth - layoutInfo.verticalScrollbarWidth) / fontInfo.typicalHalfwidthCharacterWidth))
			};
E
Erich Gamma 已提交
243 244
		} else if (wrappingColumn > 0) {
			// Wrapping is enabled
245
			bareWrappingInfo = {
E
Erich Gamma 已提交
246 247 248 249
				isViewportWrapping: false,
				wrappingColumn: wrappingColumn
			};
		} else {
250
			bareWrappingInfo = {
E
Erich Gamma 已提交
251 252 253 254
				isViewportWrapping: false,
				wrappingColumn: -1
			};
		}
255 256 257 258 259 260 261 262
		let wrappingInfo = new editorCommon.EditorWrappingInfo({
			isViewportWrapping: bareWrappingInfo.isViewportWrapping,
			wrappingColumn: bareWrappingInfo.wrappingColumn,
			wrappingIndent: wrappingIndentFromString(opts.wrappingIndent),
			wordWrapBreakBeforeCharacters: String(opts.wordWrapBreakBeforeCharacters),
			wordWrapBreakAfterCharacters: String(opts.wordWrapBreakAfterCharacters),
			wordWrapBreakObtrusiveCharacters: String(opts.wordWrapBreakObtrusiveCharacters),
		});
E
Erich Gamma 已提交
263

264 265
		let readOnly = toBoolean(opts.readOnly);

266
		let tabFocusMode = TabFocus.getTabFocusMode();
267 268 269 270
		if (readOnly) {
			tabFocusMode = true;
		}

271

272 273 274 275 276 277 278 279
		let renderWhitespace = opts.renderWhitespace;
		// Compatibility with old true or false values
		if (<any>renderWhitespace === true) {
			renderWhitespace = 'boundary';
		} else if (<any>renderWhitespace === false) {
			renderWhitespace = 'none';
		}

280
		let viewInfo = new editorCommon.InternalEditorViewOptions({
281
			theme: opts.theme,
282
			canUseTranslate3d: canUseTranslate3d,
283 284 285
			experimentalScreenReader: toBoolean(opts.experimentalScreenReader),
			rulers: toSortedIntegerArray(opts.rulers),
			ariaLabel: String(opts.ariaLabel),
286 287 288
			renderLineNumbers: renderLineNumbers,
			renderCustomLineNumbers: renderCustomLineNumbers,
			renderRelativeLineNumbers: renderRelativeLineNumbers,
E
Erich Gamma 已提交
289 290 291 292 293
			selectOnLineNumbers: toBoolean(opts.selectOnLineNumbers),
			glyphMargin: glyphMargin,
			revealHorizontalRightPadding: toInteger(opts.revealHorizontalRightPadding, 0),
			roundedSelection: toBoolean(opts.roundedSelection),
			overviewRulerLanes: toInteger(opts.overviewRulerLanes, 0, 3),
294
			cursorBlinking: cursorBlinkingStyleFromString(opts.cursorBlinking),
295
			mouseWheelZoom: toBoolean(opts.mouseWheelZoom),
A
Alex Dima 已提交
296
			cursorStyle: cursorStyleFromString(opts.cursorStyle),
E
Erich Gamma 已提交
297 298
			hideCursorInOverviewRuler: toBoolean(opts.hideCursorInOverviewRuler),
			scrollBeyondLastLine: toBoolean(opts.scrollBeyondLastLine),
299 300
			editorClassName: editorClassName,
			stopRenderingLineAfter: stopRenderingLineAfter,
301
			renderWhitespace: renderWhitespace,
302
			renderControlCharacters: toBoolean(opts.renderControlCharacters),
303
			renderIndentGuides: toBoolean(opts.renderIndentGuides),
304
			renderLineHighlight: toBoolean(opts.renderLineHighlight),
305
			scrollbar: scrollbar,
J
Joao Moreno 已提交
306
			fixedOverflowWidgets: toBoolean(opts.fixedOverflowWidgets)
307 308
		});

A
Alex Dima 已提交
309
		let contribInfo = new editorCommon.EditorContribOptions({
310
			selectionClipboard: toBoolean(opts.selectionClipboard),
E
Erich Gamma 已提交
311 312 313 314
			hover: toBoolean(opts.hover),
			contextmenu: toBoolean(opts.contextmenu),
			quickSuggestions: toBoolean(opts.quickSuggestions),
			quickSuggestionsDelay: toInteger(opts.quickSuggestionsDelay),
J
Joao Moreno 已提交
315
			parameterHints: toBoolean(opts.parameterHints),
E
Erich Gamma 已提交
316 317 318
			iconsInSuggestions: toBoolean(opts.iconsInSuggestions),
			formatOnType: toBoolean(opts.formatOnType),
			suggestOnTriggerCharacters: toBoolean(opts.suggestOnTriggerCharacters),
319
			acceptSuggestionOnEnter: toBoolean(opts.acceptSuggestionOnEnter),
320
			snippetSuggestions: opts.snippetSuggestions,
321
			emptySelectionClipboard: opts.emptySelectionClipboard,
322
			tabCompletion: opts.tabCompletion,
323
			wordBasedSuggestions: opts.wordBasedSuggestions,
J
Joao Moreno 已提交
324 325
			suggestFontSize: opts.suggestFontSize,
			suggestLineHeight: opts.suggestLineHeight,
E
Erich Gamma 已提交
326
			selectionHighlight: toBoolean(opts.selectionHighlight),
327
			codeLens: opts.referenceInfos && opts.codeLens,
M
Martin Aeschlimann 已提交
328
			folding: toBoolean(opts.folding),
A
Alex Dima 已提交
329 330
		});

331 332 333 334
		return new editorCommon.InternalEditorOptions({
			lineHeight: fontInfo.lineHeight, // todo -> duplicated in styling
			readOnly: readOnly,
			wordSeparators: String(opts.wordSeparators),
A
Alex Dima 已提交
335
			autoClosingBrackets: toBoolean(opts.autoClosingBrackets),
336
			useTabStops: toBoolean(opts.useTabStops),
337
			tabFocusMode: tabFocusMode,
E
Erich Gamma 已提交
338
			layoutInfo: layoutInfo,
339
			fontInfo: fontInfo,
340
			viewInfo: viewInfo,
E
Erich Gamma 已提交
341
			wrappingInfo: wrappingInfo,
A
Alex Dima 已提交
342
			contribInfo: contribInfo,
343
		});
E
Erich Gamma 已提交
344 345
	}

J
Johannes Rieken 已提交
346
	private static _sanitizeScrollbarOpts(raw: editorCommon.IEditorScrollbarOptions, mouseWheelScrollSensitivity: number): editorCommon.InternalEditorScrollbarOptions {
A
Alex Dima 已提交
347

A
Alex Dima 已提交
348
		let visibilityFromString = (visibility: string) => {
A
Alex Dima 已提交
349 350 351 352 353 354 355 356 357 358
			switch (visibility) {
				case 'hidden':
					return ScrollbarVisibility.Hidden;
				case 'visible':
					return ScrollbarVisibility.Visible;
				default:
					return ScrollbarVisibility.Auto;
			}
		};

A
Alex Dima 已提交
359 360
		let horizontalScrollbarSize = toIntegerWithDefault(raw.horizontalScrollbarSize, 10);
		let verticalScrollbarSize = toIntegerWithDefault(raw.verticalScrollbarSize, 14);
A
Alex Dima 已提交
361 362 363
		return new editorCommon.InternalEditorScrollbarOptions({
			vertical: visibilityFromString(raw.vertical),
			horizontal: visibilityFromString(raw.horizontal),
E
Erich Gamma 已提交
364 365 366 367 368 369 370 371 372 373 374 375 376 377 378

			arrowSize: toIntegerWithDefault(raw.arrowSize, 11),
			useShadows: toBooleanWithDefault(raw.useShadows, true),

			verticalHasArrows: toBooleanWithDefault(raw.verticalHasArrows, false),
			horizontalHasArrows: toBooleanWithDefault(raw.horizontalHasArrows, false),

			horizontalScrollbarSize: horizontalScrollbarSize,
			horizontalSliderSize: toIntegerWithDefault(raw.horizontalSliderSize, horizontalScrollbarSize),

			verticalScrollbarSize: verticalScrollbarSize,
			verticalSliderSize: toIntegerWithDefault(raw.verticalSliderSize, verticalScrollbarSize),

			handleMouseWheel: toBooleanWithDefault(raw.handleMouseWheel, true),
			mouseWheelScrollSensitivity: mouseWheelScrollSensitivity
A
Alex Dima 已提交
379
		});
E
Erich Gamma 已提交
380 381 382
	}
}

J
Johannes Rieken 已提交
383
function toBoolean(value: any): boolean {
E
Erich Gamma 已提交
384 385 386
	return value === 'false' ? false : Boolean(value);
}

J
Johannes Rieken 已提交
387
function toBooleanWithDefault(value: any, defaultValue: boolean): boolean {
E
Erich Gamma 已提交
388 389 390 391 392 393 394
	if (typeof value === 'undefined') {
		return defaultValue;
	}
	return toBoolean(value);
}

function toFloat(source: any, defaultValue: number): number {
A
Alex Dima 已提交
395
	let r = parseFloat(source);
E
Erich Gamma 已提交
396 397 398 399 400 401
	if (isNaN(r)) {
		r = defaultValue;
	}
	return r;
}

J
Johannes Rieken 已提交
402
function toInteger(source: any, minimum: number = MIN_SAFE_INT, maximum: number = MAX_SAFE_INT): number {
A
Alex Dima 已提交
403
	let r = parseInt(source, 10);
E
Erich Gamma 已提交
404 405 406
	if (isNaN(r)) {
		r = 0;
	}
407 408 409
	r = Math.max(minimum, r);
	r = Math.min(maximum, r);
	return r | 0;
E
Erich Gamma 已提交
410 411
}

J
Johannes Rieken 已提交
412
function toSortedIntegerArray(source: any): number[] {
413 414 415 416 417 418 419 420 421
	if (!Array.isArray(source)) {
		return [];
	}
	let arrSource = <any[]>source;
	let r = arrSource.map(el => toInteger(el));
	r.sort();
	return r;
}

J
Johannes Rieken 已提交
422
function wrappingIndentFromString(wrappingIndent: string): editorCommon.WrappingIndent {
A
Alex Dima 已提交
423 424 425 426 427 428 429 430 431
	if (wrappingIndent === 'indent') {
		return editorCommon.WrappingIndent.Indent;
	} else if (wrappingIndent === 'same') {
		return editorCommon.WrappingIndent.Same;
	} else {
		return editorCommon.WrappingIndent.None;
	}
}

J
Johannes Rieken 已提交
432
function cursorStyleFromString(cursorStyle: string): editorCommon.TextEditorCursorStyle {
A
Alex Dima 已提交
433 434 435 436 437 438 439 440 441 442
	if (cursorStyle === 'line') {
		return editorCommon.TextEditorCursorStyle.Line;
	} else if (cursorStyle === 'block') {
		return editorCommon.TextEditorCursorStyle.Block;
	} else if (cursorStyle === 'underline') {
		return editorCommon.TextEditorCursorStyle.Underline;
	}
	return editorCommon.TextEditorCursorStyle.Line;
}

443
function cursorBlinkingStyleFromString(cursorBlinkingStyle: string): editorCommon.TextEditorCursorBlinkingStyle {
444 445 446 447 448 449 450 451 452 453 454 455
	switch (cursorBlinkingStyle) {
		case 'blink':
			return editorCommon.TextEditorCursorBlinkingStyle.Blink;
		case 'smooth':
			return editorCommon.TextEditorCursorBlinkingStyle.Smooth;
		case 'phase':
			return editorCommon.TextEditorCursorBlinkingStyle.Phase;
		case 'expand':
			return editorCommon.TextEditorCursorBlinkingStyle.Expand;
		case 'visible': // maintain compatibility
		case 'solid':
			return editorCommon.TextEditorCursorBlinkingStyle.Solid;
456 457 458 459
	}
	return editorCommon.TextEditorCursorBlinkingStyle.Blink;
}

J
Johannes Rieken 已提交
460
function toIntegerWithDefault(source: any, defaultValue: number): number {
E
Erich Gamma 已提交
461 462 463 464 465 466
	if (typeof source === 'undefined') {
		return defaultValue;
	}
	return toInteger(source);
}

467 468
export interface IElementSizeObserver {
	startObserving(): void;
J
Johannes Rieken 已提交
469
	observe(dimension?: editorCommon.IDimension): void;
470 471 472 473 474
	dispose(): void;
	getWidth(): number;
	getHeight(): number;
}

A
Alex Dima 已提交
475
export abstract class CommonEditorConfiguration extends Disposable implements editorCommon.IConfiguration {
E
Erich Gamma 已提交
476

J
Johannes Rieken 已提交
477 478
	public editor: editorCommon.InternalEditorOptions;
	public editorClone: editorCommon.InternalEditorOptions;
E
Erich Gamma 已提交
479

J
Johannes Rieken 已提交
480
	protected _configWithDefaults: ConfigurationWithDefaults;
481
	protected _elementSizeObserver: IElementSizeObserver;
J
Johannes Rieken 已提交
482 483
	private _isDominatedByLongLines: boolean;
	private _maxLineNumber: number;
E
Erich Gamma 已提交
484

A
Alex Dima 已提交
485 486
	private _onDidChange = this._register(new Emitter<editorCommon.IConfigurationChangedEvent>());
	public onDidChange: Event<editorCommon.IConfigurationChangedEvent> = this._onDidChange.event;
A
Alex Dima 已提交
487

J
Johannes Rieken 已提交
488
	constructor(options: editorCommon.IEditorOptions, elementSizeObserver: IElementSizeObserver = null) {
A
Alex Dima 已提交
489
		super();
E
Erich Gamma 已提交
490
		this._configWithDefaults = new ConfigurationWithDefaults(options);
491
		this._elementSizeObserver = elementSizeObserver;
E
Erich Gamma 已提交
492
		this._isDominatedByLongLines = false;
493
		this._maxLineNumber = 1;
E
Erich Gamma 已提交
494
		this.editor = this._computeInternalOptions();
495
		this.editorClone = this.editor.clone();
496
		this._register(EditorZoom.onDidChangeZoomLevel(_ => this._recomputeOptions()));
497
		this._register(TabFocus.onDidChangeTabFocus(_ => this._recomputeOptions()));
E
Erich Gamma 已提交
498 499 500 501 502 503 504
	}

	public dispose(): void {
		super.dispose();
	}

	protected _recomputeOptions(): void {
505 506
		this._setOptions(this._computeInternalOptions());
	}
507

J
Johannes Rieken 已提交
508
	private _setOptions(newOptions: editorCommon.InternalEditorOptions): void {
509 510
		if (this.editor && this.editor.equals(newOptions)) {
			return;
E
Erich Gamma 已提交
511 512
		}

513 514 515 516
		let changeEvent = this.editor.createChangeEvent(newOptions);
		this.editor = newOptions;
		this.editorClone = this.editor.clone();
		this._onDidChange.fire(changeEvent);
E
Erich Gamma 已提交
517 518
	}

A
Alex Dima 已提交
519
	public getRawOptions(): editorCommon.IEditorOptions {
E
Erich Gamma 已提交
520 521 522
		return this._configWithDefaults.getEditorOptions();
	}

523
	private _computeInternalOptions(): editorCommon.InternalEditorOptions {
E
Erich Gamma 已提交
524 525
		let opts = this._configWithDefaults.getEditorOptions();

526
		let editorClassName = this._getEditorClassName(opts.theme, toBoolean(opts.fontLigatures));
527
		let fontFamily = String(opts.fontFamily) || DefaultConfig.editor.fontFamily;
528
		let fontWeight = String(opts.fontWeight) || DefaultConfig.editor.fontWeight;
529 530 531
		let fontSize = toFloat(opts.fontSize, DefaultConfig.editor.fontSize);
		fontSize = Math.max(0, fontSize);
		fontSize = Math.min(100, fontSize);
D
Daniel Imms 已提交
532 533 534
		if (fontSize === 0) {
			fontSize = DefaultConfig.editor.fontSize;
		}
E
Erich Gamma 已提交
535

536 537
		let lineHeight = toInteger(opts.lineHeight, 0, 150);
		if (lineHeight === 0) {
538
			lineHeight = Math.round(GOLDEN_LINE_HEIGHT_RATIO * fontSize);
E
Erich Gamma 已提交
539
		}
540
		let editorZoomLevelMultiplier = 1 + (EditorZoom.getZoomLevel() * 0.1);
541 542
		fontSize *= editorZoomLevelMultiplier;
		lineHeight *= editorZoomLevelMultiplier;
E
Erich Gamma 已提交
543

544 545 546 547 548 549
		let disableTranslate3d = toBoolean(opts.disableTranslate3d);
		let canUseTranslate3d = this._getCanUseTranslate3d();
		if (disableTranslate3d) {
			canUseTranslate3d = false;
		}

550
		return InternalEditorOptionsHelper.createInternalEditorOptions(
E
Erich Gamma 已提交
551 552 553
			this.getOuterWidth(),
			this.getOuterHeight(),
			opts,
A
Alex Dima 已提交
554 555
			this.readConfiguration(new editorCommon.BareFontInfo({
				fontFamily: fontFamily,
556
				fontWeight: fontWeight,
A
Alex Dima 已提交
557 558 559
				fontSize: fontSize,
				lineHeight: lineHeight
			})),
560
			editorClassName,
E
Erich Gamma 已提交
561
			this._isDominatedByLongLines,
562
			this._maxLineNumber,
563
			canUseTranslate3d
E
Erich Gamma 已提交
564 565 566
		);
	}

J
Johannes Rieken 已提交
567
	public updateOptions(newOptions: editorCommon.IEditorOptions): void {
E
Erich Gamma 已提交
568 569 570 571
		this._configWithDefaults.updateOptions(newOptions);
		this._recomputeOptions();
	}

J
Johannes Rieken 已提交
572
	public setIsDominatedByLongLines(isDominatedByLongLines: boolean): void {
E
Erich Gamma 已提交
573 574 575 576
		this._isDominatedByLongLines = isDominatedByLongLines;
		this._recomputeOptions();
	}

J
Johannes Rieken 已提交
577
	public setMaxLineNumber(maxLineNumber: number): void {
578
		this._maxLineNumber = maxLineNumber;
E
Erich Gamma 已提交
579 580 581
		this._recomputeOptions();
	}

J
Johannes Rieken 已提交
582
	protected abstract _getEditorClassName(theme: string, fontLigatures: boolean): string;
E
Erich Gamma 已提交
583

584
	protected abstract getOuterWidth(): number;
E
Erich Gamma 已提交
585

586
	protected abstract getOuterHeight(): number;
E
Erich Gamma 已提交
587

588 589
	protected abstract _getCanUseTranslate3d(): boolean;

590
	protected abstract readConfiguration(styling: editorCommon.BareFontInfo): editorCommon.FontInfo;
E
Erich Gamma 已提交
591 592 593 594 595 596 597 598 599 600 601 602
}

/**
 * Helper to update Monaco Editor Settings from configurations service.
 */
export class EditorConfiguration {
	public static EDITOR_SECTION = 'editor';
	public static DIFF_EDITOR_SECTION = 'diffEditor';

	/**
	 * Ask the provided configuration service to apply its configuration to the provided editor.
	 */
603
	public static apply(config: any, editor: editorCommon.IEditor): void {
E
Erich Gamma 已提交
604 605 606 607
		if (!config) {
			return;
		}

608 609 610 611 612 613
		// Editor Settings (Code Editor, Diff, Terminal)
		if (editor && typeof editor.updateOptions === 'function') {
			let type = editor.getEditorType();
			if (type !== editorCommon.EditorType.ICodeEditor && type !== editorCommon.EditorType.IDiffEditor) {
				return;
			}
E
Erich Gamma 已提交
614

615 616 617 618 619 620 621 622
			let editorConfig = config[EditorConfiguration.EDITOR_SECTION];
			if (type === editorCommon.EditorType.IDiffEditor) {
				let diffEditorConfig = config[EditorConfiguration.DIFF_EDITOR_SECTION];
				if (diffEditorConfig) {
					if (!editorConfig) {
						editorConfig = diffEditorConfig;
					} else {
						editorConfig = objects.mixin(editorConfig, diffEditorConfig);
E
Erich Gamma 已提交
623 624
					}
				}
625
			}
E
Erich Gamma 已提交
626

627 628 629
			if (editorConfig) {
				delete editorConfig.readOnly; // Prevent someone from making editor readonly
				editor.updateOptions(editorConfig);
E
Erich Gamma 已提交
630 631 632 633 634
			}
		}
	}
}

A
Alex Dima 已提交
635
let configurationRegistry = <IConfigurationRegistry>Registry.as(Extensions.Configuration);
J
Johannes Rieken 已提交
636
let editorConfiguration: IConfigurationNode = {
E
Erich Gamma 已提交
637 638 639
	'id': 'editor',
	'order': 5,
	'type': 'object',
640
	'title': nls.localize('editorConfigurationTitle', "Editor"),
J
Johannes Rieken 已提交
641 642
	'properties': {
		'editor.fontFamily': {
E
Erich Gamma 已提交
643 644 645 646
			'type': 'string',
			'default': DefaultConfig.editor.fontFamily,
			'description': nls.localize('fontFamily', "Controls the font family.")
		},
J
Johannes Rieken 已提交
647
		'editor.fontWeight': {
648
			'type': 'string',
649
			'enum': ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900'],
650 651 652
			'default': DefaultConfig.editor.fontWeight,
			'description': nls.localize('fontWeight', "Controls the font weight.")
		},
J
Johannes Rieken 已提交
653
		'editor.fontSize': {
E
Erich Gamma 已提交
654 655
			'type': 'number',
			'default': DefaultConfig.editor.fontSize,
656
			'description': nls.localize('fontSize', "Controls the font size in pixels.")
E
Erich Gamma 已提交
657
		},
J
Johannes Rieken 已提交
658
		'editor.lineHeight': {
E
Erich Gamma 已提交
659 660
			'type': 'number',
			'default': DefaultConfig.editor.lineHeight,
661
			'description': nls.localize('lineHeight', "Controls the line height. Use 0 to compute the lineHeight from the fontSize.")
E
Erich Gamma 已提交
662
		},
J
Johannes Rieken 已提交
663
		'editor.lineNumbers': {
664 665
			'type': 'string',
			'enum': ['off', 'on', 'relative'],
E
Erich Gamma 已提交
666
			'default': DefaultConfig.editor.lineNumbers,
667
			'description': nls.localize('lineNumbers', "Controls the display of line numbers. Possible values are 'on', 'off', and 'relative'. 'relative' shows the line count from the current cursor position.")
E
Erich Gamma 已提交
668
		},
J
Johannes Rieken 已提交
669
		'editor.rulers': {
670 671 672 673 674 675 676
			'type': 'array',
			'items': {
				'type': 'number'
			},
			'default': DefaultConfig.editor.rulers,
			'description': nls.localize('rulers', "Columns at which to show vertical rulers")
		},
J
Johannes Rieken 已提交
677
		'editor.wordSeparators': {
A
Alex Dima 已提交
678 679 680 681
			'type': 'string',
			'default': DefaultConfig.editor.wordSeparators,
			'description': nls.localize('wordSeparators', "Characters that will be used as word separators when doing word related navigations or operations")
		},
J
Johannes Rieken 已提交
682
		'editor.tabSize': {
683 684
			'type': 'number',
			'default': DEFAULT_INDENTATION.tabSize,
E
Erich Gamma 已提交
685
			'minimum': 1,
686
			'description': nls.localize('tabSize', "The number of spaces a tab is equal to. This setting is overriden based on the file contents when `editor.detectIndentation` is on."),
687
			'errorMessage': nls.localize('tabSize.errorMessage', "Expected 'number'. Note that the value \"auto\" has been replaced by the `editor.detectIndentation` setting.")
E
Erich Gamma 已提交
688
		},
J
Johannes Rieken 已提交
689
		'editor.insertSpaces': {
690 691
			'type': 'boolean',
			'default': DEFAULT_INDENTATION.insertSpaces,
692
			'description': nls.localize('insertSpaces', "Insert spaces when pressing Tab. This setting is overriden based on the file contents when `editor.detectIndentation` is on."),
693
			'errorMessage': nls.localize('insertSpaces.errorMessage', "Expected 'boolean'. Note that the value \"auto\" has been replaced by the `editor.detectIndentation` setting.")
E
Erich Gamma 已提交
694
		},
J
Johannes Rieken 已提交
695
		'editor.detectIndentation': {
696 697
			'type': 'boolean',
			'default': DEFAULT_INDENTATION.detectIndentation,
A
Alex Dima 已提交
698
			'description': nls.localize('detectIndentation', "When opening a file, `editor.tabSize` and `editor.insertSpaces` will be detected based on the file contents.")
699
		},
J
Johannes Rieken 已提交
700
		'editor.roundedSelection': {
E
Erich Gamma 已提交
701 702 703 704
			'type': 'boolean',
			'default': DefaultConfig.editor.roundedSelection,
			'description': nls.localize('roundedSelection', "Controls if selections have rounded corners")
		},
J
Johannes Rieken 已提交
705
		'editor.scrollBeyondLastLine': {
E
Erich Gamma 已提交
706 707 708 709
			'type': 'boolean',
			'default': DefaultConfig.editor.scrollBeyondLastLine,
			'description': nls.localize('scrollBeyondLastLine', "Controls if the editor will scroll beyond the last line")
		},
J
Johannes Rieken 已提交
710
		'editor.wrappingColumn': {
E
Erich Gamma 已提交
711 712 713
			'type': 'integer',
			'default': DefaultConfig.editor.wrappingColumn,
			'minimum': -1,
714
			'description': nls.localize('wrappingColumn', "Controls after how many characters the editor will wrap to the next line. Setting this to 0 turns on viewport width wrapping (word wrapping). Setting this to -1 forces the editor to never wrap.")
E
Erich Gamma 已提交
715
		},
J
Johannes Rieken 已提交
716
		'editor.wordWrap': {
717
			'type': 'boolean',
718 719
			'default': DefaultConfig.editor.wordWrap,
			'description': nls.localize('wordWrap', "Controls if lines should wrap. The lines will wrap at min(editor.wrappingColumn, viewportWidthInColumns).")
720
		},
J
Johannes Rieken 已提交
721
		'editor.wrappingIndent': {
E
Erich Gamma 已提交
722 723 724 725 726
			'type': 'string',
			'enum': ['none', 'same', 'indent'],
			'default': DefaultConfig.editor.wrappingIndent,
			'description': nls.localize('wrappingIndent', "Controls the indentation of wrapped lines. Can be one of 'none', 'same' or 'indent'.")
		},
J
Johannes Rieken 已提交
727
		'editor.mouseWheelScrollSensitivity': {
E
Erich Gamma 已提交
728 729 730 731
			'type': 'number',
			'default': DefaultConfig.editor.mouseWheelScrollSensitivity,
			'description': nls.localize('mouseWheelScrollSensitivity', "A multiplier to be used on the `deltaX` and `deltaY` of mouse wheel scroll events")
		},
J
Johannes Rieken 已提交
732
		'editor.quickSuggestions': {
E
Erich Gamma 已提交
733 734 735 736
			'type': 'boolean',
			'default': DefaultConfig.editor.quickSuggestions,
			'description': nls.localize('quickSuggestions', "Controls if quick suggestions should show up or not while typing")
		},
J
Johannes Rieken 已提交
737
		'editor.quickSuggestionsDelay': {
E
Erich Gamma 已提交
738 739 740 741 742
			'type': 'integer',
			'default': DefaultConfig.editor.quickSuggestionsDelay,
			'minimum': 0,
			'description': nls.localize('quickSuggestionsDelay', "Controls the delay in ms after which quick suggestions will show up")
		},
J
Johannes Rieken 已提交
743
		'editor.parameterHints': {
J
Joao Moreno 已提交
744 745 746 747
			'type': 'boolean',
			'default': DefaultConfig.editor.parameterHints,
			'description': nls.localize('parameterHints', "Enables parameter hints")
		},
J
Johannes Rieken 已提交
748
		'editor.autoClosingBrackets': {
E
Erich Gamma 已提交
749 750 751 752
			'type': 'boolean',
			'default': DefaultConfig.editor.autoClosingBrackets,
			'description': nls.localize('autoClosingBrackets', "Controls if the editor should automatically close brackets after opening them")
		},
J
Johannes Rieken 已提交
753
		'editor.formatOnType': {
E
Erich Gamma 已提交
754 755 756 757
			'type': 'boolean',
			'default': DefaultConfig.editor.formatOnType,
			'description': nls.localize('formatOnType', "Controls if the editor should automatically format the line after typing")
		},
J
Johannes Rieken 已提交
758
		'editor.suggestOnTriggerCharacters': {
E
Erich Gamma 已提交
759 760 761 762
			'type': 'boolean',
			'default': DefaultConfig.editor.suggestOnTriggerCharacters,
			'description': nls.localize('suggestOnTriggerCharacters', "Controls if suggestions should automatically show up when typing trigger characters")
		},
J
Johannes Rieken 已提交
763
		'editor.acceptSuggestionOnEnter': {
764 765 766 767
			'type': 'boolean',
			'default': DefaultConfig.editor.acceptSuggestionOnEnter,
			'description': nls.localize('acceptSuggestionOnEnter', "Controls if suggestions should be accepted 'Enter' - in addition to 'Tab'. Helps to avoid ambiguity between inserting new lines or accepting suggestions.")
		},
768
		'editor.snippetSuggestions': {
769
			'type': 'string',
770 771 772
			'enum': ['top', 'bottom', 'inline', 'none'],
			'default': DefaultConfig.editor.snippetSuggestions,
			'description': nls.localize('snippetSuggestions', "Controls whether snippets are shown with other suggestions and how they are sorted.")
773
		},
774 775 776 777 778
		'editor.emptySelectionClipboard': {
			'type': 'boolean',
			'default': DefaultConfig.editor.emptySelectionClipboard,
			'description': nls.localize('emptySelectionClipboard', "Controls whether copying without a selection copies the current line.")
		},
779 780 781 782 783
		'editor.wordBasedSuggestions': {
			'type': 'boolean',
			'default': DefaultConfig.editor.wordBasedSuggestions,
			'description': nls.localize('wordBasedSuggestions', "Enable word based suggestions.")
		},
J
Johannes Rieken 已提交
784
		'editor.suggestFontSize': {
J
Joao Moreno 已提交
785 786 787 788 789
			'type': 'integer',
			'default': 0,
			'minimum': 0,
			'description': nls.localize('suggestFontSize', "Font size for the suggest widget")
		},
J
Johannes Rieken 已提交
790
		'editor.suggestLineHeight': {
J
Joao Moreno 已提交
791 792 793 794 795
			'type': 'integer',
			'default': 0,
			'minimum': 0,
			'description': nls.localize('suggestLineHeight', "Line height for the suggest widget")
		},
796 797 798
		'editor.tabCompletion': {
			'type': 'boolean',
			'default': DefaultConfig.editor.tabCompletion,
799
			'description': nls.localize('tabCompletion', "Insert snippets when their prefix matches. Works best when 'quickSuggestions' aren't enabled.")
800
		},
J
Johannes Rieken 已提交
801
		'editor.selectionHighlight': {
E
Erich Gamma 已提交
802 803 804 805
			'type': 'boolean',
			'default': DefaultConfig.editor.selectionHighlight,
			'description': nls.localize('selectionHighlight', "Controls whether the editor should highlight similar matches to the selection")
		},
J
Johannes Rieken 已提交
806
		'editor.overviewRulerLanes': {
E
Erich Gamma 已提交
807 808 809 810
			'type': 'integer',
			'default': 3,
			'description': nls.localize('overviewRulerLanes', "Controls the number of decorations that can show up at the same position in the overview ruler")
		},
J
Johannes Rieken 已提交
811
		'editor.cursorBlinking': {
812
			'type': 'string',
813
			'enum': ['blink', 'smooth', 'phase', 'expand', 'solid'],
814
			'default': DefaultConfig.editor.cursorBlinking,
815
			'description': nls.localize('cursorBlinking', "Control the cursor animation style, possible values are 'blink', 'smooth', 'phase', 'expand' and 'solid'")
816
		},
817 818 819 820 821
		'editor.mouseWheelZoom': {
			'type': 'boolean',
			'default': DefaultConfig.editor.mouseWheelZoom,
			'description': nls.localize('mouseWheelZoom', "Zoom the font of the editor when using mouse wheel and holding Ctrl")
		},
J
Johannes Rieken 已提交
822
		'editor.cursorStyle': {
M
markrendle 已提交
823
			'type': 'string',
824
			'enum': ['block', 'line', 'underline'],
M
markrendle 已提交
825
			'default': DefaultConfig.editor.cursorStyle,
826
			'description': nls.localize('cursorStyle', "Controls the cursor style, accepted values are 'block', 'line' and 'underline'")
M
markrendle 已提交
827
		},
J
Johannes Rieken 已提交
828
		'editor.fontLigatures': {
829 830 831 832
			'type': 'boolean',
			'default': DefaultConfig.editor.fontLigatures,
			'description': nls.localize('fontLigatures', "Enables font ligatures")
		},
J
Johannes Rieken 已提交
833
		'editor.hideCursorInOverviewRuler': {
E
Erich Gamma 已提交
834 835 836 837 838
			'type': 'boolean',
			'default': DefaultConfig.editor.hideCursorInOverviewRuler,
			'description': nls.localize('hideCursorInOverviewRuler', "Controls if the cursor should be hidden in the overview ruler.")
		},
		'editor.renderWhitespace': {
839 840
			'type': 'string',
			'enum': ['none', 'boundary', 'all'],
E
Erich Gamma 已提交
841
			default: DefaultConfig.editor.renderWhitespace,
842
			description: nls.localize('renderWhitespace', "Controls how the editor should render whitespace characters, posibilties are 'none', 'boundary', and 'all'. The 'boundary' option does not render single spaces between words.")
E
Erich Gamma 已提交
843
		},
844 845 846 847 848
		'editor.renderControlCharacters': {
			'type': 'boolean',
			default: DefaultConfig.editor.renderControlCharacters,
			description: nls.localize('renderControlCharacters', "Controls whether the editor should render control characters")
		},
849 850 851 852 853
		'editor.renderIndentGuides': {
			'type': 'boolean',
			default: DefaultConfig.editor.renderIndentGuides,
			description: nls.localize('renderIndentGuides', "Controls whether the editor should render indent guides")
		},
854
		'editor.renderLineHighlight': {
855
			'type': 'boolean',
856
			default: DefaultConfig.editor.renderLineHighlight,
857
			description: nls.localize('renderLineHighlight', "Controls whether the editor should render the current line highlight")
858
		},
J
Johannes Rieken 已提交
859
		'editor.codeLens': {
E
Erich Gamma 已提交
860
			'type': 'boolean',
861 862
			'default': DefaultConfig.editor.codeLens,
			'description': nls.localize('codeLens', "Controls if the editor shows code lenses")
E
Erich Gamma 已提交
863
		},
J
Johannes Rieken 已提交
864
		'editor.folding': {
M
Martin Aeschlimann 已提交
865 866
			'type': 'boolean',
			'default': DefaultConfig.editor.folding,
867
			'description': nls.localize('folding', "Controls whether the editor has code folding enabled")
M
Martin Aeschlimann 已提交
868
		},
I
isidor 已提交
869 870 871 872 873
		'editor.glyphMargin': {
			'type': 'boolean',
			'default': DefaultConfig.editor.glyphMargin,
			'description': nls.localize('glyphMargin', "Controls whether the editor should render the vertical glyph margin. Glyph margin is mostly used for debugging.")
		},
J
Johannes Rieken 已提交
874
		'editor.useTabStops': {
875 876 877 878
			'type': 'boolean',
			'default': DefaultConfig.editor.useTabStops,
			'description': nls.localize('useTabStops', "Inserting and deleting whitespace follows tab stops")
		},
J
Johannes Rieken 已提交
879
		'editor.trimAutoWhitespace': {
880
			'type': 'boolean',
881
			'default': DEFAULT_TRIM_AUTO_WHITESPACE,
882 883
			'description': nls.localize('trimAutoWhitespace', "Remove trailing auto inserted whitespace")
		},
J
Johannes Rieken 已提交
884
		'editor.stablePeek': {
885
			'type': 'boolean',
886
			'default': false,
887
			'description': nls.localize('stablePeek', "Keep peek editors open even when double clicking their content or when hitting Escape.")
888
		},
J
Johannes Rieken 已提交
889
		'diffEditor.renderSideBySide': {
E
Erich Gamma 已提交
890 891 892 893
			'type': 'boolean',
			'default': true,
			'description': nls.localize('sideBySide', "Controls if the diff editor shows the diff side by side or inline")
		},
J
Johannes Rieken 已提交
894
		'diffEditor.ignoreTrimWhitespace': {
E
Erich Gamma 已提交
895 896 897 898 899
			'type': 'boolean',
			'default': true,
			'description': nls.localize('ignoreTrimWhitespace', "Controls if the diff editor shows changes in leading or trailing whitespace as diffs")
		}
	}
A
Alex Dima 已提交
900 901 902 903 904 905 906 907 908 909
};

if (platform.isLinux) {
	editorConfiguration['properties']['editor.selectionClipboard'] = {
		'type': 'boolean',
		'default': DefaultConfig.editor.selectionClipboard,
		'description': nls.localize('selectionClipboard', "Controls if the Linux primary clipboard should be supported.")
	};
}

910
configurationRegistry.registerConfiguration(editorConfiguration);