61867731d96eeecc06e737d2a3242810d22836a46b1df2eb7d949f2515d03796d16ca622de39fc2a028d68200202281c0c923f0b120c0586722785acecf31d 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  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. var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
  6. var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
  7. if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
  8. else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
  9. return c > 3 && r && Object.defineProperty(target, key, r), r;
  10. };
  11. var __param = (this && this.__param) || function (paramIndex, decorator) {
  12. return function (target, key) { decorator(target, key, paramIndex); }
  13. };
  14. import * as browser from '../../../base/browser/browser.js';
  15. import * as arrays from '../../../base/common/arrays.js';
  16. import { Emitter } from '../../../base/common/event.js';
  17. import { Disposable } from '../../../base/common/lifecycle.js';
  18. import * as objects from '../../../base/common/objects.js';
  19. import * as platform from '../../../base/common/platform.js';
  20. import { ElementSizeObserver } from './elementSizeObserver.js';
  21. import { FontMeasurements } from './fontMeasurements.js';
  22. import { migrateOptions } from './migrateOptions.js';
  23. import { TabFocus } from './tabFocus.js';
  24. import { ComputeOptionsMemory, ConfigurationChangedEvent, editorOptionsRegistry } from '../../common/config/editorOptions.js';
  25. import { EditorZoom } from '../../common/config/editorZoom.js';
  26. import { BareFontInfo } from '../../common/config/fontInfo.js';
  27. import { IAccessibilityService } from '../../../platform/accessibility/common/accessibility.js';
  28. let EditorConfiguration = class EditorConfiguration extends Disposable {
  29. constructor(isSimpleWidget, options, container, _accessibilityService) {
  30. super();
  31. this._accessibilityService = _accessibilityService;
  32. this._onDidChange = this._register(new Emitter());
  33. this.onDidChange = this._onDidChange.event;
  34. this._onDidChangeFast = this._register(new Emitter());
  35. this.onDidChangeFast = this._onDidChangeFast.event;
  36. this._isDominatedByLongLines = false;
  37. this._viewLineCount = 1;
  38. this._lineNumbersDigitCount = 1;
  39. this._reservedHeight = 0;
  40. this._computeOptionsMemory = new ComputeOptionsMemory();
  41. this.isSimpleWidget = isSimpleWidget;
  42. this._containerObserver = this._register(new ElementSizeObserver(container, options.dimension));
  43. this._rawOptions = deepCloneAndMigrateOptions(options);
  44. this._validatedOptions = EditorOptionsUtil.validateOptions(this._rawOptions);
  45. this.options = this._computeOptions();
  46. if (this.options.get(10 /* EditorOption.automaticLayout */)) {
  47. this._containerObserver.startObserving();
  48. }
  49. this._register(EditorZoom.onDidChangeZoomLevel(() => this._recomputeOptions()));
  50. this._register(TabFocus.onDidChangeTabFocus(() => this._recomputeOptions()));
  51. this._register(this._containerObserver.onDidChange(() => this._recomputeOptions()));
  52. this._register(FontMeasurements.onDidChange(() => this._recomputeOptions()));
  53. this._register(browser.PixelRatio.onDidChange(() => this._recomputeOptions()));
  54. this._register(this._accessibilityService.onDidChangeScreenReaderOptimized(() => this._recomputeOptions()));
  55. }
  56. _recomputeOptions() {
  57. const newOptions = this._computeOptions();
  58. const changeEvent = EditorOptionsUtil.checkEquals(this.options, newOptions);
  59. if (changeEvent === null) {
  60. // nothing changed!
  61. return;
  62. }
  63. this.options = newOptions;
  64. this._onDidChangeFast.fire(changeEvent);
  65. this._onDidChange.fire(changeEvent);
  66. }
  67. _computeOptions() {
  68. const partialEnv = this._readEnvConfiguration();
  69. const bareFontInfo = BareFontInfo.createFromValidatedSettings(this._validatedOptions, partialEnv.pixelRatio, this.isSimpleWidget);
  70. const fontInfo = this._readFontInfo(bareFontInfo);
  71. const env = {
  72. memory: this._computeOptionsMemory,
  73. outerWidth: partialEnv.outerWidth,
  74. outerHeight: partialEnv.outerHeight - this._reservedHeight,
  75. fontInfo: fontInfo,
  76. extraEditorClassName: partialEnv.extraEditorClassName,
  77. isDominatedByLongLines: this._isDominatedByLongLines,
  78. viewLineCount: this._viewLineCount,
  79. lineNumbersDigitCount: this._lineNumbersDigitCount,
  80. emptySelectionClipboard: partialEnv.emptySelectionClipboard,
  81. pixelRatio: partialEnv.pixelRatio,
  82. tabFocusMode: TabFocus.getTabFocusMode(),
  83. accessibilitySupport: partialEnv.accessibilitySupport
  84. };
  85. return EditorOptionsUtil.computeOptions(this._validatedOptions, env);
  86. }
  87. _readEnvConfiguration() {
  88. return {
  89. extraEditorClassName: getExtraEditorClassName(),
  90. outerWidth: this._containerObserver.getWidth(),
  91. outerHeight: this._containerObserver.getHeight(),
  92. emptySelectionClipboard: browser.isWebKit || browser.isFirefox,
  93. pixelRatio: browser.PixelRatio.value,
  94. accessibilitySupport: (this._accessibilityService.isScreenReaderOptimized()
  95. ? 2 /* AccessibilitySupport.Enabled */
  96. : this._accessibilityService.getAccessibilitySupport())
  97. };
  98. }
  99. _readFontInfo(bareFontInfo) {
  100. return FontMeasurements.readFontInfo(bareFontInfo);
  101. }
  102. getRawOptions() {
  103. return this._rawOptions;
  104. }
  105. updateOptions(_newOptions) {
  106. const newOptions = deepCloneAndMigrateOptions(_newOptions);
  107. const didChange = EditorOptionsUtil.applyUpdate(this._rawOptions, newOptions);
  108. if (!didChange) {
  109. return;
  110. }
  111. this._validatedOptions = EditorOptionsUtil.validateOptions(this._rawOptions);
  112. this._recomputeOptions();
  113. }
  114. observeContainer(dimension) {
  115. this._containerObserver.observe(dimension);
  116. }
  117. setIsDominatedByLongLines(isDominatedByLongLines) {
  118. if (this._isDominatedByLongLines === isDominatedByLongLines) {
  119. return;
  120. }
  121. this._isDominatedByLongLines = isDominatedByLongLines;
  122. this._recomputeOptions();
  123. }
  124. setModelLineCount(modelLineCount) {
  125. const lineNumbersDigitCount = digitCount(modelLineCount);
  126. if (this._lineNumbersDigitCount === lineNumbersDigitCount) {
  127. return;
  128. }
  129. this._lineNumbersDigitCount = lineNumbersDigitCount;
  130. this._recomputeOptions();
  131. }
  132. setViewLineCount(viewLineCount) {
  133. if (this._viewLineCount === viewLineCount) {
  134. return;
  135. }
  136. this._viewLineCount = viewLineCount;
  137. this._recomputeOptions();
  138. }
  139. setReservedHeight(reservedHeight) {
  140. if (this._reservedHeight === reservedHeight) {
  141. return;
  142. }
  143. this._reservedHeight = reservedHeight;
  144. this._recomputeOptions();
  145. }
  146. };
  147. EditorConfiguration = __decorate([
  148. __param(3, IAccessibilityService)
  149. ], EditorConfiguration);
  150. export { EditorConfiguration };
  151. function digitCount(n) {
  152. let r = 0;
  153. while (n) {
  154. n = Math.floor(n / 10);
  155. r++;
  156. }
  157. return r ? r : 1;
  158. }
  159. function getExtraEditorClassName() {
  160. let extra = '';
  161. if (!browser.isSafari && !browser.isWebkitWebView) {
  162. // Use user-select: none in all browsers except Safari and native macOS WebView
  163. extra += 'no-user-select ';
  164. }
  165. if (browser.isSafari) {
  166. // See https://github.com/microsoft/vscode/issues/108822
  167. extra += 'no-minimap-shadow ';
  168. extra += 'enable-user-select ';
  169. }
  170. if (platform.isMacintosh) {
  171. extra += 'mac ';
  172. }
  173. return extra;
  174. }
  175. class ValidatedEditorOptions {
  176. constructor() {
  177. this._values = [];
  178. }
  179. _read(option) {
  180. return this._values[option];
  181. }
  182. get(id) {
  183. return this._values[id];
  184. }
  185. _write(option, value) {
  186. this._values[option] = value;
  187. }
  188. }
  189. export class ComputedEditorOptions {
  190. constructor() {
  191. this._values = [];
  192. }
  193. _read(id) {
  194. if (id >= this._values.length) {
  195. throw new Error('Cannot read uninitialized value');
  196. }
  197. return this._values[id];
  198. }
  199. get(id) {
  200. return this._read(id);
  201. }
  202. _write(id, value) {
  203. this._values[id] = value;
  204. }
  205. }
  206. class EditorOptionsUtil {
  207. static validateOptions(options) {
  208. const result = new ValidatedEditorOptions();
  209. for (const editorOption of editorOptionsRegistry) {
  210. const value = (editorOption.name === '_never_' ? undefined : options[editorOption.name]);
  211. result._write(editorOption.id, editorOption.validate(value));
  212. }
  213. return result;
  214. }
  215. static computeOptions(options, env) {
  216. const result = new ComputedEditorOptions();
  217. for (const editorOption of editorOptionsRegistry) {
  218. result._write(editorOption.id, editorOption.compute(env, result, options._read(editorOption.id)));
  219. }
  220. return result;
  221. }
  222. static _deepEquals(a, b) {
  223. if (typeof a !== 'object' || typeof b !== 'object' || !a || !b) {
  224. return a === b;
  225. }
  226. if (Array.isArray(a) || Array.isArray(b)) {
  227. return (Array.isArray(a) && Array.isArray(b) ? arrays.equals(a, b) : false);
  228. }
  229. if (Object.keys(a).length !== Object.keys(b).length) {
  230. return false;
  231. }
  232. for (const key in a) {
  233. if (!EditorOptionsUtil._deepEquals(a[key], b[key])) {
  234. return false;
  235. }
  236. }
  237. return true;
  238. }
  239. static checkEquals(a, b) {
  240. const result = [];
  241. let somethingChanged = false;
  242. for (const editorOption of editorOptionsRegistry) {
  243. const changed = !EditorOptionsUtil._deepEquals(a._read(editorOption.id), b._read(editorOption.id));
  244. result[editorOption.id] = changed;
  245. if (changed) {
  246. somethingChanged = true;
  247. }
  248. }
  249. return (somethingChanged ? new ConfigurationChangedEvent(result) : null);
  250. }
  251. /**
  252. * Returns true if something changed.
  253. * Modifies `options`.
  254. */
  255. static applyUpdate(options, update) {
  256. let changed = false;
  257. for (const editorOption of editorOptionsRegistry) {
  258. if (update.hasOwnProperty(editorOption.name)) {
  259. const result = editorOption.applyUpdate(options[editorOption.name], update[editorOption.name]);
  260. options[editorOption.name] = result.newValue;
  261. changed = changed || result.didChange;
  262. }
  263. }
  264. return changed;
  265. }
  266. }
  267. function deepCloneAndMigrateOptions(_options) {
  268. const options = objects.deepClone(_options);
  269. migrateOptions(options);
  270. return options;
  271. }