Skip to content

Commit

Permalink
Editor Preview widget implementation.
Browse files Browse the repository at this point in the history
Signed-off-by: Casey Flynn <caseyflynn@google.com>
  • Loading branch information
caseyflynn-google authored and svenefftinge committed Nov 20, 2018
1 parent 8e9e4e7 commit 00e7d9d
Show file tree
Hide file tree
Showing 23 changed files with 947 additions and 3 deletions.
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ cache:
- packages/cpp/node_modules
- packages/debug-nodejs/node_modules
- packages/debug/node_modules
- packages/editor-preview/node_modules
- packages/editor/node_modules
- packages/editorconfig/node_modules
- packages/extension-manager/node_modules
Expand Down
1 change: 1 addition & 0 deletions examples/browser/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"@theia/debug": "^0.3.16",
"@theia/debug-nodejs": "^0.3.16",
"@theia/editor": "^0.3.16",
"@theia/editor-preview": "^0.3.16",
"@theia/editorconfig": "^0.3.16",
"@theia/extension-manager": "^0.3.16",
"@theia/file-search": "^0.3.16",
Expand Down
1 change: 1 addition & 0 deletions examples/electron/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"@theia/debug": "^0.3.16",
"@theia/debug-nodejs": "^0.3.16",
"@theia/editor": "^0.3.16",
"@theia/editor-preview": "^0.3.16",
"@theia/editorconfig": "^0.3.16",
"@theia/extension-manager": "^0.3.16",
"@theia/file-search": "^0.3.16",
Expand Down
52 changes: 52 additions & 0 deletions packages/core/src/browser/core-preferences.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/********************************************************************************
* Copyright (C) 2018 Google 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 { interfaces } from 'inversify';
import { createPreferenceProxy, PreferenceProxy, PreferenceService, PreferenceContribution, PreferenceSchema } from './preferences';

export const corePreferenceSchema: PreferenceSchema = {
'type': 'object',
properties: {
'list.openMode': {
type: 'string',
enum: [
'singleClick',
'doubleClick'
],
default: 'singleClick',
description: 'Controls how to open items in trees using the mouse.'
}
}
};

export interface CoreConfiguration {
'list.openMode': string;
}

export const CorePreferences = Symbol('CorePreferences');
export type CorePreferences = PreferenceProxy<CoreConfiguration>;

export function createCorePreferences(preferences: PreferenceService): CorePreferences {
return createPreferenceProxy(preferences, corePreferenceSchema);
}

export function bindCorePreferences(bind: interfaces.Bind): void {
bind(CorePreferences).toDynamicValue(ctx => {
const preferences = ctx.container.get<PreferenceService>(PreferenceService);
return createCorePreferences(preferences);
}).inSingletonScope();
bind(PreferenceContribution).toConstantValue({ schema: corePreferenceSchema});
}
4 changes: 4 additions & 0 deletions packages/core/src/browser/frontend-application-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ import { EnvVariablesServer, envVariablesPath } from './../common/env-variables'
import { FrontendApplicationStateService } from './frontend-application-state';
import { JsonSchemaStore } from './json-schema-store';
import { TabBarToolbarRegistry, TabBarToolbarContribution, TabBarToolbarFactory, TabBarToolbar } from './shell/tab-bar-toolbar';
import { WidgetTracker } from './widgets';
import { bindCorePreferences } from './core-preferences';

export const frontendApplicationModule = new ContainerModule((bind, unbind, isBound, rebind) => {
const themeService = ThemeService.get();
Expand Down Expand Up @@ -215,4 +217,6 @@ export const frontendApplicationModule = new ContainerModule((bind, unbind, isBo
[CommandContribution, MenuContribution].forEach(serviceIdentifier =>
bind(serviceIdentifier).toService(ThemingCommandContribution),
);

bindCorePreferences(bind);
});
1 change: 1 addition & 0 deletions packages/core/src/browser/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,4 @@ export * from './label-provider';
export * from './widget-open-handler';
export * from './navigatable';
export * from './diff-uris';
export * from './core-preferences';
2 changes: 1 addition & 1 deletion packages/core/src/browser/opener-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ export interface OpenerService {
}

export async function open(openerService: OpenerService, uri: URI, options?: OpenerOptions): Promise<object | undefined> {
const opener = await openerService.getOpener(uri);
const opener = await openerService.getOpener(uri, options);
return opener.open(uri, options);
}

Expand Down
6 changes: 5 additions & 1 deletion packages/core/src/browser/shell/application-shell.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import {
} from '@phosphor/widgets';
import { Message } from '@phosphor/messaging';
import { IDragEvent } from '@phosphor/dragdrop';
import { RecursivePartial, MaybePromise } from '../../common';
import { RecursivePartial, MaybePromise, Event as CommonEvent } from '../../common';
import { Saveable } from '../saveable';
import { StatusBarImpl, StatusBarEntry, StatusBarAlignment } from '../status-bar/status-bar';
import { TheiaDockPanel } from './theia-dock-panel';
Expand Down Expand Up @@ -807,6 +807,9 @@ export class ApplicationShell extends Widget {
this.tracker.add(toTrack);
Saveable.apply(toTrack);
}
if (widget.onDidChangeTrackableWidgets) {
widget.onDidChangeTrackableWidgets(widgets => widgets.forEach(w => this.track(w)));
}
}
}

Expand Down Expand Up @@ -1435,6 +1438,7 @@ export namespace ApplicationShell {
*/
export interface TrackableWidgetProvider {
getTrackableWidgets(): MaybePromise<Widget[]>
readonly onDidChangeTrackableWidgets?: CommonEvent<Widget[]>
}

