123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261 |
- /*---------------------------------------------------------------------------------------------
- * Copyright (c) Microsoft Corporation. All rights reserved.
- * Licensed under the MIT License. See License.txt in the project root for license information.
- *--------------------------------------------------------------------------------------------*/
- import * as browser from '../../../base/browser/browser.js';
- import { Emitter } from '../../../base/common/event.js';
- import { Disposable } from '../../../base/common/lifecycle.js';
- import * as platform from '../../../base/common/platform.js';
- import { CharWidthRequest, readCharWidths } from './charWidthReader.js';
- import { ElementSizeObserver } from './elementSizeObserver.js';
- import { CommonEditorConfiguration } from '../../common/config/commonEditorConfig.js';
- import { EditorFontLigatures, EDITOR_FONT_DEFAULTS } from '../../common/config/editorOptions.js';
- import { FontInfo } from '../../common/config/fontInfo.js';
- class CSSBasedConfigurationCache {
- constructor() {
- this._keys = Object.create(null);
- this._values = Object.create(null);
- }
- has(item) {
- const itemId = item.getId();
- return !!this._values[itemId];
- }
- get(item) {
- const itemId = item.getId();
- return this._values[itemId];
- }
- put(item, value) {
- const itemId = item.getId();
- this._keys[itemId] = item;
- this._values[itemId] = value;
- }
- remove(item) {
- const itemId = item.getId();
- delete this._keys[itemId];
- delete this._values[itemId];
- }
- getValues() {
- return Object.keys(this._keys).map(id => this._values[id]);
- }
- }
- export function clearAllFontInfos() {
- CSSBasedConfiguration.INSTANCE.clearCache();
- }
- class CSSBasedConfiguration extends Disposable {
- constructor() {
- super();
- this._onDidChange = this._register(new Emitter());
- this.onDidChange = this._onDidChange.event;
- this._cache = new CSSBasedConfigurationCache();
- this._evictUntrustedReadingsTimeout = -1;
- }
- dispose() {
- if (this._evictUntrustedReadingsTimeout !== -1) {
- clearTimeout(this._evictUntrustedReadingsTimeout);
- this._evictUntrustedReadingsTimeout = -1;
- }
- super.dispose();
- }
- clearCache() {
- this._cache = new CSSBasedConfigurationCache();
- this._onDidChange.fire();
- }
- _writeToCache(item, value) {
- this._cache.put(item, value);
- if (!value.isTrusted && this._evictUntrustedReadingsTimeout === -1) {
- // Try reading again after some time
- this._evictUntrustedReadingsTimeout = setTimeout(() => {
- this._evictUntrustedReadingsTimeout = -1;
- this._evictUntrustedReadings();
- }, 5000);
- }
- }
- _evictUntrustedReadings() {
- const values = this._cache.getValues();
- let somethingRemoved = false;
- for (const item of values) {
- if (!item.isTrusted) {
- somethingRemoved = true;
- this._cache.remove(item);
- }
- }
- if (somethingRemoved) {
- this._onDidChange.fire();
- }
- }
- readConfiguration(bareFontInfo) {
- if (!this._cache.has(bareFontInfo)) {
- let readConfig = CSSBasedConfiguration._actualReadConfiguration(bareFontInfo);
- if (readConfig.typicalHalfwidthCharacterWidth <= 2 || readConfig.typicalFullwidthCharacterWidth <= 2 || readConfig.spaceWidth <= 2 || readConfig.maxDigitWidth <= 2) {
- // Hey, it's Bug 14341 ... we couldn't read
- readConfig = new FontInfo({
- zoomLevel: browser.getZoomLevel(),
- pixelRatio: browser.getPixelRatio(),
- fontFamily: readConfig.fontFamily,
- fontWeight: readConfig.fontWeight,
- fontSize: readConfig.fontSize,
- fontFeatureSettings: readConfig.fontFeatureSettings,
- lineHeight: readConfig.lineHeight,
- letterSpacing: readConfig.letterSpacing,
- isMonospace: readConfig.isMonospace,
- typicalHalfwidthCharacterWidth: Math.max(readConfig.typicalHalfwidthCharacterWidth, 5),
- typicalFullwidthCharacterWidth: Math.max(readConfig.typicalFullwidthCharacterWidth, 5),
- canUseHalfwidthRightwardsArrow: readConfig.canUseHalfwidthRightwardsArrow,
- spaceWidth: Math.max(readConfig.spaceWidth, 5),
- middotWidth: Math.max(readConfig.middotWidth, 5),
- wsmiddotWidth: Math.max(readConfig.wsmiddotWidth, 5),
- maxDigitWidth: Math.max(readConfig.maxDigitWidth, 5),
- }, false);
- }
- this._writeToCache(bareFontInfo, readConfig);
- }
- return this._cache.get(bareFontInfo);
- }
- static createRequest(chr, type, all, monospace) {
- const result = new CharWidthRequest(chr, type);
- all.push(result);
- if (monospace) {
- monospace.push(result);
- }
- return result;
- }
- static _actualReadConfiguration(bareFontInfo) {
- const all = [];
- const monospace = [];
- const typicalHalfwidthCharacter = this.createRequest('n', 0 /* Regular */, all, monospace);
- const typicalFullwidthCharacter = this.createRequest('\uff4d', 0 /* Regular */, all, null);
- const space = this.createRequest(' ', 0 /* Regular */, all, monospace);
- const digit0 = this.createRequest('0', 0 /* Regular */, all, monospace);
- const digit1 = this.createRequest('1', 0 /* Regular */, all, monospace);
- const digit2 = this.createRequest('2', 0 /* Regular */, all, monospace);
- const digit3 = this.createRequest('3', 0 /* Regular */, all, monospace);
- const digit4 = this.createRequest('4', 0 /* Regular */, all, monospace);
- const digit5 = this.createRequest('5', 0 /* Regular */, all, monospace);
- const digit6 = this.createRequest('6', 0 /* Regular */, all, monospace);
- const digit7 = this.createRequest('7', 0 /* Regular */, all, monospace);
- const digit8 = this.createRequest('8', 0 /* Regular */, all, monospace);
- const digit9 = this.createRequest('9', 0 /* Regular */, all, monospace);
- // monospace test: used for whitespace rendering
- const rightwardsArrow = this.createRequest('→', 0 /* Regular */, all, monospace);
- const halfwidthRightwardsArrow = this.createRequest('→', 0 /* Regular */, all, null);
- // U+00B7 - MIDDLE DOT
- const middot = this.createRequest('·', 0 /* Regular */, all, monospace);
- // U+2E31 - WORD SEPARATOR MIDDLE DOT
- const wsmiddotWidth = this.createRequest(String.fromCharCode(0x2E31), 0 /* Regular */, all, null);
- // monospace test: some characters
- const monospaceTestChars = '|/-_ilm%';
- for (let i = 0, len = monospaceTestChars.length; i < len; i++) {
- this.createRequest(monospaceTestChars.charAt(i), 0 /* Regular */, all, monospace);
- this.createRequest(monospaceTestChars.charAt(i), 1 /* Italic */, all, monospace);
- this.createRequest(monospaceTestChars.charAt(i), 2 /* Bold */, all, monospace);
- }
- readCharWidths(bareFontInfo, all);
- const maxDigitWidth = Math.max(digit0.width, digit1.width, digit2.width, digit3.width, digit4.width, digit5.width, digit6.width, digit7.width, digit8.width, digit9.width);
- let isMonospace = (bareFontInfo.fontFeatureSettings === EditorFontLigatures.OFF);
- const referenceWidth = monospace[0].width;
- for (let i = 1, len = monospace.length; isMonospace && i < len; i++) {
- const diff = referenceWidth - monospace[i].width;
- if (diff < -0.001 || diff > 0.001) {
- isMonospace = false;
- break;
- }
- }
- let canUseHalfwidthRightwardsArrow = true;
- if (isMonospace && halfwidthRightwardsArrow.width !== referenceWidth) {
- // using a halfwidth rightwards arrow would break monospace...
- canUseHalfwidthRightwardsArrow = false;
- }
- if (halfwidthRightwardsArrow.width > rightwardsArrow.width) {
- // using a halfwidth rightwards arrow would paint a larger arrow than a regular rightwards arrow
- canUseHalfwidthRightwardsArrow = false;
- }
- // let's trust the zoom level only 2s after it was changed.
- const canTrustBrowserZoomLevel = (browser.getTimeSinceLastZoomLevelChanged() > 2000);
- return new FontInfo({
- zoomLevel: browser.getZoomLevel(),
- pixelRatio: browser.getPixelRatio(),
- fontFamily: bareFontInfo.fontFamily,
- fontWeight: bareFontInfo.fontWeight,
- fontSize: bareFontInfo.fontSize,
- fontFeatureSettings: bareFontInfo.fontFeatureSettings,
- lineHeight: bareFontInfo.lineHeight,
- letterSpacing: bareFontInfo.letterSpacing,
- isMonospace: isMonospace,
- typicalHalfwidthCharacterWidth: typicalHalfwidthCharacter.width,
- typicalFullwidthCharacterWidth: typicalFullwidthCharacter.width,
- canUseHalfwidthRightwardsArrow: canUseHalfwidthRightwardsArrow,
- spaceWidth: space.width,
- middotWidth: middot.width,
- wsmiddotWidth: wsmiddotWidth.width,
- maxDigitWidth: maxDigitWidth
- }, canTrustBrowserZoomLevel);
- }
- }
- CSSBasedConfiguration.INSTANCE = new CSSBasedConfiguration();
- export class Configuration extends CommonEditorConfiguration {
- constructor(isSimpleWidget, options, referenceDomElement = null, accessibilityService) {
- super(isSimpleWidget, options);
- this.accessibilityService = accessibilityService;
- this._elementSizeObserver = this._register(new ElementSizeObserver(referenceDomElement, options.dimension, () => this._recomputeOptions()));
- this._register(CSSBasedConfiguration.INSTANCE.onDidChange(() => this._recomputeOptions()));
- if (this._validatedOptions.get(10 /* automaticLayout */)) {
- this._elementSizeObserver.startObserving();
- }
- this._register(browser.onDidChangeZoomLevel(_ => this._recomputeOptions()));
- this._register(this.accessibilityService.onDidChangeScreenReaderOptimized(() => this._recomputeOptions()));
- this._recomputeOptions();
- }
- static applyFontInfoSlow(domNode, fontInfo) {
- domNode.style.fontFamily = fontInfo.getMassagedFontFamily(browser.isSafari ? EDITOR_FONT_DEFAULTS.fontFamily : null);
- domNode.style.fontWeight = fontInfo.fontWeight;
- domNode.style.fontSize = fontInfo.fontSize + 'px';
- domNode.style.fontFeatureSettings = fontInfo.fontFeatureSettings;
- domNode.style.lineHeight = fontInfo.lineHeight + 'px';
- domNode.style.letterSpacing = fontInfo.letterSpacing + 'px';
- }
- static applyFontInfo(domNode, fontInfo) {
- domNode.setFontFamily(fontInfo.getMassagedFontFamily(browser.isSafari ? EDITOR_FONT_DEFAULTS.fontFamily : null));
- domNode.setFontWeight(fontInfo.fontWeight);
- domNode.setFontSize(fontInfo.fontSize);
- domNode.setFontFeatureSettings(fontInfo.fontFeatureSettings);
- domNode.setLineHeight(fontInfo.lineHeight);
- domNode.setLetterSpacing(fontInfo.letterSpacing);
- }
- observeReferenceElement(dimension) {
- this._elementSizeObserver.observe(dimension);
- }
- updatePixelRatio() {
- this._recomputeOptions();
- }
- static _getExtraEditorClassName() {
- let extra = '';
- if (!browser.isSafari && !browser.isWebkitWebView) {
- // Use user-select: none in all browsers except Safari and native macOS WebView
- extra += 'no-user-select ';
- }
- if (browser.isSafari) {
- // See https://github.com/microsoft/vscode/issues/108822
- extra += 'no-minimap-shadow ';
- }
- if (platform.isMacintosh) {
- extra += 'mac ';
- }
- return extra;
- }
- _getEnvConfiguration() {
- return {
- extraEditorClassName: Configuration._getExtraEditorClassName(),
- outerWidth: this._elementSizeObserver.getWidth(),
- outerHeight: this._elementSizeObserver.getHeight(),
- emptySelectionClipboard: browser.isWebKit || browser.isFirefox,
- pixelRatio: browser.getPixelRatio(),
- zoomLevel: browser.getZoomLevel(),
- accessibilitySupport: (this.accessibilityService.isScreenReaderOptimized()
- ? 2 /* Enabled */
- : this.accessibilityService.getAccessibilitySupport())
- };
- }
- readConfiguration(bareFontInfo) {
- return CSSBasedConfiguration.INSTANCE.readConfiguration(bareFontInfo);
- }
- }
|