'use strict'; import { Disposable, workspace } from 'vscode'; import { Logger } from '../logger'; interface ConfigurationInspection { key: string; defaultValue?: string; globalValue?: string; workspaceValue?: string; } enum SettingLocation { workspace, global, default } class RenderWhitespaceConfiguration { constructor(public inspection: ConfigurationInspection) { } get location(): SettingLocation { if (this.inspection.workspaceValue) return SettingLocation.workspace; if (this.inspection.globalValue) return SettingLocation.global; return SettingLocation.default; } get overrideRequired() { return this.value != null && this.value !== 'none'; } get value(): string | undefined { return this.inspection.workspaceValue || this.inspection.globalValue || this.inspection.defaultValue; } update(replacement: ConfigurationInspection): boolean { let override = false; switch (this.location) { case SettingLocation.workspace: this.inspection.defaultValue = replacement.defaultValue; this.inspection.globalValue = replacement.globalValue; if (replacement.workspaceValue !== 'none') { this.inspection.workspaceValue = replacement.workspaceValue; override = true; } break; case SettingLocation.global: this.inspection.defaultValue = replacement.defaultValue; this.inspection.workspaceValue = replacement.workspaceValue; if (replacement.globalValue !== 'none') { this.inspection.globalValue = replacement.globalValue; override = true; } break; case SettingLocation.default: this.inspection.globalValue = replacement.globalValue; this.inspection.workspaceValue = replacement.workspaceValue; if (replacement.defaultValue !== 'none') { this.inspection.defaultValue = replacement.defaultValue; override = true; } break; } return override; } } export class WhitespaceController extends Disposable { private _configuration: RenderWhitespaceConfiguration; private _count: number = 0; private _disposable: Disposable; private _disposed: boolean = false; constructor() { super(() => this.dispose()); const subscriptions: Disposable[] = []; subscriptions.push(workspace.onDidChangeConfiguration(this._onConfigurationChanged, this)); this._disposable = Disposable.from(...subscriptions); this._onConfigurationChanged(); } async dispose() { this._disposed = true; if (this._count !== 0) { await this._restoreWhitespace(); this._count = 0; } } private _onConfigurationChanged() { if (this._disposed) return; const inspection = workspace.getConfiguration('editor').inspect('renderWhitespace')!; if (!this._count) { this._configuration = new RenderWhitespaceConfiguration(inspection); return; } if (this._configuration.update(inspection)) { // Since we were currently overriding whitespace, re-override setTimeout(() => this._overrideWhitespace(), 1); } } async override() { if (this._disposed) return; Logger.log(`Request whitespace override; count=${this._count}`); this._count++; if (this._count === 1 && this._configuration.overrideRequired) { // Override whitespace (turn off) await this._overrideWhitespace(); } } private async _overrideWhitespace() { Logger.log(`Override whitespace`); const cfg = workspace.getConfiguration('editor'); return cfg.update('renderWhitespace', 'none', this._configuration.location === SettingLocation.global); } async restore() { if (this._disposed || this._count === 0) return; Logger.log(`Request whitespace restore; count=${this._count}`); this._count--; if (this._count === 0 && this._configuration.overrideRequired) { // restore whitespace await this._restoreWhitespace(); } } private async _restoreWhitespace() { Logger.log(`Restore whitespace`); const cfg = workspace.getConfiguration('editor'); return cfg.update('renderWhitespace', this._configuration.location === SettingLocation.default ? undefined : this._configuration.value, this._configuration.location === SettingLocation.global); } }