export namespace TrackableWidgetProvider {
Expand Down
7 changes: 7 additions & 0 deletions packages/editor-preview/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Theia - Editor Preview Extension

See [here](https://www.theia-ide.org/doc/index.html) for a detailed documentation.

## License
- [Eclipse Public License 2.0](http://www.eclipse.org/legal/epl-2.0/)
- [一 (Secondary) GNU General Public License, version 2 with the GNU Classpath Exception](https://projects.eclipse.org/license/secondary-gpl-2.0-cp)
10 changes: 10 additions & 0 deletions packages/editor-preview/compile.tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"extends": "../../configs/base.tsconfig",
"compilerOptions": {
"rootDir": "src",
"outDir": "lib"
},
"include": [
"src"
]
}
48 changes: 48 additions & 0 deletions packages/editor-preview/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
{
"name": "@theia/editor-preview",
"version": "0.3.16",
"description": "Theia - Editor Preview Extension",
"dependencies": {
"@theia/core": "^0.3.16",
"@theia/editor": "^0.3.16",
"@theia/navigator": "^0.3.16"
},
"publishConfig": {
"access": "public"
},
"theiaExtensions": [
{
"frontend": "lib/browser/editor-preview-frontend-module"
}
],
"keywords": [
"theia-extension"
],
"license": "EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0",
"repository": {
"type": "git",
"url": "https://github.com/theia-ide/theia.git"
},
"bugs": {
"url": "https://github.com/theia-ide/theia/issues"
},
"homepage": "https://github.com/theia-ide/theia",
"files": [
"lib",
"src"
],
"scripts": {
"prepare": "yarn run clean && yarn run build",
"clean": "theiaext clean",
"build": "theiaext build",
"watch": "theiaext watch",
"test": "theiaext test",
"docs": "theiaext docs"
},
"devDependencies": {
"@theia/ext-scripts": "^0.3.16"
},
"nyc": {
"extends": "../../configs/nyc.json"
}
}
85 changes: 85 additions & 0 deletions packages/editor-preview/src/browser/editor-preview-factory.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/********************************************************************************
* Copyright (C) 2018 Google 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
********************************************************************************/

// This file is strictly for testing; disable no-any so we can mock out objects not under test
// disable no-unused-expression for chai.
// tslint:disable:no-any no-unused-expression

import {enableJSDOM} from '@theia/core/lib/browser/test/jsdom';
const disableJsDom = enableJSDOM();

import { Container } from 'inversify';
import { WidgetFactory, WidgetManager } from '@theia/core/lib/browser';
import { EditorWidget, EditorManager } from '@theia/editor/lib/browser';
import { EditorPreviewWidgetFactory, EditorPreviewWidgetOptions } from './editor-preview-factory';
import { expect } from 'chai';
import * as sinon from 'sinon';
import * as previewFrontEndModule from './editor-preview-frontend-module';

const mockEditorWidget = sinon.createStubInstance(EditorWidget);
const mockEditorManager = {
getOrCreateByUri: () => {}
};
const getOrCreateStub = sinon.stub(mockEditorManager, 'getOrCreateByUri').returns(mockEditorWidget);

let testContainer: Container;

before(() => {
testContainer = new Container();
// Mock out injected dependencies.
testContainer.bind(WidgetManager).toDynamicValue(ctx => ({} as any));
testContainer.bind(EditorManager).toDynamicValue(ctx => (mockEditorManager as any));
testContainer.load(previewFrontEndModule.default);
});

after(() => {
disableJsDom();
});

describe('editor-preview-factory', () => {
let widgetFactory: EditorPreviewWidgetFactory;

beforeEach(() => {
widgetFactory = testContainer.get<EditorPreviewWidgetFactory>(WidgetFactory);
getOrCreateStub.resetHistory();
});

it('should create a new editor widget via editor manager if same session', async () => {
const opts: EditorPreviewWidgetOptions = {
kind: 'editor-preview-widget',
id: '1',
initialUri: 'file://a/b/c',
session: EditorPreviewWidgetFactory.sessionId
};
const widget = await widgetFactory.createWidget(opts);
expect((mockEditorManager.getOrCreateByUri as sinon.SinonStub).calledOnce).to.be.true;
expect(widget.id).to.equal(opts.id);
expect(widget.editorWidget).to.equal(mockEditorWidget);
});

it('should not create a widget if restoring from previous session', async () => {
const opts: EditorPreviewWidgetOptions = {
kind: 'editor-preview-widget',
id: '2',
initialUri: 'file://a/b/c',
session: 'session-mismatch'
};
const widget = await widgetFactory.createWidget(opts);
expect((mockEditorManager.getOrCreateByUri as sinon.SinonStub).called).to.be.false;
expect(widget.id).to.equal(opts.id);
expect(widget.editorWidget).to.be.undefined;
});
});
61 changes: 61 additions & 0 deletions packages/editor-preview/src/browser/editor-preview-factory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/********************************************************************************
* Copyright (C) 2018 Google 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 URI from '@theia/core/lib/common/uri';
import { WidgetFactory, WidgetManager } from '@theia/core/lib/browser';
import { MaybePromise } from '@theia/core/lib/common/types';
import { EditorPreviewWidget } from './editor-preview-widget';
import { inject, injectable } from 'inversify';
import { EditorManager } from '@theia/editor/lib/browser';
import { UUID } from '@phosphor/coreutils';

export interface EditorPreviewWidgetOptions {
kind: 'editor-preview-widget',
id: string,
initialUri: string,
session: string,
}

@injectable()
export class EditorPreviewWidgetFactory implements WidgetFactory {

static ID: string = 'editor-preview-widget';

static generateUniqueId(): string {
return UUID.uuid4();
}

readonly id = EditorPreviewWidgetFactory.ID;
static readonly sessionId = EditorPreviewWidgetFactory.generateUniqueId();

@inject(WidgetManager)
protected readonly widgetManager: WidgetManager;

@inject(EditorManager)
protected readonly editorManager: EditorManager;

createWidget(options: EditorPreviewWidgetOptions): MaybePromise<EditorPreviewWidget> {
return this.doCreate(options);
}

protected async doCreate(options: EditorPreviewWidgetOptions): Promise<EditorPreviewWidget> {
const widget = (options.session === EditorPreviewWidgetFactory.sessionId) ?
await this.editorManager.getOrCreateByUri(new URI(options.initialUri)) : undefined;
const previewWidget = new EditorPreviewWidget(this.widgetManager, widget);
previewWidget.id = options.id;
return previewWidget;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/********************************************************************************
* Copyright (C) 2018 Google 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 Liffcense, 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 { OpenHandler, WidgetFactory } from '@theia/core/lib/browser';
import {ContainerModule} from 'inversify';
import { EditorPreviewManager } from './editor-preview-manager';
import { EditorPreviewWidgetFactory } from './editor-preview-factory';
import { bindEditorPreviewPreferences } from './editor-preview-preferences';

import '../../src/browser/style/index.css';

export default new ContainerModule(bind => {

bind(WidgetFactory).to(EditorPreviewWidgetFactory).inSingletonScope();

bind(EditorPreviewManager).toSelf().inSingletonScope();
bind(OpenHandler).to(EditorPreviewManager);

bindEditorPreviewPreferences(bind);
});
Loading

0 comments on commit 00e7d9d

Please sign in to comment.