Skip to content

Commit

Permalink
fix #6992: expose monaco indexed db
Browse files Browse the repository at this point in the history
Signed-off-by: Anton Kosyakov <anton.kosyakov@typefox.io>
  • Loading branch information
akosyakov committed Jan 29, 2020
1 parent a0d74d3 commit 287d3ca
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 77 deletions.
84 changes: 84 additions & 0 deletions packages/monaco/src/browser/monaco-indexed-db.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/********************************************************************************
* Copyright (C) 2020 TypeFox and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the Eclipse
* Public License v. 2.0 are satisfied: GNU General Public License, version 2
* with the GNU Classpath Exception which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/

import * as idb from 'idb';
import { Disposable, DisposableCollection } from '@theia/core/lib/common/disposable';
type ThemeMix = import('./textmate/monaco-theme-registry').ThemeMix;

let _monacoDB: Promise<idb.IDBPDatabase> | undefined;
if ('indexedDB' in window) {
_monacoDB = idb.openDB('theia-monaco', 1, {
upgrade: db => {
if (!db.objectStoreNames.contains('themes')) {
db.createObjectStore('themes', { keyPath: 'id' });
}
}
});
}

export const monacoDB = _monacoDB;

export interface MonacoThemeState {
id: string,
label: string,
description?: string,
uiTheme: monaco.editor.BuiltinTheme
data: ThemeMix
}
export namespace MonacoThemeState {
export function is(state: Object | undefined): state is MonacoThemeState {
return !!state && typeof state === 'object' && 'id' in state && 'label' in state && 'uiTheme' in state && 'data' in state;
}
}

export async function getThemes(): Promise<MonacoThemeState[]> {
if (!monacoDB) {
return [];
}
const db = await monacoDB;
const result = await db.transaction('themes', 'readonly').objectStore('themes').getAll();
return result.filter(MonacoThemeState.is);
}

export function putTheme(state: MonacoThemeState): Disposable {
const toDispose = new DisposableCollection(Disposable.create(() => { /* mark as not disposed */ }));
doPutTheme(state, toDispose);
return toDispose;
}
async function doPutTheme(state: MonacoThemeState, toDispose: DisposableCollection): Promise<void> {
if (!monacoDB) {
return;
}
const db = await monacoDB;
if (toDispose.disposed) {
return;
}
const id = state.id;
await db.transaction('themes', 'readwrite').objectStore('themes').put(state);
if (toDispose.disposed) {
await deleteTheme(id);
return;
}
toDispose.push(Disposable.create(() => deleteTheme(id)));
}

export async function deleteTheme(id: string): Promise<void> {
if (!monacoDB) {
return;
}
const db = await monacoDB;
await db.transaction('themes', 'readwrite').objectStore('themes').delete(id);
}
99 changes: 22 additions & 77 deletions packages/monaco/src/browser/monaco-theming-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,15 @@

/* eslint-disable @typescript-eslint/no-explicit-any */

import * as idb from 'idb';
import { injectable, inject } from 'inversify';
import * as jsoncparser from 'jsonc-parser';
import * as plistparser from 'fast-plist';
import { ThemeService, BuiltinThemeProvider } from '@theia/core/lib/browser/theming';
import URI from '@theia/core/lib/common/uri';
import { Disposable, DisposableCollection } from '@theia/core/lib/common/disposable';
import { FileSystem } from '@theia/filesystem/lib/common/filesystem';
import { MonacoThemeRegistry, ThemeMix } from './textmate/monaco-theme-registry';
import { MonacoThemeRegistry } from './textmate/monaco-theme-registry';
import { getThemes, putTheme, MonacoThemeState } from './monaco-indexed-db';

export interface MonacoTheme {
id?: string;
Expand Down Expand Up @@ -55,17 +55,6 @@ export interface MonacoThemeJson {
includes?: { [includePath: string]: any }
}

let monacoDB: Promise<idb.IDBPDatabase> | undefined;
if ('indexedDB' in window) {
monacoDB = idb.openDB('theia-monaco', 1, {
upgrade: db => {
if (!db.objectStoreNames.contains('themes')) {
db.createObjectStore('themes', { keyPath: 'id' });
}
}
});
}

@injectable()
export class MonacoThemingService {

Expand Down Expand Up @@ -171,70 +160,40 @@ export class MonacoThemingService {
this.toUpdateUiTheme.push(Disposable.create(() => document.body.classList.remove(uiTheme)));
}

protected static doRegister(state: MonacoThemingService.MonacoThemeState): Disposable {
protected static doRegister(state: MonacoThemeState): Disposable {
const { id, label, description, uiTheme, data } = state;
const type = uiTheme === 'vs' ? 'light' : uiTheme === 'vs-dark' ? 'dark' : 'hc';
const builtInTheme = uiTheme === 'vs' ? BuiltinThemeProvider.lightCss : BuiltinThemeProvider.darkCss;
const toDispose = new DisposableCollection(ThemeService.get().register({
type,
id,
label,
description: description,
editorTheme: data.name!,
activate(): void {
builtInTheme.use();
},
deactivate(): void {
builtInTheme.unuse();
}
}));
this.storeTheme(state, toDispose);
return toDispose;
return new DisposableCollection(
ThemeService.get().register({
type,
id,
label,
description: description,
editorTheme: data.name!,
activate(): void {
builtInTheme.use();
},
deactivate(): void {
builtInTheme.unuse();
}
}),
putTheme(state)
);
}

protected static async restore(): Promise<void> {
if (!monacoDB) {
return;
}
try {
const db = await monacoDB;
const themes = await db.transaction('themes', 'readonly').objectStore('themes').getAll();
const themes = await getThemes();
for (const state of themes) {
if (MonacoThemingService.MonacoThemeState.is(state)) {
MonacoThemeRegistry.SINGLETON.setTheme(state.data.name!, state.data);
MonacoThemingService.doRegister(state);
}
MonacoThemeRegistry.SINGLETON.setTheme(state.data.name!, state.data);
MonacoThemingService.doRegister(state);
}
} catch (e) {
console.error('Failed to restore monaco themes', e);
}
}

protected static async storeTheme(state: MonacoThemingService.MonacoThemeState, toDispose: DisposableCollection): Promise<void> {
if (!monacoDB) {
return;
}
const db = await monacoDB;
if (toDispose.disposed) {
return;
}
const id = state.id;
await db.transaction('themes', 'readwrite').objectStore('themes').put(state);
if (toDispose.disposed) {
await this.cleanTheme(id);
return;
}
toDispose.push(Disposable.create(() => this.cleanTheme(id)));
}

protected static async cleanTheme(id: string): Promise<void> {
if (!monacoDB) {
return;
}
const db = await monacoDB;
await db.transaction('themes', 'readwrite').objectStore('themes').delete(id);
}

/* remove all characters that are not allowed in css */
protected static toCssSelector(str: string): string {
str = str.replace(/[^\-a-zA-Z0-9]/g, '-');
Expand All @@ -245,17 +204,3 @@ export class MonacoThemingService {
}

}
export namespace MonacoThemingService {
export interface MonacoThemeState {
id: string,
label: string,
description?: string,
uiTheme: monaco.editor.BuiltinTheme
data: ThemeMix
}
export namespace MonacoThemeState {
export function is(state: Object | undefined): state is MonacoThemeState {
return !!state && typeof state === 'object' && 'id' in state && 'label' in state && 'uiTheme' in state && 'data' in state;
}
}
}

0 comments on commit 287d3ca

Please sign in to comment.