mirror of
https://github.com/Microsoft/vscode
synced 2024-09-13 13:46:13 +00:00
Merge pull request #60875 from Microsoft/ben/sqlite
Introduce SQLite (in-memory) for localStorage
This commit is contained in:
commit
05e1c11111
|
@ -310,19 +310,20 @@ function packageTask(platform, arch, opts) {
|
|||
const deps = gulp.src(depsSrc, { base: '.', dot: true })
|
||||
.pipe(filter(['**', '!**/package-lock.json']))
|
||||
.pipe(util.cleanNodeModule('fsevents', ['binding.gyp', 'fsevents.cc', 'build/**', 'src/**', 'test/**'], ['**/*.node']))
|
||||
.pipe(util.cleanNodeModule('oniguruma', ['binding.gyp', 'build/**', 'src/**', 'deps/**'], ['**/*.node', 'src/*.js']))
|
||||
.pipe(util.cleanNodeModule('vscode-sqlite3', ['binding.gyp', 'benchmark/**', 'cloudformation/**', 'deps/**', 'test/**', 'build/**', 'src/**'], ['build/Release/*.node']))
|
||||
.pipe(util.cleanNodeModule('oniguruma', ['binding.gyp', 'build/**', 'src/**', 'deps/**'], ['build/Release/*.node', 'src/*.js']))
|
||||
.pipe(util.cleanNodeModule('windows-mutex', ['binding.gyp', 'build/**', 'src/**'], ['**/*.node']))
|
||||
.pipe(util.cleanNodeModule('native-keymap', ['binding.gyp', 'build/**', 'src/**', 'deps/**'], ['**/*.node']))
|
||||
.pipe(util.cleanNodeModule('native-is-elevated', ['binding.gyp', 'build/**', 'src/**', 'deps/**'], ['**/*.node']))
|
||||
.pipe(util.cleanNodeModule('native-watchdog', ['binding.gyp', 'build/**', 'src/**'], ['**/*.node']))
|
||||
.pipe(util.cleanNodeModule('spdlog', ['binding.gyp', 'build/**', 'deps/**', 'src/**', 'test/**'], ['**/*.node']))
|
||||
.pipe(util.cleanNodeModule('native-keymap', ['binding.gyp', 'build/**', 'src/**', 'deps/**'], ['build/Release/*.node']))
|
||||
.pipe(util.cleanNodeModule('native-is-elevated', ['binding.gyp', 'build/**', 'src/**', 'deps/**'], ['build/Release/*.node']))
|
||||
.pipe(util.cleanNodeModule('native-watchdog', ['binding.gyp', 'build/**', 'src/**'], ['build/Release/*.node']))
|
||||
.pipe(util.cleanNodeModule('spdlog', ['binding.gyp', 'build/**', 'deps/**', 'src/**', 'test/**'], ['build/Release/*.node']))
|
||||
.pipe(util.cleanNodeModule('jschardet', ['dist/**']))
|
||||
.pipe(util.cleanNodeModule('windows-foreground-love', ['binding.gyp', 'build/**', 'src/**'], ['**/*.node']))
|
||||
.pipe(util.cleanNodeModule('windows-process-tree', ['binding.gyp', 'build/**', 'src/**'], ['**/*.node']))
|
||||
.pipe(util.cleanNodeModule('gc-signals', ['binding.gyp', 'build/**', 'src/**', 'deps/**'], ['**/*.node', 'src/index.js']))
|
||||
.pipe(util.cleanNodeModule('gc-signals', ['binding.gyp', 'build/**', 'src/**', 'deps/**'], ['build/Release/*.node', 'src/index.js']))
|
||||
.pipe(util.cleanNodeModule('keytar', ['binding.gyp', 'build/**', 'src/**', 'script/**', 'node_modules/**'], ['**/*.node']))
|
||||
.pipe(util.cleanNodeModule('node-pty', ['binding.gyp', 'build/**', 'src/**', 'tools/**'], ['build/Release/*.exe', 'build/Release/*.dll', 'build/Release/*.node']))
|
||||
.pipe(util.cleanNodeModule('vscode-nsfw', ['binding.gyp', 'build/**', 'src/**', 'openpa/**', 'includes/**'], ['**/*.node', '**/*.a']))
|
||||
.pipe(util.cleanNodeModule('vscode-nsfw', ['binding.gyp', 'build/**', 'src/**', 'openpa/**', 'includes/**'], ['build/Release/*.node', '**/*.a']))
|
||||
.pipe(util.cleanNodeModule('vsda', ['binding.gyp', 'README.md', 'build/**', '*.bat', '*.sh', '*.cpp', '*.h'], ['build/Release/vsda.node']))
|
||||
.pipe(createAsar(path.join(process.cwd(), 'node_modules'), ['**/*.node', '**/vscode-ripgrep/bin/*', '**/node-pty/build/Release/*'], 'app/node_modules.asar'));
|
||||
|
||||
|
|
|
@ -43,7 +43,7 @@ function createCompile(src, build, emitError) {
|
|||
const opts = _.clone(getTypeScriptCompilerOptions(src));
|
||||
opts.inlineSources = !!build;
|
||||
opts.noFilesystemLookup = true;
|
||||
const ts = tsb.create(opts, true, undefined, err => reporter(err.toString()));
|
||||
const ts = tsb.create(opts, true, undefined, err => new reporter(err.toString()));
|
||||
return function (token) {
|
||||
const utf8Filter = util.filter(data => /(\/|\\)test(\/|\\).*utf8/.test(data.path));
|
||||
const tsFilter = util.filter(data => /\.ts$/.test(data.path));
|
||||
|
|
|
@ -47,7 +47,7 @@ function createCompile(src: string, build: boolean, emitError?: boolean): (token
|
|||
opts.inlineSources = !!build;
|
||||
opts.noFilesystemLookup = true;
|
||||
|
||||
const ts = tsb.create(opts, true, undefined, err => reporter(err.toString()));
|
||||
const ts = tsb.create(opts, true, undefined, err => new (<any>reporter)(err.toString()));
|
||||
|
||||
return function (token?: util.ICancellationToken) {
|
||||
|
||||
|
|
|
@ -49,6 +49,7 @@
|
|||
"vscode-debugprotocol": "1.32.0",
|
||||
"vscode-nsfw": "1.0.17",
|
||||
"vscode-ripgrep": "^1.2.2",
|
||||
"vscode-sqlite3": "4.0.2",
|
||||
"vscode-textmate": "^4.0.1",
|
||||
"vscode-xterm": "3.9.0-beta7",
|
||||
"winreg": "^1.2.4",
|
||||
|
|
3
src/bootstrap.js
vendored
3
src/bootstrap.js
vendored
|
@ -37,7 +37,9 @@ exports.enableASARSupport = function (nodeModulesPath) {
|
|||
|
||||
const NODE_MODULES_ASAR_PATH = NODE_MODULES_PATH + '.asar';
|
||||
|
||||
// @ts-ignore
|
||||
const originalResolveLookupPaths = Module._resolveLookupPaths;
|
||||
// @ts-ignore
|
||||
Module._resolveLookupPaths = function (request, parent, newReturn) {
|
||||
const result = originalResolveLookupPaths(request, parent, newReturn);
|
||||
|
||||
|
@ -225,6 +227,7 @@ exports.configurePortable = function () {
|
|||
* This should be called before importing the applicationinsights module
|
||||
*/
|
||||
exports.avoidMonkeyPatchFromAppInsights = function () {
|
||||
// @ts-ignore
|
||||
process.env['APPLICATION_INSIGHTS_NO_DIAGNOSTIC_CHANNEL'] = true; // Skip monkey patching of 3rd party modules by appinsights
|
||||
global['diagnosticsSource'] = {}; // Prevents diagnostic channel (which patches "require") from initializing entirely
|
||||
};
|
||||
|
|
109
src/typings/vscode-sqlite3.d.ts
vendored
Normal file
109
src/typings/vscode-sqlite3.d.ts
vendored
Normal file
|
@ -0,0 +1,109 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
// Type definitions for sqlite3 3.1
|
||||
// Project: https://github.com/mapbox/node-sqlite3
|
||||
// Definitions by: Nick Malaguti <https://github.com/nmalaguti>
|
||||
// Sumant Manne <https://github.com/dpyro>
|
||||
// Behind The Math <https://github.com/BehindTheMath>
|
||||
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
|
||||
|
||||
/// <reference types="node" />
|
||||
|
||||
declare module 'vscode-sqlite3' {
|
||||
import events = require("events");
|
||||
|
||||
export const OPEN_READONLY: number;
|
||||
export const OPEN_READWRITE: number;
|
||||
export const OPEN_CREATE: number;
|
||||
|
||||
export const cached: {
|
||||
Database(filename: string, callback?: (this: Database, err: Error | null) => void): Database;
|
||||
Database(filename: string, mode?: number, callback?: (this: Database, err: Error | null) => void): Database;
|
||||
};
|
||||
|
||||
export interface RunResult extends Statement {
|
||||
lastID: number;
|
||||
changes: number;
|
||||
}
|
||||
|
||||
export class Statement extends events.EventEmitter {
|
||||
bind(callback?: (err: Error | null) => void): this;
|
||||
bind(...params: any[]): this;
|
||||
|
||||
reset(callback?: (err: null) => void): this;
|
||||
|
||||
finalize(callback?: (err: Error) => void): Database;
|
||||
|
||||
run(callback?: (err: Error | null) => void): this;
|
||||
run(params: any, callback?: (this: RunResult, err: Error | null) => void): this;
|
||||
run(...params: any[]): this;
|
||||
|
||||
get(callback?: (err: Error | null, row?: any) => void): this;
|
||||
get(params: any, callback?: (this: RunResult, err: Error | null, row?: any) => void): this;
|
||||
get(...params: any[]): this;
|
||||
|
||||
all(callback?: (err: Error | null, rows: any[]) => void): this;
|
||||
all(params: any, callback?: (this: RunResult, err: Error | null, rows: any[]) => void): this;
|
||||
all(...params: any[]): this;
|
||||
|
||||
each(callback?: (err: Error | null, row: any) => void, complete?: (err: Error | null, count: number) => void): this;
|
||||
each(params: any, callback?: (this: RunResult, err: Error | null, row: any) => void, complete?: (err: Error | null, count: number) => void): this;
|
||||
each(...params: any[]): this;
|
||||
}
|
||||
|
||||
export class Database extends events.EventEmitter {
|
||||
constructor(filename: string, callback?: (err: Error | null) => void);
|
||||
constructor(filename: string, mode?: number, callback?: (err: Error | null) => void);
|
||||
|
||||
close(callback?: (err: Error | null) => void): void;
|
||||
|
||||
run(sql: string, callback?: (this: RunResult, err: Error | null) => void): this;
|
||||
run(sql: string, params: any, callback?: (this: RunResult, err: Error | null) => void): this;
|
||||
run(sql: string, ...params: any[]): this;
|
||||
|
||||
get(sql: string, callback?: (this: Statement, err: Error | null, row: any) => void): this;
|
||||
get(sql: string, params: any, callback?: (this: Statement, err: Error | null, row: any) => void): this;
|
||||
get(sql: string, ...params: any[]): this;
|
||||
|
||||
all(sql: string, callback?: (this: Statement, err: Error | null, rows: any[]) => void): this;
|
||||
all(sql: string, params: any, callback?: (this: Statement, err: Error | null, rows: any[]) => void): this;
|
||||
all(sql: string, ...params: any[]): this;
|
||||
|
||||
each(sql: string, callback?: (this: Statement, err: Error | null, row: any) => void, complete?: (err: Error | null, count: number) => void): this;
|
||||
each(sql: string, params: any, callback?: (this: Statement, err: Error | null, row: any) => void, complete?: (err: Error | null, count: number) => void): this;
|
||||
each(sql: string, ...params: any[]): this;
|
||||
|
||||
exec(sql: string, callback?: (this: Statement, err: Error | null) => void): this;
|
||||
|
||||
prepare(sql: string, callback?: (this: Statement, err: Error | null) => void): Statement;
|
||||
prepare(sql: string, params: any, callback?: (this: Statement, err: Error | null) => void): Statement;
|
||||
prepare(sql: string, ...params: any[]): Statement;
|
||||
|
||||
serialize(callback?: () => void): void;
|
||||
parallelize(callback?: () => void): void;
|
||||
|
||||
on(event: "trace", listener: (sql: string) => void): this;
|
||||
on(event: "profile", listener: (sql: string, time: number) => void): this;
|
||||
on(event: "error", listener: (err: Error) => void): this;
|
||||
on(event: "open" | "close", listener: () => void): this;
|
||||
on(event: string, listener: (...args: any[]) => void): this;
|
||||
|
||||
configure(option: "busyTimeout", value: number): void;
|
||||
}
|
||||
|
||||
export function verbose(): sqlite3;
|
||||
|
||||
export interface sqlite3 {
|
||||
OPEN_READONLY: number;
|
||||
OPEN_READWRITE: number;
|
||||
OPEN_CREATE: number;
|
||||
cached: typeof cached;
|
||||
RunResult: RunResult;
|
||||
Statement: typeof Statement;
|
||||
Database: typeof Database;
|
||||
verbose(): this;
|
||||
}
|
||||
}
|
|
@ -172,7 +172,7 @@ export class SimpleThrottler {
|
|||
* delayer.trigger(() => { return makeTheTrip(); });
|
||||
* }
|
||||
*/
|
||||
export class Delayer<T> {
|
||||
export class Delayer<T> implements IDisposable {
|
||||
|
||||
private timeout: any;
|
||||
private completionPromise: TPromise | null;
|
||||
|
@ -232,6 +232,10 @@ export class Delayer<T> {
|
|||
this.timeout = null;
|
||||
}
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.cancelTimeout();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -32,6 +32,24 @@ export function getOrSet<K, V>(map: Map<K, V>, key: K, value: V): V {
|
|||
return result;
|
||||
}
|
||||
|
||||
export function mapToString<K, V>(map: Map<K, V>): string {
|
||||
const entries: string[] = [];
|
||||
map.forEach((value, key) => {
|
||||
entries.push(`${key} => ${value}`);
|
||||
});
|
||||
|
||||
return `Map(${map.size}) {${entries.join(', ')}}`;
|
||||
}
|
||||
|
||||
export function setToString<K>(set: Set<K>): string {
|
||||
const entries: K[] = [];
|
||||
set.forEach(value => {
|
||||
entries.push(value);
|
||||
});
|
||||
|
||||
return `Set(${set.size}) {${entries.join(', ')}}`;
|
||||
}
|
||||
|
||||
export interface IKeyIterator {
|
||||
reset(key: string): this;
|
||||
next(): this;
|
||||
|
|
450
src/vs/base/node/storage.ts
Normal file
450
src/vs/base/node/storage.ts
Normal file
|
@ -0,0 +1,450 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Database, Statement } from 'vscode-sqlite3';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { RunOnceScheduler } from 'vs/base/common/async';
|
||||
import { isUndefinedOrNull } from 'vs/base/common/types';
|
||||
import { mapToString, setToString } from 'vs/base/common/map';
|
||||
import { basename } from 'path';
|
||||
|
||||
export interface IStorageOptions {
|
||||
path: string;
|
||||
|
||||
logging?: IStorageLoggingOptions;
|
||||
}
|
||||
|
||||
export interface IStorageLoggingOptions {
|
||||
errorLogger?: (error: string | Error) => void;
|
||||
infoLogger?: (msg: string) => void;
|
||||
|
||||
info?: boolean;
|
||||
trace?: boolean;
|
||||
profile?: boolean;
|
||||
}
|
||||
|
||||
enum StorageState {
|
||||
None,
|
||||
Initialized,
|
||||
Closed
|
||||
}
|
||||
|
||||
export class Storage extends Disposable {
|
||||
_serviceBrand: any;
|
||||
|
||||
private static readonly FLUSH_DELAY = 100;
|
||||
|
||||
private _onDidChangeStorage: Emitter<string> = this._register(new Emitter<string>());
|
||||
get onDidChangeStorage(): Event<string> { return this._onDidChangeStorage.event; }
|
||||
|
||||
private state = StorageState.None;
|
||||
|
||||
private storage: SQLiteStorageImpl;
|
||||
private cache: Map<string, string> = new Map<string, string>();
|
||||
|
||||
private pendingScheduler: RunOnceScheduler;
|
||||
private pendingDeletes: Set<string> = new Set<string>();
|
||||
private pendingInserts: Map<string, string> = new Map();
|
||||
private pendingPromises: { resolve: Function, reject: Function }[] = [];
|
||||
|
||||
constructor(options: IStorageOptions) {
|
||||
super();
|
||||
|
||||
this.storage = new SQLiteStorageImpl(options);
|
||||
|
||||
this.pendingScheduler = new RunOnceScheduler(() => this.flushPending(), Storage.FLUSH_DELAY);
|
||||
}
|
||||
|
||||
init(): Promise<void> {
|
||||
if (this.state !== StorageState.None) {
|
||||
return Promise.resolve(); // either closed or already initialized
|
||||
}
|
||||
|
||||
this.state = StorageState.Initialized;
|
||||
|
||||
return this.storage.getItems().then(items => {
|
||||
this.cache = items;
|
||||
});
|
||||
}
|
||||
|
||||
get(key: string, fallbackValue?: any): string {
|
||||
const value = this.cache.get(key);
|
||||
|
||||
if (isUndefinedOrNull(value)) {
|
||||
return fallbackValue;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
getBoolean(key: string, fallbackValue?: boolean): boolean {
|
||||
const value = this.get(key);
|
||||
|
||||
if (isUndefinedOrNull(value)) {
|
||||
return fallbackValue;
|
||||
}
|
||||
|
||||
return value === 'true';
|
||||
}
|
||||
|
||||
getInteger(key: string, fallbackValue?: number): number {
|
||||
const value = this.get(key);
|
||||
|
||||
if (isUndefinedOrNull(value)) {
|
||||
return fallbackValue;
|
||||
}
|
||||
|
||||
return parseInt(value, 10);
|
||||
}
|
||||
|
||||
set(key: string, value: any): Promise<void> {
|
||||
if (this.state === StorageState.Closed) {
|
||||
return Promise.resolve(); // Return early if we are already closed
|
||||
}
|
||||
|
||||
// We remove the key for undefined/null values
|
||||
if (isUndefinedOrNull(value)) {
|
||||
return this.delete(key);
|
||||
}
|
||||
|
||||
// Otherwise, convert to String and store
|
||||
const valueStr = String(value);
|
||||
|
||||
// Return early if value already set
|
||||
const currentValue = this.cache.get(key);
|
||||
if (currentValue === valueStr) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
// Update in cache and pending
|
||||
this.cache.set(key, valueStr);
|
||||
this.pendingInserts.set(key, valueStr);
|
||||
this.pendingDeletes.delete(key);
|
||||
|
||||
// Event
|
||||
this._onDidChangeStorage.fire(key);
|
||||
|
||||
return this.update();
|
||||
}
|
||||
|
||||
delete(key: string): Promise<void> {
|
||||
if (this.state === StorageState.Closed) {
|
||||
return Promise.resolve(); // Return early if we are already closed
|
||||
}
|
||||
|
||||
// Remove from cache and add to pending
|
||||
const wasDeleted = this.cache.delete(key);
|
||||
if (!wasDeleted) {
|
||||
return Promise.resolve(); // Return early if value already deleted
|
||||
}
|
||||
|
||||
if (!this.pendingDeletes.has(key)) {
|
||||
this.pendingDeletes.add(key);
|
||||
}
|
||||
|
||||
this.pendingInserts.delete(key);
|
||||
|
||||
// Event
|
||||
this._onDidChangeStorage.fire(key);
|
||||
|
||||
return this.update();
|
||||
}
|
||||
|
||||
private update(): Promise<void> {
|
||||
|
||||
// Schedule
|
||||
if (!this.pendingScheduler.isScheduled()) {
|
||||
this.pendingScheduler.schedule();
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => this.pendingPromises.push({ resolve, reject }));
|
||||
}
|
||||
|
||||
close(): Promise<void> {
|
||||
if (this.state === StorageState.Closed) {
|
||||
return Promise.resolve(); // return if already closed
|
||||
}
|
||||
|
||||
// Update state
|
||||
this.state = StorageState.Closed;
|
||||
|
||||
// Dispose scheduler (no more scheduling possible)
|
||||
this.pendingScheduler.dispose();
|
||||
|
||||
// Flush & close
|
||||
return this.flushPending().then(() => {
|
||||
return this.storage.close();
|
||||
});
|
||||
}
|
||||
|
||||
private flushPending(): Promise<void> {
|
||||
|
||||
// Get pending data
|
||||
const pendingPromises = this.pendingPromises;
|
||||
const pendingDeletes = this.pendingDeletes;
|
||||
const pendingInserts = this.pendingInserts;
|
||||
|
||||
// Reset pending data for next run
|
||||
this.pendingPromises = [];
|
||||
this.pendingDeletes = new Set<string>();
|
||||
this.pendingInserts = new Map<string, string>();
|
||||
|
||||
return this.storage.updateItems({ insert: pendingInserts, delete: pendingDeletes }).then(() => {
|
||||
|
||||
// Resolve pending
|
||||
pendingPromises.forEach(promise => promise.resolve());
|
||||
}, error => {
|
||||
|
||||
// Forward error to pending
|
||||
pendingPromises.forEach(promise => promise.reject(error));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export interface IUpdateRequest {
|
||||
insert?: Map<string, string>;
|
||||
delete?: Set<string>;
|
||||
}
|
||||
|
||||
export class SQLiteStorageImpl {
|
||||
private db: Promise<Database>;
|
||||
private name: string;
|
||||
private logger: SQLiteStorageLogger;
|
||||
|
||||
constructor(private options: IStorageOptions) {
|
||||
this.name = basename(options.path);
|
||||
this.logger = new SQLiteStorageLogger(options.logging);
|
||||
this.db = this.open();
|
||||
}
|
||||
|
||||
getItems(): Promise<Map<string, string>> {
|
||||
return this.db.then(db => {
|
||||
const items = new Map<string, string>();
|
||||
|
||||
return this.each(db, 'SELECT * FROM ItemTable', row => {
|
||||
items.set(row.key, row.value);
|
||||
}).then(() => {
|
||||
if (this.logger.verbose) {
|
||||
this.logger.info(`[storage ${this.name}] getItems(): ${mapToString(items)}`);
|
||||
}
|
||||
|
||||
return items;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
updateItems(request: IUpdateRequest): Promise<void> {
|
||||
let updateCount = 0;
|
||||
if (request.insert) {
|
||||
updateCount += request.insert.size;
|
||||
}
|
||||
if (request.delete) {
|
||||
updateCount += request.delete.size;
|
||||
}
|
||||
|
||||
if (updateCount === 0) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
if (this.logger.verbose) {
|
||||
this.logger.info(`[storage ${this.name}] updateItems(): insert(${request.insert ? mapToString(request.insert) : '0'}), delete(${request.delete ? setToString(request.delete) : '0'})`);
|
||||
}
|
||||
|
||||
return this.db.then(db => {
|
||||
return this.transaction(db, () => {
|
||||
if (request.insert && request.insert.size > 0) {
|
||||
this.prepare(db, 'INSERT INTO ItemTable VALUES (?,?)', stmt => {
|
||||
request.insert.forEach((value, key) => {
|
||||
stmt.run([key, value]);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (request.delete && request.delete.size) {
|
||||
this.prepare(db, 'DELETE FROM ItemTable WHERE key=?', stmt => {
|
||||
request.delete.forEach(key => {
|
||||
stmt.run(key);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
close(): Promise<void> {
|
||||
this.logger.info(`[storage ${this.name}] close()`);
|
||||
|
||||
return this.db.then(db => {
|
||||
return new Promise((resolve, reject) => {
|
||||
db.close(error => {
|
||||
if (error) {
|
||||
this.logger.error(`[storage ${this.name}] close(): ${error}`);
|
||||
|
||||
return reject(error);
|
||||
}
|
||||
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private open(): Promise<Database> {
|
||||
this.logger.info(`[storage ${this.name}] open()`);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
this.doOpen(this.options.path).then(resolve, error => {
|
||||
this.logger.error(`[storage ${this.name}] open(): Error (open DB): ${error}`);
|
||||
this.logger.error(`[storage ${this.name}] open(): Falling back to in-memory DB`);
|
||||
|
||||
// In case of any error to open the DB, use an in-memory
|
||||
// DB so that we always have a valid DB to talk to.
|
||||
this.doOpen(':memory:').then(resolve, reject);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private doOpen(path: string): Promise<Database> {
|
||||
return new Promise((resolve, reject) => {
|
||||
import('vscode-sqlite3').then(sqlite3 => {
|
||||
const db = new (this.logger.verbose ? sqlite3.verbose().Database : sqlite3.Database)(path, error => {
|
||||
if (error) {
|
||||
return reject(error);
|
||||
}
|
||||
|
||||
// Setup schema
|
||||
this.exec(db, [
|
||||
'PRAGMA user_version = 1;',
|
||||
'CREATE TABLE IF NOT EXISTS ItemTable (key TEXT UNIQUE ON CONFLICT REPLACE, value BLOB)'
|
||||
].join('')).then(() => resolve(db), error => reject(error));
|
||||
});
|
||||
|
||||
// Check for errors
|
||||
db.on('error', error => this.logger.error(`[storage ${this.name}] Error (event): ${error}`));
|
||||
|
||||
// Tracing
|
||||
if (this.logger.trace) {
|
||||
db.on('trace', sql => this.logger.info(`[storage ${this.name}] Trace (event): ${sql}`));
|
||||
}
|
||||
|
||||
// Profiling
|
||||
if (this.logger.profile) {
|
||||
db.on('profile', (sql, time) => this.logger.info(`[storage ${this.name}] Profile (event): ${sql} (${time}ms)`));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private exec(db: Database, sql: string): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
db.exec(sql, error => {
|
||||
if (error) {
|
||||
this.logger.error(`[storage ${this.name}] exec(): ${error}`);
|
||||
|
||||
return reject(error);
|
||||
}
|
||||
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private each(db: Database, sql: string, callback: (row: any) => void): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
db.each(sql, (error, row) => {
|
||||
if (error) {
|
||||
this.logger.error(`[storage ${this.name}] each(): ${error}`);
|
||||
|
||||
return reject(error);
|
||||
}
|
||||
|
||||
callback(row);
|
||||
}, error => {
|
||||
if (error) {
|
||||
this.logger.error(`[storage ${this.name}] each(): ${error}`);
|
||||
|
||||
return reject(error);
|
||||
}
|
||||
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private transaction(db: Database, transactions: () => void): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
db.serialize(() => {
|
||||
db.run('BEGIN TRANSACTION');
|
||||
|
||||
transactions();
|
||||
|
||||
db.run('END TRANSACTION', error => {
|
||||
if (error) {
|
||||
this.logger.error(`[storage ${this.name}] transaction(): ${error}`);
|
||||
|
||||
return reject(error);
|
||||
}
|
||||
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private prepare(db: Database, sql: string, runCallback: (stmt: Statement) => void): void {
|
||||
const stmt = db.prepare(sql);
|
||||
|
||||
runCallback(stmt);
|
||||
|
||||
const statementErrorListener = error => {
|
||||
this.logger.error(`[storage ${this.name}] prepare(): ${error} (${sql})`);
|
||||
};
|
||||
|
||||
stmt.on('error', statementErrorListener);
|
||||
|
||||
stmt.finalize(error => {
|
||||
if (error) {
|
||||
statementErrorListener(error);
|
||||
}
|
||||
|
||||
stmt.removeListener('error', statementErrorListener);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class SQLiteStorageLogger {
|
||||
private logInfo: boolean;
|
||||
private logError: boolean;
|
||||
|
||||
constructor(private options?: IStorageLoggingOptions) {
|
||||
this.logInfo = this.verbose && options && !!options.infoLogger;
|
||||
this.logError = options && !!options.errorLogger;
|
||||
}
|
||||
|
||||
get verbose(): boolean {
|
||||
return this.options && (this.options.info || this.options.trace || this.options.profile);
|
||||
}
|
||||
|
||||
get trace(): boolean {
|
||||
return this.options && this.options.trace;
|
||||
}
|
||||
|
||||
get profile(): boolean {
|
||||
return this.options && this.options.profile;
|
||||
}
|
||||
|
||||
info(msg: string): void {
|
||||
if (this.logInfo) {
|
||||
this.options.infoLogger(msg);
|
||||
}
|
||||
}
|
||||
|
||||
error(error: string | Error): void {
|
||||
if (this.logError) {
|
||||
this.options.errorLogger(error);
|
||||
}
|
||||
}
|
||||
}
|
6
src/vs/base/test/node/storage/broken.db
Normal file
6
src/vs/base/test/node/storage/broken.db
Normal file
|
@ -0,0 +1,6 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
this is not a sqlite DB
|
390
src/vs/base/test/node/storage/storage.test.ts
Normal file
390
src/vs/base/test/node/storage/storage.test.ts
Normal file
File diff suppressed because one or more lines are too long
|
@ -53,14 +53,9 @@ bootstrapWindow.load([
|
|||
function showPartsSplash(configuration) {
|
||||
perf.mark('willShowPartsSplash');
|
||||
|
||||
// TODO@Ben remove me after a while
|
||||
perf.mark('willAccessLocalStorage');
|
||||
let storage = window.localStorage;
|
||||
perf.mark('didAccessLocalStorage');
|
||||
|
||||
let data;
|
||||
try {
|
||||
let raw = storage.getItem('storage://global/parts-splash-data');
|
||||
let raw = window.localStorage.getItem('storage://global/parts-splash-data');
|
||||
data = JSON.parse(raw);
|
||||
} catch (e) {
|
||||
// ignore
|
||||
|
@ -134,6 +129,7 @@ function getLazyEnv() {
|
|||
ipc.once('vscode:acceptShellEnv', function (event, shellEnv) {
|
||||
clearTimeout(handle);
|
||||
bootstrapWindow.assign(process.env, shellEnv);
|
||||
// @ts-ignore
|
||||
resolve(process.env);
|
||||
});
|
||||
|
||||
|
|
|
@ -322,7 +322,7 @@ export class WindowsManager implements IWindowsMainService {
|
|||
|
||||
// See note on #onBeforeShutdown() for details how these events are flowing
|
||||
private onBeforeWindowClose(win: ICodeWindow): void {
|
||||
if (this.lifecycleService.isQuitRequested) {
|
||||
if (this.lifecycleService.quitRequested) {
|
||||
return; // during quit, many windows close in parallel so let it be handled in the before-quit handler
|
||||
}
|
||||
|
||||
|
@ -1174,6 +1174,7 @@ export class WindowsManager implements IWindowsMainService {
|
|||
const configuration: IWindowConfiguration = mixin({}, options.cli); // inherit all properties from CLI
|
||||
configuration.appRoot = this.environmentService.appRoot;
|
||||
configuration.machineId = this.machineId;
|
||||
configuration.nodeCachedDataDir = this.environmentService.nodeCachedDataDir;
|
||||
configuration.mainPid = process.pid;
|
||||
configuration.execPath = process.execPath;
|
||||
configuration.userEnv = assign({}, this.initialUserEnv, options.userEnv || {});
|
||||
|
@ -1188,7 +1189,6 @@ export class WindowsManager implements IWindowsMainService {
|
|||
configuration.filesToDiff = fileInputs.filesToDiff;
|
||||
configuration.filesToWait = fileInputs.filesToWait;
|
||||
}
|
||||
configuration.nodeCachedDataDir = this.environmentService.nodeCachedDataDir;
|
||||
|
||||
// if we know the backup folder upfront (for empty windows to restore), we can set it
|
||||
// directly here which helps for restoring UI state associated with that window.
|
||||
|
|
|
@ -72,7 +72,7 @@ export class CommonFindController extends Disposable implements editorCommon.IEd
|
|||
protected _state: FindReplaceState;
|
||||
protected _updateHistoryDelayer: Delayer<void>;
|
||||
private _model: FindModelBoundToEditorModel;
|
||||
protected _storageService: IStorageService;
|
||||
private _storageService: IStorageService;
|
||||
private _clipboardService: IClipboardService;
|
||||
protected readonly _contextKeyService: IContextKeyService;
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ import { withTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor';
|
|||
import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
|
||||
import { Delayer } from 'vs/base/common/async';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
|
@ -59,9 +60,14 @@ suite('FindController', () => {
|
|||
let clipboardState = '';
|
||||
let serviceCollection = new ServiceCollection();
|
||||
serviceCollection.set(IStorageService, {
|
||||
_serviceBrand: undefined,
|
||||
onDidChangeStorage: Event.None,
|
||||
onWillSaveState: Event.None,
|
||||
get: (key: string) => queryState[key],
|
||||
getBoolean: (key: string) => !!queryState[key],
|
||||
store: (key: string, value: any) => { queryState[key] = value; }
|
||||
getInteger: (key: string) => undefined,
|
||||
store: (key: string, value: any) => { queryState[key] = value; return Promise.resolve(); },
|
||||
remove: (key) => void 0
|
||||
} as IStorageService);
|
||||
|
||||
if (platform.isMacintosh) {
|
||||
|
@ -429,9 +435,14 @@ suite('FindController query options persistence', () => {
|
|||
queryState['editor.wholeWord'] = false;
|
||||
let serviceCollection = new ServiceCollection();
|
||||
serviceCollection.set(IStorageService, {
|
||||
_serviceBrand: undefined,
|
||||
onDidChangeStorage: Event.None,
|
||||
onWillSaveState: Event.None,
|
||||
get: (key: string) => queryState[key],
|
||||
getBoolean: (key: string) => !!queryState[key],
|
||||
store: (key: string, value: any) => { queryState[key] = value; }
|
||||
getInteger: (key: string) => undefined,
|
||||
store: (key: string, value: any) => { queryState[key] = value; return Promise.resolve(); },
|
||||
remove: (key) => void 0
|
||||
} as IStorageService);
|
||||
|
||||
test('matchCase', () => {
|
||||
|
|
|
@ -10,6 +10,7 @@ import { InsertCursorAbove, InsertCursorBelow, MultiCursorSelectionController, S
|
|||
import { Handler } from 'vs/editor/common/editorCommon';
|
||||
import { EndOfLineSequence } from 'vs/editor/common/model';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
|
||||
import { CommonFindController } from 'vs/editor/contrib/find/findController';
|
||||
|
||||
|
@ -59,9 +60,14 @@ suite('Multicursor selection', () => {
|
|||
let queryState: { [key: string]: any; } = {};
|
||||
let serviceCollection = new ServiceCollection();
|
||||
serviceCollection.set(IStorageService, {
|
||||
_serviceBrand: undefined,
|
||||
onDidChangeStorage: Event.None,
|
||||
onWillSaveState: Event.None,
|
||||
get: (key: string) => queryState[key],
|
||||
getBoolean: (key: string) => !!queryState[key],
|
||||
store: (key: string, value: any) => { queryState[key] = value; }
|
||||
getInteger: (key: string) => undefined,
|
||||
store: (key: string, value: any) => { queryState[key] = value; return Promise.resolve(); },
|
||||
remove: (key) => void 0
|
||||
} as IStorageService);
|
||||
|
||||
test('issue #8817: Cursor position changes when you cancel multicursor', () => {
|
||||
|
|
|
@ -10,7 +10,7 @@ import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService
|
|||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import * as editorCommon from 'vs/editor/common/editorCommon';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { ReferencesModel } from './referencesModel';
|
||||
|
@ -95,14 +95,14 @@ export abstract class ReferencesController implements editorCommon.IEditorContri
|
|||
}
|
||||
}));
|
||||
const storageKey = 'peekViewLayout';
|
||||
const data = <LayoutData>JSON.parse(this._storageService.get(storageKey, undefined, '{}'));
|
||||
const data = <LayoutData>JSON.parse(this._storageService.get(storageKey, StorageScope.GLOBAL, '{}'));
|
||||
this._widget = this._instantiationService.createInstance(ReferenceWidget, this._editor, this._defaultTreeKeyboardSupport, data);
|
||||
this._widget.setTitle(nls.localize('labelLoading', "Loading..."));
|
||||
this._widget.show(range);
|
||||
this._disposables.push(this._widget.onDidClose(() => {
|
||||
modelPromise.cancel();
|
||||
|
||||
this._storageService.store(storageKey, JSON.stringify(this._widget.layoutData));
|
||||
this._storageService.store(storageKey, JSON.stringify(this._widget.layoutData), StorageScope.GLOBAL);
|
||||
this._widget = null;
|
||||
this.closeWidget();
|
||||
}));
|
||||
|
|
|
@ -8,10 +8,9 @@ import { LRUCache, TernarySearchTree } from 'vs/base/common/map';
|
|||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { IPosition } from 'vs/editor/common/core/position';
|
||||
import { RunOnceScheduler } from 'vs/base/common/async';
|
||||
import { CompletionItemKind, completionKindFromLegacyString } from 'vs/editor/common/modes';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
|
||||
export abstract class Memory {
|
||||
|
||||
|
@ -194,26 +193,22 @@ export class PrefixMemory extends Memory {
|
|||
|
||||
export type MemMode = 'first' | 'recentlyUsed' | 'recentlyUsedByPrefix';
|
||||
|
||||
export class SuggestMemories {
|
||||
export class SuggestMemories extends Disposable {
|
||||
|
||||
private readonly _storagePrefix = 'suggest/memories';
|
||||
|
||||
private _mode: MemMode;
|
||||
private _strategy: Memory;
|
||||
private readonly _persistSoon: RunOnceScheduler;
|
||||
private readonly _listener: IDisposable;
|
||||
|
||||
constructor(
|
||||
editor: ICodeEditor,
|
||||
@IStorageService private readonly _storageService: IStorageService,
|
||||
) {
|
||||
this._persistSoon = new RunOnceScheduler(() => this._flush(), 3000);
|
||||
this._setMode(editor.getConfiguration().contribInfo.suggestSelection);
|
||||
this._listener = editor.onDidChangeConfiguration(e => e.contribInfo && this._setMode(editor.getConfiguration().contribInfo.suggestSelection));
|
||||
}
|
||||
super();
|
||||
|
||||
dispose(): void {
|
||||
this._listener.dispose();
|
||||
this._setMode(editor.getConfiguration().contribInfo.suggestSelection);
|
||||
this._register(editor.onDidChangeConfiguration(e => e.contribInfo && this._setMode(editor.getConfiguration().contribInfo.suggestSelection)));
|
||||
this._register(_storageService.onWillSaveState(() => this._saveState()));
|
||||
}
|
||||
|
||||
private _setMode(mode: MemMode): void {
|
||||
|
@ -235,15 +230,14 @@ export class SuggestMemories {
|
|||
|
||||
memorize(model: ITextModel, pos: IPosition, item: ICompletionItem): void {
|
||||
this._strategy.memorize(model, pos, item);
|
||||
this._persistSoon.schedule();
|
||||
}
|
||||
|
||||
select(model: ITextModel, pos: IPosition, items: ICompletionItem[]): number {
|
||||
return this._strategy.select(model, pos, items);
|
||||
}
|
||||
|
||||
private _flush() {
|
||||
private _saveState() {
|
||||
const raw = JSON.stringify(this._strategy);
|
||||
this._storageService.store(`${this._storagePrefix}/${this._mode}`, raw, StorageScope.WORKSPACE);
|
||||
this._storageService.store(`${this._storagePrefix}/${this._mode}`, raw, StorageScope.WORKSPACE); //
|
||||
}
|
||||
}
|
||||
|
|
|
@ -401,9 +401,6 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<IComp
|
|||
private detailsFocusBorderColor: string;
|
||||
private detailsBorderColor: string;
|
||||
|
||||
private storageServiceAvailable: boolean = true;
|
||||
private expandSuggestionDocs: boolean = false;
|
||||
|
||||
private firstFocusInCurrentList: boolean = false;
|
||||
|
||||
constructor(
|
||||
|
@ -424,13 +421,6 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<IComp
|
|||
this.focusedItem = null;
|
||||
this.storageService = storageService;
|
||||
|
||||
if (this.expandDocsSettingFromStorage() === undefined) {
|
||||
this.storageService.store('expandSuggestionDocs', expandSuggestionDocsByDefault, StorageScope.GLOBAL);
|
||||
if (this.expandDocsSettingFromStorage() === undefined) {
|
||||
this.storageServiceAvailable = false;
|
||||
}
|
||||
}
|
||||
|
||||
this.element = $('.editor-widget.suggest-widget');
|
||||
if (!this.editor.getConfiguration().contribInfo.iconsInSuggestions) {
|
||||
addClass(this.element, 'no-icons');
|
||||
|
@ -1053,22 +1043,12 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<IComp
|
|||
return 'suggestion';
|
||||
}
|
||||
|
||||
// Monaco Editor does not have a storage service
|
||||
private expandDocsSettingFromStorage(): boolean {
|
||||
if (this.storageServiceAvailable) {
|
||||
return this.storageService.getBoolean('expandSuggestionDocs', StorageScope.GLOBAL);
|
||||
} else {
|
||||
return this.expandSuggestionDocs;
|
||||
}
|
||||
return this.storageService.getBoolean('expandSuggestionDocs', StorageScope.GLOBAL, expandSuggestionDocsByDefault);
|
||||
}
|
||||
|
||||
// Monaco Editor does not have a storage service
|
||||
private updateExpandDocsSetting(value: boolean) {
|
||||
if (this.storageServiceAvailable) {
|
||||
this.storageService.store('expandSuggestionDocs', value, StorageScope.GLOBAL);
|
||||
} else {
|
||||
this.expandSuggestionDocs = value;
|
||||
}
|
||||
this.storageService.store('expandSuggestionDocs', value, StorageScope.GLOBAL);
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
|
|
|
@ -32,7 +32,7 @@ export class StandaloneReferencesController extends ReferencesController {
|
|||
notificationService,
|
||||
instantiationService,
|
||||
storageService,
|
||||
configurationService,
|
||||
configurationService
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,6 +35,7 @@ export interface ParsedArgs {
|
|||
'trace-options'?: string;
|
||||
log?: string;
|
||||
logExtensionHostCommunication?: boolean;
|
||||
logStorage?: boolean;
|
||||
'extensions-dir'?: string;
|
||||
'builtin-extensions-dir'?: string;
|
||||
extensionDevelopmentPath?: string;
|
||||
|
@ -117,8 +118,8 @@ export interface IEnvironmentService {
|
|||
debugExtensionHost: IExtensionHostDebugParams;
|
||||
debugSearch: IDebugParams;
|
||||
|
||||
|
||||
logExtensionHostCommunication: boolean;
|
||||
logStorage: boolean;
|
||||
|
||||
isBuilt: boolean;
|
||||
wait: boolean;
|
||||
|
|
|
@ -54,6 +54,7 @@ const options: minimist.Opts = {
|
|||
'prof-startup',
|
||||
'verbose',
|
||||
'logExtensionHostCommunication',
|
||||
'logStorage',
|
||||
'disable-extensions',
|
||||
'list-extensions',
|
||||
'show-versions',
|
||||
|
|
|
@ -206,7 +206,9 @@ export class EnvironmentService implements IEnvironmentService {
|
|||
get log(): string { return this._args.log; }
|
||||
|
||||
get wait(): boolean { return this._args.wait; }
|
||||
|
||||
get logExtensionHostCommunication(): boolean { return this._args.logExtensionHostCommunication; }
|
||||
get logStorage(): boolean { return this._args.logStorage; }
|
||||
|
||||
get performance(): boolean { return this._args.performance; }
|
||||
get status(): boolean { return this._args.status; }
|
||||
|
|
|
@ -8,10 +8,10 @@ import { IExtensionManagementService, IExtensionEnablementService, DidUninstallE
|
|||
import { ExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionEnablementService';
|
||||
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { StorageService, InMemoryLocalStorage } from 'vs/platform/storage/common/storageService';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { TestStorageService } from 'vs/workbench/test/workbenchTestServices';
|
||||
|
||||
function storageService(instantiationService: TestInstantiationService): IStorageService {
|
||||
let service = instantiationService.get(IStorageService);
|
||||
|
@ -22,12 +22,11 @@ function storageService(instantiationService: TestInstantiationService): IStorag
|
|||
getWorkbenchState: () => WorkbenchState.FOLDER,
|
||||
});
|
||||
}
|
||||
service = instantiationService.stub(IStorageService, instantiationService.createInstance(StorageService, new InMemoryLocalStorage(), new InMemoryLocalStorage()));
|
||||
service = instantiationService.stub(IStorageService, new TestStorageService());
|
||||
}
|
||||
return service;
|
||||
}
|
||||
|
||||
|
||||
export class TestExtensionEnablementService extends ExtensionEnablementService {
|
||||
constructor(instantiationService: TestInstantiationService) {
|
||||
super(storageService(instantiationService), instantiationService.get(IWorkspaceContextService),
|
|
@ -22,16 +22,16 @@ interface IStorageData {
|
|||
class IntegrityStorage {
|
||||
private static readonly KEY = 'integrityService';
|
||||
|
||||
private _storageService: IStorageService;
|
||||
private _value: IStorageData;
|
||||
private storageService: IStorageService;
|
||||
private value: IStorageData;
|
||||
|
||||
constructor(storageService: IStorageService) {
|
||||
this._storageService = storageService;
|
||||
this._value = this._read();
|
||||
this.storageService = storageService;
|
||||
this.value = this._read();
|
||||
}
|
||||
|
||||
private _read(): IStorageData {
|
||||
let jsonValue = this._storageService.get(IntegrityStorage.KEY, StorageScope.GLOBAL);
|
||||
let jsonValue = this.storageService.get(IntegrityStorage.KEY, StorageScope.GLOBAL);
|
||||
if (!jsonValue) {
|
||||
return null;
|
||||
}
|
||||
|
@ -42,19 +42,19 @@ class IntegrityStorage {
|
|||
}
|
||||
}
|
||||
|
||||
public get(): IStorageData {
|
||||
return this._value;
|
||||
get(): IStorageData {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
public set(data: IStorageData): void {
|
||||
this._value = data;
|
||||
this._storageService.store(IntegrityStorage.KEY, JSON.stringify(this._value), StorageScope.GLOBAL);
|
||||
set(data: IStorageData): void {
|
||||
this.value = data;
|
||||
this.storageService.store(IntegrityStorage.KEY, JSON.stringify(this.value), StorageScope.GLOBAL);
|
||||
}
|
||||
}
|
||||
|
||||
export class IntegrityServiceImpl implements IIntegrityService {
|
||||
|
||||
public _serviceBrand: any;
|
||||
_serviceBrand: any;
|
||||
|
||||
private _storage: IntegrityStorage;
|
||||
private _isPurePromise: Thenable<IntegrityTestResult>;
|
||||
|
@ -101,7 +101,7 @@ export class IntegrityServiceImpl implements IIntegrityService {
|
|||
);
|
||||
}
|
||||
|
||||
public isPure(): Thenable<IntegrityTestResult> {
|
||||
isPure(): Thenable<IntegrityTestResult> {
|
||||
return this._isPurePromise;
|
||||
}
|
||||
|
||||
|
|
|
@ -11,19 +11,43 @@ import { isThenable } from 'vs/base/common/async';
|
|||
export const ILifecycleService = createDecorator<ILifecycleService>('lifecycleService');
|
||||
|
||||
/**
|
||||
* An event that is send out when the window is about to close. Clients have a chance to veto the closing by either calling veto
|
||||
* with a boolean "true" directly or with a promise that resolves to a boolean. Returning a promise is useful
|
||||
* in cases of long running operations on shutdown.
|
||||
* An event that is send out when the window is about to close. Clients have a chance to veto
|
||||
* the closing by either calling veto with a boolean "true" directly or with a promise that
|
||||
* resolves to a boolean. Returning a promise is useful in cases of long running operations
|
||||
* on shutdown.
|
||||
*
|
||||
* Note: It is absolutely important to avoid long running promises on this call. Please try hard to return
|
||||
* a boolean directly. Returning a promise has quite an impact on the shutdown sequence!
|
||||
* Note: It is absolutely important to avoid long running promises if possible. Please try hard
|
||||
* to return a boolean directly. Returning a promise has quite an impact on the shutdown sequence!
|
||||
*/
|
||||
export interface WillShutdownEvent {
|
||||
|
||||
/**
|
||||
* Allows to veto the shutdown. The veto can be a long running operation but it
|
||||
* will block the application from closing.
|
||||
*/
|
||||
veto(value: boolean | Thenable<boolean>): void;
|
||||
|
||||
/**
|
||||
* The reason why Code will be shutting down.
|
||||
*/
|
||||
reason: ShutdownReason;
|
||||
}
|
||||
|
||||
/**
|
||||
* An event that is send out when the window closes. Clients have a chance to join the closing
|
||||
* by providing a promise from the join method. Returning a promise is useful in cases of long
|
||||
* running operations on shutdown.
|
||||
*
|
||||
* Note: It is absolutely important to avoid long running promises if possible. Please try hard
|
||||
* to return a boolean directly. Returning a promise has quite an impact on the shutdown sequence!
|
||||
*/
|
||||
export interface ShutdownEvent {
|
||||
|
||||
/**
|
||||
* Allows to veto the shutdown. The veto can be a long running operation.
|
||||
* Allows to join the shutdown. The promise can be a long running operation but it
|
||||
* will block the application from closing.
|
||||
*/
|
||||
veto(value: boolean | Thenable<boolean>): void;
|
||||
join(promise: Thenable<void>): void;
|
||||
|
||||
/**
|
||||
* The reason why Code is shutting down.
|
||||
|
@ -51,6 +75,7 @@ export const enum StartupKind {
|
|||
ReloadedWindow = 3,
|
||||
ReopenedWindow = 4,
|
||||
}
|
||||
|
||||
export function StartupKindToString(startupKind: StartupKind): string {
|
||||
switch (startupKind) {
|
||||
case StartupKind.NewWindow: return 'NewWindow';
|
||||
|
@ -65,6 +90,7 @@ export const enum LifecyclePhase {
|
|||
Running = 3,
|
||||
Eventually = 4
|
||||
}
|
||||
|
||||
export function LifecyclePhaseToString(phase: LifecyclePhase) {
|
||||
switch (phase) {
|
||||
case LifecyclePhase.Starting: return 'Starting';
|
||||
|
@ -92,17 +118,11 @@ export interface ILifecycleService {
|
|||
*/
|
||||
readonly phase: LifecyclePhase;
|
||||
|
||||
/**
|
||||
* Returns a promise that resolves when a certain lifecycle phase
|
||||
* has started.
|
||||
*/
|
||||
when(phase: LifecyclePhase): Thenable<void>;
|
||||
|
||||
/**
|
||||
* Fired before shutdown happens. Allows listeners to veto against the
|
||||
* shutdown.
|
||||
*/
|
||||
readonly onWillShutdown: Event<ShutdownEvent>;
|
||||
readonly onWillShutdown: Event<WillShutdownEvent>;
|
||||
|
||||
/**
|
||||
* Fired when no client is preventing the shutdown from happening. Can be used to dispose heavy resources
|
||||
|
@ -110,16 +130,22 @@ export interface ILifecycleService {
|
|||
*
|
||||
* The event carries a shutdown reason that indicates how the shutdown was triggered.
|
||||
*/
|
||||
readonly onShutdown: Event<ShutdownReason>;
|
||||
readonly onShutdown: Event<ShutdownEvent>;
|
||||
|
||||
/**
|
||||
* Returns a promise that resolves when a certain lifecycle phase
|
||||
* has started.
|
||||
*/
|
||||
when(phase: LifecyclePhase): Thenable<void>;
|
||||
}
|
||||
|
||||
export const NullLifecycleService: ILifecycleService = {
|
||||
_serviceBrand: null,
|
||||
phase: LifecyclePhase.Running,
|
||||
when() { return Promise.resolve(); },
|
||||
startupKind: StartupKind.NewWindow,
|
||||
onWillShutdown: Event.None,
|
||||
onShutdown: Event.None
|
||||
onShutdown: Event.None,
|
||||
phase: LifecyclePhase.Running,
|
||||
startupKind: StartupKind.NewWindow,
|
||||
when() { return Promise.resolve(); }
|
||||
};
|
||||
|
||||
// Shared veto handling across main and renderer
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { toErrorMessage } from 'vs/base/common/errorMessage';
|
||||
import { ILifecycleService, ShutdownEvent, ShutdownReason, StartupKind, LifecyclePhase, handleVetos, LifecyclePhaseToString } from 'vs/platform/lifecycle/common/lifecycle';
|
||||
import { ILifecycleService, WillShutdownEvent, ShutdownReason, StartupKind, LifecyclePhase, handleVetos, LifecyclePhaseToString, ShutdownEvent } from 'vs/platform/lifecycle/common/lifecycle';
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import { ipcRenderer as ipc } from 'electron';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
|
@ -14,59 +14,78 @@ import { mark } from 'vs/base/common/performance';
|
|||
import { Barrier } from 'vs/base/common/async';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
|
||||
export class LifecycleService implements ILifecycleService {
|
||||
export class LifecycleService extends Disposable implements ILifecycleService {
|
||||
|
||||
private static readonly _lastShutdownReasonKey = 'lifecyle.lastShutdownReason';
|
||||
private static readonly LAST_SHUTDOWN_REASON_KEY = 'lifecyle.lastShutdownReason';
|
||||
|
||||
_serviceBrand: any;
|
||||
|
||||
private readonly _onWillShutdown = new Emitter<ShutdownEvent>();
|
||||
private readonly _onShutdown = new Emitter<ShutdownReason>();
|
||||
private readonly _onWillShutdown = this._register(new Emitter<WillShutdownEvent>());
|
||||
get onWillShutdown(): Event<WillShutdownEvent> { return this._onWillShutdown.event; }
|
||||
|
||||
private readonly _onShutdown = this._register(new Emitter<ShutdownEvent>());
|
||||
get onShutdown(): Event<ShutdownEvent> { return this._onShutdown.event; }
|
||||
|
||||
private readonly _startupKind: StartupKind;
|
||||
get startupKind(): StartupKind { return this._startupKind; }
|
||||
|
||||
private _phase: LifecyclePhase = LifecyclePhase.Starting;
|
||||
private _phaseWhen = new Map<LifecyclePhase, Barrier>();
|
||||
get phase(): LifecyclePhase { return this._phase; }
|
||||
|
||||
private phaseWhen = new Map<LifecyclePhase, Barrier>();
|
||||
|
||||
constructor(
|
||||
@INotificationService private readonly _notificationService: INotificationService,
|
||||
@IWindowService private readonly _windowService: IWindowService,
|
||||
@IStorageService private readonly _storageService: IStorageService,
|
||||
@ILogService private readonly _logService: ILogService
|
||||
@INotificationService private notificationService: INotificationService,
|
||||
@IWindowService private windowService: IWindowService,
|
||||
@IStorageService private storageService: IStorageService,
|
||||
@ILogService private logService: ILogService
|
||||
) {
|
||||
const lastShutdownReason = this._storageService.getInteger(LifecycleService._lastShutdownReasonKey, StorageScope.WORKSPACE);
|
||||
this._storageService.remove(LifecycleService._lastShutdownReasonKey, StorageScope.WORKSPACE);
|
||||
if (lastShutdownReason === ShutdownReason.RELOAD) {
|
||||
this._startupKind = StartupKind.ReloadedWindow;
|
||||
} else if (lastShutdownReason === ShutdownReason.LOAD) {
|
||||
this._startupKind = StartupKind.ReopenedWindow;
|
||||
} else {
|
||||
this._startupKind = StartupKind.NewWindow;
|
||||
}
|
||||
super();
|
||||
|
||||
this._logService.trace(`lifecycle: starting up (startup kind: ${this._startupKind})`);
|
||||
this._startupKind = this.resolveStartupKind();
|
||||
|
||||
this._registerListeners();
|
||||
this.registerListeners();
|
||||
}
|
||||
|
||||
private _registerListeners(): void {
|
||||
const windowId = this._windowService.getCurrentWindowId();
|
||||
private resolveStartupKind(): StartupKind {
|
||||
const lastShutdownReason = this.storageService.getInteger(LifecycleService.LAST_SHUTDOWN_REASON_KEY, StorageScope.WORKSPACE);
|
||||
this.storageService.remove(LifecycleService.LAST_SHUTDOWN_REASON_KEY, StorageScope.WORKSPACE);
|
||||
|
||||
let startupKind: StartupKind;
|
||||
if (lastShutdownReason === ShutdownReason.RELOAD) {
|
||||
startupKind = StartupKind.ReloadedWindow;
|
||||
} else if (lastShutdownReason === ShutdownReason.LOAD) {
|
||||
startupKind = StartupKind.ReopenedWindow;
|
||||
} else {
|
||||
startupKind = StartupKind.NewWindow;
|
||||
}
|
||||
|
||||
this.logService.trace(`lifecycle: starting up (startup kind: ${this._startupKind})`);
|
||||
|
||||
return startupKind;
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
const windowId = this.windowService.getCurrentWindowId();
|
||||
|
||||
// Main side indicates that window is about to unload, check for vetos
|
||||
ipc.on('vscode:onBeforeUnload', (event, reply: { okChannel: string, cancelChannel: string, reason: ShutdownReason }) => {
|
||||
this._logService.trace(`lifecycle: onBeforeUnload (reason: ${reply.reason})`);
|
||||
this.logService.trace(`lifecycle: onBeforeUnload (reason: ${reply.reason})`);
|
||||
|
||||
// store shutdown reason to retrieve next startup
|
||||
this._storageService.store(LifecycleService._lastShutdownReasonKey, JSON.stringify(reply.reason), StorageScope.WORKSPACE);
|
||||
this.storageService.store(LifecycleService.LAST_SHUTDOWN_REASON_KEY, JSON.stringify(reply.reason), StorageScope.WORKSPACE);
|
||||
|
||||
// trigger onWillShutdown events and veto collecting
|
||||
this.onBeforeUnload(reply.reason).then(veto => {
|
||||
this.handleWillShutdown(reply.reason).then(veto => {
|
||||
if (veto) {
|
||||
this._logService.trace('lifecycle: onBeforeUnload prevented via veto');
|
||||
this._storageService.remove(LifecycleService._lastShutdownReasonKey, StorageScope.WORKSPACE);
|
||||
this.logService.trace('lifecycle: onBeforeUnload prevented via veto');
|
||||
this.storageService.remove(LifecycleService.LAST_SHUTDOWN_REASON_KEY, StorageScope.WORKSPACE);
|
||||
ipc.send(reply.cancelChannel, windowId);
|
||||
} else {
|
||||
this._logService.trace('lifecycle: onBeforeUnload continues without veto');
|
||||
this.logService.trace('lifecycle: onBeforeUnload continues without veto');
|
||||
ipc.send(reply.okChannel, windowId);
|
||||
}
|
||||
});
|
||||
|
@ -74,14 +93,16 @@ export class LifecycleService implements ILifecycleService {
|
|||
|
||||
// Main side indicates that we will indeed shutdown
|
||||
ipc.on('vscode:onWillUnload', (event, reply: { replyChannel: string, reason: ShutdownReason }) => {
|
||||
this._logService.trace(`lifecycle: onWillUnload (reason: ${reply.reason})`);
|
||||
this.logService.trace(`lifecycle: onWillUnload (reason: ${reply.reason})`);
|
||||
|
||||
this._onShutdown.fire(reply.reason);
|
||||
ipc.send(reply.replyChannel, windowId);
|
||||
// trigger onShutdown events and joining
|
||||
return this.handleShutdown(reply.reason).then(() => {
|
||||
ipc.send(reply.replyChannel, windowId);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private onBeforeUnload(reason: ShutdownReason): TPromise<boolean> {
|
||||
private handleWillShutdown(reason: ShutdownReason): TPromise<boolean> {
|
||||
const vetos: (boolean | Thenable<boolean>)[] = [];
|
||||
|
||||
this._onWillShutdown.fire({
|
||||
|
@ -91,11 +112,28 @@ export class LifecycleService implements ILifecycleService {
|
|||
reason
|
||||
});
|
||||
|
||||
return handleVetos(vetos, err => this._notificationService.error(toErrorMessage(err)));
|
||||
return handleVetos(vetos, err => {
|
||||
this.notificationService.error(toErrorMessage(err));
|
||||
onUnexpectedError(err);
|
||||
});
|
||||
}
|
||||
|
||||
get phase(): LifecyclePhase {
|
||||
return this._phase;
|
||||
private handleShutdown(reason: ShutdownReason): Thenable<void> {
|
||||
const joiners: Thenable<void>[] = [];
|
||||
|
||||
this._onShutdown.fire({
|
||||
join(promise) {
|
||||
if (promise) {
|
||||
joiners.push(promise);
|
||||
}
|
||||
},
|
||||
reason
|
||||
});
|
||||
|
||||
return TPromise.join(joiners).then(() => void 0, err => {
|
||||
this.notificationService.error(toErrorMessage(err));
|
||||
onUnexpectedError(err);
|
||||
});
|
||||
}
|
||||
|
||||
set phase(value: LifecyclePhase) {
|
||||
|
@ -107,14 +145,14 @@ export class LifecycleService implements ILifecycleService {
|
|||
return;
|
||||
}
|
||||
|
||||
this._logService.trace(`lifecycle: phase changed (value: ${value})`);
|
||||
this.logService.trace(`lifecycle: phase changed (value: ${value})`);
|
||||
|
||||
this._phase = value;
|
||||
mark(`LifecyclePhase/${LifecyclePhaseToString(value)}`);
|
||||
|
||||
if (this._phaseWhen.has(this._phase)) {
|
||||
this._phaseWhen.get(this._phase).open();
|
||||
this._phaseWhen.delete(this._phase);
|
||||
if (this.phaseWhen.has(this._phase)) {
|
||||
this.phaseWhen.get(this._phase).open();
|
||||
this.phaseWhen.delete(this._phase);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -123,24 +161,12 @@ export class LifecycleService implements ILifecycleService {
|
|||
return Promise.resolve();
|
||||
}
|
||||
|
||||
let barrier = this._phaseWhen.get(phase);
|
||||
let barrier = this.phaseWhen.get(phase);
|
||||
if (!barrier) {
|
||||
barrier = new Barrier();
|
||||
this._phaseWhen.set(phase, barrier);
|
||||
this.phaseWhen.set(phase, barrier);
|
||||
}
|
||||
|
||||
return barrier.wait();
|
||||
}
|
||||
|
||||
get startupKind(): StartupKind {
|
||||
return this._startupKind;
|
||||
}
|
||||
|
||||
get onWillShutdown(): Event<ShutdownEvent> {
|
||||
return this._onWillShutdown.event;
|
||||
}
|
||||
|
||||
get onShutdown(): Event<ShutdownReason> {
|
||||
return this._onShutdown.event;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ import { ICodeWindow } from 'vs/platform/windows/electron-main/windows';
|
|||
import { ReadyState } from 'vs/platform/windows/common/windows';
|
||||
import { handleVetos } from 'vs/platform/lifecycle/common/lifecycle';
|
||||
import { isMacintosh, isWindows } from 'vs/base/common/platform';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
|
||||
export const ILifecycleService = createDecorator<ILifecycleService>('lifecycleService');
|
||||
|
||||
|
@ -40,7 +41,7 @@ export interface ILifecycleService {
|
|||
/**
|
||||
* Will be true if the program was requested to quit.
|
||||
*/
|
||||
isQuitRequested: boolean;
|
||||
quitRequested: boolean;
|
||||
|
||||
/**
|
||||
* Due to the way we handle lifecycle with eventing, the general app.on('before-quit')
|
||||
|
@ -68,53 +69,65 @@ export interface ILifecycleService {
|
|||
*/
|
||||
onBeforeWindowUnload: Event<IWindowUnloadEvent>;
|
||||
|
||||
ready(): void;
|
||||
registerWindow(window: ICodeWindow): void;
|
||||
|
||||
/**
|
||||
* Close a window for the provided reason. Shutdown handlers are triggered.
|
||||
*/
|
||||
unload(window: ICodeWindow, reason: UnloadReason): TPromise<boolean /* veto */>;
|
||||
|
||||
/**
|
||||
* Restart the application with optional arguments (CLI). Shutdown handlers are triggered.
|
||||
*/
|
||||
relaunch(options?: { addArgs?: string[], removeArgs?: string[] }): void;
|
||||
|
||||
/**
|
||||
* Shutdown the application normally. Shutdown handlers are triggered.
|
||||
*/
|
||||
quit(fromUpdate?: boolean): TPromise<boolean /* veto */>;
|
||||
|
||||
/**
|
||||
* Forcefully shutdown the application. No shutdown handlers are triggered.
|
||||
*/
|
||||
kill(code?: number): void;
|
||||
|
||||
ready(): void;
|
||||
registerWindow(window: ICodeWindow): void;
|
||||
}
|
||||
|
||||
export class LifecycleService implements ILifecycleService {
|
||||
export class LifecycleService extends Disposable implements ILifecycleService {
|
||||
|
||||
_serviceBrand: any;
|
||||
|
||||
private static readonly QUIT_FROM_RESTART_MARKER = 'quit.from.restart'; // use a marker to find out if the session was restarted
|
||||
|
||||
private windowToCloseRequest: { [windowId: string]: boolean };
|
||||
private quitRequested: boolean;
|
||||
private windowToCloseRequest: { [windowId: string]: boolean } = Object.create(null);
|
||||
private pendingQuitPromise: TPromise<boolean>;
|
||||
private pendingQuitPromiseComplete: TValueCallback<boolean>;
|
||||
private oneTimeListenerTokenGenerator: number;
|
||||
private _wasRestarted: boolean;
|
||||
private windowCounter: number;
|
||||
private oneTimeListenerTokenGenerator = 0;
|
||||
private windowCounter = 0;
|
||||
|
||||
private _onBeforeShutdown = new Emitter<void>();
|
||||
onBeforeShutdown: Event<void> = this._onBeforeShutdown.event;
|
||||
private _quitRequested = false;
|
||||
get quitRequested(): boolean { return this._quitRequested; }
|
||||
|
||||
private _onShutdown = new Emitter<void>();
|
||||
onShutdown: Event<void> = this._onShutdown.event;
|
||||
private _wasRestarted: boolean = false;
|
||||
get wasRestarted(): boolean { return this._wasRestarted; }
|
||||
|
||||
private _onBeforeWindowClose = new Emitter<ICodeWindow>();
|
||||
onBeforeWindowClose: Event<ICodeWindow> = this._onBeforeWindowClose.event;
|
||||
private readonly _onBeforeShutdown = this._register(new Emitter<void>());
|
||||
readonly onBeforeShutdown: Event<void> = this._onBeforeShutdown.event;
|
||||
|
||||
private _onBeforeWindowUnload = new Emitter<IWindowUnloadEvent>();
|
||||
onBeforeWindowUnload: Event<IWindowUnloadEvent> = this._onBeforeWindowUnload.event;
|
||||
private readonly _onShutdown = this._register(new Emitter<void>());
|
||||
readonly onShutdown: Event<void> = this._onShutdown.event;
|
||||
|
||||
private readonly _onBeforeWindowClose = this._register(new Emitter<ICodeWindow>());
|
||||
readonly onBeforeWindowClose: Event<ICodeWindow> = this._onBeforeWindowClose.event;
|
||||
|
||||
private readonly _onBeforeWindowUnload = this._register(new Emitter<IWindowUnloadEvent>());
|
||||
readonly onBeforeWindowUnload: Event<IWindowUnloadEvent> = this._onBeforeWindowUnload.event;
|
||||
|
||||
constructor(
|
||||
@ILogService private logService: ILogService,
|
||||
@IStateService private stateService: IStateService
|
||||
) {
|
||||
this.windowToCloseRequest = Object.create(null);
|
||||
this.quitRequested = false;
|
||||
this.oneTimeListenerTokenGenerator = 0;
|
||||
this._wasRestarted = false;
|
||||
this.windowCounter = 0;
|
||||
super();
|
||||
|
||||
this.handleRestarted();
|
||||
}
|
||||
|
@ -127,14 +140,6 @@ export class LifecycleService implements ILifecycleService {
|
|||
}
|
||||
}
|
||||
|
||||
get wasRestarted(): boolean {
|
||||
return this._wasRestarted;
|
||||
}
|
||||
|
||||
get isQuitRequested(): boolean {
|
||||
return !!this.quitRequested;
|
||||
}
|
||||
|
||||
ready(): void {
|
||||
this.registerListeners();
|
||||
}
|
||||
|
@ -145,12 +150,12 @@ export class LifecycleService implements ILifecycleService {
|
|||
app.on('before-quit', e => {
|
||||
this.logService.trace('Lifecycle#before-quit');
|
||||
|
||||
if (this.quitRequested) {
|
||||
if (this._quitRequested) {
|
||||
this.logService.trace('Lifecycle#before-quit - returning because quit was already requested');
|
||||
return;
|
||||
}
|
||||
|
||||
this.quitRequested = true;
|
||||
this._quitRequested = true;
|
||||
|
||||
// Emit event to indicate that we are about to shutdown
|
||||
this.logService.trace('Lifecycle#onBeforeShutdown.fire()');
|
||||
|
@ -170,7 +175,7 @@ export class LifecycleService implements ILifecycleService {
|
|||
|
||||
// Windows/Linux: we quit when all windows have closed
|
||||
// Mac: we only quit when quit was requested
|
||||
if (this.quitRequested || process.platform !== 'darwin') {
|
||||
if (this._quitRequested || process.platform !== 'darwin') {
|
||||
app.quit();
|
||||
}
|
||||
});
|
||||
|
@ -206,7 +211,7 @@ export class LifecycleService implements ILifecycleService {
|
|||
|
||||
window.close();
|
||||
} else {
|
||||
this.quitRequested = false;
|
||||
this._quitRequested = false;
|
||||
delete this.windowToCloseRequest[windowId];
|
||||
}
|
||||
});
|
||||
|
@ -223,7 +228,7 @@ export class LifecycleService implements ILifecycleService {
|
|||
// if there are no more code windows opened, fire the onShutdown event, unless
|
||||
// we are on macOS where it is perfectly fine to close the last window and
|
||||
// the application continues running (unless quit was actually requested)
|
||||
if (this.windowCounter === 0 && (!isMacintosh || this.isQuitRequested)) {
|
||||
if (this.windowCounter === 0 && (!isMacintosh || this._quitRequested)) {
|
||||
this.logService.trace('Lifecycle#onShutdown.fire()');
|
||||
this._onShutdown.fire();
|
||||
}
|
||||
|
@ -239,7 +244,7 @@ export class LifecycleService implements ILifecycleService {
|
|||
|
||||
this.logService.trace('Lifecycle#unload()', window.id);
|
||||
|
||||
const windowUnloadReason = this.quitRequested ? UnloadReason.QUIT : reason;
|
||||
const windowUnloadReason = this._quitRequested ? UnloadReason.QUIT : reason;
|
||||
|
||||
// first ask the window itself if it vetos the unload
|
||||
return this.onBeforeUnloadWindowInRenderer(window, windowUnloadReason).then(veto => {
|
||||
|
|
|
@ -4,59 +4,77 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { ShutdownReason } from 'vs/platform/lifecycle/common/lifecycle';
|
||||
|
||||
export const ID = 'storageService';
|
||||
|
||||
export const IStorageService = createDecorator<IStorageService>(ID);
|
||||
export const IStorageService = createDecorator<IStorageService>('storageService');
|
||||
|
||||
export interface IStorageService {
|
||||
_serviceBrand: any;
|
||||
|
||||
/**
|
||||
* Store a string value under the given key to local storage.
|
||||
*
|
||||
* The optional scope argument allows to define the scope of the operation.
|
||||
* Emitted whenever data is updated or deleted.
|
||||
*/
|
||||
store(key: string, value: any, scope?: StorageScope): void;
|
||||
readonly onDidChangeStorage: Event<IWorkspaceStorageChangeEvent>;
|
||||
|
||||
/**
|
||||
* Delete an element stored under the provided key from local storage.
|
||||
*
|
||||
* The optional scope argument allows to define the scope of the operation.
|
||||
* Emitted when the storage is about to persist. This is the right time
|
||||
* to persist data to ensure it is stored before the application shuts
|
||||
* down.
|
||||
*/
|
||||
remove(key: string, scope?: StorageScope): void;
|
||||
readonly onWillSaveState: Event<ShutdownReason>;
|
||||
|
||||
/**
|
||||
* Retrieve an element stored with the given key from local storage. Use
|
||||
* Retrieve an element stored with the given key from storage. Use
|
||||
* the provided defaultValue if the element is null or undefined.
|
||||
*
|
||||
* The optional scope argument allows to define the scope of the operation.
|
||||
* The scope argument allows to define the scope of the storage
|
||||
* operation to either the current workspace only or all workspaces.
|
||||
*/
|
||||
get(key: string, scope?: StorageScope, defaultValue?: string): string | undefined;
|
||||
get(key: string, scope: StorageScope, fallbackValue?: string): string | undefined;
|
||||
|
||||
/**
|
||||
* Retrieve an element stored with the given key from local storage. Use
|
||||
* the provided defaultValue if the element is null or undefined. The element
|
||||
* will be converted to a number using parseInt with a base of 10.
|
||||
*
|
||||
* The optional scope argument allows to define the scope of the operation.
|
||||
*/
|
||||
getInteger(key: string, scope?: StorageScope, defaultValue?: number): number | undefined;
|
||||
|
||||
/**
|
||||
* Retrieve an element stored with the given key from local storage. Use
|
||||
* Retrieve an element stored with the given key from storage. Use
|
||||
* the provided defaultValue if the element is null or undefined. The element
|
||||
* will be converted to a boolean.
|
||||
*
|
||||
* The optional scope argument allows to define the scope of the operation.
|
||||
* The scope argument allows to define the scope of the storage
|
||||
* operation to either the current workspace only or all workspaces.
|
||||
*/
|
||||
getBoolean(key: string, scope?: StorageScope, defaultValue?: boolean): boolean | undefined;
|
||||
getBoolean(key: string, scope: StorageScope, fallbackValue?: boolean): boolean | undefined;
|
||||
|
||||
/**
|
||||
* Retrieve an element stored with the given key from storage. Use
|
||||
* the provided defaultValue if the element is null or undefined. The element
|
||||
* will be converted to a number using parseInt with a base of 10.
|
||||
*
|
||||
* The scope argument allows to define the scope of the storage
|
||||
* operation to either the current workspace only or all workspaces.
|
||||
*/
|
||||
getInteger(key: string, scope: StorageScope, fallbackValue?: number): number | undefined;
|
||||
|
||||
/**
|
||||
* Store a string value under the given key to storage. The value will
|
||||
* be converted to a string.
|
||||
*
|
||||
* The scope argument allows to define the scope of the storage
|
||||
* operation to either the current workspace only or all workspaces.
|
||||
*/
|
||||
store(key: string, value: any, scope: StorageScope): Promise<void>;
|
||||
|
||||
/**
|
||||
* Delete an element stored under the provided key from storage.
|
||||
*
|
||||
* The scope argument allows to define the scope of the storage
|
||||
* operation to either the current workspace only or all workspaces.
|
||||
*/
|
||||
remove(key: string, scope: StorageScope): Promise<void>;
|
||||
}
|
||||
|
||||
export const enum StorageScope {
|
||||
|
||||
/**
|
||||
* The stored data will be scoped to all workspaces of this domain.
|
||||
* The stored data will be scoped to all workspaces.
|
||||
*/
|
||||
GLOBAL,
|
||||
|
||||
|
@ -66,12 +84,34 @@ export const enum StorageScope {
|
|||
WORKSPACE
|
||||
}
|
||||
|
||||
export interface IWorkspaceStorageChangeEvent {
|
||||
key: string;
|
||||
scope: StorageScope;
|
||||
}
|
||||
|
||||
export const NullStorageService: IStorageService = {
|
||||
_serviceBrand: undefined,
|
||||
store() { return undefined; },
|
||||
remove() { return undefined; },
|
||||
get(a, b, defaultValue) { return defaultValue; },
|
||||
getInteger(a, b, defaultValue) { return defaultValue; },
|
||||
getBoolean(a, b, defaultValue) { return defaultValue; }
|
||||
};
|
||||
|
||||
onDidChangeStorage: Event.None,
|
||||
onWillSaveState: Event.None,
|
||||
|
||||
get(key: string, scope: StorageScope, fallbackValue?: string): string | undefined {
|
||||
return fallbackValue;
|
||||
},
|
||||
|
||||
getBoolean(key: string, scope: StorageScope, fallbackValue?: boolean): boolean | undefined {
|
||||
return fallbackValue;
|
||||
},
|
||||
|
||||
getInteger(key: string, scope: StorageScope, fallbackValue?: number): number | undefined {
|
||||
return fallbackValue;
|
||||
},
|
||||
|
||||
store(key: string, value: any, scope: StorageScope): Promise<void> {
|
||||
return Promise.resolve();
|
||||
},
|
||||
|
||||
remove(key: string, scope: StorageScope): Promise<void> {
|
||||
return Promise.resolve();
|
||||
}
|
||||
};
|
|
@ -3,7 +3,7 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IStorage, StorageService } from 'vs/platform/storage/common/storageService';
|
||||
import { StorageLegacyService, IStorageLegacy } from 'vs/platform/storage/common/storageLegacyService';
|
||||
import { endsWith, startsWith, rtrim } from 'vs/base/common/strings';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
|
||||
|
@ -29,8 +29,8 @@ import { IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
|
|||
* => no longer being used (used for empty workspaces previously)
|
||||
*/
|
||||
|
||||
const EMPTY_WORKSPACE_PREFIX = `${StorageService.COMMON_PREFIX}workspace/empty:`;
|
||||
const MULTI_ROOT_WORKSPACE_PREFIX = `${StorageService.COMMON_PREFIX}workspace/root:`;
|
||||
const EMPTY_WORKSPACE_PREFIX = `${StorageLegacyService.COMMON_PREFIX}workspace/empty:`;
|
||||
const MULTI_ROOT_WORKSPACE_PREFIX = `${StorageLegacyService.COMMON_PREFIX}workspace/root:`;
|
||||
|
||||
export type StorageObject = { [key: string]: string };
|
||||
|
||||
|
@ -44,7 +44,7 @@ export interface IParsedStorage {
|
|||
/**
|
||||
* Parses the local storage implementation into global, multi root, folder and empty storage.
|
||||
*/
|
||||
export function parseStorage(storage: IStorage): IParsedStorage {
|
||||
export function parseStorage(storage: IStorageLegacy): IParsedStorage {
|
||||
const globalStorage = new Map<string, string>();
|
||||
const folderWorkspacesStorage = new Map<string /* workspace file resource */, StorageObject>();
|
||||
const emptyWorkspacesStorage = new Map<string /* empty workspace id */, StorageObject>();
|
||||
|
@ -55,14 +55,14 @@ export function parseStorage(storage: IStorage): IParsedStorage {
|
|||
const key = storage.key(i);
|
||||
|
||||
// Workspace Storage (storage://workspace/)
|
||||
if (startsWith(key, StorageService.WORKSPACE_PREFIX)) {
|
||||
if (startsWith(key, StorageLegacyService.WORKSPACE_PREFIX)) {
|
||||
|
||||
// We are looking for key: storage://workspace/<folder>/workspaceIdentifier to be able to find all folder
|
||||
// paths that are known to the storage. is the only way how to parse all folder paths known in storage.
|
||||
if (endsWith(key, StorageService.WORKSPACE_IDENTIFIER)) {
|
||||
if (endsWith(key, StorageLegacyService.WORKSPACE_IDENTIFIER)) {
|
||||
|
||||
// storage://workspace/<folder>/workspaceIdentifier => <folder>/
|
||||
let workspace = key.substring(StorageService.WORKSPACE_PREFIX.length, key.length - StorageService.WORKSPACE_IDENTIFIER.length);
|
||||
let workspace = key.substring(StorageLegacyService.WORKSPACE_PREFIX.length, key.length - StorageLegacyService.WORKSPACE_IDENTIFIER.length);
|
||||
|
||||
// macOS/Unix: Users/name/folder/
|
||||
// Windows: c%3A/Users/name/folder/
|
||||
|
@ -76,7 +76,7 @@ export function parseStorage(storage: IStorage): IParsedStorage {
|
|||
}
|
||||
|
||||
// storage://workspace/<folder>/workspaceIdentifier => storage://workspace/<folder>/
|
||||
const prefix = key.substr(0, key.length - StorageService.WORKSPACE_IDENTIFIER.length);
|
||||
const prefix = key.substr(0, key.length - StorageLegacyService.WORKSPACE_IDENTIFIER.length);
|
||||
workspaces.push({ prefix, resource: workspace });
|
||||
}
|
||||
|
||||
|
@ -120,11 +120,11 @@ export function parseStorage(storage: IStorage): IParsedStorage {
|
|||
}
|
||||
|
||||
// Global Storage (storage://global)
|
||||
else if (startsWith(key, StorageService.GLOBAL_PREFIX)) {
|
||||
else if (startsWith(key, StorageLegacyService.GLOBAL_PREFIX)) {
|
||||
|
||||
// storage://global/someKey => someKey
|
||||
const globalStorageKey = key.substr(StorageService.GLOBAL_PREFIX.length);
|
||||
if (startsWith(globalStorageKey, StorageService.COMMON_PREFIX)) {
|
||||
const globalStorageKey = key.substr(StorageLegacyService.GLOBAL_PREFIX.length);
|
||||
if (startsWith(globalStorageKey, StorageLegacyService.COMMON_PREFIX)) {
|
||||
continue; // filter out faulty keys that have the form storage://something/storage://
|
||||
}
|
||||
|
||||
|
@ -168,7 +168,7 @@ export function parseStorage(storage: IStorage): IParsedStorage {
|
|||
};
|
||||
}
|
||||
|
||||
export function migrateStorageToMultiRootWorkspace(fromWorkspaceId: string, toWorkspace: IWorkspaceIdentifier, storage: IStorage): string {
|
||||
export function migrateStorageToMultiRootWorkspace(fromWorkspaceId: string, toWorkspace: IWorkspaceIdentifier, storage: IStorageLegacy): string {
|
||||
const parsed = parseStorage(storage);
|
||||
|
||||
const newWorkspaceId = URI.from({ path: toWorkspace.id, scheme: 'root' }).toString();
|
||||
|
@ -186,11 +186,11 @@ export function migrateStorageToMultiRootWorkspace(fromWorkspaceId: string, toWo
|
|||
// Migrate existing storage to new workspace id
|
||||
if (storageForWorkspace) {
|
||||
Object.keys(storageForWorkspace).forEach(key => {
|
||||
if (key === StorageService.WORKSPACE_IDENTIFIER) {
|
||||
if (key === StorageLegacyService.WORKSPACE_IDENTIFIER) {
|
||||
return; // make sure to never migrate the workspace identifier
|
||||
}
|
||||
|
||||
storage.setItem(`${StorageService.WORKSPACE_PREFIX}${newWorkspaceId}/${key}`, storageForWorkspace[key]);
|
||||
storage.setItem(`${StorageLegacyService.WORKSPACE_PREFIX}${newWorkspaceId}/${key}`, storageForWorkspace[key]);
|
||||
});
|
||||
}
|
||||
|
|
@ -6,11 +6,11 @@
|
|||
import * as types from 'vs/base/common/types';
|
||||
import * as errors from 'vs/base/common/errors';
|
||||
import * as strings from 'vs/base/common/strings';
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import * as perf from 'vs/base/common/performance';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
|
||||
// Browser localStorage interface
|
||||
export interface IStorage {
|
||||
export interface IStorageLegacy {
|
||||
length: number;
|
||||
key(index: number): string;
|
||||
setItem(key: string, value: any): void;
|
||||
|
@ -18,25 +18,87 @@ export interface IStorage {
|
|||
removeItem(key: string): void;
|
||||
}
|
||||
|
||||
export class StorageService implements IStorageService {
|
||||
export const ID = 'storageLegacyService';
|
||||
|
||||
export const IStorageLegacyService = createDecorator<IStorageLegacyService>(ID);
|
||||
|
||||
export interface IStorageLegacyService {
|
||||
_serviceBrand: any;
|
||||
|
||||
/**
|
||||
* Store a string value under the given key to local storage.
|
||||
*
|
||||
* The optional scope argument allows to define the scope of the operation.
|
||||
*/
|
||||
store(key: string, value: any, scope?: StorageLegacyScope): void;
|
||||
|
||||
/**
|
||||
* Delete an element stored under the provided key from local storage.
|
||||
*
|
||||
* The optional scope argument allows to define the scope of the operation.
|
||||
*/
|
||||
remove(key: string, scope?: StorageLegacyScope): void;
|
||||
|
||||
/**
|
||||
* Retrieve an element stored with the given key from local storage. Use
|
||||
* the provided defaultValue if the element is null or undefined.
|
||||
*
|
||||
* The optional scope argument allows to define the scope of the operation.
|
||||
*/
|
||||
get(key: string, scope?: StorageLegacyScope, defaultValue?: string): string | undefined;
|
||||
|
||||
/**
|
||||
* Retrieve an element stored with the given key from local storage. Use
|
||||
* the provided defaultValue if the element is null or undefined. The element
|
||||
* will be converted to a number using parseInt with a base of 10.
|
||||
*
|
||||
* The optional scope argument allows to define the scope of the operation.
|
||||
*/
|
||||
getInteger(key: string, scope?: StorageLegacyScope, defaultValue?: number): number | undefined;
|
||||
|
||||
/**
|
||||
* Retrieve an element stored with the given key from local storage. Use
|
||||
* the provided defaultValue if the element is null or undefined. The element
|
||||
* will be converted to a boolean.
|
||||
*
|
||||
* The optional scope argument allows to define the scope of the operation.
|
||||
*/
|
||||
getBoolean(key: string, scope?: StorageLegacyScope, defaultValue?: boolean): boolean | undefined;
|
||||
}
|
||||
|
||||
export const enum StorageLegacyScope {
|
||||
|
||||
/**
|
||||
* The stored data will be scoped to all workspaces of this domain.
|
||||
*/
|
||||
GLOBAL,
|
||||
|
||||
/**
|
||||
* The stored data will be scoped to the current workspace.
|
||||
*/
|
||||
WORKSPACE
|
||||
}
|
||||
|
||||
|
||||
export class StorageLegacyService implements IStorageLegacyService {
|
||||
|
||||
_serviceBrand: any;
|
||||
|
||||
static readonly COMMON_PREFIX = 'storage://';
|
||||
static readonly GLOBAL_PREFIX = `${StorageService.COMMON_PREFIX}global/`;
|
||||
static readonly WORKSPACE_PREFIX = `${StorageService.COMMON_PREFIX}workspace/`;
|
||||
static readonly GLOBAL_PREFIX = `${StorageLegacyService.COMMON_PREFIX}global/`;
|
||||
static readonly WORKSPACE_PREFIX = `${StorageLegacyService.COMMON_PREFIX}workspace/`;
|
||||
static readonly WORKSPACE_IDENTIFIER = 'workspaceidentifier';
|
||||
static readonly NO_WORKSPACE_IDENTIFIER = '__$noWorkspace__';
|
||||
|
||||
private _workspaceStorage: IStorage;
|
||||
private _globalStorage: IStorage;
|
||||
private _workspaceStorage: IStorageLegacy;
|
||||
private _globalStorage: IStorageLegacy;
|
||||
|
||||
private workspaceKey: string;
|
||||
private _workspaceId: string;
|
||||
|
||||
constructor(
|
||||
globalStorage: IStorage,
|
||||
workspaceStorage: IStorage,
|
||||
globalStorage: IStorageLegacy,
|
||||
workspaceStorage: IStorageLegacy,
|
||||
workspaceId?: string,
|
||||
legacyWorkspaceId?: number
|
||||
) {
|
||||
|
@ -63,17 +125,17 @@ export class StorageService implements IStorageService {
|
|||
}
|
||||
}
|
||||
|
||||
get globalStorage(): IStorage {
|
||||
get globalStorage(): IStorageLegacy {
|
||||
return this._globalStorage;
|
||||
}
|
||||
|
||||
get workspaceStorage(): IStorage {
|
||||
get workspaceStorage(): IStorageLegacy {
|
||||
return this._workspaceStorage;
|
||||
}
|
||||
|
||||
private getWorkspaceKey(id?: string): string {
|
||||
if (!id) {
|
||||
return StorageService.NO_WORKSPACE_IDENTIFIER;
|
||||
return StorageLegacyService.NO_WORKSPACE_IDENTIFIER;
|
||||
}
|
||||
|
||||
// Special case file:// URIs: strip protocol from key to produce shorter key
|
||||
|
@ -90,18 +152,18 @@ export class StorageService implements IStorageService {
|
|||
|
||||
// Get stored identifier from storage
|
||||
perf.mark('willReadWorkspaceIdentifier');
|
||||
const id = this.getInteger(StorageService.WORKSPACE_IDENTIFIER, StorageScope.WORKSPACE);
|
||||
const id = this.getInteger(StorageLegacyService.WORKSPACE_IDENTIFIER, StorageLegacyScope.WORKSPACE);
|
||||
perf.mark('didReadWorkspaceIdentifier');
|
||||
|
||||
// If identifier differs, assume the workspace got recreated and thus clean all storage for this workspace
|
||||
if (types.isNumber(id) && workspaceUid !== id) {
|
||||
const keyPrefix = this.toStorageKey('', StorageScope.WORKSPACE);
|
||||
const keyPrefix = this.toStorageKey('', StorageLegacyScope.WORKSPACE);
|
||||
const toDelete: string[] = [];
|
||||
const length = this._workspaceStorage.length;
|
||||
|
||||
for (let i = 0; i < length; i++) {
|
||||
const key = this._workspaceStorage.key(i);
|
||||
if (key.indexOf(StorageService.WORKSPACE_PREFIX) < 0) {
|
||||
if (key.indexOf(StorageLegacyService.WORKSPACE_PREFIX) < 0) {
|
||||
continue; // ignore stored things that don't belong to storage service or are defined globally
|
||||
}
|
||||
|
||||
|
@ -119,12 +181,12 @@ export class StorageService implements IStorageService {
|
|||
|
||||
// Store workspace identifier now
|
||||
if (workspaceUid !== id) {
|
||||
this.store(StorageService.WORKSPACE_IDENTIFIER, workspaceUid, StorageScope.WORKSPACE);
|
||||
this.store(StorageLegacyService.WORKSPACE_IDENTIFIER, workspaceUid, StorageLegacyScope.WORKSPACE);
|
||||
}
|
||||
}
|
||||
|
||||
store(key: string, value: any, scope = StorageScope.GLOBAL): void {
|
||||
const storage = (scope === StorageScope.GLOBAL) ? this._globalStorage : this._workspaceStorage;
|
||||
store(key: string, value: any, scope = StorageLegacyScope.GLOBAL): void {
|
||||
const storage = (scope === StorageLegacyScope.GLOBAL) ? this._globalStorage : this._workspaceStorage;
|
||||
|
||||
if (types.isUndefinedOrNull(value)) {
|
||||
this.remove(key, scope); // we cannot store null or undefined, in that case we remove the key
|
||||
|
@ -141,8 +203,8 @@ export class StorageService implements IStorageService {
|
|||
}
|
||||
}
|
||||
|
||||
get(key: string, scope = StorageScope.GLOBAL, defaultValue?: any): string {
|
||||
const storage = (scope === StorageScope.GLOBAL) ? this._globalStorage : this._workspaceStorage;
|
||||
get(key: string, scope = StorageLegacyScope.GLOBAL, defaultValue?: any): string {
|
||||
const storage = (scope === StorageLegacyScope.GLOBAL) ? this._globalStorage : this._workspaceStorage;
|
||||
|
||||
const value = storage.getItem(this.toStorageKey(key, scope));
|
||||
if (types.isUndefinedOrNull(value)) {
|
||||
|
@ -152,7 +214,7 @@ export class StorageService implements IStorageService {
|
|||
return value;
|
||||
}
|
||||
|
||||
getInteger(key: string, scope = StorageScope.GLOBAL, defaultValue?: number): number {
|
||||
getInteger(key: string, scope = StorageLegacyScope.GLOBAL, defaultValue?: number): number {
|
||||
const value = this.get(key, scope, defaultValue);
|
||||
|
||||
if (types.isUndefinedOrNull(value)) {
|
||||
|
@ -162,7 +224,7 @@ export class StorageService implements IStorageService {
|
|||
return parseInt(value, 10);
|
||||
}
|
||||
|
||||
getBoolean(key: string, scope = StorageScope.GLOBAL, defaultValue?: boolean): boolean {
|
||||
getBoolean(key: string, scope = StorageLegacyScope.GLOBAL, defaultValue?: boolean): boolean {
|
||||
const value = this.get(key, scope, defaultValue);
|
||||
|
||||
if (types.isUndefinedOrNull(value)) {
|
||||
|
@ -176,24 +238,24 @@ export class StorageService implements IStorageService {
|
|||
return value ? true : false;
|
||||
}
|
||||
|
||||
remove(key: string, scope = StorageScope.GLOBAL): void {
|
||||
const storage = (scope === StorageScope.GLOBAL) ? this._globalStorage : this._workspaceStorage;
|
||||
remove(key: string, scope = StorageLegacyScope.GLOBAL): void {
|
||||
const storage = (scope === StorageLegacyScope.GLOBAL) ? this._globalStorage : this._workspaceStorage;
|
||||
const storageKey = this.toStorageKey(key, scope);
|
||||
|
||||
// Remove
|
||||
storage.removeItem(storageKey);
|
||||
}
|
||||
|
||||
private toStorageKey(key: string, scope: StorageScope): string {
|
||||
if (scope === StorageScope.GLOBAL) {
|
||||
return StorageService.GLOBAL_PREFIX + key.toLowerCase();
|
||||
private toStorageKey(key: string, scope: StorageLegacyScope): string {
|
||||
if (scope === StorageLegacyScope.GLOBAL) {
|
||||
return StorageLegacyService.GLOBAL_PREFIX + key.toLowerCase();
|
||||
}
|
||||
|
||||
return StorageService.WORKSPACE_PREFIX + this.workspaceKey + key.toLowerCase();
|
||||
return StorageLegacyService.WORKSPACE_PREFIX + this.workspaceKey + key.toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
export class InMemoryLocalStorage implements IStorage {
|
||||
export class InMemoryLocalStorage implements IStorageLegacy {
|
||||
private store: { [key: string]: string; };
|
||||
|
||||
constructor() {
|
206
src/vs/platform/storage/electron-browser/storageService.ts
Normal file
206
src/vs/platform/storage/electron-browser/storageService.ts
Normal file
|
@ -0,0 +1,206 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { IWorkspaceStorageChangeEvent, IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import { Storage, IStorageLoggingOptions } from 'vs/base/node/storage';
|
||||
import { IStorageLegacyService, StorageLegacyScope } from 'vs/platform/storage/common/storageLegacyService';
|
||||
import { addDisposableListener } from 'vs/base/browser/dom';
|
||||
import { startsWith } from 'vs/base/common/strings';
|
||||
import { ShutdownReason } from 'vs/platform/lifecycle/common/lifecycle';
|
||||
|
||||
export class StorageService extends Disposable implements IStorageService {
|
||||
_serviceBrand: any;
|
||||
|
||||
private static IN_MEMORY_PATH = ':memory:';
|
||||
|
||||
private _onDidChangeStorage: Emitter<IWorkspaceStorageChangeEvent> = this._register(new Emitter<IWorkspaceStorageChangeEvent>());
|
||||
get onDidChangeStorage(): Event<IWorkspaceStorageChangeEvent> { return this._onDidChangeStorage.event; }
|
||||
|
||||
private _onWillClose: Emitter<ShutdownReason> = this._register(new Emitter<ShutdownReason>());
|
||||
get onWillSaveState(): Event<ShutdownReason> { return this._onWillClose.event; }
|
||||
|
||||
private globalStorage: Storage;
|
||||
private workspaceStorage: Storage;
|
||||
|
||||
constructor(
|
||||
workspaceDBPath: string,
|
||||
@ILogService logService: ILogService,
|
||||
@IEnvironmentService environmentService: IEnvironmentService
|
||||
) {
|
||||
super();
|
||||
|
||||
const loggingOptions: IStorageLoggingOptions = {
|
||||
info: environmentService.verbose || environmentService.logStorage,
|
||||
infoLogger: msg => logService.info(msg),
|
||||
errorLogger: error => logService.error(error)
|
||||
};
|
||||
|
||||
const useInMemoryStorage = !!environmentService.extensionTestsPath; // never keep any state when running extension tests
|
||||
|
||||
this.globalStorage = new Storage({ path: useInMemoryStorage ? StorageService.IN_MEMORY_PATH : StorageService.IN_MEMORY_PATH, logging: loggingOptions });
|
||||
this.workspaceStorage = new Storage({ path: useInMemoryStorage ? StorageService.IN_MEMORY_PATH : workspaceDBPath, logging: loggingOptions });
|
||||
|
||||
this.registerListeners();
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
this._register(this.globalStorage.onDidChangeStorage(key => this.handleDidChangeStorage(key, StorageScope.GLOBAL)));
|
||||
this._register(this.workspaceStorage.onDidChangeStorage(key => this.handleDidChangeStorage(key, StorageScope.WORKSPACE)));
|
||||
}
|
||||
|
||||
private handleDidChangeStorage(key: string, scope: StorageScope): void {
|
||||
this._onDidChangeStorage.fire({ key, scope });
|
||||
}
|
||||
|
||||
init(): Promise<void> {
|
||||
return Promise.all([this.globalStorage.init(), this.workspaceStorage.init()]).then(() => void 0);
|
||||
}
|
||||
|
||||
get(key: string, scope: StorageScope, fallbackValue?: any): string {
|
||||
return this.getStorage(scope).get(key, fallbackValue);
|
||||
}
|
||||
|
||||
getBoolean(key: string, scope: StorageScope, fallbackValue?: boolean): boolean {
|
||||
return this.getStorage(scope).getBoolean(key, fallbackValue);
|
||||
}
|
||||
|
||||
getInteger(key: string, scope: StorageScope, fallbackValue?: number): number {
|
||||
return this.getStorage(scope).getInteger(key, fallbackValue);
|
||||
}
|
||||
|
||||
store(key: string, value: any, scope: StorageScope): Promise<void> {
|
||||
return this.getStorage(scope).set(key, value);
|
||||
}
|
||||
|
||||
remove(key: string, scope: StorageScope): Promise<void> {
|
||||
return this.getStorage(scope).delete(key);
|
||||
}
|
||||
|
||||
close(reason: ShutdownReason): Promise<void> {
|
||||
|
||||
// Signal as event so that clients can still store data
|
||||
this._onWillClose.fire(reason);
|
||||
|
||||
// Do it
|
||||
return Promise.all([
|
||||
this.globalStorage.close(),
|
||||
this.workspaceStorage.close()
|
||||
]).then(() => void 0);
|
||||
}
|
||||
|
||||
private getStorage(scope: StorageScope): Storage {
|
||||
return scope === StorageScope.GLOBAL ? this.globalStorage : this.workspaceStorage;
|
||||
}
|
||||
}
|
||||
|
||||
export class DelegatingStorageService extends Disposable implements IStorageService {
|
||||
_serviceBrand: any;
|
||||
|
||||
private _onDidChangeStorage: Emitter<IWorkspaceStorageChangeEvent> = this._register(new Emitter<IWorkspaceStorageChangeEvent>());
|
||||
get onDidChangeStorage(): Event<IWorkspaceStorageChangeEvent> { return this._onDidChangeStorage.event; }
|
||||
|
||||
private _onWillClose: Emitter<ShutdownReason> = this._register(new Emitter<ShutdownReason>());
|
||||
get onWillSaveState(): Event<ShutdownReason> { return this._onWillClose.event; }
|
||||
|
||||
private closed: boolean;
|
||||
|
||||
constructor(
|
||||
@IStorageService private storageService: StorageService,
|
||||
@IStorageLegacyService private storageLegacyService: IStorageLegacyService,
|
||||
@ILogService private logService: ILogService,
|
||||
@IEnvironmentService environmentService: IEnvironmentService
|
||||
) {
|
||||
super();
|
||||
|
||||
this.registerListeners();
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
this._register(this.storageService.onDidChangeStorage(e => this._onDidChangeStorage.fire(e)));
|
||||
this._register(this.storageService.onWillSaveState(reason => this._onWillClose.fire(reason)));
|
||||
|
||||
const globalKeyMarker = 'storage://global/';
|
||||
this._register(addDisposableListener(window, 'storage', (e: StorageEvent) => {
|
||||
if (startsWith(e.key, globalKeyMarker)) {
|
||||
const key = e.key.substr(globalKeyMarker.length);
|
||||
|
||||
this._onDidChangeStorage.fire({ key, scope: StorageScope.GLOBAL });
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
get(key: string, scope: StorageScope, fallbackValue?: any): string {
|
||||
const localStorageValue = this.storageLegacyService.get(key, this.convertScope(scope), fallbackValue);
|
||||
const dbValue = this.storageService.get(key, scope, localStorageValue);
|
||||
|
||||
this.assertStorageValue(key, scope, dbValue, localStorageValue);
|
||||
|
||||
return dbValue;
|
||||
}
|
||||
|
||||
getBoolean(key: string, scope: StorageScope, fallbackValue?: boolean): boolean {
|
||||
const localStorageValue = this.storageLegacyService.getBoolean(key, this.convertScope(scope), fallbackValue);
|
||||
const dbValue = this.storageService.getBoolean(key, scope, localStorageValue);
|
||||
|
||||
this.assertStorageValue(key, scope, dbValue, localStorageValue);
|
||||
|
||||
return dbValue;
|
||||
}
|
||||
|
||||
getInteger(key: string, scope: StorageScope, fallbackValue?: number): number {
|
||||
const localStorageValue = this.storageLegacyService.getInteger(key, this.convertScope(scope), fallbackValue);
|
||||
const dbValue = this.storageService.getInteger(key, scope, localStorageValue);
|
||||
|
||||
this.assertStorageValue(key, scope, dbValue, localStorageValue);
|
||||
|
||||
return dbValue;
|
||||
}
|
||||
|
||||
private assertStorageValue(key: string, scope: StorageScope, dbValue: any, storageValue: any): void {
|
||||
if (dbValue !== storageValue) {
|
||||
this.logService.error(`Unexpected storage value (key: ${key}, scope: ${scope === StorageScope.GLOBAL ? 'global' : 'workspace'}), actual: ${dbValue}, expected: ${storageValue}`);
|
||||
}
|
||||
}
|
||||
|
||||
store(key: string, value: any, scope: StorageScope): Promise<void> {
|
||||
if (this.closed) {
|
||||
this.logService.warn(`Unsupported write (store) access after close (key: ${key})`);
|
||||
|
||||
return Promise.resolve(); // prevent writing after close to detect late write access
|
||||
}
|
||||
|
||||
this.storageLegacyService.store(key, value, this.convertScope(scope));
|
||||
|
||||
return this.storageService.store(key, value, scope);
|
||||
}
|
||||
|
||||
remove(key: string, scope: StorageScope): Promise<void> {
|
||||
if (this.closed) {
|
||||
this.logService.warn(`Unsupported write (remove) access after close (key: ${key})`);
|
||||
|
||||
return Promise.resolve(); // prevent writing after close to detect late write access
|
||||
}
|
||||
|
||||
this.storageLegacyService.remove(key, this.convertScope(scope));
|
||||
|
||||
return this.storageService.remove(key, scope);
|
||||
}
|
||||
|
||||
close(reason: ShutdownReason): Promise<void> {
|
||||
const promise = this.storageService.close(reason);
|
||||
|
||||
this.closed = true;
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
private convertScope(scope: StorageScope): StorageLegacyScope {
|
||||
return scope === StorageScope.GLOBAL ? StorageLegacyScope.GLOBAL : StorageLegacyScope.WORKSPACE;
|
||||
}
|
||||
}
|
|
@ -4,10 +4,9 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { StorageService } from 'vs/platform/storage/common/storageService';
|
||||
import { parseStorage, migrateStorageToMultiRootWorkspace } from 'vs/platform/storage/common/migration';
|
||||
import { StorageLegacyScope, StorageLegacyService } from 'vs/platform/storage/common/storageLegacyService';
|
||||
import { parseStorage, migrateStorageToMultiRootWorkspace } from 'vs/platform/storage/common/storageLegacyMigration';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import { startsWith } from 'vs/base/common/strings';
|
||||
|
||||
suite('Storage Migration', () => {
|
||||
|
@ -92,10 +91,10 @@ suite('Storage Migration', () => {
|
|||
}
|
||||
|
||||
assert.equal(Object.keys(storageToTest).length, expectedKeyCount);
|
||||
assert.equal(storageToTest['key1'], service.get('key1', StorageScope.WORKSPACE));
|
||||
assert.equal(storageToTest['key2.something'], service.get('key2.something', StorageScope.WORKSPACE));
|
||||
assert.equal(storageToTest['key3/special'], service.get('key3/special', StorageScope.WORKSPACE));
|
||||
assert.equal(storageToTest['key4 space'], service.get('key4 space', StorageScope.WORKSPACE));
|
||||
assert.equal(storageToTest['key1'], service.get('key1', StorageLegacyScope.WORKSPACE));
|
||||
assert.equal(storageToTest['key2.something'], service.get('key2.something', StorageLegacyScope.WORKSPACE));
|
||||
assert.equal(storageToTest['key3/special'], service.get('key3/special', StorageLegacyScope.WORKSPACE));
|
||||
assert.equal(storageToTest['key4 space'], service.get('key4 space', StorageLegacyScope.WORKSPACE));
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -103,38 +102,38 @@ suite('Storage Migration', () => {
|
|||
const ws1 = URI.file('/some/folder/folder1').toString();
|
||||
const ws2 = URI.file('/some/folder/folder1/sub1').toString();
|
||||
|
||||
const s1 = new StorageService(storage, storage, ws1, Date.now());
|
||||
const s2 = new StorageService(storage, storage, ws2, Date.now());
|
||||
const s1 = new StorageLegacyService(storage, storage, ws1, Date.now());
|
||||
const s2 = new StorageLegacyService(storage, storage, ws2, Date.now());
|
||||
|
||||
s1.store('s1key1', 'value1', StorageScope.WORKSPACE);
|
||||
s1.store('s1key2.something', JSON.stringify({ foo: 'bar' }), StorageScope.WORKSPACE);
|
||||
s1.store('s1key3/special', true, StorageScope.WORKSPACE);
|
||||
s1.store('s1key4 space', 4, StorageScope.WORKSPACE);
|
||||
s1.store('s1key1', 'value1', StorageLegacyScope.WORKSPACE);
|
||||
s1.store('s1key2.something', JSON.stringify({ foo: 'bar' }), StorageLegacyScope.WORKSPACE);
|
||||
s1.store('s1key3/special', true, StorageLegacyScope.WORKSPACE);
|
||||
s1.store('s1key4 space', 4, StorageLegacyScope.WORKSPACE);
|
||||
|
||||
s2.store('s2key1', 'value1', StorageScope.WORKSPACE);
|
||||
s2.store('s2key2.something', JSON.stringify({ foo: 'bar' }), StorageScope.WORKSPACE);
|
||||
s2.store('s2key3/special', true, StorageScope.WORKSPACE);
|
||||
s2.store('s2key4 space', 4, StorageScope.WORKSPACE);
|
||||
s2.store('s2key1', 'value1', StorageLegacyScope.WORKSPACE);
|
||||
s2.store('s2key2.something', JSON.stringify({ foo: 'bar' }), StorageLegacyScope.WORKSPACE);
|
||||
s2.store('s2key3/special', true, StorageLegacyScope.WORKSPACE);
|
||||
s2.store('s2key4 space', 4, StorageLegacyScope.WORKSPACE);
|
||||
|
||||
const parsed = parseStorage(storage);
|
||||
|
||||
const s1Storage = parsed.folder.get(ws1);
|
||||
assert.equal(Object.keys(s1Storage).length, 5);
|
||||
assert.equal(s1Storage['s1key1'], s1.get('s1key1', StorageScope.WORKSPACE));
|
||||
assert.equal(s1Storage['s1key2.something'], s1.get('s1key2.something', StorageScope.WORKSPACE));
|
||||
assert.equal(s1Storage['s1key3/special'], s1.get('s1key3/special', StorageScope.WORKSPACE));
|
||||
assert.equal(s1Storage['s1key4 space'], s1.get('s1key4 space', StorageScope.WORKSPACE));
|
||||
assert.equal(s1Storage['s1key1'], s1.get('s1key1', StorageLegacyScope.WORKSPACE));
|
||||
assert.equal(s1Storage['s1key2.something'], s1.get('s1key2.something', StorageLegacyScope.WORKSPACE));
|
||||
assert.equal(s1Storage['s1key3/special'], s1.get('s1key3/special', StorageLegacyScope.WORKSPACE));
|
||||
assert.equal(s1Storage['s1key4 space'], s1.get('s1key4 space', StorageLegacyScope.WORKSPACE));
|
||||
|
||||
const s2Storage = parsed.folder.get(ws2);
|
||||
assert.equal(Object.keys(s2Storage).length, 5);
|
||||
assert.equal(s2Storage['s2key1'], s2.get('s2key1', StorageScope.WORKSPACE));
|
||||
assert.equal(s2Storage['s2key2.something'], s2.get('s2key2.something', StorageScope.WORKSPACE));
|
||||
assert.equal(s2Storage['s2key3/special'], s2.get('s2key3/special', StorageScope.WORKSPACE));
|
||||
assert.equal(s2Storage['s2key4 space'], s2.get('s2key4 space', StorageScope.WORKSPACE));
|
||||
assert.equal(s2Storage['s2key1'], s2.get('s2key1', StorageLegacyScope.WORKSPACE));
|
||||
assert.equal(s2Storage['s2key2.something'], s2.get('s2key2.something', StorageLegacyScope.WORKSPACE));
|
||||
assert.equal(s2Storage['s2key3/special'], s2.get('s2key3/special', StorageLegacyScope.WORKSPACE));
|
||||
assert.equal(s2Storage['s2key4 space'], s2.get('s2key4 space', StorageLegacyScope.WORKSPACE));
|
||||
});
|
||||
|
||||
function createService(workspaceId?: string): StorageService {
|
||||
const service = new StorageService(storage, storage, workspaceId, workspaceId && startsWith(workspaceId, 'file:') ? Date.now() : void 0);
|
||||
function createService(workspaceId?: string): StorageLegacyService {
|
||||
const service = new StorageLegacyService(storage, storage, workspaceId, workspaceId && startsWith(workspaceId, 'file:') ? Date.now() : void 0);
|
||||
|
||||
// Unrelated
|
||||
storage.setItem('foo', 'bar');
|
||||
|
@ -148,10 +147,10 @@ suite('Storage Migration', () => {
|
|||
service.store('key4 space', 4);
|
||||
|
||||
// Workspace
|
||||
service.store('key1', 'value1', StorageScope.WORKSPACE);
|
||||
service.store('key2.something', JSON.stringify({ foo: 'bar' }), StorageScope.WORKSPACE);
|
||||
service.store('key3/special', true, StorageScope.WORKSPACE);
|
||||
service.store('key4 space', 4, StorageScope.WORKSPACE);
|
||||
service.store('key1', 'value1', StorageLegacyScope.WORKSPACE);
|
||||
service.store('key2.something', JSON.stringify({ foo: 'bar' }), StorageLegacyScope.WORKSPACE);
|
||||
service.store('key3/special', true, StorageLegacyScope.WORKSPACE);
|
||||
service.store('key4 space', 4, StorageLegacyScope.WORKSPACE);
|
||||
|
||||
return service;
|
||||
}
|
||||
|
@ -164,7 +163,7 @@ suite('Storage Migration', () => {
|
|||
|
||||
migrateStorageToMultiRootWorkspace(workspaceToMigrateFrom, { id: '1500007676869', configPath: null }, storage);
|
||||
|
||||
const s2 = new StorageService(storage, storage, workspaceToMigrateTo);
|
||||
const s2 = new StorageLegacyService(storage, storage, workspaceToMigrateTo);
|
||||
const parsed = parseStorage(storage);
|
||||
|
||||
assert.equal(parsed.empty.size, 0);
|
||||
|
@ -173,10 +172,10 @@ suite('Storage Migration', () => {
|
|||
|
||||
const migratedStorage = parsed.multiRoot.get(workspaceToMigrateTo);
|
||||
assert.equal(Object.keys(migratedStorage).length, 4);
|
||||
assert.equal(migratedStorage['key1'], s2.get('key1', StorageScope.WORKSPACE));
|
||||
assert.equal(migratedStorage['key2.something'], s2.get('key2.something', StorageScope.WORKSPACE));
|
||||
assert.equal(migratedStorage['key3/special'], s2.get('key3/special', StorageScope.WORKSPACE));
|
||||
assert.equal(migratedStorage['key4 space'], s2.get('key4 space', StorageScope.WORKSPACE));
|
||||
assert.equal(migratedStorage['key1'], s2.get('key1', StorageLegacyScope.WORKSPACE));
|
||||
assert.equal(migratedStorage['key2.something'], s2.get('key2.something', StorageLegacyScope.WORKSPACE));
|
||||
assert.equal(migratedStorage['key3/special'], s2.get('key3/special', StorageLegacyScope.WORKSPACE));
|
||||
assert.equal(migratedStorage['key4 space'], s2.get('key4 space', StorageLegacyScope.WORKSPACE));
|
||||
});
|
||||
|
||||
test('Migrate Storage (folder (Windows) => multi root)', () => {
|
||||
|
@ -187,7 +186,7 @@ suite('Storage Migration', () => {
|
|||
|
||||
migrateStorageToMultiRootWorkspace(workspaceToMigrateFrom, { id: '1500007676869', configPath: null }, storage);
|
||||
|
||||
const s2 = new StorageService(storage, storage, workspaceToMigrateTo);
|
||||
const s2 = new StorageLegacyService(storage, storage, workspaceToMigrateTo);
|
||||
const parsed = parseStorage(storage);
|
||||
|
||||
assert.equal(parsed.empty.size, 0);
|
||||
|
@ -196,10 +195,10 @@ suite('Storage Migration', () => {
|
|||
|
||||
const migratedStorage = parsed.multiRoot.get(workspaceToMigrateTo);
|
||||
assert.equal(Object.keys(migratedStorage).length, 4);
|
||||
assert.equal(migratedStorage['key1'], s2.get('key1', StorageScope.WORKSPACE));
|
||||
assert.equal(migratedStorage['key2.something'], s2.get('key2.something', StorageScope.WORKSPACE));
|
||||
assert.equal(migratedStorage['key3/special'], s2.get('key3/special', StorageScope.WORKSPACE));
|
||||
assert.equal(migratedStorage['key4 space'], s2.get('key4 space', StorageScope.WORKSPACE));
|
||||
assert.equal(migratedStorage['key1'], s2.get('key1', StorageLegacyScope.WORKSPACE));
|
||||
assert.equal(migratedStorage['key2.something'], s2.get('key2.something', StorageLegacyScope.WORKSPACE));
|
||||
assert.equal(migratedStorage['key3/special'], s2.get('key3/special', StorageLegacyScope.WORKSPACE));
|
||||
assert.equal(migratedStorage['key4 space'], s2.get('key4 space', StorageLegacyScope.WORKSPACE));
|
||||
});
|
||||
|
||||
test('Migrate Storage (folder (Windows UNC) => multi root)', () => {
|
||||
|
@ -210,7 +209,7 @@ suite('Storage Migration', () => {
|
|||
|
||||
migrateStorageToMultiRootWorkspace(workspaceToMigrateFrom, { id: '1500007676869', configPath: null }, storage);
|
||||
|
||||
const s2 = new StorageService(storage, storage, workspaceToMigrateTo);
|
||||
const s2 = new StorageLegacyService(storage, storage, workspaceToMigrateTo);
|
||||
const parsed = parseStorage(storage);
|
||||
|
||||
assert.equal(parsed.empty.size, 0);
|
||||
|
@ -219,10 +218,10 @@ suite('Storage Migration', () => {
|
|||
|
||||
const migratedStorage = parsed.multiRoot.get(workspaceToMigrateTo);
|
||||
assert.equal(Object.keys(migratedStorage).length, 4);
|
||||
assert.equal(migratedStorage['key1'], s2.get('key1', StorageScope.WORKSPACE));
|
||||
assert.equal(migratedStorage['key2.something'], s2.get('key2.something', StorageScope.WORKSPACE));
|
||||
assert.equal(migratedStorage['key3/special'], s2.get('key3/special', StorageScope.WORKSPACE));
|
||||
assert.equal(migratedStorage['key4 space'], s2.get('key4 space', StorageScope.WORKSPACE));
|
||||
assert.equal(migratedStorage['key1'], s2.get('key1', StorageLegacyScope.WORKSPACE));
|
||||
assert.equal(migratedStorage['key2.something'], s2.get('key2.something', StorageLegacyScope.WORKSPACE));
|
||||
assert.equal(migratedStorage['key3/special'], s2.get('key3/special', StorageLegacyScope.WORKSPACE));
|
||||
assert.equal(migratedStorage['key4 space'], s2.get('key4 space', StorageLegacyScope.WORKSPACE));
|
||||
});
|
||||
|
||||
test('Migrate Storage (empty => multi root)', () => {
|
||||
|
@ -233,7 +232,7 @@ suite('Storage Migration', () => {
|
|||
|
||||
migrateStorageToMultiRootWorkspace(workspaceToMigrateFrom, { id: '2500007676869', configPath: null }, storage);
|
||||
|
||||
const s2 = new StorageService(storage, storage, workspaceToMigrateTo);
|
||||
const s2 = new StorageLegacyService(storage, storage, workspaceToMigrateTo);
|
||||
const parsed = parseStorage(storage);
|
||||
|
||||
assert.equal(parsed.empty.size, 1);
|
||||
|
@ -242,9 +241,9 @@ suite('Storage Migration', () => {
|
|||
|
||||
const migratedStorage = parsed.multiRoot.get(workspaceToMigrateTo);
|
||||
assert.equal(Object.keys(migratedStorage).length, 4);
|
||||
assert.equal(migratedStorage['key1'], s2.get('key1', StorageScope.WORKSPACE));
|
||||
assert.equal(migratedStorage['key2.something'], s2.get('key2.something', StorageScope.WORKSPACE));
|
||||
assert.equal(migratedStorage['key3/special'], s2.get('key3/special', StorageScope.WORKSPACE));
|
||||
assert.equal(migratedStorage['key4 space'], s2.get('key4 space', StorageScope.WORKSPACE));
|
||||
assert.equal(migratedStorage['key1'], s2.get('key1', StorageLegacyScope.WORKSPACE));
|
||||
assert.equal(migratedStorage['key2.something'], s2.get('key2.something', StorageLegacyScope.WORKSPACE));
|
||||
assert.equal(migratedStorage['key3/special'], s2.get('key3/special', StorageLegacyScope.WORKSPACE));
|
||||
assert.equal(migratedStorage['key4 space'], s2.get('key4 space', StorageLegacyScope.WORKSPACE));
|
||||
});
|
||||
});
|
|
@ -4,94 +4,51 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
|
||||
import { StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import { IWorkspaceContextService, IWorkspace, WorkbenchState } from 'vs/platform/workspace/common/workspace';
|
||||
import { StorageService, InMemoryLocalStorage } from 'vs/platform/storage/common/storageService';
|
||||
import { TestWorkspace } from 'vs/platform/workspace/test/common/testWorkspace';
|
||||
// tslint:disable-next-line:import-patterns
|
||||
import { TestStorageService } from 'vs/workbench/test/workbenchTestServices';
|
||||
|
||||
suite('Workbench StorageSevice', () => {
|
||||
let contextService: IWorkspaceContextService;
|
||||
let instantiationService: TestInstantiationService;
|
||||
|
||||
setup(() => {
|
||||
instantiationService = new TestInstantiationService();
|
||||
contextService = instantiationService.stub(IWorkspaceContextService, <IWorkspaceContextService>{
|
||||
getWorkbenchState: () => WorkbenchState.FOLDER,
|
||||
getWorkspace: () => {
|
||||
return <IWorkspace>TestWorkspace;
|
||||
}
|
||||
});
|
||||
});
|
||||
suite('StorageService', () => {
|
||||
|
||||
test('Remove Data', () => {
|
||||
let s = new StorageService(new InMemoryLocalStorage(), null, contextService.getWorkspace().id);
|
||||
s.store('Monaco.IDE.Core.Storage.Test.remove', 'foobar');
|
||||
assert.strictEqual('foobar', s.get('Monaco.IDE.Core.Storage.Test.remove'));
|
||||
let storage = new TestStorageService();
|
||||
storage.store('Monaco.IDE.Core.Storage.Test.remove', 'foobar', StorageScope.GLOBAL);
|
||||
assert.strictEqual('foobar', storage.get('Monaco.IDE.Core.Storage.Test.remove', StorageScope.GLOBAL));
|
||||
|
||||
s.remove('Monaco.IDE.Core.Storage.Test.remove');
|
||||
assert.ok(!s.get('Monaco.IDE.Core.Storage.Test.remove'));
|
||||
storage.remove('Monaco.IDE.Core.Storage.Test.remove', StorageScope.GLOBAL);
|
||||
assert.ok(!storage.get('Monaco.IDE.Core.Storage.Test.remove', StorageScope.GLOBAL));
|
||||
});
|
||||
|
||||
test('Get Data, Integer, Boolean', () => {
|
||||
let s = new StorageService(new InMemoryLocalStorage(), null, contextService.getWorkspace().id);
|
||||
let storage = new TestStorageService();
|
||||
|
||||
assert.strictEqual(s.get('Monaco.IDE.Core.Storage.Test.get', StorageScope.GLOBAL, 'foobar'), 'foobar');
|
||||
assert.strictEqual(s.get('Monaco.IDE.Core.Storage.Test.get', StorageScope.GLOBAL, ''), '');
|
||||
assert.strictEqual(s.get('Monaco.IDE.Core.Storage.Test.getInteger', StorageScope.GLOBAL, 5), 5);
|
||||
assert.strictEqual(s.get('Monaco.IDE.Core.Storage.Test.getInteger', StorageScope.GLOBAL, 0), 0);
|
||||
assert.strictEqual(s.get('Monaco.IDE.Core.Storage.Test.getBoolean', StorageScope.GLOBAL, true), true);
|
||||
assert.strictEqual(s.get('Monaco.IDE.Core.Storage.Test.getBoolean', StorageScope.GLOBAL, false), false);
|
||||
assert.strictEqual(storage.get('Monaco.IDE.Core.Storage.Test.get', StorageScope.GLOBAL, 'foobar'), 'foobar');
|
||||
assert.strictEqual(storage.get('Monaco.IDE.Core.Storage.Test.get', StorageScope.GLOBAL, ''), '');
|
||||
assert.strictEqual(storage.get('Monaco.IDE.Core.Storage.Test.getInteger', StorageScope.GLOBAL, 5), 5);
|
||||
assert.strictEqual(storage.get('Monaco.IDE.Core.Storage.Test.getInteger', StorageScope.GLOBAL, 0), 0);
|
||||
assert.strictEqual(storage.get('Monaco.IDE.Core.Storage.Test.getBoolean', StorageScope.GLOBAL, true), true);
|
||||
assert.strictEqual(storage.get('Monaco.IDE.Core.Storage.Test.getBoolean', StorageScope.GLOBAL, false), false);
|
||||
|
||||
s.store('Monaco.IDE.Core.Storage.Test.get', 'foobar');
|
||||
assert.strictEqual(s.get('Monaco.IDE.Core.Storage.Test.get'), 'foobar');
|
||||
storage.store('Monaco.IDE.Core.Storage.Test.get', 'foobar', StorageScope.GLOBAL);
|
||||
assert.strictEqual(storage.get('Monaco.IDE.Core.Storage.Test.get', StorageScope.GLOBAL), 'foobar');
|
||||
|
||||
s.store('Monaco.IDE.Core.Storage.Test.get', '');
|
||||
assert.strictEqual(s.get('Monaco.IDE.Core.Storage.Test.get'), '');
|
||||
storage.store('Monaco.IDE.Core.Storage.Test.get', '', StorageScope.GLOBAL);
|
||||
assert.strictEqual(storage.get('Monaco.IDE.Core.Storage.Test.get', StorageScope.GLOBAL), '');
|
||||
|
||||
s.store('Monaco.IDE.Core.Storage.Test.getInteger', 5);
|
||||
assert.strictEqual(s.getInteger('Monaco.IDE.Core.Storage.Test.getInteger'), 5);
|
||||
storage.store('Monaco.IDE.Core.Storage.Test.getInteger', 5, StorageScope.GLOBAL);
|
||||
assert.strictEqual(storage.getInteger('Monaco.IDE.Core.Storage.Test.getInteger', StorageScope.GLOBAL), 5);
|
||||
|
||||
s.store('Monaco.IDE.Core.Storage.Test.getInteger', 0);
|
||||
assert.strictEqual(s.getInteger('Monaco.IDE.Core.Storage.Test.getInteger'), 0);
|
||||
storage.store('Monaco.IDE.Core.Storage.Test.getInteger', 0, StorageScope.GLOBAL);
|
||||
assert.strictEqual(storage.getInteger('Monaco.IDE.Core.Storage.Test.getInteger', StorageScope.GLOBAL), 0);
|
||||
|
||||
s.store('Monaco.IDE.Core.Storage.Test.getBoolean', true);
|
||||
assert.strictEqual(s.getBoolean('Monaco.IDE.Core.Storage.Test.getBoolean'), true);
|
||||
storage.store('Monaco.IDE.Core.Storage.Test.getBoolean', true, StorageScope.GLOBAL);
|
||||
assert.strictEqual(storage.getBoolean('Monaco.IDE.Core.Storage.Test.getBoolean', StorageScope.GLOBAL), true);
|
||||
|
||||
s.store('Monaco.IDE.Core.Storage.Test.getBoolean', false);
|
||||
assert.strictEqual(s.getBoolean('Monaco.IDE.Core.Storage.Test.getBoolean'), false);
|
||||
storage.store('Monaco.IDE.Core.Storage.Test.getBoolean', false, StorageScope.GLOBAL);
|
||||
assert.strictEqual(storage.getBoolean('Monaco.IDE.Core.Storage.Test.getBoolean', StorageScope.GLOBAL), false);
|
||||
|
||||
assert.strictEqual(s.get('Monaco.IDE.Core.Storage.Test.getDefault', StorageScope.GLOBAL, 'getDefault'), 'getDefault');
|
||||
assert.strictEqual(s.getInteger('Monaco.IDE.Core.Storage.Test.getIntegerDefault', StorageScope.GLOBAL, 5), 5);
|
||||
assert.strictEqual(s.getBoolean('Monaco.IDE.Core.Storage.Test.getBooleanDefault', StorageScope.GLOBAL, true), true);
|
||||
});
|
||||
|
||||
test('StorageSevice cleans up when workspace changes', () => {
|
||||
let storageImpl = new InMemoryLocalStorage();
|
||||
let time = new Date().getTime();
|
||||
let s = new StorageService(storageImpl, null, contextService.getWorkspace().id, time);
|
||||
|
||||
s.store('key1', 'foobar');
|
||||
s.store('key2', 'something');
|
||||
s.store('wkey1', 'foo', StorageScope.WORKSPACE);
|
||||
s.store('wkey2', 'foo2', StorageScope.WORKSPACE);
|
||||
|
||||
s = new StorageService(storageImpl, null, contextService.getWorkspace().id, time);
|
||||
|
||||
assert.strictEqual(s.get('key1', StorageScope.GLOBAL), 'foobar');
|
||||
assert.strictEqual(s.get('key1', StorageScope.WORKSPACE, null), null);
|
||||
|
||||
assert.strictEqual(s.get('key2', StorageScope.GLOBAL), 'something');
|
||||
assert.strictEqual(s.get('wkey1', StorageScope.WORKSPACE), 'foo');
|
||||
assert.strictEqual(s.get('wkey2', StorageScope.WORKSPACE), 'foo2');
|
||||
|
||||
s = new StorageService(storageImpl, null, contextService.getWorkspace().id, time + 100);
|
||||
|
||||
assert.strictEqual(s.get('key1', StorageScope.GLOBAL), 'foobar');
|
||||
assert.strictEqual(s.get('key1', StorageScope.WORKSPACE, null), null);
|
||||
|
||||
assert.strictEqual(s.get('key2', StorageScope.GLOBAL), 'something');
|
||||
assert(!s.get('wkey1', StorageScope.WORKSPACE));
|
||||
assert(!s.get('wkey2', StorageScope.WORKSPACE));
|
||||
assert.strictEqual(storage.get('Monaco.IDE.Core.Storage.Test.getDefault', StorageScope.GLOBAL, 'getDefault'), 'getDefault');
|
||||
assert.strictEqual(storage.getInteger('Monaco.IDE.Core.Storage.Test.getIntegerDefault', StorageScope.GLOBAL, 5), 5);
|
||||
assert.strictEqual(storage.getBoolean('Monaco.IDE.Core.Storage.Test.getBooleanDefault', StorageScope.GLOBAL, true), true);
|
||||
});
|
||||
});
|
|
@ -5,8 +5,8 @@
|
|||
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import * as uuid from 'vs/base/common/uuid';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { resolveCommonProperties } from '../node/commonProperties';
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import { resolveCommonProperties } from 'vs/platform/telemetry/node/commonProperties';
|
||||
|
||||
export function resolveWorkbenchCommonProperties(storageService: IStorageService, commit: string, version: string, machineId: string, installSourcePath: string): TPromise<{ [name: string]: string }> {
|
||||
return resolveCommonProperties(commit, version, machineId, installSourcePath).then(result => {
|
||||
|
@ -15,13 +15,11 @@ export function resolveWorkbenchCommonProperties(storageService: IStorageService
|
|||
// __GDPR__COMMON__ "common.version.renderer" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }
|
||||
result['common.version.renderer'] = process.versions && (<any>process).versions['chrome'];
|
||||
|
||||
const lastSessionDate = storageService.get('telemetry.lastSessionDate');
|
||||
const firstSessionDate = storageService.get('telemetry.firstSessionDate') || new Date().toUTCString();
|
||||
storageService.store('telemetry.firstSessionDate', firstSessionDate);
|
||||
storageService.store('telemetry.lastSessionDate', new Date().toUTCString());
|
||||
const lastSessionDate = storageService.get('telemetry.lastSessionDate', StorageScope.GLOBAL);
|
||||
storageService.store('telemetry.lastSessionDate', new Date().toUTCString(), StorageScope.GLOBAL);
|
||||
|
||||
// __GDPR__COMMON__ "common.firstSessionDate" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||
result['common.firstSessionDate'] = firstSessionDate;
|
||||
result['common.firstSessionDate'] = getOrCreateFirstSessionDate(storageService);
|
||||
// __GDPR__COMMON__ "common.lastSessionDate" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||
result['common.lastSessionDate'] = lastSessionDate;
|
||||
// __GDPR__COMMON__ "common.isNewSession" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||
|
@ -34,8 +32,19 @@ export function resolveWorkbenchCommonProperties(storageService: IStorageService
|
|||
}
|
||||
|
||||
function getOrCreateInstanceId(storageService: IStorageService): string {
|
||||
const result = storageService.get('telemetry.instanceId') || uuid.generateUuid();
|
||||
storageService.store('telemetry.instanceId', result);
|
||||
const key = 'telemetry.instanceId';
|
||||
|
||||
const result = storageService.get(key, StorageScope.GLOBAL, uuid.generateUuid());
|
||||
storageService.store(key, result, StorageScope.GLOBAL);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function getOrCreateFirstSessionDate(storageService: IStorageService): string {
|
||||
const key = 'telemetry.firstSessionDate';
|
||||
|
||||
const firstSessionDate = storageService.get(key, StorageScope.GLOBAL, new Date().toUTCString());
|
||||
storageService.store(key, firstSessionDate, StorageScope.GLOBAL);
|
||||
|
||||
return firstSessionDate;
|
||||
}
|
|
@ -7,9 +7,10 @@ import * as path from 'path';
|
|||
import * as os from 'os';
|
||||
import * as fs from 'fs';
|
||||
import { resolveWorkbenchCommonProperties } from 'vs/platform/telemetry/node/workbenchCommonProperties';
|
||||
import { StorageService, InMemoryLocalStorage } from 'vs/platform/storage/common/storageService';
|
||||
import { TestWorkspace } from 'vs/platform/workspace/test/common/testWorkspace';
|
||||
import { getRandomTestPath } from 'vs/workbench/test/workbenchTestServices';
|
||||
import { getRandomTestPath, TestEnvironmentService } from 'vs/workbench/test/workbenchTestServices';
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import { StorageService } from 'vs/platform/storage/electron-browser/storageService';
|
||||
import { NullLogService } from 'vs/platform/log/common/log';
|
||||
import { del } from 'vs/base/node/extfs';
|
||||
import { mkdirp } from 'vs/base/node/pfs';
|
||||
import { timeout } from 'vs/base/common/async';
|
||||
|
@ -20,10 +21,10 @@ suite('Telemetry - common properties', function () {
|
|||
|
||||
const commit: string = void 0;
|
||||
const version: string = void 0;
|
||||
let storageService: StorageService;
|
||||
let nestStorage2Service: IStorageService;
|
||||
|
||||
setup(() => {
|
||||
storageService = new StorageService(new InMemoryLocalStorage(), null, TestWorkspace.id);
|
||||
nestStorage2Service = new StorageService(':memory:', new NullLogService(), TestEnvironmentService);
|
||||
});
|
||||
|
||||
teardown(done => {
|
||||
|
@ -33,7 +34,7 @@ suite('Telemetry - common properties', function () {
|
|||
test('default', async function () {
|
||||
await mkdirp(parentDir);
|
||||
fs.writeFileSync(installSource, 'my.install.source');
|
||||
const props = await resolveWorkbenchCommonProperties(storageService, commit, version, 'someMachineId', installSource);
|
||||
const props = await resolveWorkbenchCommonProperties(nestStorage2Service, commit, version, 'someMachineId', installSource);
|
||||
assert.ok('commitHash' in props);
|
||||
assert.ok('sessionID' in props);
|
||||
assert.ok('timestamp' in props);
|
||||
|
@ -54,22 +55,22 @@ suite('Telemetry - common properties', function () {
|
|||
assert.ok('common.instanceId' in props, 'instanceId');
|
||||
assert.ok('common.machineId' in props, 'machineId');
|
||||
fs.unlinkSync(installSource);
|
||||
const props_1 = await resolveWorkbenchCommonProperties(storageService, commit, version, 'someMachineId', installSource);
|
||||
const props_1 = await resolveWorkbenchCommonProperties(nestStorage2Service, commit, version, 'someMachineId', installSource);
|
||||
assert.ok(!('common.source' in props_1));
|
||||
});
|
||||
|
||||
test('lastSessionDate when aviablale', async function () {
|
||||
|
||||
storageService.store('telemetry.lastSessionDate', new Date().toUTCString());
|
||||
nestStorage2Service.store('telemetry.lastSessionDate', new Date().toUTCString(), StorageScope.GLOBAL);
|
||||
|
||||
const props = await resolveWorkbenchCommonProperties(storageService, commit, version, 'someMachineId', installSource);
|
||||
const props = await resolveWorkbenchCommonProperties(nestStorage2Service, commit, version, 'someMachineId', installSource);
|
||||
assert.ok('common.lastSessionDate' in props); // conditional, see below
|
||||
assert.ok('common.isNewSession' in props);
|
||||
assert.equal(props['common.isNewSession'], 0);
|
||||
});
|
||||
|
||||
test('values chance on ask', async function () {
|
||||
const props = await resolveWorkbenchCommonProperties(storageService, commit, version, 'someMachineId', installSource);
|
||||
const props = await resolveWorkbenchCommonProperties(nestStorage2Service, commit, version, 'someMachineId', installSource);
|
||||
let value1 = props['common.sequence'];
|
||||
let value2 = props['common.sequence'];
|
||||
assert.ok(value1 !== value2, 'seq');
|
||||
|
|
|
@ -12,12 +12,16 @@ import { Event, Emitter } from 'vs/base/common/event';
|
|||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { IConstructorSignature0, IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { trackFocus, Dimension } from 'vs/base/browser/dom';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
|
||||
/**
|
||||
* Composites are layed out in the sidebar and panel part of the workbench. At a time only one composite
|
||||
* can be open in the sidebar, and only one composite can be open in the panel.
|
||||
*
|
||||
* Each composite has a minimized representation that is good enough to provide some
|
||||
* information about the state of the composite data.
|
||||
*
|
||||
* The workbench will keep a composite alive after it has been created and show/hide it based on
|
||||
* user interaction. The lifecycle of a composite goes in the order create(), setVisible(true|false),
|
||||
* layout(), focus(), dispose(). During use of the workbench, a composite will often receive a setVisible,
|
||||
|
@ -33,6 +37,7 @@ export abstract class Composite extends Component implements IComposite {
|
|||
if (!this._onDidFocus) {
|
||||
this._registerFocusTrackEvents();
|
||||
}
|
||||
|
||||
return this._onDidFocus.event;
|
||||
}
|
||||
|
||||
|
@ -41,6 +46,7 @@ export abstract class Composite extends Component implements IComposite {
|
|||
if (!this._onDidBlur) {
|
||||
this._registerFocusTrackEvents();
|
||||
}
|
||||
|
||||
return this._onDidBlur.event;
|
||||
}
|
||||
|
||||
|
@ -64,9 +70,10 @@ export abstract class Composite extends Component implements IComposite {
|
|||
constructor(
|
||||
id: string,
|
||||
private _telemetryService: ITelemetryService,
|
||||
themeService: IThemeService
|
||||
themeService: IThemeService,
|
||||
storageService: IStorageService
|
||||
) {
|
||||
super(id, themeService);
|
||||
super(id, themeService, storageService);
|
||||
|
||||
this.visible = false;
|
||||
}
|
||||
|
@ -232,9 +239,9 @@ export abstract class CompositeDescriptor<T extends Composite> {
|
|||
}
|
||||
}
|
||||
|
||||
export abstract class CompositeRegistry<T extends Composite> {
|
||||
export abstract class CompositeRegistry<T extends Composite> extends Disposable {
|
||||
|
||||
private readonly _onDidRegister: Emitter<CompositeDescriptor<T>> = new Emitter<CompositeDescriptor<T>>();
|
||||
private readonly _onDidRegister: Emitter<CompositeDescriptor<T>> = this._register(new Emitter<CompositeDescriptor<T>>());
|
||||
get onDidRegister(): Event<CompositeDescriptor<T>> { return this._onDidRegister.event; }
|
||||
|
||||
private composites: CompositeDescriptor<T>[] = [];
|
||||
|
|
|
@ -25,6 +25,7 @@ import { SidebarPart } from 'vs/workbench/browser/parts/sidebar/sidebarPart';
|
|||
import { PanelPart } from 'vs/workbench/browser/parts/panel/panelPart';
|
||||
import { StatusbarPart } from 'vs/workbench/browser/parts/statusbar/statusbarPart';
|
||||
import { getZoomFactor } from 'vs/base/browser/browser';
|
||||
import { RunOnceScheduler } from 'vs/base/common/async';
|
||||
|
||||
const TITLE_BAR_HEIGHT = isMacintosh ? 22 : 30;
|
||||
const STATUS_BAR_HEIGHT = 22;
|
||||
|
@ -67,6 +68,8 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr
|
|||
private _panelHeight: number;
|
||||
private _panelWidth: number;
|
||||
|
||||
private saveStateScheduler: RunOnceScheduler;
|
||||
|
||||
constructor(
|
||||
private parent: HTMLElement,
|
||||
private workbenchContainer: HTMLElement,
|
||||
|
@ -99,6 +102,9 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr
|
|||
this.sashXTwo = new Sash(this.workbenchContainer, this);
|
||||
this.sashY = new Sash(this.workbenchContainer, this, { orientation: Orientation.HORIZONTAL });
|
||||
|
||||
// State scheduler
|
||||
this.saveStateScheduler = new RunOnceScheduler(() => this.saveState(), 1000);
|
||||
|
||||
this.registerListeners();
|
||||
}
|
||||
|
||||
|
@ -115,6 +121,10 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr
|
|||
private registerListeners(): void {
|
||||
this._register(this.themeService.onThemeChange(_ => this.layout()));
|
||||
this._register(this.parts.editor.onDidSizeConstraintsChange(() => this.onDidEditorSizeConstraintsChange()));
|
||||
this._register(this.storageService.onWillSaveState(() => {
|
||||
this.saveStateScheduler.dispose();
|
||||
this.saveState();
|
||||
}));
|
||||
|
||||
this.registerSashListeners();
|
||||
}
|
||||
|
@ -372,20 +382,22 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr
|
|||
}));
|
||||
|
||||
this._register(this.sashXOne.onDidEnd(() => {
|
||||
this.storageService.store(WorkbenchLayout.sashXOneWidthSettingsKey, this.sidebarWidth, StorageScope.GLOBAL);
|
||||
this.saveStateScheduler.schedule();
|
||||
}));
|
||||
|
||||
this._register(this.sashY.onDidEnd(() => {
|
||||
this.storageService.store(WorkbenchLayout.sashYHeightSettingsKey, this.panelHeight, StorageScope.GLOBAL);
|
||||
this.saveStateScheduler.schedule();
|
||||
}));
|
||||
|
||||
this._register(this.sashXTwo.onDidEnd(() => {
|
||||
this.storageService.store(WorkbenchLayout.sashXTwoWidthSettingsKey, this.panelWidth, StorageScope.GLOBAL);
|
||||
this.saveStateScheduler.schedule();
|
||||
}));
|
||||
|
||||
this._register(this.sashY.onDidReset(() => {
|
||||
this.panelHeight = this.sidebarHeight * DEFAULT_PANEL_SIZE_COEFFICIENT;
|
||||
this.storageService.store(WorkbenchLayout.sashYHeightSettingsKey, this.panelHeight, StorageScope.GLOBAL);
|
||||
|
||||
this.saveStateScheduler.schedule();
|
||||
|
||||
this.layout();
|
||||
}));
|
||||
|
||||
|
@ -393,18 +405,22 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr
|
|||
let activeViewlet = this.viewletService.getActiveViewlet();
|
||||
let optimalWidth = activeViewlet && activeViewlet.getOptimalWidth();
|
||||
this.sidebarWidth = Math.max(optimalWidth, DEFAULT_SIDEBAR_PART_WIDTH);
|
||||
this.storageService.store(WorkbenchLayout.sashXOneWidthSettingsKey, this.sidebarWidth, StorageScope.GLOBAL);
|
||||
|
||||
this.saveStateScheduler.schedule();
|
||||
|
||||
this.partService.setSideBarHidden(false).then(() => this.layout());
|
||||
}));
|
||||
|
||||
this._register(this.sashXTwo.onDidReset(() => {
|
||||
this.panelWidth = (this.workbenchSize.width - this.sidebarWidth - this.activitybarWidth) * DEFAULT_PANEL_SIZE_COEFFICIENT;
|
||||
this.storageService.store(WorkbenchLayout.sashXTwoWidthSettingsKey, this.panelWidth, StorageScope.GLOBAL);
|
||||
|
||||
this.saveStateScheduler.schedule();
|
||||
|
||||
this.layout();
|
||||
}));
|
||||
}
|
||||
|
||||
layout(options?: ILayoutOptions): void {
|
||||
layout(options?: ILayoutOptions): void { //
|
||||
this.workbenchSize = getClientArea(this.parent);
|
||||
|
||||
const isActivityBarHidden = !this.partService.isVisible(Parts.ACTIVITYBAR_PART);
|
||||
|
@ -472,7 +488,7 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr
|
|||
this.panelSizeBeforeMaximized = panelWidth;
|
||||
}
|
||||
}
|
||||
this.storageService.store(WorkbenchLayout.panelSizeBeforeMaximizedKey, this.panelSizeBeforeMaximized, StorageScope.GLOBAL);
|
||||
|
||||
const panelDimension = new Dimension(panelWidth, panelHeight);
|
||||
|
||||
// Editor
|
||||
|
@ -527,16 +543,13 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr
|
|||
|
||||
if (!isSidebarHidden) {
|
||||
this.sidebarWidth = sidebarSize.width;
|
||||
this.storageService.store(WorkbenchLayout.sashXOneWidthSettingsKey, this.sidebarWidth, StorageScope.GLOBAL);
|
||||
}
|
||||
|
||||
if (!isPanelHidden) {
|
||||
if (panelPosition === Position.BOTTOM) {
|
||||
this.panelHeight = panelDimension.height;
|
||||
this.storageService.store(WorkbenchLayout.sashYHeightSettingsKey, this.panelHeight, StorageScope.GLOBAL);
|
||||
} else {
|
||||
this.panelWidth = panelDimension.width;
|
||||
this.storageService.store(WorkbenchLayout.sashXTwoWidthSettingsKey, this.panelWidth, StorageScope.GLOBAL);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -642,6 +655,9 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr
|
|||
|
||||
// Propagate to Context View
|
||||
this.contextViewService.layout();
|
||||
|
||||
// Schedule save state
|
||||
this.saveStateScheduler.schedule();
|
||||
}
|
||||
|
||||
getVerticalSashTop(sash: Sash): number {
|
||||
|
@ -743,4 +759,13 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr
|
|||
this.layout();
|
||||
}
|
||||
}
|
||||
|
||||
private saveState(): void {
|
||||
this.storageService.store(WorkbenchLayout.sashXOneWidthSettingsKey, this.sidebarWidth, StorageScope.GLOBAL);
|
||||
|
||||
this.storageService.store(WorkbenchLayout.sashYHeightSettingsKey, this.panelHeight, StorageScope.GLOBAL);
|
||||
this.storageService.store(WorkbenchLayout.sashXTwoWidthSettingsKey, this.panelWidth, StorageScope.GLOBAL);
|
||||
|
||||
this.storageService.store(WorkbenchLayout.panelSizeBeforeMaximizedKey, this.panelSizeBeforeMaximized, StorageScope.GLOBAL);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import 'vs/css!./media/part';
|
|||
import { Component } from 'vs/workbench/common/component';
|
||||
import { IThemeService, ITheme } from 'vs/platform/theme/common/themeService';
|
||||
import { Dimension, size } from 'vs/base/browser/dom';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
|
||||
export interface IPartOptions {
|
||||
hasTitle?: boolean;
|
||||
|
@ -14,8 +15,8 @@ export interface IPartOptions {
|
|||
}
|
||||
|
||||
/**
|
||||
* Parts are layed out in the workbench and have their own layout that arranges an optional title
|
||||
* and mandatory content area to show content.
|
||||
* Parts are layed out in the workbench and have their own layout that
|
||||
* arranges an optional title and mandatory content area to show content.
|
||||
*/
|
||||
export abstract class Part extends Component {
|
||||
private parent: HTMLElement;
|
||||
|
@ -26,9 +27,10 @@ export abstract class Part extends Component {
|
|||
constructor(
|
||||
id: string,
|
||||
private options: IPartOptions,
|
||||
themeService: IThemeService
|
||||
themeService: IThemeService,
|
||||
storageService: IStorageService
|
||||
) {
|
||||
super(id, themeService);
|
||||
super(id, themeService, storageService);
|
||||
}
|
||||
|
||||
protected onThemeChange(theme: ITheme): void {
|
||||
|
@ -98,10 +100,10 @@ export abstract class Part extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
const TITLE_HEIGHT = 35;
|
||||
|
||||
export class PartLayout {
|
||||
|
||||
private static readonly TITLE_HEIGHT = 35;
|
||||
|
||||
constructor(container: HTMLElement, private options: IPartOptions, titleArea: HTMLElement, private contentArea: HTMLElement) { }
|
||||
|
||||
layout(dimension: Dimension): Dimension[] {
|
||||
|
@ -113,7 +115,7 @@ export class PartLayout {
|
|||
// Title Size: Width (Fill), Height (Variable)
|
||||
let titleSize: Dimension;
|
||||
if (this.options && this.options.hasTitle) {
|
||||
titleSize = new Dimension(width, Math.min(height, TITLE_HEIGHT));
|
||||
titleSize = new Dimension(width, Math.min(height, PartLayout.TITLE_HEIGHT));
|
||||
} else {
|
||||
titleSize = new Dimension(0, 0);
|
||||
}
|
||||
|
|
|
@ -61,7 +61,7 @@ export class ActivitybarPart extends Part {
|
|||
@IStorageService private storageService: IStorageService,
|
||||
@IExtensionService private extensionService: IExtensionService
|
||||
) {
|
||||
super(id, { hasTitle: false }, themeService);
|
||||
super(id, { hasTitle: false }, themeService, storageService);
|
||||
|
||||
this.compositeBar = this._register(this.instantiationService.createInstance(CompositeBar, {
|
||||
icon: true,
|
||||
|
@ -332,10 +332,10 @@ export class ActivitybarPart extends Part {
|
|||
return sizes;
|
||||
}
|
||||
|
||||
shutdown(): void {
|
||||
protected saveState(): void {
|
||||
const state = this.viewletService.getAllViewlets().map(({ id, iconUrl }) => ({ id, iconUrl }));
|
||||
this.storageService.store(ActivitybarPart.PLACEHOLDER_VIEWLETS, JSON.stringify(state), StorageScope.GLOBAL);
|
||||
|
||||
super.shutdown();
|
||||
super.saveState();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -85,7 +85,7 @@ export abstract class CompositePart<T extends Composite> extends Part {
|
|||
id: string,
|
||||
options: IPartOptions
|
||||
) {
|
||||
super(id, options, themeService);
|
||||
super(id, options, themeService, storageService);
|
||||
|
||||
this.instantiatedCompositeListeners = [];
|
||||
this.mapCompositeToCompositeContainer = {};
|
||||
|
@ -495,12 +495,6 @@ export abstract class CompositePart<T extends Composite> extends Part {
|
|||
return sizes;
|
||||
}
|
||||
|
||||
shutdown(): void {
|
||||
this.instantiatedComposites.forEach(i => i.shutdown());
|
||||
|
||||
super.shutdown();
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.mapCompositeToCompositeContainer = null;
|
||||
this.mapProgressServiceToComposite = null;
|
||||
|
|
|
@ -10,13 +10,12 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
|||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import { LRUCache } from 'vs/base/common/map';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { once, Event } from 'vs/base/common/event';
|
||||
import { isEmptyObject } from 'vs/base/common/types';
|
||||
import { DEFAULT_EDITOR_MIN_DIMENSIONS, DEFAULT_EDITOR_MAX_DIMENSIONS } from 'vs/workbench/browser/parts/editor/editor';
|
||||
import { Scope } from 'vs/workbench/common/memento';
|
||||
|
||||
/**
|
||||
* The base class of editors in the workbench. Editors register themselves for specific editor inputs.
|
||||
|
@ -50,9 +49,10 @@ export abstract class BaseEditor extends Panel implements IEditor {
|
|||
constructor(
|
||||
id: string,
|
||||
telemetryService: ITelemetryService,
|
||||
themeService: IThemeService
|
||||
themeService: IThemeService,
|
||||
storageService: IStorageService
|
||||
) {
|
||||
super(id, telemetryService, themeService);
|
||||
super(id, telemetryService, themeService, storageService);
|
||||
}
|
||||
|
||||
get input(): EditorInput {
|
||||
|
@ -143,28 +143,28 @@ export abstract class BaseEditor extends Panel implements IEditor {
|
|||
this._group = group;
|
||||
}
|
||||
|
||||
protected getEditorMemento<T>(storageService: IStorageService, editorGroupService: IEditorGroupsService, key: string, limit: number = 10): IEditorMemento<T> {
|
||||
protected getEditorMemento<T>(editorGroupService: IEditorGroupsService, key: string, limit: number = 10): IEditorMemento<T> {
|
||||
const mementoKey = `${this.getId()}${key}`;
|
||||
|
||||
let editorMemento = BaseEditor.EDITOR_MEMENTOS.get(mementoKey);
|
||||
if (!editorMemento) {
|
||||
editorMemento = new EditorMemento(this.getId(), key, this.getMemento(storageService, Scope.WORKSPACE), limit, editorGroupService);
|
||||
editorMemento = new EditorMemento(this.getId(), key, this.getMemento(StorageScope.WORKSPACE), limit, editorGroupService);
|
||||
BaseEditor.EDITOR_MEMENTOS.set(mementoKey, editorMemento);
|
||||
}
|
||||
|
||||
return editorMemento;
|
||||
}
|
||||
|
||||
shutdown(): void {
|
||||
protected saveState(): void {
|
||||
|
||||
// Shutdown all editor memento for this editor type
|
||||
// Save all editor memento for this editor type
|
||||
BaseEditor.EDITOR_MEMENTOS.forEach(editorMemento => {
|
||||
if (editorMemento.id === this.getId()) {
|
||||
editorMemento.shutdown();
|
||||
editorMemento.saveState();
|
||||
}
|
||||
});
|
||||
|
||||
super.shutdown();
|
||||
super.saveState();
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
|
@ -195,9 +195,9 @@ export class EditorMemento<T> implements IEditorMemento<T> {
|
|||
return this._id;
|
||||
}
|
||||
|
||||
saveState(group: IEditorGroup, resource: URI, state: T): void;
|
||||
saveState(group: IEditorGroup, editor: EditorInput, state: T): void;
|
||||
saveState(group: IEditorGroup, resourceOrEditor: URI | EditorInput, state: T): void {
|
||||
saveEditorState(group: IEditorGroup, resource: URI, state: T): void;
|
||||
saveEditorState(group: IEditorGroup, editor: EditorInput, state: T): void;
|
||||
saveEditorState(group: IEditorGroup, resourceOrEditor: URI | EditorInput, state: T): void {
|
||||
const resource = this.doGetResource(resourceOrEditor);
|
||||
if (!resource || !group) {
|
||||
return; // we are not in a good state to save any state for a resource
|
||||
|
@ -216,14 +216,14 @@ export class EditorMemento<T> implements IEditorMemento<T> {
|
|||
// Automatically clear when editor input gets disposed if any
|
||||
if (resourceOrEditor instanceof EditorInput) {
|
||||
once(resourceOrEditor.onDispose)(() => {
|
||||
this.clearState(resource);
|
||||
this.clearEditorState(resource);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
loadState(group: IEditorGroup, resource: URI): T;
|
||||
loadState(group: IEditorGroup, editor: EditorInput): T;
|
||||
loadState(group: IEditorGroup, resourceOrEditor: URI | EditorInput): T {
|
||||
loadEditorState(group: IEditorGroup, resource: URI): T;
|
||||
loadEditorState(group: IEditorGroup, editor: EditorInput): T;
|
||||
loadEditorState(group: IEditorGroup, resourceOrEditor: URI | EditorInput): T {
|
||||
const resource = this.doGetResource(resourceOrEditor);
|
||||
if (!resource || !group) {
|
||||
return void 0; // we are not in a good state to load any state for a resource
|
||||
|
@ -239,9 +239,9 @@ export class EditorMemento<T> implements IEditorMemento<T> {
|
|||
return void 0;
|
||||
}
|
||||
|
||||
clearState(resource: URI, group?: IEditorGroup): void;
|
||||
clearState(editor: EditorInput, group?: IEditorGroup): void;
|
||||
clearState(resourceOrEditor: URI | EditorInput, group?: IEditorGroup): void {
|
||||
clearEditorState(resource: URI, group?: IEditorGroup): void;
|
||||
clearEditorState(editor: EditorInput, group?: IEditorGroup): void;
|
||||
clearEditorState(resourceOrEditor: URI | EditorInput, group?: IEditorGroup): void {
|
||||
const resource = this.doGetResource(resourceOrEditor);
|
||||
if (resource) {
|
||||
const cache = this.doLoad();
|
||||
|
@ -279,7 +279,7 @@ export class EditorMemento<T> implements IEditorMemento<T> {
|
|||
return this.cache;
|
||||
}
|
||||
|
||||
shutdown(): void {
|
||||
saveState(): void {
|
||||
const cache = this.doLoad();
|
||||
|
||||
// Cleanup once during shutdown
|
||||
|
|
|
@ -10,6 +10,7 @@ import { IThemeService } from 'vs/platform/theme/common/themeService';
|
|||
import { SideBySideEditor } from 'vs/workbench/browser/parts/editor/sideBySideEditor';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { BaseBinaryResourceEditor } from 'vs/workbench/browser/parts/editor/binaryEditor';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
|
||||
/**
|
||||
* An implementation of editor for diffing binary files like images or videos.
|
||||
|
@ -21,9 +22,10 @@ export class BinaryResourceDiffEditor extends SideBySideEditor {
|
|||
constructor(
|
||||
@ITelemetryService telemetryService: ITelemetryService,
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
@IThemeService themeService: IThemeService
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IStorageService storageService: IStorageService
|
||||
) {
|
||||
super(telemetryService, instantiationService, themeService);
|
||||
super(telemetryService, instantiationService, themeService, storageService);
|
||||
}
|
||||
|
||||
getMetadata(): string {
|
||||
|
|
|
@ -19,6 +19,7 @@ import { Dimension, size, clearNode } from 'vs/base/browser/dom';
|
|||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { dispose } from 'vs/base/common/lifecycle';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
|
||||
export interface IOpenCallbacks {
|
||||
openInternal: (input: EditorInput, options: EditorOptions) => Thenable<void>;
|
||||
|
@ -48,8 +49,9 @@ export abstract class BaseBinaryResourceEditor extends BaseEditor {
|
|||
telemetryService: ITelemetryService,
|
||||
themeService: IThemeService,
|
||||
@IFileService private readonly _fileService: IFileService,
|
||||
@IStorageService storageService: IStorageService
|
||||
) {
|
||||
super(id, telemetryService, themeService);
|
||||
super(id, telemetryService, themeService, storageService);
|
||||
|
||||
this.callbacks = callbacks;
|
||||
}
|
||||
|
|
|
@ -117,8 +117,6 @@ export interface IEditorGroupView extends IDisposable, ISerializableView, IEdito
|
|||
setActive(isActive: boolean): void;
|
||||
setLabel(label: string): void;
|
||||
relayout(): void;
|
||||
|
||||
shutdown(): void;
|
||||
}
|
||||
|
||||
export function getActiveTextEditorOptions(group: IEditorGroup, expectedActiveEditor?: IEditorInput, presetOptions?: EditorOptions): EditorOptions {
|
||||
|
|
|
@ -230,12 +230,6 @@ export class EditorControl extends Disposable {
|
|||
}
|
||||
}
|
||||
|
||||
shutdown(): void {
|
||||
|
||||
// Forward to all editor controls
|
||||
this.controls.forEach(editor => editor.shutdown());
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.activeControlDisposeables = dispose(this.activeControlDisposeables);
|
||||
|
||||
|
|
|
@ -1366,10 +1366,6 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
|||
|
||||
//#endregion
|
||||
|
||||
shutdown(): void {
|
||||
this.editorControl.shutdown();
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this._disposed = true;
|
||||
|
||||
|
|
|
@ -21,8 +21,7 @@ import { EditorGroupView } from 'vs/workbench/browser/parts/editor/editorGroupVi
|
|||
import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration';
|
||||
import { IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { assign } from 'vs/base/common/objects';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { Scope } from 'vs/workbench/common/memento';
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import { ISerializedEditorGroup, isSerializedEditorGroup } from 'vs/workbench/common/editor/editorGroup';
|
||||
import { TValueCallback, TPromise } from 'vs/base/common/winjs.base';
|
||||
import { always } from 'vs/base/common/async';
|
||||
|
@ -117,7 +116,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
|
|||
private dimension: Dimension;
|
||||
private _preferredSize: Dimension;
|
||||
|
||||
private memento: object;
|
||||
private workspaceMemento: object;
|
||||
private globalMemento: object;
|
||||
|
||||
private _partOptions: IEditorPartOptions;
|
||||
|
@ -140,15 +139,16 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
|
|||
@IInstantiationService private instantiationService: IInstantiationService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IConfigurationService private configurationService: IConfigurationService,
|
||||
@IStorageService private storageService: IStorageService
|
||||
@IStorageService storageService: IStorageService
|
||||
) {
|
||||
super(id, { hasTitle: false }, themeService);
|
||||
super(id, { hasTitle: false }, themeService, storageService);
|
||||
|
||||
this.gridWidgetView = new GridWidgetView<IEditorGroupView>();
|
||||
|
||||
this._partOptions = getEditorPartOptions(this.configurationService.getValue<IWorkbenchEditorConfiguration>());
|
||||
this.memento = this.getMemento(this.storageService, Scope.WORKSPACE);
|
||||
this.globalMemento = this.getMemento(this.storageService, Scope.GLOBAL);
|
||||
|
||||
this.workspaceMemento = this.getMemento(StorageScope.WORKSPACE);
|
||||
this.globalMemento = this.getMemento(StorageScope.GLOBAL);
|
||||
|
||||
this._whenRestored = new TPromise(resolve => {
|
||||
this.whenRestoredComplete = resolve;
|
||||
|
@ -818,7 +818,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
|
|||
}
|
||||
|
||||
private doCreateGridControlWithPreviousState(): void {
|
||||
const uiState = this.memento[EditorPart.EDITOR_PART_UI_STATE_STORAGE_KEY] as IEditorPartUIState;
|
||||
const uiState = this.workspaceMemento[EditorPart.EDITOR_PART_UI_STATE_STORAGE_KEY] as IEditorPartUIState;
|
||||
if (uiState && uiState.serializedGrid) {
|
||||
try {
|
||||
|
||||
|
@ -955,7 +955,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
|
|||
this._onDidLayout.fire(dimension);
|
||||
}
|
||||
|
||||
shutdown(): void {
|
||||
protected saveState(): void {
|
||||
|
||||
// Persist grid UI state
|
||||
if (this.gridWidget) {
|
||||
|
@ -966,19 +966,16 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
|
|||
};
|
||||
|
||||
if (this.isEmpty()) {
|
||||
delete this.memento[EditorPart.EDITOR_PART_UI_STATE_STORAGE_KEY];
|
||||
delete this.workspaceMemento[EditorPart.EDITOR_PART_UI_STATE_STORAGE_KEY];
|
||||
} else {
|
||||
this.memento[EditorPart.EDITOR_PART_UI_STATE_STORAGE_KEY] = uiState;
|
||||
this.workspaceMemento[EditorPart.EDITOR_PART_UI_STATE_STORAGE_KEY] = uiState;
|
||||
}
|
||||
}
|
||||
|
||||
// Persist centered view state
|
||||
this.globalMemento[EditorPart.EDITOR_PART_CENTERED_VIEW_STORAGE_KEY] = this.centeredLayoutWidget.state;
|
||||
|
||||
// Forward to all groups
|
||||
this.groupViews.forEach(group => group.shutdown());
|
||||
|
||||
super.shutdown();
|
||||
super.saveState();
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
|
|
|
@ -17,6 +17,7 @@ import { CancellationToken } from 'vs/base/common/cancellation';
|
|||
import { IEditorGroup } from 'vs/workbench/services/group/common/editorGroupsService';
|
||||
import { SplitView, Sizing, Orientation } from 'vs/base/browser/ui/splitview/splitview';
|
||||
import { Event, Relay, anyEvent, mapEvent, Emitter } from 'vs/base/common/event';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
|
||||
export class SideBySideEditor extends BaseEditor {
|
||||
|
||||
|
@ -59,9 +60,10 @@ export class SideBySideEditor extends BaseEditor {
|
|||
constructor(
|
||||
@ITelemetryService telemetryService: ITelemetryService,
|
||||
@IInstantiationService private instantiationService: IInstantiationService,
|
||||
@IThemeService themeService: IThemeService
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IStorageService storageService: IStorageService
|
||||
) {
|
||||
super(SideBySideEditor.ID, telemetryService, themeService);
|
||||
super(SideBySideEditor.ID, telemetryService, themeService, storageService);
|
||||
}
|
||||
|
||||
protected createEditor(parent: HTMLElement): void {
|
||||
|
|
|
@ -58,7 +58,7 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditor {
|
|||
super(TextDiffEditor.ID, telemetryService, instantiationService, storageService, configurationService, themeService, textFileService, editorService, editorGroupService, windowService);
|
||||
}
|
||||
|
||||
protected getEditorMemento<T>(storageService: IStorageService, editorGroupService: IEditorGroupsService, key: string, limit: number = 10): IEditorMemento<T> {
|
||||
protected getEditorMemento<T>(editorGroupService: IEditorGroupsService, key: string, limit: number = 10): IEditorMemento<T> {
|
||||
return new EditorMemento(this.getId(), key, Object.create(null), limit, editorGroupService); // do not persist in storage as diff editors are never persisted
|
||||
}
|
||||
|
||||
|
|
|
@ -55,9 +55,9 @@ export abstract class BaseTextEditor extends BaseEditor implements ITextEditor {
|
|||
@IEditorGroupsService protected editorGroupService: IEditorGroupsService,
|
||||
@IWindowService private windowService: IWindowService
|
||||
) {
|
||||
super(id, telemetryService, themeService);
|
||||
super(id, telemetryService, themeService, storageService);
|
||||
|
||||
this.editorMemento = this.getEditorMemento<IEditorViewState>(storageService, editorGroupService, TEXT_EDITOR_VIEW_STATE_PREFERENCE_KEY, 100);
|
||||
this.editorMemento = this.getEditorMemento<IEditorViewState>(editorGroupService, TEXT_EDITOR_VIEW_STATE_PREFERENCE_KEY, 100);
|
||||
|
||||
this._register(this.configurationService.onDidChangeConfiguration(e => this.handleConfigurationChangeEvent(this.configurationService.getValue<IEditorConfiguration>(this.getResource()))));
|
||||
}
|
||||
|
@ -233,7 +233,7 @@ export abstract class BaseTextEditor extends BaseEditor implements ITextEditor {
|
|||
return;
|
||||
}
|
||||
|
||||
this.editorMemento.saveState(this.group, resource, editorViewState);
|
||||
this.editorMemento.saveEditorState(this.group, resource, editorViewState);
|
||||
}
|
||||
|
||||
protected retrieveTextEditorViewState(resource: URI): IEditorViewState {
|
||||
|
@ -260,7 +260,7 @@ export abstract class BaseTextEditor extends BaseEditor implements ITextEditor {
|
|||
*/
|
||||
protected clearTextEditorViewState(resources: URI[], group?: IEditorGroup): void {
|
||||
resources.forEach(resource => {
|
||||
this.editorMemento.clearState(resource, group);
|
||||
this.editorMemento.clearEditorState(resource, group);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -268,7 +268,7 @@ export abstract class BaseTextEditor extends BaseEditor implements ITextEditor {
|
|||
* Loads the text editor view state for the given resource and returns it.
|
||||
*/
|
||||
protected loadTextEditorViewState(resource: URI): IEditorViewState {
|
||||
return this.editorMemento.loadState(this.group, resource);
|
||||
return this.editorMemento.loadEditorState(this.group, resource);
|
||||
}
|
||||
|
||||
private updateEditorConfiguration(configuration = this.configurationService.getValue<IEditorConfiguration>(this.getResource())): void {
|
||||
|
|
|
@ -164,15 +164,14 @@ export class AbstractTextResourceEditor extends BaseTextEditor {
|
|||
super.clearInput();
|
||||
}
|
||||
|
||||
shutdown(): void {
|
||||
protected saveState(): void {
|
||||
|
||||
// Save View State (only for untitled)
|
||||
if (this.input instanceof UntitledEditorInput) {
|
||||
this.saveTextResourceEditorViewState(this.input);
|
||||
}
|
||||
|
||||
// Call Super
|
||||
super.shutdown();
|
||||
super.saveState();
|
||||
}
|
||||
|
||||
private saveTextResourceEditorViewState(input: EditorInput): void {
|
||||
|
|
|
@ -44,6 +44,7 @@ import { getIconClass } from 'vs/workbench/browser/parts/quickinput/quickInputUt
|
|||
import { AccessibilitySupport } from 'vs/base/common/platform';
|
||||
import * as browser from 'vs/base/browser/browser';
|
||||
import { IEditorOptions } from 'vs/editor/common/config/editorOptions';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
|
||||
const $ = dom.$;
|
||||
|
||||
|
@ -796,9 +797,10 @@ export class QuickInputService extends Component implements IQuickInputService {
|
|||
@IEditorGroupsService private editorGroupService: IEditorGroupsService,
|
||||
@IKeybindingService private keybindingService: IKeybindingService,
|
||||
@IContextKeyService private contextKeyService: IContextKeyService,
|
||||
@IThemeService themeService: IThemeService
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IStorageService storageService: IStorageService
|
||||
) {
|
||||
super(QuickInputService.ID, themeService);
|
||||
super(QuickInputService.ID, themeService, storageService);
|
||||
this.inQuickOpenContext = new RawContextKey<boolean>('inQuickOpen', false).bindTo(contextKeyService);
|
||||
this._register(this.quickOpenService.onShow(() => this.inQuickOpen('quickOpen', true)));
|
||||
this._register(this.quickOpenService.onHide(() => this.inQuickOpen('quickOpen', false)));
|
||||
|
|
|
@ -50,6 +50,7 @@ import { ILabelService } from 'vs/platform/label/common/label';
|
|||
import { timeout } from 'vs/base/common/async';
|
||||
import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { CancellationTokenSource, CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
|
||||
const HELP_PREFIX = '?';
|
||||
|
||||
|
@ -91,9 +92,10 @@ export class QuickOpenController extends Component implements IQuickOpenService
|
|||
@IInstantiationService private instantiationService: IInstantiationService,
|
||||
@IPartService private partService: IPartService,
|
||||
@IEnvironmentService private environmentService: IEnvironmentService,
|
||||
@IThemeService themeService: IThemeService
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IStorageService storageService: IStorageService
|
||||
) {
|
||||
super(QuickOpenController.ID, themeService);
|
||||
super(QuickOpenController.ID, themeService, storageService);
|
||||
|
||||
this.editorHistoryHandler = this.instantiationService.createInstance(EditorHistoryHandler);
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@ import { isThemeColor } from 'vs/editor/common/editorCommon';
|
|||
import { Color } from 'vs/base/common/color';
|
||||
import { addClass, EventHelper, createStyleSheet, addDisposableListener } from 'vs/base/browser/dom';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
|
||||
export class StatusbarPart extends Part implements IStatusbarService {
|
||||
|
||||
|
@ -44,9 +45,10 @@ export class StatusbarPart extends Part implements IStatusbarService {
|
|||
id: string,
|
||||
@IInstantiationService private instantiationService: IInstantiationService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IWorkspaceContextService private contextService: IWorkspaceContextService
|
||||
@IWorkspaceContextService private contextService: IWorkspaceContextService,
|
||||
@IStorageService storageService: IStorageService
|
||||
) {
|
||||
super(id, { hasTitle: false }, themeService);
|
||||
super(id, { hasTitle: false }, themeService, storageService);
|
||||
|
||||
this.registerListeners();
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
|
|||
import { template, getBaseLabel } from 'vs/base/common/labels';
|
||||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
|
||||
export class TitlebarPart extends Part implements ITitleService {
|
||||
|
||||
|
@ -80,9 +81,10 @@ export class TitlebarPart extends Part implements ITitleService {
|
|||
@IWorkspaceContextService private contextService: IWorkspaceContextService,
|
||||
@IInstantiationService private instantiationService: IInstantiationService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@ILabelService private labelService: ILabelService
|
||||
@ILabelService private labelService: ILabelService,
|
||||
@IStorageService storageService: IStorageService
|
||||
) {
|
||||
super(id, { hasTitle: false }, themeService);
|
||||
super(id, { hasTitle: false }, themeService, storageService);
|
||||
|
||||
this.properties = { isPure: true, isAdmin: false };
|
||||
this.activeEditorListeners = [];
|
||||
|
|
|
@ -28,6 +28,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
|
|||
import { IPartService } from 'vs/workbench/services/part/common/partService';
|
||||
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
|
||||
import { IView } from 'vs/workbench/common/views';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
|
||||
export interface IPanelColors extends IColorMapping {
|
||||
dropBackground?: ColorIdentifier;
|
||||
|
@ -165,7 +166,8 @@ export abstract class ViewletPanel extends Panel implements IView {
|
|||
return 0;
|
||||
}
|
||||
|
||||
shutdown(): void {
|
||||
saveState(): void {
|
||||
// Subclasses to implement for saving state
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -203,9 +205,10 @@ export class PanelViewlet extends Viewlet {
|
|||
@IPartService partService: IPartService,
|
||||
@IContextMenuService protected contextMenuService: IContextMenuService,
|
||||
@ITelemetryService telemetryService: ITelemetryService,
|
||||
@IThemeService themeService: IThemeService
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IStorageService storageService: IStorageService
|
||||
) {
|
||||
super(id, configurationService, partService, telemetryService, themeService);
|
||||
super(id, configurationService, partService, telemetryService, themeService, storageService);
|
||||
}
|
||||
|
||||
create(parent: HTMLElement): Promise<void> {
|
||||
|
|
|
@ -431,9 +431,10 @@ export class PersistentContributableViewsModel extends ContributableViewsModel {
|
|||
|
||||
this._register(this.onDidAdd(viewDescriptorRefs => this.saveVisibilityStates(viewDescriptorRefs.map(r => r.viewDescriptor))));
|
||||
this._register(this.onDidRemove(viewDescriptorRefs => this.saveVisibilityStates(viewDescriptorRefs.map(r => r.viewDescriptor))));
|
||||
this._register(this.storageService.onWillSaveState(() => this.saveViewsStates()));
|
||||
}
|
||||
|
||||
saveViewsStates(): void {
|
||||
private saveViewsStates(): void {
|
||||
const storedViewsStates: { [id: string]: { collapsed: boolean, size: number, order: number } } = {};
|
||||
for (const viewDescriptor of this.viewDescriptors) {
|
||||
const viewState = this.viewStates.get(viewDescriptor.id);
|
||||
|
@ -480,11 +481,6 @@ export class PersistentContributableViewsModel extends ContributableViewsModel {
|
|||
const storedVisibilityStates = <Array<string | { id: string, isHidden: boolean }>>JSON.parse(storageService.get(hiddenViewsStorageId, StorageScope.GLOBAL, '[]'));
|
||||
return <{ id: string, isHidden: boolean }[]>storedVisibilityStates.map(c => typeof c === 'string' /* migration */ ? { id: c, isHidden: true } : c);
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.saveViewsStates();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
const SCM_VIEWLET_ID = 'workbench.view.scm';
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import { Scope } from 'vs/workbench/common/memento';
|
||||
import { dispose, IDisposable, combinedDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IAction } from 'vs/base/common/actions';
|
||||
import { Separator } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
|
@ -104,12 +103,12 @@ export abstract class TreeViewsViewletPanel extends ViewletPanel {
|
|||
}
|
||||
|
||||
export interface IViewletViewOptions extends IViewletPanelOptions {
|
||||
viewletSettings: object;
|
||||
viewletState: object;
|
||||
}
|
||||
|
||||
export abstract class ViewContainerViewlet extends PanelViewlet implements IViewsViewlet {
|
||||
|
||||
private readonly viewletSettings: Object;
|
||||
private readonly viewletState: object;
|
||||
private didLayout = false;
|
||||
private dimension: DOM.Dimension;
|
||||
private areExtensionsReady: boolean = false;
|
||||
|
@ -133,11 +132,11 @@ export abstract class ViewContainerViewlet extends PanelViewlet implements IView
|
|||
@IExtensionService protected extensionService: IExtensionService,
|
||||
@IWorkspaceContextService protected contextService: IWorkspaceContextService
|
||||
) {
|
||||
super(id, { showHeaderInTitleWhenSingleView, dnd: new DefaultPanelDndController() }, configurationService, partService, contextMenuService, telemetryService, themeService);
|
||||
super(id, { showHeaderInTitleWhenSingleView, dnd: new DefaultPanelDndController() }, configurationService, partService, contextMenuService, telemetryService, themeService, storageService);
|
||||
|
||||
const container = Registry.as<IViewContainersRegistry>(ViewContainerExtensions.ViewContainersRegistry).get(id);
|
||||
this.viewsModel = this._register(this.instantiationService.createInstance(PersistentContributableViewsModel, container, viewletStateStorageId));
|
||||
this.viewletSettings = this.getMemento(storageService, Scope.WORKSPACE);
|
||||
this.viewletState = this.getMemento(StorageScope.WORKSPACE);
|
||||
|
||||
this.visibleViewsStorageId = `${id}.numberOfVisibleViews`;
|
||||
this.visibleViewsCountFromCache = this.storageService.getInteger(this.visibleViewsStorageId, this.contextService.getWorkbenchState() === WorkbenchState.EMPTY ? StorageScope.GLOBAL : StorageScope.WORKSPACE, 0);
|
||||
|
@ -241,13 +240,6 @@ export abstract class ViewContainerViewlet extends PanelViewlet implements IView
|
|||
return optimalWidth + additionalMargin;
|
||||
}
|
||||
|
||||
shutdown(): void {
|
||||
this.panels.forEach((view) => view.shutdown());
|
||||
this.storageService.store(this.visibleViewsStorageId, this.length, this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY ? StorageScope.WORKSPACE : StorageScope.GLOBAL);
|
||||
this.viewsModel.saveViewsStates();
|
||||
super.shutdown();
|
||||
}
|
||||
|
||||
protected isSingleView(): boolean {
|
||||
if (!super.isSingleView()) {
|
||||
return false;
|
||||
|
@ -276,7 +268,7 @@ export abstract class ViewContainerViewlet extends PanelViewlet implements IView
|
|||
title: viewDescriptor.name,
|
||||
actionRunner: this.getActionRunner(),
|
||||
expanded: !collapsed,
|
||||
viewletSettings: this.viewletSettings
|
||||
viewletState: this.viewletState
|
||||
});
|
||||
panel.render();
|
||||
panel.setVisible(true);
|
||||
|
@ -384,6 +376,13 @@ export abstract class ViewContainerViewlet extends PanelViewlet implements IView
|
|||
}
|
||||
return sizes;
|
||||
}
|
||||
|
||||
protected saveState(): void {
|
||||
this.panels.forEach((view) => view.saveState());
|
||||
this.storageService.store(this.visibleViewsStorageId, this.length, this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY ? StorageScope.WORKSPACE : StorageScope.GLOBAL);
|
||||
|
||||
super.saveState();
|
||||
}
|
||||
}
|
||||
|
||||
export class FileIconThemableWorkbenchTree extends WorkbenchTree {
|
||||
|
|
|
@ -20,6 +20,7 @@ import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorG
|
|||
import { URI } from 'vs/base/common/uri';
|
||||
import { ToggleSidebarPositionAction } from 'vs/workbench/browser/actions/toggleSidebarPosition';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
|
||||
export abstract class Viewlet extends Composite implements IViewlet {
|
||||
|
||||
|
@ -27,9 +28,10 @@ export abstract class Viewlet extends Composite implements IViewlet {
|
|||
protected configurationService: IConfigurationService,
|
||||
private partService: IPartService,
|
||||
telemetryService: ITelemetryService,
|
||||
themeService: IThemeService
|
||||
themeService: IThemeService,
|
||||
storageService: IStorageService
|
||||
) {
|
||||
super(id, telemetryService, themeService);
|
||||
super(id, telemetryService, themeService, storageService);
|
||||
}
|
||||
|
||||
getOptimalWidth(): number {
|
||||
|
|
|
@ -3,78 +3,44 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { Scope, Memento } from 'vs/workbench/common/memento';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { Memento } from 'vs/workbench/common/memento';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { Themable } from 'vs/workbench/common/theme';
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
|
||||
/**
|
||||
* Base class of any core/ui component in the workbench. Examples include services, extensions, parts, viewlets and quick open.
|
||||
* Provides some convinience methods to participate in the workbench lifecycle (dispose, shutdown) and
|
||||
* loading and saving settings through memento.
|
||||
*/
|
||||
export interface IWorkbenchComponent extends IDisposable {
|
||||
|
||||
/**
|
||||
* The unique identifier of this component.
|
||||
*/
|
||||
getId(): string;
|
||||
|
||||
/**
|
||||
* Called when the browser containing the container is closed.
|
||||
*
|
||||
* Use this function to store settings that you want to restore next time. Should not be used to free resources
|
||||
* because dispose() is being called for this purpose and shutdown() has a chance to be vetoed by the user.
|
||||
*/
|
||||
shutdown(): void;
|
||||
}
|
||||
|
||||
export class Component extends Themable implements IWorkbenchComponent {
|
||||
export class Component extends Themable {
|
||||
private id: string;
|
||||
private componentMemento: Memento;
|
||||
private memento: Memento;
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
themeService: IThemeService
|
||||
themeService: IThemeService,
|
||||
storageService: IStorageService
|
||||
) {
|
||||
super(themeService);
|
||||
|
||||
this.id = id;
|
||||
this.componentMemento = new Memento(this.id);
|
||||
this.memento = new Memento(this.id, storageService);
|
||||
|
||||
this._register(storageService.onWillSaveState(() => {
|
||||
|
||||
// Ask the component to persist state into the memento
|
||||
this.saveState();
|
||||
|
||||
// Then save the memento into storage
|
||||
this.memento.saveMemento();
|
||||
}));
|
||||
}
|
||||
|
||||
getId(): string {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a JSON Object that represents the data of this memento. The optional
|
||||
* parameter scope allows to specify the scope of the memento to load. If not
|
||||
* provided, the scope will be global, Scope.WORKSPACE can be used to
|
||||
* scope the memento to the workspace.
|
||||
*
|
||||
* Mementos are shared across components with the same id. This means that multiple components
|
||||
* with the same id will store data into the same data structure.
|
||||
*/
|
||||
protected getMemento(storageService: IStorageService, scope: Scope = Scope.GLOBAL): object {
|
||||
return this.componentMemento.getMemento(storageService, scope);
|
||||
protected getMemento(scope: StorageScope): object {
|
||||
return this.memento.getMemento(scope);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves all data of the mementos that have been loaded to the local storage. This includes
|
||||
* global and workspace scope.
|
||||
*
|
||||
* Mementos are shared across components with the same id. This means that multiple components
|
||||
* with the same id will store data into the same data structure.
|
||||
*/
|
||||
protected saveMemento(): void {
|
||||
this.componentMemento.saveMemento();
|
||||
}
|
||||
|
||||
shutdown(): void {
|
||||
|
||||
// Save Memento
|
||||
this.saveMemento();
|
||||
protected saveState(): void {
|
||||
// Subclasses to implement for storing state
|
||||
}
|
||||
}
|
|
@ -1005,14 +1005,14 @@ export const enum CloseDirection {
|
|||
|
||||
export interface IEditorMemento<T> {
|
||||
|
||||
saveState(group: IEditorGroup, resource: URI, state: T): void;
|
||||
saveState(group: IEditorGroup, editor: EditorInput, state: T): void;
|
||||
saveEditorState(group: IEditorGroup, resource: URI, state: T): void;
|
||||
saveEditorState(group: IEditorGroup, editor: EditorInput, state: T): void;
|
||||
|
||||
loadState(group: IEditorGroup, resource: URI): T;
|
||||
loadState(group: IEditorGroup, editor: EditorInput): T;
|
||||
loadEditorState(group: IEditorGroup, resource: URI): T;
|
||||
loadEditorState(group: IEditorGroup, editor: EditorInput): T;
|
||||
|
||||
clearState(resource: URI, group?: IEditorGroup): void;
|
||||
clearState(editor: EditorInput, group?: IEditorGroup): void;
|
||||
clearEditorState(resource: URI, group?: IEditorGroup): void;
|
||||
clearEditorState(editor: EditorInput, group?: IEditorGroup): void;
|
||||
}
|
||||
|
||||
class EditorInputFactoryRegistry implements IEditorInputFactoryRegistry {
|
||||
|
|
|
@ -3,109 +3,74 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as types from 'vs/base/common/types';
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import { isEmptyObject } from 'vs/base/common/types';
|
||||
|
||||
/**
|
||||
* Supported memento scopes.
|
||||
*/
|
||||
export const enum Scope {
|
||||
|
||||
/**
|
||||
* The memento will be scoped to all workspaces of this domain.
|
||||
*/
|
||||
GLOBAL,
|
||||
|
||||
/**
|
||||
* The memento will be scoped to the current workspace.
|
||||
*/
|
||||
WORKSPACE
|
||||
}
|
||||
|
||||
/**
|
||||
* A memento provides access to a datastructure that is persisted and restored as part of the workbench lifecycle.
|
||||
*/
|
||||
export class Memento {
|
||||
|
||||
// Mementos are static to ensure that for a given component with an id only ever one memento gets loaded
|
||||
private static globalMementos: { [id: string]: ScopedMemento } = {};
|
||||
private static workspaceMementos: { [id: string]: ScopedMemento } = {};
|
||||
private static globalMementos: { [id: string]: ScopedMemento } = Object.create(null);
|
||||
private static workspaceMementos: { [id: string]: ScopedMemento } = Object.create(null);
|
||||
|
||||
private static readonly COMMON_PREFIX = 'memento/';
|
||||
|
||||
private id: string;
|
||||
|
||||
constructor(id: string) {
|
||||
constructor(id: string, private storageService: IStorageService) {
|
||||
this.id = Memento.COMMON_PREFIX + id.toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a JSON Object that represents the data of this memento. The optional
|
||||
* parameter scope allows to specify the scope of the memento to load. If not
|
||||
* provided, the scope will be global, Memento.Scope.WORKSPACE can be used to
|
||||
* scope the memento to the workspace.
|
||||
*/
|
||||
getMemento(storageService: IStorageService, scope: Scope = Scope.GLOBAL): object {
|
||||
getMemento(scope: StorageScope): object {
|
||||
|
||||
// Scope by Workspace
|
||||
if (scope === Scope.WORKSPACE) {
|
||||
if (scope === StorageScope.WORKSPACE) {
|
||||
let workspaceMemento = Memento.workspaceMementos[this.id];
|
||||
if (!workspaceMemento) {
|
||||
workspaceMemento = new ScopedMemento(this.id, scope, storageService);
|
||||
workspaceMemento = new ScopedMemento(this.id, scope, this.storageService);
|
||||
Memento.workspaceMementos[this.id] = workspaceMemento;
|
||||
}
|
||||
|
||||
return workspaceMemento.getMemento();
|
||||
}
|
||||
|
||||
// Use global scope
|
||||
// Scope Global
|
||||
let globalMemento = Memento.globalMementos[this.id];
|
||||
if (!globalMemento) {
|
||||
globalMemento = new ScopedMemento(this.id, scope, storageService);
|
||||
globalMemento = new ScopedMemento(this.id, scope, this.storageService);
|
||||
Memento.globalMementos[this.id] = globalMemento;
|
||||
}
|
||||
|
||||
return globalMemento.getMemento();
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves all data of the mementos that have been loaded to the local storage. This includes
|
||||
* global and workspace scope.
|
||||
*/
|
||||
saveMemento(): void {
|
||||
|
||||
// Global
|
||||
const globalMemento = Memento.globalMementos[this.id];
|
||||
if (globalMemento) {
|
||||
globalMemento.save();
|
||||
}
|
||||
|
||||
// Workspace
|
||||
const workspaceMemento = Memento.workspaceMementos[this.id];
|
||||
if (workspaceMemento) {
|
||||
workspaceMemento.save();
|
||||
}
|
||||
|
||||
// Global
|
||||
const globalMemento = Memento.globalMementos[this.id];
|
||||
if (globalMemento) {
|
||||
globalMemento.save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ScopedMemento {
|
||||
private id: string;
|
||||
private mementoObj: object;
|
||||
private scope: Scope;
|
||||
|
||||
constructor(id: string, scope: Scope, private storageService: IStorageService) {
|
||||
this.id = id;
|
||||
this.scope = scope;
|
||||
this.mementoObj = this.loadMemento();
|
||||
constructor(private id: string, private scope: StorageScope, private storageService: IStorageService) {
|
||||
this.mementoObj = this.load();
|
||||
}
|
||||
|
||||
getMemento(): object {
|
||||
return this.mementoObj;
|
||||
}
|
||||
|
||||
private loadMemento(): object {
|
||||
let storageScope = this.scope === Scope.GLOBAL ? StorageScope.GLOBAL : StorageScope.WORKSPACE;
|
||||
let memento = this.storageService.get(this.id, storageScope);
|
||||
private load(): object {
|
||||
const memento = this.storageService.get(this.id, this.scope);
|
||||
if (memento) {
|
||||
return JSON.parse(memento);
|
||||
}
|
||||
|
@ -114,12 +79,10 @@ class ScopedMemento {
|
|||
}
|
||||
|
||||
save(): void {
|
||||
let storageScope = this.scope === Scope.GLOBAL ? StorageScope.GLOBAL : StorageScope.WORKSPACE;
|
||||
|
||||
if (!types.isEmptyObject(this.mementoObj)) {
|
||||
this.storageService.store(this.id, JSON.stringify(this.mementoObj), storageScope);
|
||||
if (!isEmptyObject(this.mementoObj)) {
|
||||
this.storageService.store(this.id, JSON.stringify(this.mementoObj), this.scope);
|
||||
} else {
|
||||
this.storageService.remove(this.id, storageScope);
|
||||
this.storageService.remove(this.id, this.scope);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,7 +5,6 @@
|
|||
|
||||
import * as nls from 'vs/nls';
|
||||
import * as perf from 'vs/base/common/performance';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { WorkbenchShell } from 'vs/workbench/electron-browser/shell';
|
||||
import * as browser from 'vs/base/browser/browser';
|
||||
import { domContentLoaded } from 'vs/base/browser/dom';
|
||||
|
@ -23,9 +22,8 @@ import * as gracefulFs from 'graceful-fs';
|
|||
import { KeyboardMapperFactory } from 'vs/workbench/services/keybinding/electron-browser/keybindingService';
|
||||
import { IWindowConfiguration, IWindowsService } from 'vs/platform/windows/common/windows';
|
||||
import { WindowsChannelClient } from 'vs/platform/windows/node/windowsIpc';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { IStorageLegacyService, StorageLegacyService, inMemoryLocalStorageInstance, IStorageLegacy } from 'vs/platform/storage/common/storageLegacyService';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { StorageService, inMemoryLocalStorageInstance, IStorage } from 'vs/platform/storage/common/storageService';
|
||||
import { Client as ElectronIPCClient } from 'vs/base/parts/ipc/electron-browser/ipc.electron-browser';
|
||||
import { webFrame } from 'electron';
|
||||
import { UpdateChannelClient } from 'vs/platform/update/node/updateIpc';
|
||||
|
@ -37,6 +35,7 @@ import { IWorkspacesService, ISingleFolderWorkspaceIdentifier } from 'vs/platfor
|
|||
import { createSpdLogService } from 'vs/platform/log/node/spdlogService';
|
||||
import * as fs from 'fs';
|
||||
import { ConsoleLogService, MultiplexLogService, ILogService } from 'vs/platform/log/common/log';
|
||||
import { StorageService, DelegatingStorageService } from 'vs/platform/storage/electron-browser/storageService';
|
||||
import { IssueChannelClient } from 'vs/platform/issue/node/issueIpc';
|
||||
import { IIssueService } from 'vs/platform/issue/common/issue';
|
||||
import { LogLevelSetterChannelClient, FollowerLogService } from 'vs/platform/log/node/logIpc';
|
||||
|
@ -48,26 +47,24 @@ import { sanitizeFilePath } from 'vs/base/node/extfs';
|
|||
|
||||
gracefulFs.gracefulify(fs); // enable gracefulFs
|
||||
|
||||
export function startup(configuration: IWindowConfiguration): TPromise<void> {
|
||||
export function startup(configuration: IWindowConfiguration): Promise<void> {
|
||||
|
||||
// Massage configuration file URIs
|
||||
revive(configuration);
|
||||
|
||||
// Setup perf
|
||||
perf.importEntries(configuration.perfEntries);
|
||||
|
||||
// Ensure others can listen to zoom level changes
|
||||
browser.setZoomFactor(webFrame.getZoomFactor());
|
||||
|
||||
// See https://github.com/Microsoft/vscode/issues/26151
|
||||
// Can be trusted because we are not setting it ourselves.
|
||||
browser.setZoomLevel(webFrame.getZoomLevel(), true /* isTrusted */);
|
||||
|
||||
// Browser config
|
||||
browser.setZoomFactor(webFrame.getZoomFactor()); // Ensure others can listen to zoom level changes
|
||||
browser.setZoomLevel(webFrame.getZoomLevel(), true /* isTrusted */); // Can be trusted because we are not setting it ourselves (https://github.com/Microsoft/vscode/issues/26151)
|
||||
browser.setFullscreen(!!configuration.fullscreen);
|
||||
|
||||
KeyboardMapperFactory.INSTANCE._onKeyboardLayoutChanged();
|
||||
|
||||
browser.setAccessibilitySupport(configuration.accessibilitySupport ? platform.AccessibilitySupport.Enabled : platform.AccessibilitySupport.Disabled);
|
||||
|
||||
// Setup Intl
|
||||
// Keyboard support
|
||||
KeyboardMapperFactory.INSTANCE._onKeyboardLayoutChanged();
|
||||
|
||||
// Setup Intl for comparers
|
||||
comparer.setFileNameComparer(new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' }));
|
||||
|
||||
// Open workbench
|
||||
|
@ -78,6 +75,7 @@ function revive(workbench: IWindowConfiguration) {
|
|||
if (workbench.folderUri) {
|
||||
workbench.folderUri = uri.revive(workbench.folderUri);
|
||||
}
|
||||
|
||||
const filesToWaitPaths = workbench.filesToWait && workbench.filesToWait.paths;
|
||||
[filesToWaitPaths, workbench.filesToOpen, workbench.filesToCreate, workbench.filesToDiff].forEach(paths => {
|
||||
if (Array.isArray(paths)) {
|
||||
|
@ -90,30 +88,42 @@ function revive(workbench: IWindowConfiguration) {
|
|||
});
|
||||
}
|
||||
|
||||
function openWorkbench(configuration: IWindowConfiguration): TPromise<void> {
|
||||
function openWorkbench(configuration: IWindowConfiguration): Promise<void> {
|
||||
const mainProcessClient = new ElectronIPCClient(`window:${configuration.windowId}`);
|
||||
const mainServices = createMainProcessServices(mainProcessClient, configuration);
|
||||
|
||||
const environmentService = new EnvironmentService(configuration, configuration.execPath);
|
||||
|
||||
const logService = createLogService(mainProcessClient, configuration, environmentService);
|
||||
logService.trace('openWorkbench configuration', JSON.stringify(configuration));
|
||||
|
||||
// Since the configuration service is one of the core services that is used in so many places, we initialize it
|
||||
// right before startup of the workbench shell to have its data ready for consumers
|
||||
return createAndInitializeWorkspaceService(configuration, environmentService).then(workspaceService => {
|
||||
const storageService = createStorageService(workspaceService, environmentService);
|
||||
return Promise.all([
|
||||
createAndInitializeWorkspaceService(configuration, environmentService),
|
||||
createStorageService(environmentService, logService)
|
||||
]).then(services => {
|
||||
const workspaceService = services[0];
|
||||
const storageLegacyService = createStorageLegacyService(workspaceService, environmentService);
|
||||
const storageService = new DelegatingStorageService(services[1], storageLegacyService, logService, environmentService);
|
||||
|
||||
return domContentLoaded().then(() => {
|
||||
|
||||
// Open Shell
|
||||
perf.mark('willStartWorkbench');
|
||||
|
||||
// Create Shell
|
||||
const shell = new WorkbenchShell(document.body, {
|
||||
contextService: workspaceService,
|
||||
configurationService: workspaceService,
|
||||
environmentService,
|
||||
logService,
|
||||
storageLegacyService,
|
||||
storageService
|
||||
}, mainServices, mainProcessClient, configuration);
|
||||
|
||||
// Gracefully Shutdown Storage
|
||||
shell.onShutdown(event => {
|
||||
event.join(storageService.close(event.reason));
|
||||
});
|
||||
|
||||
// Open Shell
|
||||
shell.open();
|
||||
|
||||
// Inform user about loading issues from the loader
|
||||
|
@ -128,20 +138,19 @@ function openWorkbench(configuration: IWindowConfiguration): TPromise<void> {
|
|||
});
|
||||
}
|
||||
|
||||
function createAndInitializeWorkspaceService(configuration: IWindowConfiguration, environmentService: EnvironmentService): TPromise<WorkspaceService> {
|
||||
function createAndInitializeWorkspaceService(configuration: IWindowConfiguration, environmentService: EnvironmentService): Promise<WorkspaceService> {
|
||||
return validateFolderUri(configuration.folderUri, configuration.verbose).then(validatedFolderUri => {
|
||||
|
||||
const workspaceService = new WorkspaceService(environmentService);
|
||||
|
||||
return workspaceService.initialize(configuration.workspace || validatedFolderUri || configuration).then(() => workspaceService, error => workspaceService);
|
||||
});
|
||||
}
|
||||
|
||||
function validateFolderUri(folderUri: ISingleFolderWorkspaceIdentifier, verbose: boolean): TPromise<uri> {
|
||||
function validateFolderUri(folderUri: ISingleFolderWorkspaceIdentifier, verbose: boolean): Promise<uri> {
|
||||
|
||||
// Return early if we do not have a single folder uri or if it is a non file uri
|
||||
if (!folderUri || folderUri.scheme !== Schemas.file) {
|
||||
return TPromise.as(folderUri);
|
||||
return Promise.resolve(folderUri);
|
||||
}
|
||||
|
||||
// Ensure absolute existing folder path
|
||||
|
@ -156,7 +165,19 @@ function validateFolderUri(folderUri: ISingleFolderWorkspaceIdentifier, verbose:
|
|||
});
|
||||
}
|
||||
|
||||
function createStorageService(workspaceService: IWorkspaceContextService, environmentService: IEnvironmentService): IStorageService {
|
||||
function createStorageService(environmentService: IEnvironmentService, logService: ILogService): Promise<StorageService> {
|
||||
perf.mark('willCreateStorageService');
|
||||
|
||||
const storageService = new StorageService(':memory:', logService, environmentService);
|
||||
|
||||
return storageService.init().then(() => {
|
||||
perf.mark('didCreateStorageService');
|
||||
|
||||
return storageService;
|
||||
});
|
||||
}
|
||||
|
||||
function createStorageLegacyService(workspaceService: IWorkspaceContextService, environmentService: IEnvironmentService): IStorageLegacyService {
|
||||
let workspaceId: string;
|
||||
let secondaryWorkspaceId: number;
|
||||
|
||||
|
@ -188,14 +209,14 @@ function createStorageService(workspaceService: IWorkspaceContextService, enviro
|
|||
|
||||
const disableStorage = !!environmentService.extensionTestsPath; // never keep any state when running extension tests!
|
||||
|
||||
let storage: IStorage;
|
||||
let storage: IStorageLegacy;
|
||||
if (disableStorage) {
|
||||
storage = inMemoryLocalStorageInstance;
|
||||
} else {
|
||||
storage = window.localStorage;
|
||||
}
|
||||
|
||||
return new StorageService(storage, storage, workspaceId, secondaryWorkspaceId);
|
||||
return new StorageLegacyService(storage, storage, workspaceId, secondaryWorkspaceId);
|
||||
}
|
||||
|
||||
function createLogService(mainProcessClient: ElectronIPCClient, configuration: IWindowConfiguration, environmentService: IEnvironmentService): ILogService {
|
||||
|
@ -203,6 +224,7 @@ function createLogService(mainProcessClient: ElectronIPCClient, configuration: I
|
|||
const consoleLogService = new ConsoleLogService(configuration.logLevel);
|
||||
const logService = new MultiplexLogService([consoleLogService, spdlogService]);
|
||||
const logLevelClient = new LogLevelSetterChannelClient(mainProcessClient.getChannel('loglevel'));
|
||||
|
||||
return new FollowerLogService(logLevelClient, logService);
|
||||
}
|
||||
|
||||
|
|
|
@ -39,12 +39,11 @@ import { IIntegrityService } from 'vs/platform/integrity/common/integrity';
|
|||
import { EditorWorkerServiceImpl } from 'vs/editor/common/services/editorWorkerServiceImpl';
|
||||
import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService';
|
||||
import { ExtensionService } from 'vs/workbench/services/extensions/electron-browser/extensionService';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { IStorageLegacyService } from 'vs/platform/storage/common/storageLegacyService';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
|
||||
import { InstantiationService } from 'vs/platform/instantiation/node/instantiationService';
|
||||
// import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService';
|
||||
import { ILifecycleService, LifecyclePhase, ShutdownReason, StartupKind } from 'vs/platform/lifecycle/common/lifecycle';
|
||||
import { ILifecycleService, LifecyclePhase, ShutdownReason, ShutdownEvent } from 'vs/platform/lifecycle/common/lifecycle';
|
||||
import { IMarkerService } from 'vs/platform/markers/common/markers';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { ISearchService, ISearchHistoryService } from 'vs/platform/search/common/search';
|
||||
|
@ -76,9 +75,9 @@ import { IBroadcastService, BroadcastService } from 'vs/platform/broadcast/elect
|
|||
import { HashService } from 'vs/workbench/services/hash/node/hashService';
|
||||
import { IHashService } from 'vs/workbench/services/hash/common/hashService';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { WORKBENCH_BACKGROUND } from 'vs/workbench/common/theme';
|
||||
import { stat } from 'fs';
|
||||
import { join } from 'path';
|
||||
import { ILocalizationsChannel, LocalizationsChannelClient } from 'vs/platform/localizations/node/localizationsIpc';
|
||||
import { ILocalizationsService } from 'vs/platform/localizations/common/localizations';
|
||||
import { IWorkbenchIssueService } from 'vs/workbench/services/issue/common/issue';
|
||||
|
@ -108,6 +107,7 @@ export interface ICoreServices {
|
|||
configurationService: IConfigurationService;
|
||||
environmentService: IEnvironmentService;
|
||||
logService: ILogService;
|
||||
storageLegacyService: IStorageLegacyService;
|
||||
storageService: IStorageService;
|
||||
}
|
||||
|
||||
|
@ -116,6 +116,11 @@ export interface ICoreServices {
|
|||
* With the Shell being the top level element in the page, it is also responsible for driving the layouting.
|
||||
*/
|
||||
export class WorkbenchShell extends Disposable {
|
||||
|
||||
private readonly _onShutdown = this._register(new Emitter<ShutdownEvent>());
|
||||
get onShutdown(): Event<ShutdownEvent> { return this._onShutdown.event; }
|
||||
|
||||
private storageLegacyService: IStorageLegacyService;
|
||||
private storageService: IStorageService;
|
||||
private environmentService: IEnvironmentService;
|
||||
private logService: ILogService;
|
||||
|
@ -146,6 +151,7 @@ export class WorkbenchShell extends Disposable {
|
|||
this.configurationService = coreServices.configurationService;
|
||||
this.environmentService = coreServices.environmentService;
|
||||
this.logService = coreServices.logService;
|
||||
this.storageLegacyService = coreServices.storageLegacyService;
|
||||
this.storageService = coreServices.storageService;
|
||||
|
||||
this.mainProcessServices = mainProcessServices;
|
||||
|
@ -164,6 +170,9 @@ export class WorkbenchShell extends Disposable {
|
|||
// Warm up font cache information before building up too many dom elements
|
||||
restoreFontInfo(this.storageService);
|
||||
readFontInfo(BareFontInfo.createFromRawSettings(this.configurationService.getValue('editor'), browser.getZoomLevel()));
|
||||
this._register(this.storageService.onWillSaveState(() => {
|
||||
saveFontInfo(this.storageService); // Keep font info for next startup around
|
||||
}));
|
||||
|
||||
// Workbench
|
||||
this.workbench = this.createWorkbench(instantiationService, serviceCollection, this.container);
|
||||
|
@ -214,11 +223,6 @@ export class WorkbenchShell extends Disposable {
|
|||
}, 5000);
|
||||
|
||||
this._register(eventuallPhaseTimeoutHandle);
|
||||
|
||||
// localStorage metrics (TODO@Ben remove me later)
|
||||
if (!this.environmentService.extensionTestsPath && this.contextService.getWorkbenchState() === WorkbenchState.FOLDER) {
|
||||
this.logLocalStorageMetrics();
|
||||
}
|
||||
}, error => handleStartupError(this.logService, error));
|
||||
|
||||
return workbench;
|
||||
|
@ -272,53 +276,6 @@ export class WorkbenchShell extends Disposable {
|
|||
perf.mark('didStartWorkbench');
|
||||
}
|
||||
|
||||
private logLocalStorageMetrics(): void {
|
||||
if (this.lifecycleService.startupKind === StartupKind.ReloadedWindow || this.lifecycleService.startupKind === StartupKind.ReopenedWindow) {
|
||||
return; // avoid logging localStorage metrics for reload/reopen, we prefer cold startup numbers
|
||||
}
|
||||
|
||||
perf.mark('willReadLocalStorage');
|
||||
const readyToSend = this.storageService.getBoolean('localStorageMetricsReadyToSend2');
|
||||
perf.mark('didReadLocalStorage');
|
||||
|
||||
if (!readyToSend) {
|
||||
this.storageService.store('localStorageMetricsReadyToSend2', true);
|
||||
return; // avoid logging localStorage metrics directly after the update, we prefer cold startup numbers
|
||||
}
|
||||
|
||||
if (!this.storageService.getBoolean('localStorageMetricsSent2')) {
|
||||
perf.mark('willWriteLocalStorage');
|
||||
this.storageService.store('localStorageMetricsSent2', true);
|
||||
perf.mark('didWriteLocalStorage');
|
||||
|
||||
perf.mark('willStatLocalStorage');
|
||||
stat(join(this.environmentService.userDataPath, 'Local Storage', 'file__0.localstorage'), (error, stat) => {
|
||||
perf.mark('didStatLocalStorage');
|
||||
|
||||
/* __GDPR__
|
||||
"localStorageTimers<NUMBER>" : {
|
||||
"statTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"accessTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"firstReadTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"subsequentReadTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"writeTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"keys" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"size": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }
|
||||
}
|
||||
*/
|
||||
this.telemetryService.publicLog('localStorageTimers2', {
|
||||
'statTime': perf.getDuration('willStatLocalStorage', 'didStatLocalStorage'),
|
||||
'accessTime': perf.getDuration('willAccessLocalStorage', 'didAccessLocalStorage'),
|
||||
'firstReadTime': perf.getDuration('willReadWorkspaceIdentifier', 'didReadWorkspaceIdentifier'),
|
||||
'subsequentReadTime': perf.getDuration('willReadLocalStorage', 'didReadLocalStorage'),
|
||||
'writeTime': perf.getDuration('willWriteLocalStorage', 'didWriteLocalStorage'),
|
||||
'keys': window.localStorage.length,
|
||||
'size': stat ? stat.size : -1
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private initServiceCollection(container: HTMLElement): [IInstantiationService, ServiceCollection] {
|
||||
const serviceCollection = new ServiceCollection();
|
||||
serviceCollection.set(IWorkspaceContextService, this.contextService);
|
||||
|
@ -326,8 +283,9 @@ export class WorkbenchShell extends Disposable {
|
|||
serviceCollection.set(IEnvironmentService, this.environmentService);
|
||||
serviceCollection.set(ILabelService, new SyncDescriptor(LabelService));
|
||||
serviceCollection.set(ILogService, this._register(this.logService));
|
||||
|
||||
serviceCollection.set(IStorageLegacyService, this.storageLegacyService);
|
||||
serviceCollection.set(IStorageService, this.storageService);
|
||||
|
||||
this.mainProcessServices.forEach((serviceIdentifier, serviceInstance) => {
|
||||
serviceCollection.set(serviceIdentifier, serviceInstance);
|
||||
});
|
||||
|
@ -353,7 +311,6 @@ export class WorkbenchShell extends Disposable {
|
|||
serviceCollection.set(IHashService, new SyncDescriptor(HashService));
|
||||
|
||||
// Telemetry
|
||||
|
||||
if (!this.environmentService.isExtensionDevelopment && !this.environmentService.args['disable-telemetry'] && !!product.enableTelemetry) {
|
||||
const channel = getDelayedChannel<ITelemetryAppenderChannel>(sharedProcess.then(c => c.getChannel('telemetryAppender')));
|
||||
const config: ITelemetryServiceConfig = {
|
||||
|
@ -380,7 +337,11 @@ export class WorkbenchShell extends Disposable {
|
|||
serviceCollection.set(IDialogService, instantiationService.createInstance(DialogService));
|
||||
|
||||
const lifecycleService = instantiationService.createInstance(LifecycleService);
|
||||
this._register(lifecycleService.onShutdown(reason => this.dispose(reason)));
|
||||
this._register(lifecycleService.onShutdown(event => {
|
||||
this._onShutdown.fire(event);
|
||||
|
||||
this.dispose(event.reason);
|
||||
}));
|
||||
serviceCollection.set(ILifecycleService, lifecycleService);
|
||||
this.lifecycleService = lifecycleService;
|
||||
|
||||
|
@ -505,9 +466,6 @@ export class WorkbenchShell extends Disposable {
|
|||
dispose(reason = ShutdownReason.QUIT): void {
|
||||
super.dispose();
|
||||
|
||||
// Keep font info for next startup around
|
||||
saveFontInfo(this.storageService);
|
||||
|
||||
// Dispose Workbench
|
||||
if (this.workbench) {
|
||||
this.workbench.dispose(reason);
|
||||
|
@ -517,7 +475,6 @@ export class WorkbenchShell extends Disposable {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
|
||||
|
||||
// Foreground
|
||||
|
|
|
@ -7,7 +7,7 @@ import 'vs/css!./media/workbench';
|
|||
|
||||
import { localize } from 'vs/nls';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { IDisposable, dispose, toDisposable, Disposable } from 'vs/base/common/lifecycle';
|
||||
import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle';
|
||||
import { Event, Emitter, once } from 'vs/base/common/event';
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import { RunOnceScheduler } from 'vs/base/common/async';
|
||||
|
@ -335,7 +335,6 @@ export class Workbench extends Disposable implements IPartService {
|
|||
|
||||
// Status bar
|
||||
this.statusbarPart = this.instantiationService.createInstance(StatusbarPart, Identifiers.STATUSBAR_PART);
|
||||
this._register(toDisposable(() => this.statusbarPart.shutdown()));
|
||||
serviceCollection.set(IStatusbarService, this.statusbarPart);
|
||||
|
||||
// Progress 2
|
||||
|
@ -367,7 +366,6 @@ export class Workbench extends Disposable implements IPartService {
|
|||
|
||||
// Sidebar part
|
||||
this.sidebarPart = this.instantiationService.createInstance(SidebarPart, Identifiers.SIDEBAR_PART);
|
||||
this._register(toDisposable(() => this.sidebarPart.shutdown()));
|
||||
|
||||
// Viewlet service
|
||||
this.viewletService = this.instantiationService.createInstance(ViewletService, this.sidebarPart);
|
||||
|
@ -375,7 +373,6 @@ export class Workbench extends Disposable implements IPartService {
|
|||
|
||||
// Panel service (panel part)
|
||||
this.panelPart = this.instantiationService.createInstance(PanelPart, Identifiers.PANEL_PART);
|
||||
this._register(toDisposable(() => this.panelPart.shutdown()));
|
||||
serviceCollection.set(IPanelService, this.panelPart);
|
||||
|
||||
// views service
|
||||
|
@ -384,7 +381,6 @@ export class Workbench extends Disposable implements IPartService {
|
|||
|
||||
// Activity service (activitybar part)
|
||||
this.activitybarPart = this.instantiationService.createInstance(ActivitybarPart, Identifiers.ACTIVITYBAR_PART);
|
||||
this._register(toDisposable(() => this.activitybarPart.shutdown()));
|
||||
const activityService = this.instantiationService.createInstance(ActivityService, this.activitybarPart, this.panelPart);
|
||||
serviceCollection.set(IActivityService, activityService);
|
||||
|
||||
|
@ -397,7 +393,6 @@ export class Workbench extends Disposable implements IPartService {
|
|||
// Editor and Group services
|
||||
const restorePreviousEditorState = !this.hasInitialFilesToOpen;
|
||||
this.editorPart = this.instantiationService.createInstance(EditorPart, Identifiers.EDITOR_PART, restorePreviousEditorState);
|
||||
this._register(toDisposable(() => this.editorPart.shutdown()));
|
||||
this.editorGroupService = this.editorPart;
|
||||
serviceCollection.set(IEditorGroupsService, this.editorPart);
|
||||
this.editorService = this.instantiationService.createInstance(EditorService);
|
||||
|
@ -405,7 +400,6 @@ export class Workbench extends Disposable implements IPartService {
|
|||
|
||||
// Title bar
|
||||
this.titlebarPart = this.instantiationService.createInstance(TitlebarPart, Identifiers.TITLEBAR_PART);
|
||||
this._register(toDisposable(() => this.titlebarPart.shutdown()));
|
||||
serviceCollection.set(ITitleService, this.titlebarPart);
|
||||
|
||||
// History
|
||||
|
@ -452,12 +446,10 @@ export class Workbench extends Disposable implements IPartService {
|
|||
|
||||
// Quick open service (quick open controller)
|
||||
this.quickOpen = this.instantiationService.createInstance(QuickOpenController);
|
||||
this._register(toDisposable(() => this.quickOpen.shutdown()));
|
||||
serviceCollection.set(IQuickOpenService, this.quickOpen);
|
||||
|
||||
// Quick input service
|
||||
this.quickInput = this.instantiationService.createInstance(QuickInputService);
|
||||
this._register(toDisposable(() => this.quickInput.shutdown()));
|
||||
serviceCollection.set(IQuickInputService, this.quickInput);
|
||||
|
||||
// PreferencesService
|
||||
|
@ -483,6 +475,9 @@ export class Workbench extends Disposable implements IPartService {
|
|||
|
||||
private registerListeners(): void {
|
||||
|
||||
// Storage lifecycle
|
||||
this._register(this.storageService.onWillSaveState(reason => this.saveState(reason)));
|
||||
|
||||
// Listen to visible editor changes
|
||||
this._register(this.editorService.onDidVisibleEditorsChange(() => this.onDidVisibleEditorsChange()));
|
||||
|
||||
|
@ -934,6 +929,7 @@ export class Workbench extends Disposable implements IPartService {
|
|||
private setPanelPositionFromStorageOrConfig() {
|
||||
const defaultPanelPosition = this.configurationService.getValue<string>(Workbench.defaultPanelPositionStorageKey);
|
||||
const panelPosition = this.storageService.get(Workbench.panelPositionStorageKey, StorageScope.WORKSPACE, defaultPanelPosition);
|
||||
|
||||
this.panelPosition = (panelPosition === 'right') ? Position.RIGHT : Position.BOTTOM;
|
||||
}
|
||||
|
||||
|
@ -1132,12 +1128,11 @@ export class Workbench extends Disposable implements IPartService {
|
|||
return this.instantiationService;
|
||||
}
|
||||
|
||||
dispose(reason = ShutdownReason.QUIT): void {
|
||||
super.dispose();
|
||||
private saveState(reason: ShutdownReason): void {
|
||||
|
||||
// Restore sidebar if we are being shutdown as a matter of a reload
|
||||
if (reason === ShutdownReason.RELOAD) {
|
||||
this.storageService.store(Workbench.sidebarRestoreStorageKey, 'true', StorageScope.WORKSPACE);
|
||||
this.storageService.store(Workbench.sidebarRestoreStorageKey, true, StorageScope.WORKSPACE);
|
||||
}
|
||||
|
||||
// Preserve zen mode only on reload. Real quit gets out of zen mode so novice users do not get stuck in zen mode.
|
||||
|
@ -1149,8 +1144,13 @@ export class Workbench extends Disposable implements IPartService {
|
|||
if (this.zenMode.active) {
|
||||
this.toggleZenMode(true);
|
||||
}
|
||||
|
||||
this.storageService.remove(Workbench.zenModeActiveStorageKey, StorageScope.WORKSPACE);
|
||||
}
|
||||
}
|
||||
|
||||
dispose(reason = ShutdownReason.QUIT): void {
|
||||
super.dispose();
|
||||
|
||||
this.workbenchShutdown = true;
|
||||
}
|
||||
|
@ -1335,8 +1335,7 @@ export class Workbench extends Disposable implements IPartService {
|
|||
this.shouldCenterLayout = active;
|
||||
let smartActive = active;
|
||||
if (this.editorPart.groups.length > 1 && this.configurationService.getValue('workbench.editor.centeredLayoutAutoResize')) {
|
||||
// Respect the auto resize setting - do not go into centered layout if there is more than 1 group.
|
||||
smartActive = false;
|
||||
smartActive = false; // Respect the auto resize setting - do not go into centered layout if there is more than 1 group.
|
||||
}
|
||||
|
||||
// Enter Centered Editor Layout
|
||||
|
|
|
@ -32,7 +32,7 @@ export class WorkbenchReferencesController extends ReferencesController {
|
|||
notificationService,
|
||||
instantiationService,
|
||||
storageService,
|
||||
configurationService,
|
||||
configurationService
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ import { IEditorService, ACTIVE_GROUP, SIDE_GROUP } from 'vs/workbench/services/
|
|||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { textLinkForeground, textLinkActiveForeground, focusBorder } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { IOpenerService } from 'vs/platform/opener/common/opener';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
|
||||
export const COMMENTS_PANEL_ID = 'workbench.panel.comments';
|
||||
export const COMMENTS_PANEL_TITLE = 'Comments';
|
||||
|
@ -42,9 +43,10 @@ export class CommentsPanel extends Panel {
|
|||
@ICommandService private commandService: ICommandService,
|
||||
@IOpenerService private openerService: IOpenerService,
|
||||
@ITelemetryService telemetryService: ITelemetryService,
|
||||
@IThemeService themeService: IThemeService
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IStorageService storageService: IStorageService
|
||||
) {
|
||||
super(COMMENTS_PANEL_ID, telemetryService, themeService);
|
||||
super(COMMENTS_PANEL_ID, telemetryService, themeService, storageService);
|
||||
}
|
||||
|
||||
public create(parent: HTMLElement): Promise<void> {
|
||||
|
|
|
@ -46,7 +46,7 @@ export class BreakpointsView extends ViewletPanel {
|
|||
|
||||
private static readonly MAX_VISIBLE_FILES = 9;
|
||||
private static readonly MEMENTO = 'breakopintsview.memento';
|
||||
private settings: any;
|
||||
private viewState: object;
|
||||
private list: WorkbenchList<IEnablement>;
|
||||
private needsRefresh: boolean;
|
||||
|
||||
|
@ -64,7 +64,7 @@ export class BreakpointsView extends ViewletPanel {
|
|||
super({ ...(options as IViewletPanelOptions), ariaHeaderLabel: nls.localize('breakpointsSection', "Breakpoints Section") }, keybindingService, contextMenuService, configurationService);
|
||||
|
||||
this.minimumBodySize = this.maximumBodySize = this.getExpandedBodySize();
|
||||
this.settings = options.viewletSettings;
|
||||
this.viewState = options.viewletState;
|
||||
this.disposables.push(this.debugService.getModel().onDidChangeBreakpoints(() => this.onBreakpointsChange()));
|
||||
}
|
||||
|
||||
|
@ -231,8 +231,10 @@ export class BreakpointsView extends ViewletPanel {
|
|||
return Math.min(BreakpointsView.MAX_VISIBLE_FILES, length) * 22;
|
||||
}
|
||||
|
||||
public shutdown(): void {
|
||||
this.settings[BreakpointsView.MEMENTO] = !this.isExpanded();
|
||||
public saveState(): void {
|
||||
this.viewState[BreakpointsView.MEMENTO] = !this.isExpanded();
|
||||
|
||||
super.saveState();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -355,7 +355,7 @@ export class LoadedScriptsView extends TreeViewsViewletPanel {
|
|||
|
||||
private treeContainer: HTMLElement;
|
||||
private loadedScriptsItemType: IContextKey<string>;
|
||||
private settings: any;
|
||||
private viewState: object;
|
||||
|
||||
constructor(
|
||||
options: IViewletViewOptions,
|
||||
|
@ -370,7 +370,7 @@ export class LoadedScriptsView extends TreeViewsViewletPanel {
|
|||
@IDebugService private debugService: IDebugService
|
||||
) {
|
||||
super({ ...(options as IViewletPanelOptions), ariaHeaderLabel: nls.localize('loadedScriptsSection', "Loaded Scripts Section") }, keybindingService, contextMenuService, configurationService);
|
||||
this.settings = options.viewletSettings;
|
||||
this.viewState = options.viewletState;
|
||||
this.loadedScriptsItemType = CONTEXT_LOADED_SCRIPTS_ITEM_TYPE.bindTo(contextKeyService);
|
||||
}
|
||||
|
||||
|
@ -464,9 +464,10 @@ export class LoadedScriptsView extends TreeViewsViewletPanel {
|
|||
super.layoutBody(size);
|
||||
}
|
||||
|
||||
public shutdown(): void {
|
||||
this.settings[LoadedScriptsView.MEMENTO] = !this.isExpanded();
|
||||
super.shutdown();
|
||||
public saveState(): void {
|
||||
this.viewState[LoadedScriptsView.MEMENTO] = !this.isExpanded();
|
||||
|
||||
super.saveState();
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
|
|
|
@ -36,7 +36,7 @@ export class CallStackView extends TreeViewsViewletPanel {
|
|||
private pauseMessage: HTMLSpanElement;
|
||||
private pauseMessageLabel: HTMLSpanElement;
|
||||
private onCallStackChangeScheduler: RunOnceScheduler;
|
||||
private settings: any;
|
||||
private viewState: object;
|
||||
private needsRefresh: boolean;
|
||||
private ignoreSelectionChangedEvent: boolean;
|
||||
private treeContainer: HTMLElement;
|
||||
|
@ -53,7 +53,7 @@ export class CallStackView extends TreeViewsViewletPanel {
|
|||
@IContextKeyService contextKeyService: IContextKeyService
|
||||
) {
|
||||
super({ ...(options as IViewletPanelOptions), ariaHeaderLabel: nls.localize('callstackSection', "Call Stack Section") }, keybindingService, contextMenuService, configurationService);
|
||||
this.settings = options.viewletSettings;
|
||||
this.viewState = options.viewletState;
|
||||
this.callStackItemType = CONTEXT_CALLSTACK_ITEM_TYPE.bindTo(contextKeyService);
|
||||
|
||||
// Create scheduler to prevent unnecessary flashing of tree when reacting to changes
|
||||
|
@ -228,9 +228,10 @@ export class CallStackView extends TreeViewsViewletPanel {
|
|||
});
|
||||
}
|
||||
|
||||
public shutdown(): void {
|
||||
this.settings[CallStackView.MEMENTO] = !this.isExpanded();
|
||||
super.shutdown();
|
||||
public saveState(): void {
|
||||
this.viewState[CallStackView.MEMENTO] = !this.isExpanded();
|
||||
|
||||
super.saveState();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -238,7 +238,7 @@ export class ConfigurationManager implements IConfigurationManager {
|
|||
}
|
||||
}));
|
||||
|
||||
this.toDispose.push(lifecycleService.onShutdown(this.store, this));
|
||||
this.toDispose.push(this.storageService.onWillSaveState(this.saveState, this));
|
||||
}
|
||||
|
||||
private initLaunches(): void {
|
||||
|
@ -382,7 +382,7 @@ export class ConfigurationManager implements IConfigurationManager {
|
|||
return this.extensionService.activateByEvent(activationEvent).then(() => this.extensionService.activateByEvent('onDebug'));
|
||||
}
|
||||
|
||||
private store(): void {
|
||||
private saveState(): void {
|
||||
this.storageService.store(DEBUG_SELECTED_CONFIG_NAME_KEY, this.selectedName, StorageScope.WORKSPACE);
|
||||
if (this.selectedLaunch) {
|
||||
this.storageService.store(DEBUG_SELECTED_ROOT, this.selectedLaunch.uri.toString(), StorageScope.WORKSPACE);
|
||||
|
|
|
@ -134,7 +134,7 @@ export class DebugService implements IDebugService {
|
|||
this.viewModel = new ViewModel(contextKeyService);
|
||||
|
||||
this.toDispose.push(this.fileService.onFileChanges(e => this.onFileChanges(e)));
|
||||
this.lifecycleService.onShutdown(this.store, this);
|
||||
this.toDispose.push(this.storageService.onWillSaveState(this.saveState, this));
|
||||
this.lifecycleService.onShutdown(this.dispose, this);
|
||||
|
||||
this.toDispose.push(this.broadcastService.onBroadcast(broadcast => {
|
||||
|
@ -968,7 +968,7 @@ export class DebugService implements IDebugService {
|
|||
return result || [];
|
||||
}
|
||||
|
||||
private store(): void {
|
||||
private saveState(): void {
|
||||
const breakpoints = this.model.getBreakpoints();
|
||||
if (breakpoints.length) {
|
||||
this.storageService.store(DEBUG_BREAKPOINTS_KEY, JSON.stringify(breakpoints), StorageScope.WORKSPACE);
|
||||
|
|
|
@ -101,9 +101,9 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati
|
|||
@IThemeService protected themeService: IThemeService,
|
||||
@IModelService private modelService: IModelService,
|
||||
@IContextKeyService private contextKeyService: IContextKeyService,
|
||||
@ICodeEditorService codeEditorService: ICodeEditorService,
|
||||
@ICodeEditorService codeEditorService: ICodeEditorService
|
||||
) {
|
||||
super(REPL_ID, telemetryService, themeService);
|
||||
super(REPL_ID, telemetryService, themeService, storageService);
|
||||
|
||||
this.replInputHeight = Repl.REPL_INPUT_INITIAL_HEIGHT;
|
||||
this.history = new HistoryNavigator(JSON.parse(this.storageService.get(HISTORY_STORAGE_KEY, StorageScope.WORKSPACE, '[]')), 50);
|
||||
|
@ -257,15 +257,6 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati
|
|||
return result;
|
||||
}
|
||||
|
||||
shutdown(): void {
|
||||
const replHistory = this.history.getHistory();
|
||||
if (replHistory.length) {
|
||||
this.storageService.store(HISTORY_STORAGE_KEY, JSON.stringify(replHistory), StorageScope.WORKSPACE);
|
||||
} else {
|
||||
this.storageService.remove(HISTORY_STORAGE_KEY, StorageScope.WORKSPACE);
|
||||
}
|
||||
}
|
||||
|
||||
// --- Cached locals
|
||||
@memoize
|
||||
private get characterWidth(): number {
|
||||
|
@ -411,6 +402,17 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati
|
|||
this.replInput.setDecorations(DECORATION_KEY, decorations);
|
||||
}
|
||||
|
||||
protected saveState(): void {
|
||||
const replHistory = this.history.getHistory();
|
||||
if (replHistory.length) {
|
||||
this.storageService.store(HISTORY_STORAGE_KEY, JSON.stringify(replHistory), StorageScope.WORKSPACE);
|
||||
} else {
|
||||
this.storageService.remove(HISTORY_STORAGE_KEY, StorageScope.WORKSPACE);
|
||||
}
|
||||
|
||||
super.saveState();
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.replInput.dispose();
|
||||
if (this.replElementsChangeListener) {
|
||||
|
|
|
@ -36,7 +36,7 @@ export class VariablesView extends TreeViewsViewletPanel {
|
|||
|
||||
private static readonly MEMENTO = 'variablesview.memento';
|
||||
private onFocusStackFrameScheduler: RunOnceScheduler;
|
||||
private settings: any;
|
||||
private viewState: object;
|
||||
private expandedElements: any[];
|
||||
private needsRefresh: boolean;
|
||||
private treeContainer: HTMLElement;
|
||||
|
@ -51,7 +51,7 @@ export class VariablesView extends TreeViewsViewletPanel {
|
|||
) {
|
||||
super({ ...(options as IViewletPanelOptions), ariaHeaderLabel: nls.localize('variablesSection', "Variables Section") }, keybindingService, contextMenuService, configurationService);
|
||||
|
||||
this.settings = options.viewletSettings;
|
||||
this.viewState = options.viewletState;
|
||||
this.expandedElements = [];
|
||||
// Use scheduler to prevent unnecessary flashing
|
||||
this.onFocusStackFrameScheduler = new RunOnceScheduler(() => {
|
||||
|
@ -150,9 +150,10 @@ export class VariablesView extends TreeViewsViewletPanel {
|
|||
});
|
||||
}
|
||||
|
||||
public shutdown(): void {
|
||||
this.settings[VariablesView.MEMENTO] = !this.isExpanded();
|
||||
super.shutdown();
|
||||
public saveState(): void {
|
||||
this.viewState[VariablesView.MEMENTO] = !this.isExpanded();
|
||||
|
||||
super.saveState();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ export class WatchExpressionsView extends TreeViewsViewletPanel {
|
|||
private static readonly MEMENTO = 'watchexpressionsview.memento';
|
||||
private onWatchExpressionsUpdatedScheduler: RunOnceScheduler;
|
||||
private treeContainer: HTMLElement;
|
||||
private settings: any;
|
||||
private viewState: object;
|
||||
private needsRefresh: boolean;
|
||||
|
||||
constructor(
|
||||
|
@ -50,7 +50,7 @@ export class WatchExpressionsView extends TreeViewsViewletPanel {
|
|||
@IConfigurationService configurationService: IConfigurationService
|
||||
) {
|
||||
super({ ...(options as IViewletPanelOptions), ariaHeaderLabel: nls.localize('watchExpressionsSection', "Watch Expressions Section") }, keybindingService, contextMenuService, configurationService);
|
||||
this.settings = options.viewletSettings;
|
||||
this.viewState = options.viewletState;
|
||||
|
||||
this.onWatchExpressionsUpdatedScheduler = new RunOnceScheduler(() => {
|
||||
this.needsRefresh = false;
|
||||
|
@ -133,9 +133,10 @@ export class WatchExpressionsView extends TreeViewsViewletPanel {
|
|||
});
|
||||
}
|
||||
|
||||
public shutdown(): void {
|
||||
this.settings[WatchExpressionsView.MEMENTO] = !this.isExpanded();
|
||||
super.shutdown();
|
||||
public saveState(): void {
|
||||
this.viewState[WatchExpressionsView.MEMENTO] = !this.isExpanded();
|
||||
|
||||
super.saveState();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -207,7 +207,7 @@ export class ExperimentService extends Disposable implements IExperimentService
|
|||
if (Array.isArray(allExperimentIdsFromStorage)) {
|
||||
allExperimentIdsFromStorage.forEach(experiment => {
|
||||
if (enabledExperiments.indexOf(experiment) === -1) {
|
||||
this.storageService.remove('experiments.' + experiment);
|
||||
this.storageService.remove(`experiments.${experiment}`, StorageScope.GLOBAL);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -255,7 +255,7 @@ export class ExperimentService extends Disposable implements IExperimentService
|
|||
|
||||
return this.shouldRunExperiment(experiment, processedExperiment).then((state: ExperimentState) => {
|
||||
experimentState.state = processedExperiment.state = state;
|
||||
this.storageService.store(storageKey, JSON.stringify(experimentState));
|
||||
this.storageService.store(storageKey, JSON.stringify(experimentState), StorageScope.GLOBAL);
|
||||
|
||||
if (state === ExperimentState.Run) {
|
||||
this.fireRunExperiment(processedExperiment);
|
||||
|
@ -280,7 +280,7 @@ export class ExperimentService extends Disposable implements IExperimentService
|
|||
const runExperimentIdsFromStorage: string[] = safeParse(this.storageService.get('currentOrPreviouslyRunExperiments', StorageScope.GLOBAL), []);
|
||||
if (runExperimentIdsFromStorage.indexOf(experiment.id)) {
|
||||
runExperimentIdsFromStorage.push(experiment.id);
|
||||
this.storageService.store('currentOrPreviouslyRunExperiments', JSON.stringify(runExperimentIdsFromStorage));
|
||||
this.storageService.store('currentOrPreviouslyRunExperiments', JSON.stringify(runExperimentIdsFromStorage), StorageScope.GLOBAL);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
import * as assert from 'assert';
|
||||
import { ExperimentService, ExperimentActionType, ExperimentState } from 'vs/workbench/parts/experiments/node/experimentService';
|
||||
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { TestLifecycleService } from 'vs/workbench/test/workbenchTestServices';
|
||||
import {
|
||||
|
@ -15,7 +14,7 @@ import {
|
|||
} from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { ExtensionManagementService, getLocalExtensionIdFromManifest } from 'vs/platform/extensionManagement/node/extensionManagementService';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { TestExtensionEnablementService } from 'vs/platform/extensionManagement/test/common/extensionEnablementService.test';
|
||||
import { TestExtensionEnablementService } from 'vs/platform/extensionManagement/test/electron-browser/extensionEnablementService.test';
|
||||
import { URLService } from 'vs/platform/url/common/urlService';
|
||||
import { IURLService } from 'vs/platform/url/common/url';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
|
@ -26,6 +25,7 @@ import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle';
|
|||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { assign } from 'vs/base/common/objects';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
|
||||
let experimentData = {
|
||||
experiments: []
|
|
@ -9,9 +9,8 @@ import { IExperiment, ExperimentActionType, IExperimentService, ExperimentState
|
|||
|
||||
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { TestExperimentService } from 'vs/workbench/parts/experiments/test/node/experimentService.test';
|
||||
import { TestExperimentService } from 'vs/workbench/parts/experiments/test/electron-browser/experimentService.test';
|
||||
import { ExperimentalPrompts } from 'vs/workbench/parts/experiments/electron-browser/experimentalPrompt';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { INotificationService, Severity, IPromptChoice } from 'vs/platform/notification/common/notification';
|
||||
import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle';
|
||||
import { TestLifecycleService } from 'vs/workbench/test/workbenchTestServices';
|
||||
|
@ -19,6 +18,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
|||
import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils';
|
||||
import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
|
||||
suite('Experimental Prompts', () => {
|
||||
let instantiationService: TestInstantiationService;
|
||||
|
|
|
@ -47,6 +47,7 @@ import { CancellationToken } from 'vs/base/common/cancellation';
|
|||
import { ExtensionsTree, IExtensionData } from 'vs/workbench/parts/extensions/browser/extensionsViewer';
|
||||
import { ShowCurrentReleaseNotesAction } from 'vs/workbench/parts/update/electron-browser/update';
|
||||
import { KeybindingParser } from 'vs/base/common/keybindingParser';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
|
||||
function renderBody(body: string): string {
|
||||
const styleSheetPath = require.toUrl('./media/markdown.css').replace('file://', 'vscode-core-resource://');
|
||||
|
@ -187,8 +188,9 @@ export class ExtensionEditor extends BaseEditor {
|
|||
@IOpenerService private readonly openerService: IOpenerService,
|
||||
@IPartService private readonly partService: IPartService,
|
||||
@IExtensionTipsService private readonly extensionTipsService: IExtensionTipsService,
|
||||
@IStorageService storageService: IStorageService
|
||||
) {
|
||||
super(ExtensionEditor.ID, telemetryService, themeService);
|
||||
super(ExtensionEditor.ID, telemetryService, themeService, storageService);
|
||||
this.disposables = [];
|
||||
this.extensionReadme = null;
|
||||
this.extensionChangelog = null;
|
||||
|
|
|
@ -38,6 +38,7 @@ import { IDebugService } from 'vs/workbench/parts/debug/common/debug';
|
|||
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { randomPort } from 'vs/base/node/ports';
|
||||
import { IContextKeyService, RawContextKey, IContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
|
||||
export const IExtensionHostProfileService = createDecorator<IExtensionHostProfileService>('extensionHostProfileService');
|
||||
export const CONTEXT_PROFILE_SESSION_STATE = new RawContextKey<string>('profileSessionState', 'none');
|
||||
|
@ -110,8 +111,9 @@ export class RuntimeExtensionsEditor extends BaseEditor {
|
|||
@IContextMenuService private readonly _contextMenuService: IContextMenuService,
|
||||
@IInstantiationService private readonly _instantiationService: IInstantiationService,
|
||||
@IExtensionHostProfileService private readonly _extensionHostProfileService: IExtensionHostProfileService,
|
||||
@IStorageService storageService: IStorageService
|
||||
) {
|
||||
super(RuntimeExtensionsEditor.ID, telemetryService, themeService);
|
||||
super(RuntimeExtensionsEditor.ID, telemetryService, themeService, storageService);
|
||||
|
||||
this._list = null;
|
||||
this._profileInfo = this._extensionHostProfileService.lastProfile;
|
||||
|
|
|
@ -16,7 +16,7 @@ import {
|
|||
import { getGalleryExtensionId, getGalleryExtensionIdFromLocal } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
|
||||
import { ExtensionManagementService, getLocalExtensionIdFromGallery, getLocalExtensionIdFromManifest } from 'vs/platform/extensionManagement/node/extensionManagementService';
|
||||
import { ExtensionTipsService } from 'vs/workbench/parts/extensions/electron-browser/extensionTipsService';
|
||||
import { TestExtensionEnablementService } from 'vs/platform/extensionManagement/test/common/extensionEnablementService.test';
|
||||
import { TestExtensionEnablementService } from 'vs/platform/extensionManagement/test/electron-browser/extensionEnablementService.test';
|
||||
import { ExtensionGalleryService } from 'vs/platform/extensionManagement/node/extensionGalleryService';
|
||||
import { IURLService } from 'vs/platform/url/common/url';
|
||||
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
|
||||
|
|
|
@ -34,10 +34,9 @@ import { IPager } from 'vs/base/common/paging';
|
|||
import { assign } from 'vs/base/common/objects';
|
||||
import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import { ConfigurationKey } from 'vs/workbench/parts/extensions/common/extensions';
|
||||
import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService';
|
||||
import { TestExtensionEnablementService } from 'vs/platform/extensionManagement/test/common/extensionEnablementService.test';
|
||||
import { TestExtensionEnablementService } from 'vs/platform/extensionManagement/test/electron-browser/extensionEnablementService.test';
|
||||
import { IURLService } from 'vs/platform/url/common/url';
|
||||
import product from 'vs/platform/node/product';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
|
@ -46,7 +45,8 @@ import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle';
|
|||
import { INotificationService, Severity, IPromptChoice, IPromptOptions } from 'vs/platform/notification/common/notification';
|
||||
import { URLService } from 'vs/platform/url/common/urlService';
|
||||
import { IExperimentService } from 'vs/workbench/parts/experiments/node/experimentService';
|
||||
import { TestExperimentService } from 'vs/workbench/parts/experiments/test/node/experimentService.test';
|
||||
import { TestExperimentService } from 'vs/workbench/parts/experiments/test/electron-browser/experimentService.test';
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
|
||||
const mockExtensionGallery: IGalleryExtension[] = [
|
||||
aGalleryExtension('MockExtension1', {
|
||||
|
|
|
@ -17,7 +17,7 @@ import {
|
|||
import { getGalleryExtensionId, getGalleryExtensionIdFromLocal } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
|
||||
import { ExtensionManagementService, getLocalExtensionIdFromManifest } from 'vs/platform/extensionManagement/node/extensionManagementService';
|
||||
import { ExtensionTipsService } from 'vs/workbench/parts/extensions/electron-browser/extensionTipsService';
|
||||
import { TestExtensionEnablementService } from 'vs/platform/extensionManagement/test/common/extensionEnablementService.test';
|
||||
import { TestExtensionEnablementService } from 'vs/platform/extensionManagement/test/electron-browser/extensionEnablementService.test';
|
||||
import { ExtensionGalleryService } from 'vs/platform/extensionManagement/node/extensionGalleryService';
|
||||
import { IURLService } from 'vs/platform/url/common/url';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
|
|
|
@ -17,7 +17,7 @@ import {
|
|||
import { getGalleryExtensionId, getGalleryExtensionIdFromLocal } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
|
||||
import { ExtensionManagementService, getLocalExtensionIdFromGallery, getLocalExtensionIdFromManifest } from 'vs/platform/extensionManagement/node/extensionManagementService';
|
||||
import { ExtensionTipsService } from 'vs/workbench/parts/extensions/electron-browser/extensionTipsService';
|
||||
import { TestExtensionEnablementService } from 'vs/platform/extensionManagement/test/common/extensionEnablementService.test';
|
||||
import { TestExtensionEnablementService } from 'vs/platform/extensionManagement/test/electron-browser/extensionEnablementService.test';
|
||||
import { ExtensionGalleryService } from 'vs/platform/extensionManagement/node/extensionGalleryService';
|
||||
import { IURLService } from 'vs/platform/url/common/url';
|
||||
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
|
||||
|
|
|
@ -14,6 +14,7 @@ import { URI } from 'vs/base/common/uri';
|
|||
import { BINARY_FILE_EDITOR_ID } from 'vs/workbench/parts/files/common/files';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
|
||||
/**
|
||||
* An implementation of editor for binary files like images.
|
||||
|
@ -27,7 +28,8 @@ export class BinaryFileEditor extends BaseBinaryResourceEditor {
|
|||
@IThemeService themeService: IThemeService,
|
||||
@IFileService fileService: IFileService,
|
||||
@IWindowsService private windowsService: IWindowsService,
|
||||
@IEditorService private editorService: IEditorService
|
||||
@IEditorService private editorService: IEditorService,
|
||||
@IStorageService storageService: IStorageService
|
||||
) {
|
||||
super(
|
||||
BinaryFileEditor.ID,
|
||||
|
@ -37,7 +39,8 @@ export class BinaryFileEditor extends BaseBinaryResourceEditor {
|
|||
},
|
||||
telemetryService,
|
||||
themeService,
|
||||
fileService
|
||||
fileService,
|
||||
storageService
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -263,13 +263,12 @@ export class TextFileEditor extends BaseTextEditor {
|
|||
super.clearInput();
|
||||
}
|
||||
|
||||
shutdown(): void {
|
||||
protected saveState(): void {
|
||||
|
||||
// Update/clear editor view State
|
||||
this.doSaveOrClearTextEditorViewState(this.input);
|
||||
|
||||
// Call Super
|
||||
super.shutdown();
|
||||
super.saveState();
|
||||
}
|
||||
|
||||
private doSaveOrClearTextEditorViewState(input: FileEditorInput): void {
|
||||
|
|
|
@ -150,7 +150,7 @@ export class ExplorerViewlet extends ViewContainerViewlet implements IExplorerVi
|
|||
|
||||
private static readonly EXPLORER_VIEWS_STATE = 'workbench.explorer.views.state';
|
||||
|
||||
private viewletState: FileViewletState;
|
||||
private fileViewletState: FileViewletState;
|
||||
private viewletVisibleContextKey: IContextKey<boolean>;
|
||||
|
||||
constructor(
|
||||
|
@ -169,7 +169,7 @@ export class ExplorerViewlet extends ViewContainerViewlet implements IExplorerVi
|
|||
) {
|
||||
super(VIEWLET_ID, ExplorerViewlet.EXPLORER_VIEWS_STATE, true, configurationService, partService, telemetryService, storageService, instantiationService, themeService, contextMenuService, extensionService, contextService);
|
||||
|
||||
this.viewletState = new FileViewletState();
|
||||
this.fileViewletState = new FileViewletState();
|
||||
this.viewletVisibleContextKey = ExplorerViewletVisibleContext.bindTo(contextKeyService);
|
||||
|
||||
this._register(this.contextService.onDidChangeWorkspaceName(e => this.updateTitleArea()));
|
||||
|
@ -218,7 +218,7 @@ export class ExplorerViewlet extends ViewContainerViewlet implements IExplorerVi
|
|||
});
|
||||
|
||||
const explorerInstantiator = this.instantiationService.createChild(new ServiceCollection([IEditorService, delegatingEditorService]));
|
||||
return explorerInstantiator.createInstance(ExplorerView, <IExplorerViewOptions>{ ...options, viewletState: this.viewletState });
|
||||
return explorerInstantiator.createInstance(ExplorerView, <IExplorerViewOptions>{ ...options, viewletState: this.fileViewletState });
|
||||
}
|
||||
return super.createView(viewDescriptor, options);
|
||||
}
|
||||
|
@ -242,13 +242,13 @@ export class ExplorerViewlet extends ViewContainerViewlet implements IExplorerVi
|
|||
|
||||
public getActionRunner(): IActionRunner {
|
||||
if (!this.actionRunner) {
|
||||
this.actionRunner = new ActionRunner(this.viewletState);
|
||||
this.actionRunner = new ActionRunner(this.fileViewletState);
|
||||
}
|
||||
return this.actionRunner;
|
||||
}
|
||||
|
||||
public getViewletState(): FileViewletState {
|
||||
return this.viewletState;
|
||||
return this.fileViewletState;
|
||||
}
|
||||
|
||||
focus(): void {
|
||||
|
|
|
@ -26,7 +26,7 @@ import { SAVE_FILE_COMMAND_ID, REVERT_FILE_COMMAND_ID, SAVE_FILE_AS_COMMAND_ID,
|
|||
import { createTextBufferFactoryFromSnapshot } from 'vs/editor/common/model/textModel';
|
||||
import { INotificationService, INotificationHandle, INotificationActions, Severity } from 'vs/platform/notification/common/notification';
|
||||
import { IOpenerService } from 'vs/platform/opener/common/opener';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import { ExecuteCommandAction } from 'vs/platform/actions/common/actions';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { once } from 'vs/base/common/event';
|
||||
|
@ -111,7 +111,7 @@ export class SaveErrorHandler extends Disposable implements ISaveErrorHandler, I
|
|||
|
||||
// If the user tried to save from the opened conflict editor, show its message again
|
||||
if (this.activeConflictResolutionResource && this.activeConflictResolutionResource.toString() === model.getResource().toString()) {
|
||||
if (this.storageService.getBoolean(LEARN_MORE_DIRTY_WRITE_IGNORE_KEY)) {
|
||||
if (this.storageService.getBoolean(LEARN_MORE_DIRTY_WRITE_IGNORE_KEY, StorageScope.GLOBAL)) {
|
||||
return; // return if this message is ignored
|
||||
}
|
||||
|
||||
|
@ -211,7 +211,7 @@ class DoNotShowResolveConflictLearnMoreAction extends Action {
|
|||
}
|
||||
|
||||
run(notification: IDisposable): Promise<any> {
|
||||
this.storageService.store(LEARN_MORE_DIRTY_WRITE_IGNORE_KEY, true);
|
||||
this.storageService.store(LEARN_MORE_DIRTY_WRITE_IGNORE_KEY, true, StorageScope.GLOBAL);
|
||||
|
||||
// Hide notification
|
||||
notification.dispose();
|
||||
|
@ -247,7 +247,7 @@ class ResolveSaveConflictAction extends Action {
|
|||
options: { pinned: true }
|
||||
}
|
||||
).then(() => {
|
||||
if (this.storageService.getBoolean(LEARN_MORE_DIRTY_WRITE_IGNORE_KEY)) {
|
||||
if (this.storageService.getBoolean(LEARN_MORE_DIRTY_WRITE_IGNORE_KEY, StorageScope.GLOBAL)) {
|
||||
return; // return if this message is ignored
|
||||
}
|
||||
|
||||
|
|
|
@ -149,8 +149,4 @@ export class EmptyView extends ViewletPanel {
|
|||
public getActionItem(action: IAction): IActionItem {
|
||||
return null;
|
||||
}
|
||||
|
||||
public shutdown(): void {
|
||||
// Subclass to implement
|
||||
}
|
||||
}
|
||||
|
|
|
@ -74,7 +74,7 @@ export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView
|
|||
private shouldRefresh: boolean;
|
||||
private autoReveal: boolean;
|
||||
private sortOrder: SortOrder;
|
||||
private settings: object;
|
||||
private viewState: object;
|
||||
private treeContainer: HTMLElement;
|
||||
private dragHandler: DelayedDragHandler;
|
||||
private decorationProvider: ExplorerDecorationsProvider;
|
||||
|
@ -98,7 +98,7 @@ export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView
|
|||
) {
|
||||
super({ ...(options as IViewletPanelOptions), ariaHeaderLabel: nls.localize('explorerSection', "Files Explorer Section") }, keybindingService, contextMenuService, configurationService);
|
||||
|
||||
this.settings = options.viewletSettings;
|
||||
this.viewState = options.viewletState;
|
||||
this.viewletState = options.viewletState;
|
||||
this.autoReveal = true;
|
||||
|
||||
|
@ -173,8 +173,8 @@ export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView
|
|||
|
||||
// Load and Fill Viewer
|
||||
let targetsToExpand = [];
|
||||
if (this.settings[ExplorerView.MEMENTO_EXPANDED_FOLDER_RESOURCES]) {
|
||||
targetsToExpand = this.settings[ExplorerView.MEMENTO_EXPANDED_FOLDER_RESOURCES].map((e: string) => URI.parse(e));
|
||||
if (this.viewState[ExplorerView.MEMENTO_EXPANDED_FOLDER_RESOURCES]) {
|
||||
targetsToExpand = this.viewState[ExplorerView.MEMENTO_EXPANDED_FOLDER_RESOURCES].map((e: string) => URI.parse(e));
|
||||
}
|
||||
this.doRefresh(targetsToExpand).then(() => {
|
||||
|
||||
|
@ -236,7 +236,7 @@ export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView
|
|||
if (activeFile) {
|
||||
|
||||
// Always remember last opened file
|
||||
this.settings[ExplorerView.MEMENTO_LAST_ACTIVE_FILE_RESOURCE] = activeFile.toString();
|
||||
this.viewState[ExplorerView.MEMENTO_LAST_ACTIVE_FILE_RESOURCE] = activeFile.toString();
|
||||
|
||||
// Select file if input is inside workspace
|
||||
if (this.isVisible() && !this.isDisposed && this.contextService.isInsideWorkspace(activeFile)) {
|
||||
|
@ -252,7 +252,7 @@ export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView
|
|||
// Handle closed or untitled file (convince explorer to not reopen any file when getting visible)
|
||||
const activeInput = this.editorService.activeEditor;
|
||||
if (!activeInput || toResource(activeInput, { supportSideBySide: true, filter: Schemas.untitled })) {
|
||||
this.settings[ExplorerView.MEMENTO_LAST_ACTIVE_FILE_RESOURCE] = void 0;
|
||||
this.viewState[ExplorerView.MEMENTO_LAST_ACTIVE_FILE_RESOURCE] = void 0;
|
||||
clearFocus = true;
|
||||
}
|
||||
|
||||
|
@ -355,8 +355,8 @@ export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView
|
|||
|
||||
// Otherwise restore last used file: By lastActiveFileResource
|
||||
let lastActiveFileResource: URI;
|
||||
if (this.settings[ExplorerView.MEMENTO_LAST_ACTIVE_FILE_RESOURCE]) {
|
||||
lastActiveFileResource = URI.parse(this.settings[ExplorerView.MEMENTO_LAST_ACTIVE_FILE_RESOURCE]);
|
||||
if (this.viewState[ExplorerView.MEMENTO_LAST_ACTIVE_FILE_RESOURCE]) {
|
||||
lastActiveFileResource = URI.parse(this.viewState[ExplorerView.MEMENTO_LAST_ACTIVE_FILE_RESOURCE]);
|
||||
}
|
||||
|
||||
if (lastActiveFileResource && this.isCreated && this.model.findClosest(lastActiveFileResource)) {
|
||||
|
@ -608,9 +608,9 @@ export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView
|
|||
// delete events can result in UI activity that will fill the memento again when multiple
|
||||
// editors are closing)
|
||||
setTimeout(() => {
|
||||
const lastActiveResource: string = this.settings[ExplorerView.MEMENTO_LAST_ACTIVE_FILE_RESOURCE];
|
||||
const lastActiveResource: string = this.viewState[ExplorerView.MEMENTO_LAST_ACTIVE_FILE_RESOURCE];
|
||||
if (lastActiveResource && e.contains(URI.parse(lastActiveResource), FileChangeType.DELETED)) {
|
||||
this.settings[ExplorerView.MEMENTO_LAST_ACTIVE_FILE_RESOURCE] = null;
|
||||
this.viewState[ExplorerView.MEMENTO_LAST_ACTIVE_FILE_RESOURCE] = null;
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -1003,7 +1003,7 @@ export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView
|
|||
return this.tree.reveal(element, relativeTop);
|
||||
}
|
||||
|
||||
public shutdown(): void {
|
||||
saveState(): void {
|
||||
|
||||
// Keep list of expanded folders to restore on next load
|
||||
if (this.isCreated) {
|
||||
|
@ -1012,18 +1012,18 @@ export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView
|
|||
.map((e: ExplorerItem) => e.resource.toString());
|
||||
|
||||
if (expanded.length) {
|
||||
this.settings[ExplorerView.MEMENTO_EXPANDED_FOLDER_RESOURCES] = expanded;
|
||||
this.viewState[ExplorerView.MEMENTO_EXPANDED_FOLDER_RESOURCES] = expanded;
|
||||
} else {
|
||||
delete this.settings[ExplorerView.MEMENTO_EXPANDED_FOLDER_RESOURCES];
|
||||
delete this.viewState[ExplorerView.MEMENTO_EXPANDED_FOLDER_RESOURCES];
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up last focused if not set
|
||||
if (!this.settings[ExplorerView.MEMENTO_LAST_ACTIVE_FILE_RESOURCE]) {
|
||||
delete this.settings[ExplorerView.MEMENTO_LAST_ACTIVE_FILE_RESOURCE];
|
||||
if (!this.viewState[ExplorerView.MEMENTO_LAST_ACTIVE_FILE_RESOURCE]) {
|
||||
delete this.viewState[ExplorerView.MEMENTO_LAST_ACTIVE_FILE_RESOURCE];
|
||||
}
|
||||
|
||||
super.shutdown();
|
||||
super.saveState();
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
|
|
|
@ -59,9 +59,9 @@ export class HtmlPreviewPart extends BaseWebviewEditor {
|
|||
@IInstantiationService private readonly _instantiationService: IInstantiationService,
|
||||
@IEditorGroupsService readonly editorGroupService: IEditorGroupsService
|
||||
) {
|
||||
super(HtmlPreviewPart.ID, telemetryService, themeService, contextKeyService);
|
||||
super(HtmlPreviewPart.ID, telemetryService, themeService, contextKeyService, _storageService);
|
||||
|
||||
this.editorMemento = this.getEditorMemento<HtmlPreviewEditorViewState>(_storageService, editorGroupService, this.viewStateStorageKey);
|
||||
this.editorMemento = this.getEditorMemento<HtmlPreviewEditorViewState>(editorGroupService, this.viewStateStorageKey);
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
|
@ -162,13 +162,14 @@ export class HtmlPreviewPart extends BaseWebviewEditor {
|
|||
super.clearInput();
|
||||
}
|
||||
|
||||
public shutdown(): void {
|
||||
protected saveState(): void {
|
||||
if (this.input instanceof HtmlInput) {
|
||||
this.saveHTMLPreviewViewState(this.input, {
|
||||
scrollYPercentage: this._scrollYPercentage
|
||||
});
|
||||
}
|
||||
super.shutdown();
|
||||
|
||||
super.saveState();
|
||||
}
|
||||
|
||||
public sendMessage(data: any): void {
|
||||
|
@ -242,10 +243,10 @@ export class HtmlPreviewPart extends BaseWebviewEditor {
|
|||
}
|
||||
|
||||
private saveHTMLPreviewViewState(input: HtmlInput, editorViewState: HtmlPreviewEditorViewState): void {
|
||||
this.editorMemento.saveState(this.group, input, editorViewState);
|
||||
this.editorMemento.saveEditorState(this.group, input, editorViewState);
|
||||
}
|
||||
|
||||
private loadHTMLPreviewViewState(input: HtmlInput): HtmlPreviewEditorViewState {
|
||||
return this.editorMemento.loadState(this.group, input);
|
||||
return this.editorMemento.loadEditorState(this.group, input);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -71,7 +71,7 @@ export class LocalizationWorkbenchContribution extends Disposable implements IWo
|
|||
|
||||
private onDidInstallExtension(e: DidInstallExtensionEvent): void {
|
||||
const donotAskUpdateKey = 'langugage.update.donotask';
|
||||
if (!this.storageService.getBoolean(donotAskUpdateKey) && e.local && e.operation === InstallOperation.Install && e.local.manifest.contributes && e.local.manifest.contributes.localizations && e.local.manifest.contributes.localizations.length) {
|
||||
if (!this.storageService.getBoolean(donotAskUpdateKey, StorageScope.GLOBAL) && e.local && e.operation === InstallOperation.Install && e.local.manifest.contributes && e.local.manifest.contributes.localizations && e.local.manifest.contributes.localizations.length) {
|
||||
const locale = e.local.manifest.contributes.localizations[0].languageId;
|
||||
if (platform.language !== locale) {
|
||||
const updateAndRestart = platform.locale !== locale;
|
||||
|
@ -92,7 +92,7 @@ export class LocalizationWorkbenchContribution extends Disposable implements IWo
|
|||
}, {
|
||||
label: localize('neverAgain', "Don't Show Again"),
|
||||
isSecondary: true,
|
||||
run: () => this.storageService.store(donotAskUpdateKey, true)
|
||||
run: () => this.storageService.store(donotAskUpdateKey, true, StorageScope.GLOBAL)
|
||||
}],
|
||||
{ sticky: true }
|
||||
);
|
||||
|
|
|
@ -22,8 +22,7 @@ import { RangeHighlightDecorations } from 'vs/workbench/browser/parts/editor/ran
|
|||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { IMarkersWorkbenchService } from 'vs/workbench/parts/markers/electron-browser/markers';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { Scope } from 'vs/workbench/common/memento';
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { Iterator } from 'vs/base/common/iterator';
|
||||
|
@ -78,7 +77,7 @@ export class MarkersPanel extends Panel implements IMarkerFilterController {
|
|||
private treeContainer: HTMLElement;
|
||||
private messageBoxContainer: HTMLElement;
|
||||
private ariaLabelElement: HTMLElement;
|
||||
private panelSettings: any;
|
||||
private panelState: object;
|
||||
private panelFoucusContextKey: IContextKey<boolean>;
|
||||
|
||||
private filter: Filter;
|
||||
|
@ -103,9 +102,9 @@ export class MarkersPanel extends Panel implements IMarkerFilterController {
|
|||
@IMenuService private menuService: IMenuService,
|
||||
@IKeybindingService private keybindingService: IKeybindingService,
|
||||
) {
|
||||
super(Constants.MARKERS_PANEL_ID, telemetryService, themeService);
|
||||
super(Constants.MARKERS_PANEL_ID, telemetryService, themeService, storageService);
|
||||
this.panelFoucusContextKey = Constants.MarkerPanelFocusContextKey.bindTo(contextKeyService);
|
||||
this.panelSettings = this.getMemento(storageService, Scope.WORKSPACE);
|
||||
this.panelState = this.getMemento(StorageScope.WORKSPACE);
|
||||
this.setCurrentActiveEditor();
|
||||
}
|
||||
|
||||
|
@ -347,7 +346,7 @@ export class MarkersPanel extends Panel implements IMarkerFilterController {
|
|||
this.tree.focusFirst();
|
||||
});
|
||||
|
||||
this.filterAction = this.instantiationService.createInstance(MarkersFilterAction, { filterText: this.panelSettings['filter'] || '', filterHistory: this.panelSettings['filterHistory'] || [], useFilesExclude: !!this.panelSettings['useFilesExclude'] });
|
||||
this.filterAction = this.instantiationService.createInstance(MarkersFilterAction, { filterText: this.panelState['filter'] || '', filterHistory: this.panelState['filterHistory'] || [], useFilesExclude: !!this.panelState['useFilesExclude'] });
|
||||
this.actions = [this.filterAction, this.collapseAllAction];
|
||||
}
|
||||
|
||||
|
@ -642,17 +641,16 @@ export class MarkersPanel extends Panel implements IMarkerFilterController {
|
|||
return { total, filtered };
|
||||
}
|
||||
|
||||
public shutdown(): void {
|
||||
// store memento
|
||||
this.panelSettings['filter'] = this.filterAction.filterText;
|
||||
this.panelSettings['filterHistory'] = this.filterAction.filterHistory;
|
||||
this.panelSettings['useFilesExclude'] = this.filterAction.useFilesExclude;
|
||||
protected saveState(): void {
|
||||
this.panelState['filter'] = this.filterAction.filterText;
|
||||
this.panelState['filterHistory'] = this.filterAction.filterHistory;
|
||||
this.panelState['useFilesExclude'] = this.filterAction.useFilesExclude;
|
||||
|
||||
super.shutdown();
|
||||
super.saveState();
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
super.dispose();
|
||||
this.tree.dispose();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -461,8 +461,8 @@ export class OutputService extends Disposable implements IOutputService, ITextMo
|
|||
}
|
||||
this._register(registry.onDidRegisterChannel(this.onDidRegisterChannel, this));
|
||||
|
||||
panelService.onDidPanelOpen(this.onDidPanelOpen, this);
|
||||
panelService.onDidPanelClose(this.onDidPanelClose, this);
|
||||
this._register(panelService.onDidPanelOpen(this.onDidPanelOpen, this));
|
||||
this._register(panelService.onDidPanelClose(this.onDidPanelClose, this));
|
||||
|
||||
// Set active channel to first channel if not set
|
||||
if (!this.activeChannel) {
|
||||
|
@ -470,7 +470,8 @@ export class OutputService extends Disposable implements IOutputService, ITextMo
|
|||
this.activeChannel = channels && channels.length > 0 ? this.getChannel(channels[0].id) : null;
|
||||
}
|
||||
|
||||
this.lifecycleService.onShutdown(() => this.onShutdown());
|
||||
this._register(this.lifecycleService.onShutdown(() => this.dispose()));
|
||||
this._register(this.storageService.onWillSaveState(() => this.saveState()));
|
||||
}
|
||||
|
||||
provideTextContent(resource: URI): TPromise<ITextModel> {
|
||||
|
@ -633,11 +634,10 @@ export class OutputService extends Disposable implements IOutputService, ITextMo
|
|||
return this.instantiationService.createInstance(ResourceEditorInput, nls.localize('output', "{0} - Output", channel.label), nls.localize('channel', "Output channel for '{0}'", channel.label), resource);
|
||||
}
|
||||
|
||||
onShutdown(): void {
|
||||
private saveState(): void {
|
||||
if (this.activeChannel) {
|
||||
this.storageService.store(OUTPUT_ACTIVE_CHANNEL_KEY, this.activeChannel.id, StorageScope.WORKSPACE);
|
||||
}
|
||||
this.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -44,6 +44,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati
|
|||
import { KeybindingsEditorInput } from 'vs/workbench/services/preferences/common/preferencesEditorInput';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { attachStylerCallback } from 'vs/platform/theme/common/styler';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
|
||||
let $ = DOM.$;
|
||||
|
||||
|
@ -89,9 +90,10 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor
|
|||
@INotificationService private notificationService: INotificationService,
|
||||
@IClipboardService private clipboardService: IClipboardService,
|
||||
@IInstantiationService private instantiationService: IInstantiationService,
|
||||
@IEditorService private editorService: IEditorService
|
||||
@IEditorService private editorService: IEditorService,
|
||||
@IStorageService storageService: IStorageService
|
||||
) {
|
||||
super(KeybindingsEditor.ID, telemetryService, themeService);
|
||||
super(KeybindingsEditor.ID, telemetryService, themeService, storageService);
|
||||
this.delayedFiltering = new Delayer<void>(300);
|
||||
this._register(keybindingsService.onDidUpdateKeybindings(() => this.render(false, CancellationToken.None)));
|
||||
|
||||
|
|
|
@ -94,9 +94,10 @@ export class PreferencesEditor extends BaseEditor {
|
|||
@IContextKeyService private contextKeyService: IContextKeyService,
|
||||
@IInstantiationService private instantiationService: IInstantiationService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IProgressService private progressService: IProgressService
|
||||
@IProgressService private progressService: IProgressService,
|
||||
@IStorageService storageService: IStorageService
|
||||
) {
|
||||
super(PreferencesEditor.ID, telemetryService, themeService);
|
||||
super(PreferencesEditor.ID, telemetryService, themeService, storageService);
|
||||
this.defaultSettingsEditorContextKey = CONTEXT_SETTINGS_EDITOR.bindTo(this.contextKeyService);
|
||||
this.searchFocusContextKey = CONTEXT_SETTINGS_SEARCH_FOCUS.bindTo(this.contextKeyService);
|
||||
this.delayedFilterLogging = new Delayer<void>(1000);
|
||||
|
|
|
@ -137,7 +137,7 @@ export class SettingsEditor2 extends BaseEditor {
|
|||
@IEditorGroupsService protected editorGroupService: IEditorGroupsService,
|
||||
@IKeybindingService private keybindingService: IKeybindingService
|
||||
) {
|
||||
super(SettingsEditor2.ID, telemetryService, themeService);
|
||||
super(SettingsEditor2.ID, telemetryService, themeService, storageService);
|
||||
this.delayedFilterLogging = new Delayer<void>(1000);
|
||||
this.localSearchDelayer = new Delayer(300);
|
||||
this.remoteSearchThrottle = new ThrottledDelayer(200);
|
||||
|
@ -153,7 +153,7 @@ export class SettingsEditor2 extends BaseEditor {
|
|||
|
||||
this.scheduledRefreshes = new Map<string, DOM.IFocusTracker>();
|
||||
|
||||
this.editorMemento = this.getEditorMemento<ISettingsEditor2State>(storageService, editorGroupService, SETTINGS_EDITOR_STATE_KEY);
|
||||
this.editorMemento = this.getEditorMemento<ISettingsEditor2State>(editorGroupService, SETTINGS_EDITOR_STATE_KEY);
|
||||
|
||||
this._register(configurationService.onDidChangeConfiguration(e => {
|
||||
if (e.source !== ConfigurationTarget.DEFAULT) {
|
||||
|
@ -222,7 +222,7 @@ export class SettingsEditor2 extends BaseEditor {
|
|||
}
|
||||
|
||||
private restoreCachedState(): void {
|
||||
const cachedState = this.editorMemento.loadState(this.group, this.input);
|
||||
const cachedState = this.editorMemento.loadEditorState(this.group, this.input);
|
||||
if (cachedState && typeof cachedState.target === 'object') {
|
||||
cachedState.target = URI.revive(cachedState.target);
|
||||
}
|
||||
|
@ -257,7 +257,7 @@ export class SettingsEditor2 extends BaseEditor {
|
|||
|
||||
clearInput(): void {
|
||||
this.inSettingsEditorContextKey.set(false);
|
||||
this.editorMemento.clearState(this.input, this.group);
|
||||
this.editorMemento.clearEditorState(this.input, this.group);
|
||||
super.clearInput();
|
||||
}
|
||||
|
||||
|
@ -920,7 +920,7 @@ export class SettingsEditor2 extends BaseEditor {
|
|||
this.settingsTreeModel.update(resolvedSettingsRoot);
|
||||
|
||||
// Make sure that all extensions' settings are included in search results
|
||||
const cachedState = this.editorMemento.loadState(this.group, this.input);
|
||||
const cachedState = this.editorMemento.loadEditorState(this.group, this.input);
|
||||
if (cachedState && cachedState.searchQuery) {
|
||||
this.triggerSearch(cachedState.searchQuery);
|
||||
} else {
|
||||
|
@ -1291,13 +1291,14 @@ export class SettingsEditor2 extends BaseEditor {
|
|||
this.settingsTreeRenderer.updateWidth(dimension.width);
|
||||
}
|
||||
|
||||
shutdown(): void {
|
||||
protected saveState(): void {
|
||||
if (this.isVisible()) {
|
||||
const searchQuery = this.searchWidget.getValue().trim();
|
||||
const target = this.settingsTargetsWidget.settingsTarget as SettingsTarget;
|
||||
this.editorMemento.saveState(this.group, this.input, { searchQuery, target });
|
||||
this.editorMemento.saveEditorState(this.group, this.input, { searchQuery, target });
|
||||
}
|
||||
super.shutdown();
|
||||
|
||||
super.saveState();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -21,8 +21,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
|||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen';
|
||||
import { registerEditorAction, EditorAction, IEditorCommandMenuOptions } from 'vs/editor/browser/editorExtensions';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle';
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import { once } from 'vs/base/common/event';
|
||||
import { LRUCache } from 'vs/base/common/map';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
|
@ -67,7 +66,6 @@ class CommandsHistory {
|
|||
|
||||
constructor(
|
||||
@IStorageService private storageService: IStorageService,
|
||||
@ILifecycleService private lifecycleService: ILifecycleService,
|
||||
@IConfigurationService private configurationService: IConfigurationService
|
||||
) {
|
||||
this.updateConfiguration();
|
||||
|
@ -85,7 +83,7 @@ class CommandsHistory {
|
|||
}
|
||||
|
||||
private load(): void {
|
||||
const raw = this.storageService.get(CommandsHistory.PREF_KEY_CACHE);
|
||||
const raw = this.storageService.get(CommandsHistory.PREF_KEY_CACHE, StorageScope.GLOBAL);
|
||||
let serializedCache: ISerializedCommandHistory;
|
||||
if (raw) {
|
||||
try {
|
||||
|
@ -106,20 +104,20 @@ class CommandsHistory {
|
|||
entries.forEach(entry => commandHistory.set(entry.key, entry.value));
|
||||
}
|
||||
|
||||
commandCounter = this.storageService.getInteger(CommandsHistory.PREF_KEY_COUNTER, void 0, commandCounter);
|
||||
commandCounter = this.storageService.getInteger(CommandsHistory.PREF_KEY_COUNTER, StorageScope.GLOBAL, commandCounter);
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
this.configurationService.onDidChangeConfiguration(e => this.updateConfiguration());
|
||||
once(this.lifecycleService.onShutdown)(reason => this.save());
|
||||
once(this.storageService.onWillSaveState)(() => this.saveState());
|
||||
}
|
||||
|
||||
private save(): void {
|
||||
private saveState(): void {
|
||||
const serializedCache: ISerializedCommandHistory = { usesLRU: true, entries: [] };
|
||||
commandHistory.forEach((value, key) => serializedCache.entries.push({ key, value }));
|
||||
|
||||
this.storageService.store(CommandsHistory.PREF_KEY_CACHE, JSON.stringify(serializedCache));
|
||||
this.storageService.store(CommandsHistory.PREF_KEY_COUNTER, commandCounter);
|
||||
this.storageService.store(CommandsHistory.PREF_KEY_CACHE, JSON.stringify(serializedCache), StorageScope.GLOBAL);
|
||||
this.storageService.store(CommandsHistory.PREF_KEY_COUNTER, commandCounter, StorageScope.GLOBAL);
|
||||
}
|
||||
|
||||
push(commandId: string): void {
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue