Skip to content

Commit

Permalink
Fixes #22090: store/restore resolved font info to local storage
Browse files Browse the repository at this point in the history
  • Loading branch information
alexdima committed Mar 8, 2017
1 parent 69d2790 commit 718b163
Show file tree
Hide file tree
Showing 6 changed files with 118 additions and 20 deletions.
99 changes: 89 additions & 10 deletions src/vs/editor/browser/config/configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { FontInfo, BareFontInfo } from 'vs/editor/common/config/fontInfo';
import { ElementSizeObserver } from 'vs/editor/browser/config/elementSizeObserver';
import { FastDomNode } from 'vs/base/browser/fastDomNode';
import { CharWidthRequest, CharWidthRequestType, readCharWidths } from 'vs/editor/browser/config/charWidthReader';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';

class CSSBasedConfigurationCache {

Expand All @@ -25,35 +26,76 @@ class CSSBasedConfigurationCache {
this._values = Object.create(null);
}

private _itemId(item: BareFontInfo): string {
return `${browser.getZoomLevel()}-${item.getId()}`;
}

public has(item: BareFontInfo): boolean {
let itemId = this._itemId(item);
let itemId = item.getId();
return !!this._values[itemId];
}

public get(item: BareFontInfo): FontInfo {
let itemId = this._itemId(item);
let itemId = item.getId();
return this._values[itemId];
}

public put(item: BareFontInfo, value: FontInfo): void {
let itemId = this._itemId(item);
let itemId = item.getId();
this._keys[itemId] = item;
this._values[itemId] = value;
}

public remove(item: BareFontInfo): void {
let itemId = item.getId();
delete this._keys[itemId];
delete this._values[itemId];
}

public getKeys(): BareFontInfo[] {
return Object.keys(this._keys).map(id => this._keys[id]);
}

public getValues(): FontInfo[] {
return Object.keys(this._keys).map(id => this._values[id]);
}
}

export function readFontInfo(bareFontInfo: BareFontInfo): FontInfo {
return CSSBasedConfiguration.INSTANCE.readConfiguration(bareFontInfo);
}

export function restoreFontInfo(storageService: IStorageService): void {
let strStoredFontInfo = storageService.get('editorFontInfo', StorageScope.GLOBAL);
if (typeof strStoredFontInfo !== 'string') {
return;
}
let storedFontInfo: ISerializedFontInfo[] = null;
try {
storedFontInfo = JSON.parse(strStoredFontInfo);
} catch (err) {
return;
}
if (!Array.isArray(storedFontInfo)) {
return;
}
CSSBasedConfiguration.INSTANCE.restoreFontInfo(storedFontInfo);
}

export function saveFontInfo(storageService: IStorageService): void {
let knownFontInfo = CSSBasedConfiguration.INSTANCE.saveFontInfo();
storageService.store('editorFontInfo', JSON.stringify(knownFontInfo), StorageScope.GLOBAL);
}

export interface ISerializedFontInfo {
readonly zoomLevel: number;
readonly fontFamily: string;
readonly fontWeight: string;
readonly fontSize: number;
readonly lineHeight: number;
readonly isMonospace: boolean;
readonly typicalHalfwidthCharacterWidth: number;
readonly typicalFullwidthCharacterWidth: number;
readonly spaceWidth: number;
readonly maxDigitWidth: number;
}

class CSSBasedConfiguration extends Disposable {

public static INSTANCE = new CSSBasedConfiguration();
Expand All @@ -78,13 +120,45 @@ class CSSBasedConfiguration extends Disposable {
super.dispose();
}

public saveFontInfo(): ISerializedFontInfo[] {
// Only save trusted font info (that has been measured in this running instance)
return this._cache.getValues().filter(item => item.isTrusted);
}

public restoreFontInfo(savedFontInfo: ISerializedFontInfo[]): void {
// Take all the saved font info and insert them in the cache without the trusted flag.
// The reason for this is that a font might have been installed on the OS in the meantime.
for (let i = 0, len = savedFontInfo.length; i < len; i++) {
let fontInfo = new FontInfo(savedFontInfo[i], false);
this._cache.put(fontInfo, fontInfo);
}

// Remove saved font info that does not have the trusted flag.
// (this forces it to be re-read).
setTimeout(() => {
let values = this._cache.getValues();
let somethingRemoved = false;
for (let i = 0, len = values.length; i < len; i++) {
let item = values[i];
if (!item.isTrusted) {
somethingRemoved = true;
this._cache.remove(item);
}
}
if (somethingRemoved) {
this._onDidChange.fire();
}
}, 5000);
}

public readConfiguration(bareFontInfo: BareFontInfo): FontInfo {
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(),
fontFamily: readConfig.fontFamily,
fontWeight: readConfig.fontWeight,
fontSize: readConfig.fontSize,
Expand All @@ -94,7 +168,7 @@ class CSSBasedConfiguration extends Disposable {
typicalFullwidthCharacterWidth: Math.max(readConfig.typicalFullwidthCharacterWidth, 5),
spaceWidth: Math.max(readConfig.spaceWidth, 5),
maxDigitWidth: Math.max(readConfig.maxDigitWidth, 5),
});
}, true);
this._installChangeMonitor();
}

