From d8a325a83f63f33140f4e0147ea07ee4c41a55d2 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 20 Nov 2015 09:42:04 +0100 Subject: [PATCH] extract reusable userSettings library --- src/vs/workbench/electron-main/settings.ts | 160 +----------------- src/vs/workbench/electron-main/windows.ts | 4 +- src/vs/workbench/node/userSettings.ts | 178 +++++++++++++++++++++ 3 files changed, 188 insertions(+), 154 deletions(-) create mode 100644 src/vs/workbench/node/userSettings.ts diff --git a/src/vs/workbench/electron-main/settings.ts b/src/vs/workbench/electron-main/settings.ts index 13a6324faea..e1b86a8c7dd 100644 --- a/src/vs/workbench/electron-main/settings.ts +++ b/src/vs/workbench/electron-main/settings.ts @@ -3,176 +3,32 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ - 'use strict'; -import fs = require('fs'); -import events = require('events'); -import path = require('path'); import app = require('app'); import env = require('vs/workbench/electron-main/env'); -import json = require('vs/base/common/json'); -import objects = require('vs/base/common/objects'); +import {UserSettings} from 'vs/workbench/node/userSettings'; -const eventEmitter = new events.EventEmitter(); - -const EventTypes = { - CHANGE: 'change' -}; - -export interface ISettings { - settings: any; - settingsParseErrors?: string[]; - keybindings: any -} - -export function onChange(clb: (settings: ISettings) => void): () => void { - eventEmitter.addListener(EventTypes.CHANGE, clb); - - return () => eventEmitter.removeListener(EventTypes.CHANGE, clb); -} - -export class SettingsManager { - - private static CHANGE_BUFFER_DELAY = 300; - - private timeoutHandle: number; - public globalSettings: ISettings; +export class SettingsManager extends UserSettings { constructor() { - this.registerWatchers(); - } - - public getValue(key: string, fallback?:any): any { - if (!key) { - return fallback; - } - - let value = this.globalSettings.settings; - - let parts = key.split('\.'); - while (parts.length && value) { - let part = parts.shift(); - value = value[part]; - } - - return typeof value !== 'undefined' ? value : fallback; - } - - private registerWatchers(): void { - let watcher = fs.watch(path.dirname(env.appSettingsPath)); - watcher.on('change', (eventType: string, fileName: string) => this.onSettingsFileChange(eventType, fileName)); + super(env.appSettingsPath, env.appKeybindingsPath); app.on('will-quit', () => { - watcher.close(); + this.dispose(); }); } - private onSettingsFileChange(eventType: string, fileName: string): void { - - // we can get multiple change events for one change, so we buffer through a timeout - if (this.timeoutHandle) { - global.clearTimeout(this.timeoutHandle); - delete this.timeoutHandle; - } - - this.timeoutHandle = global.setTimeout(() => { - - // Reload - let didChange = this.load(); - - // Emit event - if (didChange) { - eventEmitter.emit(EventTypes.CHANGE, this.globalSettings); - } - - }, SettingsManager.CHANGE_BUFFER_DELAY); - } - public load(): boolean { - let loadedSettings = this.doLoad(); - if (!objects.equals(loadedSettings, this.globalSettings)) { + const settingsChanged = super.load(); - // Keep in class - this.globalSettings = loadedSettings; - - // Store into global so that any renderer can access the value with remote.getGlobal() + // Store into global so that any renderer can access the value with remote.getGlobal() + if (settingsChanged) { global.globalSettingsValue = JSON.stringify(this.globalSettings); - - return true; // changed value } - return false; // no changed value - } - - private doLoad(): ISettings { - let settings = this.doLoadSettings(); - - return { - settings: settings.contents, - settingsParseErrors: settings.parseErrors, - keybindings: this.doLoadKeybindings() - }; - } - - private doLoadSettings(): { contents: any; parseErrors?: string[]; } { - - function setNode(root: any, key: string, value: any): any { - let segments = key.split('.'); - let last = segments.pop(); - - let curr = root; - segments.forEach((s) => { - let obj = curr[s]; - switch (typeof obj) { - case 'undefined': - obj = curr[s] = {}; - break; - case 'object': - break; - default: - console.log('Conflicting user settings: ' + key + ' at ' + s + ' with ' + JSON.stringify(obj)); - } - curr = obj; - }); - curr[last] = value; - } - - - try { - let root = {}; - let content = '{}'; - try { - content = fs.readFileSync(env.appSettingsPath).toString(); - } catch (error) { - // ignore - } - - let contents = json.parse(content) || {}; - for (let key in contents) { - setNode(root, key, contents[key]); - } - return { - contents: root - }; - } catch (error) { - // parse problem - return { - contents: {}, - parseErrors: [env.appSettingsPath] - }; - } - } - - private doLoadKeybindings(): any { - try { - return json.parse(fs.readFileSync(env.appKeybindingsPath).toString()); - } catch (error) { - // Ignore loading and parsing errors - } - - return []; + return settingsChanged; } } diff --git a/src/vs/workbench/electron-main/windows.ts b/src/vs/workbench/electron-main/windows.ts index 376e9a00bad..bd1526c97ca 100644 --- a/src/vs/workbench/electron-main/windows.ts +++ b/src/vs/workbench/electron-main/windows.ts @@ -159,9 +159,9 @@ export class WindowsManager { }, 100); }); - settings.onChange((newSettings) => { + settings.manager.onChange.add((newSettings) => { this.sendToAll('vscode:optionsChange', JSON.stringify({ globalSettings: newSettings })); - }); + }, this); ipc.on('vscode:startCrashReporter', (event: any, config: any) => { crashReporter.start(config); diff --git a/src/vs/workbench/node/userSettings.ts b/src/vs/workbench/node/userSettings.ts new file mode 100644 index 00000000000..309efa511d8 --- /dev/null +++ b/src/vs/workbench/node/userSettings.ts @@ -0,0 +1,178 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + + +'use strict'; + +import fs = require('fs'); +import path = require('path'); + +import json = require('vs/base/common/json'); +import objects = require('vs/base/common/objects'); +import {EventProvider} from 'vs/base/common/eventProvider'; +import {EventSource} from 'vs/base/common/eventSource'; + +export interface ISettings { + settings: any; + settingsParseErrors?: string[]; + keybindings: any +} + +export class UserSettings { + + private static CHANGE_BUFFER_DELAY = 300; + + public globalSettings: ISettings; + + private timeoutHandle: number; + private watcher: fs.FSWatcher; + private appSettingsPath: string; + private appKeybindingsPath: string; + + private _onChange: EventSource<(settings: ISettings) => void>; + + constructor(appSettingsPath:string, appKeybindingsPath: string) { + this.appSettingsPath = appSettingsPath; + this.appKeybindingsPath = appKeybindingsPath; + this._onChange = new EventSource<(settings: ISettings) => void>(); + + this.registerWatchers(); + } + + public get onChange(): EventProvider<(settings: ISettings) => void> { + return this._onChange.value; + } + + public getValue(key: string, fallback?: any): any { + if (!key) { + return fallback; + } + + let value = this.globalSettings.settings; + + let parts = key.split('\.'); + while (parts.length && value) { + let part = parts.shift(); + value = value[part]; + } + + return typeof value !== 'undefined' ? value : fallback; + } + + private registerWatchers(): void { + this.watcher = fs.watch(path.dirname(this.appSettingsPath)); + this.watcher.on('change', (eventType: string, fileName: string) => this.onSettingsFileChange(eventType, fileName)); + } + + private onSettingsFileChange(eventType: string, fileName: string): void { + + // we can get multiple change events for one change, so we buffer through a timeout + if (this.timeoutHandle) { + global.clearTimeout(this.timeoutHandle); + delete this.timeoutHandle; + } + + this.timeoutHandle = global.setTimeout(() => { + + // Reload + let didChange = this.load(); + + // Emit event + if (didChange) { + this._onChange.fire(this.globalSettings); + } + + }, UserSettings.CHANGE_BUFFER_DELAY); + } + + public load(): boolean { + let loadedSettings = this.doLoad(); + if (!objects.equals(loadedSettings, this.globalSettings)) { + + // Keep in class + this.globalSettings = loadedSettings; + + return true; // changed value + } + + return false; // no changed value + } + + private doLoad(): ISettings { + let settings = this.doLoadSettings(); + + return { + settings: settings.contents, + settingsParseErrors: settings.parseErrors, + keybindings: this.doLoadKeybindings() + }; + } + + private doLoadSettings(): { contents: any; parseErrors?: string[]; } { + + function setNode(root: any, key: string, value: any): any { + let segments = key.split('.'); + let last = segments.pop(); + + let curr = root; + segments.forEach((s) => { + let obj = curr[s]; + switch (typeof obj) { + case 'undefined': + obj = curr[s] = {}; + break; + case 'object': + break; + default: + console.log('Conflicting user settings: ' + key + ' at ' + s + ' with ' + JSON.stringify(obj)); + } + curr = obj; + }); + curr[last] = value; + } + + + try { + let root = {}; + let content = '{}'; + try { + content = fs.readFileSync(this.appSettingsPath).toString(); + } catch (error) { + // ignore + } + + let contents = json.parse(content) || {}; + for (let key in contents) { + setNode(root, key, contents[key]); + } + return { + contents: root + }; + } catch (error) { + // parse problem + return { + contents: {}, + parseErrors: [this.appSettingsPath] + }; + } + } + + private doLoadKeybindings(): any { + try { + return json.parse(fs.readFileSync(this.appKeybindingsPath).toString()); + } catch (error) { + // Ignore loading and parsing errors + } + + return []; + } + + public dispose(): void { + if (this.watcher) { + this.watcher.close(); + this.watcher = null; + } + } +} \ No newline at end of file