Expand Down Expand Up @@ -204,6 +278,7 @@ class CSSBasedConfiguration extends Disposable {
}

return new FontInfo({
zoomLevel: browser.getZoomLevel(),
fontFamily: bareFontInfo.fontFamily,
fontWeight: bareFontInfo.fontWeight,
fontSize: bareFontInfo.fontSize,
Expand All @@ -213,7 +288,7 @@ class CSSBasedConfiguration extends Disposable {
typicalFullwidthCharacterWidth: typicalFullwidthCharacter.width,
spaceWidth: space.width,
maxDigitWidth: maxDigitWidth
});
}, true);
}
}

Expand All @@ -236,7 +311,7 @@ export class Configuration extends CommonEditorConfiguration {
constructor(options: any, referenceDomElement: HTMLElement = null) {
super(options, new ElementSizeObserver(referenceDomElement, () => this._onReferenceDomElementSizeChanged()));

this._register(CSSBasedConfiguration.INSTANCE.onDidChange(() => () => this._onCSSBasedConfigurationChanged()));
this._register(CSSBasedConfiguration.INSTANCE.onDidChange(() => this._onCSSBasedConfigurationChanged()));

if (this._configWithDefaults.getEditorOptions().automaticLayout) {
this._elementSizeObserver.startObserving();
Expand Down Expand Up @@ -299,4 +374,8 @@ export class Configuration extends CommonEditorConfiguration {
protected readConfiguration(bareFontInfo: BareFontInfo): FontInfo {
return CSSBasedConfiguration.INSTANCE.readConfiguration(bareFontInfo);
}

protected getZoomLevel(): number {
return browser.getZoomLevel();
}
}
4 changes: 3 additions & 1 deletion src/vs/editor/common/config/commonEditorConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -543,7 +543,7 @@ export abstract class CommonEditorConfiguration extends Disposable implements ed
canUseTranslate3d = false;
}

let bareFontInfo = BareFontInfo.createFromRawSettings(opts);
let bareFontInfo = BareFontInfo.createFromRawSettings(opts, this.getZoomLevel());

return InternalEditorOptionsHelper.createInternalEditorOptions(
this.getOuterWidth(),
Expand Down Expand Up @@ -597,6 +597,8 @@ export abstract class CommonEditorConfiguration extends Disposable implements ed
protected abstract _getPixelRatio(): number;

protected abstract readConfiguration(styling: BareFontInfo): FontInfo;

protected abstract getZoomLevel(): number;
}

const configurationRegistry = <IConfigurationRegistry>Registry.as(Extensions.Configuration);
Expand Down
15 changes: 11 additions & 4 deletions src/vs/editor/common/config/fontInfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export class BareFontInfo {
fontWeight?: string;
fontSize?: number | string;
lineHeight?: number | string;
}): BareFontInfo {
}, zoomLevel: number): BareFontInfo {

let fontFamily = String(opts.fontFamily) || DefaultConfig.editor.fontFamily;
let fontWeight = String(opts.fontWeight) || DefaultConfig.editor.fontWeight;
Expand All @@ -72,13 +72,15 @@ export class BareFontInfo {
lineHeight *= editorZoomLevelMultiplier;

return new BareFontInfo({
zoomLevel: zoomLevel,
fontFamily: fontFamily,
fontWeight: fontWeight,
fontSize: fontSize,
lineHeight: lineHeight
});
}

readonly zoomLevel: number;
readonly fontFamily: string;
readonly fontWeight: string;
readonly fontSize: number;
Expand All @@ -88,11 +90,13 @@ export class BareFontInfo {
* @internal
*/
protected constructor(opts: {
zoomLevel: number;
fontFamily: string;
fontWeight: string;
fontSize: number;
lineHeight: number;
}) {
this.zoomLevel = opts.zoomLevel;
this.fontFamily = String(opts.fontFamily);
this.fontWeight = String(opts.fontWeight);
this.fontSize = opts.fontSize;
Expand All @@ -103,13 +107,14 @@ export class BareFontInfo {
* @internal
*/
public getId(): string {
return this.fontFamily + '-' + this.fontWeight + '-' + this.fontSize + '-' + this.lineHeight + '-';
return this.zoomLevel + '-' + this.fontFamily + '-' + this.fontWeight + '-' + this.fontSize + '-' + this.lineHeight;
}
}

export class FontInfo extends BareFontInfo {
readonly _editorStylingBrand: void;

readonly isTrusted: boolean;
readonly isMonospace: boolean;
readonly typicalHalfwidthCharacterWidth: number;
readonly typicalFullwidthCharacterWidth: number;
Expand All @@ -120,6 +125,7 @@ export class FontInfo extends BareFontInfo {
* @internal
*/
constructor(opts: {
zoomLevel: number;
fontFamily: string;
fontWeight: string;
fontSize: number;
Expand All @@ -129,8 +135,9 @@ export class FontInfo extends BareFontInfo {
typicalFullwidthCharacterWidth: number;
spaceWidth: number;
maxDigitWidth: number;
}) {
}, isTrusted: boolean) {
super(opts);
this.isTrusted = isTrusted;
this.isMonospace = opts.isMonospace;
this.typicalHalfwidthCharacterWidth = opts.typicalHalfwidthCharacterWidth;
this.typicalFullwidthCharacterWidth = opts.typicalFullwidthCharacterWidth;
Expand Down Expand Up @@ -158,6 +165,6 @@ export class FontInfo extends BareFontInfo {
* @internal
*/
public clone(): FontInfo {
return new FontInfo(this);
return new FontInfo(this, this.isTrusted);
}
}
7 changes: 6 additions & 1 deletion src/vs/editor/test/common/mocks/testConfiguration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export class TestConfiguration extends CommonEditorConfiguration {

protected readConfiguration(styling: BareFontInfo): FontInfo {
return new FontInfo({
zoomLevel: 0,
fontFamily: 'mockFont',
fontWeight: 'normal',
fontSize: 14,
Expand All @@ -45,6 +46,10 @@ export class TestConfiguration extends CommonEditorConfiguration {
typicalFullwidthCharacterWidth: 20,
spaceWidth: 10,
maxDigitWidth: 10,
});
}, true);
}

protected getZoomLevel(): number {
return 0;
}
}
2 changes: 2 additions & 0 deletions src/vs/monaco.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3944,6 +3944,7 @@ declare module monaco.editor {

export class FontInfo extends BareFontInfo {
readonly _editorStylingBrand: void;
readonly isTrusted: boolean;
readonly isMonospace: boolean;
readonly typicalHalfwidthCharacterWidth: number;
readonly typicalFullwidthCharacterWidth: number;
Expand All @@ -3952,6 +3953,7 @@ declare module monaco.editor {
}
export class BareFontInfo {
readonly _bareFontInfoBrand: void;
readonly zoomLevel: number;
readonly fontFamily: string;
readonly fontWeight: string;
readonly fontSize: number;
Expand Down
11 changes: 7 additions & 4 deletions src/vs/workbench/electron-browser/shell.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,8 @@ import { remote } from 'electron';
import { ITextMateService } from 'vs/editor/node/textMate/textMateService';
import { MainProcessTextMateSyntax } from 'vs/editor/electron-browser/textMate/TMSyntax';
import { BareFontInfo } from 'vs/editor/common/config/fontInfo';
import { readFontInfo } from 'vs/editor/browser/config/configuration';
import { restoreFontInfo, readFontInfo, saveFontInfo } from 'vs/editor/browser/config/configuration';
import * as browser from 'vs/base/browser/browser';
import SCMPreview from 'vs/workbench/parts/scm/browser/scmPreview';
import { readdir } from 'vs/base/node/pfs';
import { join } from 'path';
Expand Down Expand Up @@ -305,6 +306,10 @@ export class WorkbenchShell {
this.storageService = instantiationService.createInstance(StorageService, window.localStorage, disableWorkspaceStorage ? inMemoryLocalStorageInstance : window.localStorage);
serviceCollection.set(IStorageService, this.storageService);

// Warm up font cache information before building up too many dom elements
restoreFontInfo(this.storageService);
readFontInfo(BareFontInfo.createFromRawSettings(this.configurationService.getConfiguration('editor'), browser.getZoomLevel()));

// Telemetry
if (this.environmentService.isBuilt && !this.environmentService.isExtensionDevelopment && !!product.enableTelemetry) {
const channel = getDelayedChannel<ITelemetryAppenderChannel>(sharedProcess.then(c => c.getChannel('telemetryAppender')));
Expand Down Expand Up @@ -345,6 +350,7 @@ export class WorkbenchShell {

const lifecycleService = instantiationService.createInstance(LifecycleService);
this.toUnbind.push(lifecycleService.onShutdown(reason => disposables.dispose()));
this.toUnbind.push(lifecycleService.onShutdown(reason => saveFontInfo(this.storageService)));
serviceCollection.set(ILifecycleService, lifecycleService);
disposables.add(lifecycleTelemetry(this.telemetryService, lifecycleService));

Expand Down Expand Up @@ -414,9 +420,6 @@ export class WorkbenchShell {
this.onUnexpectedError(error);
});

// Warm up font cache information before building up too many dom elements
readFontInfo(BareFontInfo.createFromRawSettings(this.configurationService.getConfiguration('editor')));

// Shell Class for CSS Scoping
$(this.container).addClass('monaco-shell');

Expand Down

0 comments on commit 718b163

Please sign in to comment.