Skip to content

Commit

Permalink
[extension-manager] preserve extension widgets between sessions
Browse files Browse the repository at this point in the history
Signed-off-by: Anton Kosiakov <anton.kosyakov@typefox.io>
  • Loading branch information
akosyakov committed Oct 12, 2017
1 parent 06626ec commit 6b4294d
Show file tree
Hide file tree
Showing 10 changed files with 159 additions and 90 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ jdt.ls-java-project
lerna-debug.log
.nyc_output
coverage
errorShots
examples/*/src-gen
examples/*/webpack.config.js
.browser_modules
Expand Down
4 changes: 3 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@
"editor.formatOnSave": true,
"search.exclude": {
"**/node_modules": true,
"**/lib": true
"**/lib": true,
"**/coverage": true
},
"lcov.path": [
"packages/core/coverage/lcov.info",
"packages/cpp/coverage/lcov.info",
"packages/editor/coverage/lcov.info",
"packages/filesystem/coverage/lcov.info",
"packages/extension-manager/coverage/lcov.info",
"packages/go/coverage/lcov.info",
"packages/java/coverage/lcov.info",
"packages/languages/coverage/lcov.info",
Expand Down

This file was deleted.

45 changes: 22 additions & 23 deletions packages/extension-manager/src/browser/extension-detail-widget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,23 @@ import { h } from '@phosphor/virtualdom/lib';

export class ExtensionDetailWidget extends VirtualWidget {

constructor(id: string,
protected resolvedExtension: ResolvedExtension) {
constructor(
protected readonly resolvedExtension: ResolvedExtension
) {
super();
this.id = id;
this.addClass('theia-extension-detail');
this.title.closable = true;
this.title.label = resolvedExtension.name;

resolvedExtension.resolve().then(rex => {
this.update();
});


resolvedExtension.onDidChange(change => {
if (change.name === this.resolvedExtension.name) {
this.update();
}
});
this.update();
}

onActivateRequest(msg: Message): void {
super.onActivateRequest(msg);
this.update();
}

protected onUpdateRequest(msg: Message): void {
Expand All @@ -44,25 +43,25 @@ export class ExtensionDetailWidget extends VirtualWidget {
protected render(): h.Child {
const r = this.resolvedExtension;

const name = h.h2({className: 'extensionName'}, r.name);
const extversion = h.div({className: 'extensionVersion'}, r.version);
const author = h.div({className: 'extensionAuthor'}, r.author);
const titleInfo = h.div({className: 'extensionSubtitle'}, author, extversion);
const titleContainer = h.div({className: 'extensionTitleContainer'},
const name = h.h2({ className: 'extensionName' }, r.name);
const extversion = h.div({ className: 'extensionVersion' }, r.version);
const author = h.div({ className: 'extensionAuthor' }, r.author);
const titleInfo = h.div({ className: 'extensionSubtitle' }, author, extversion);
const titleContainer = h.div({ className: 'extensionTitleContainer' },
name, titleInfo);

const description = h.div({className: 'extensionDescription'}, r.description);
const description = h.div({ className: 'extensionDescription' }, r.description);

const buttonRow = h.div({className: 'extensionButtonRow'},
const buttonRow = h.div({ className: 'extensionButtonRow' },
VirtualRenderer.flatten(this.createButtons(this.resolvedExtension)));

const buttonContainer = h.div({className: 'extensionButtonContainer'}, buttonRow);
const buttonContainer = h.div({ className: 'extensionButtonContainer' }, buttonRow);

const headerContainer = h.div({className: 'extensionHeaderContainer'},
const headerContainer = h.div({ className: 'extensionHeaderContainer' },
titleContainer, description, buttonContainer);

const documentation = h.div({className: 'extensionDocumentation', id: this.id + 'Doc'}, '');
const docContainer = h.div({className: 'extensionDocContainer flexcontainer'}, documentation);
const documentation = h.div({ className: 'extensionDocumentation', id: this.id + 'Doc' }, '');
const docContainer = h.div({ className: 'extensionDocContainer flexcontainer' }, documentation);

return [headerContainer, docContainer];
}
Expand All @@ -74,7 +73,7 @@ export class ExtensionDetailWidget extends VirtualWidget {
btnLabel = 'Uninstall';
}

const faEl = h.i({className: 'fa fa-spinner fa-pulse fa-fw'});
const faEl = h.i({ className: 'fa fa-spinner fa-pulse fa-fw' });
const content = extension.busy ? faEl : btnLabel;

buttonArr.push(h.div({
Expand Down Expand Up @@ -107,4 +106,4 @@ export class ExtensionDetailWidget extends VirtualWidget {
return buttonArr;
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@
*/

import { ContainerModule } from 'inversify';
import { FrontendApplicationContribution, WebSocketConnectionProvider } from '@theia/core/lib/browser';
import { FrontendApplicationContribution, WebSocketConnectionProvider, WidgetFactory, OpenHandler } from '@theia/core/lib/browser';
import { ExtensionServer, extensionPath } from '../common/extension-protocol';
import { ExtensionManager } from '../common';
import { ExtensionContribution } from './extension-contribution';
import { ExtensionWidget } from './extension-widget';
import { ExtensionDetailWidgetService } from './extension-detail-widget-service';
import { ExtensionWidgetFactory } from './extension-widget-factory';
import { ExtensionOpenHandler } from './extension-open-handler';

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

Expand All @@ -23,6 +24,11 @@ export default new ContainerModule(bind => {
bind(ExtensionManager).toSelf().inSingletonScope();

bind(FrontendApplicationContribution).to(ExtensionContribution).inSingletonScope();
bind(ExtensionDetailWidgetService).toSelf().inSingletonScope();
bind(ExtensionWidget).toSelf().inSingletonScope();

bind(ExtensionWidgetFactory).toSelf().inSingletonScope();
bind(WidgetFactory).toDynamicValue(ctx => ctx.container.get(ExtensionWidgetFactory)).inSingletonScope();

bind(ExtensionOpenHandler).toSelf().inSingletonScope();
bind(OpenHandler).toDynamicValue(ctx => ctx.container.get(ExtensionOpenHandler)).inSingletonScope();
});
44 changes: 44 additions & 0 deletions packages/extension-manager/src/browser/extension-open-handler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* Copyright (C) 2017 TypeFox and others.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*/

import { injectable, inject } from "inversify";
import URI from "@theia/core/lib/common/uri";
import { OpenHandler, WidgetManager, FrontendApplication } from "@theia/core/lib/browser";
import { ExtensionUri } from "./extension-uri";
import { ExtensionWidgetOptions } from './extension-widget-factory';
import { ExtensionDetailWidget } from './extension-detail-widget';

@injectable()
export class ExtensionOpenHandler implements OpenHandler {

readonly id = ExtensionUri.scheme;

constructor(
@inject(FrontendApplication) protected readonly app: FrontendApplication,
@inject(WidgetManager) protected readonly widgetManager: WidgetManager
) { }

canHandle(uri: URI): number {
try {
ExtensionUri.toExtensionName(uri);
return 500;
} catch {
return 0;
}
}

async open(uri: URI): Promise<ExtensionDetailWidget> {
const options: ExtensionWidgetOptions = {
name: ExtensionUri.toExtensionName(uri)
};
const widget = await this.widgetManager.getOrCreateWidget<ExtensionDetailWidget>(ExtensionUri.scheme, options);
this.app.shell.addToMainArea(widget);
this.app.shell.activateMain(widget.id);
return widget;
}

}
21 changes: 21 additions & 0 deletions packages/extension-manager/src/browser/extension-uri.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* Copyright (C) 2017 TypeFox and others.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*/

import URI from "@theia/core/lib/common/uri";

export namespace ExtensionUri {
export const scheme = 'extension';
export function toUri(extensionName: string): URI {
return new URI('').withScheme(scheme).withFragment(extensionName);
}
export function toExtensionName(uri: URI): string {
if (uri.scheme === scheme) {
return uri.fragment;
}
throw new Error('The given uri is not an extension URI, uri: ' + uri);
}
}
37 changes: 37 additions & 0 deletions packages/extension-manager/src/browser/extension-widget-factory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright (C) 2017 TypeFox and others.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*/

import { injectable, inject } from "inversify";
import { WidgetFactory, FrontendApplication } from "@theia/core/lib/browser";
import { ExtensionManager } from '../common';
import { ExtensionUri } from "./extension-uri";
import { ExtensionDetailWidget } from './extension-detail-widget';

export class ExtensionWidgetOptions {
readonly name: string;
}

@injectable()
export class ExtensionWidgetFactory implements WidgetFactory {

readonly id = ExtensionUri.scheme;

constructor(
@inject(FrontendApplication) protected readonly app: FrontendApplication,
@inject(ExtensionManager) protected readonly extensionManager: ExtensionManager
) { }

async createWidget(options: ExtensionWidgetOptions): Promise<ExtensionDetailWidget> {
const extension = await this.extensionManager.resolve(options.name);
const widget = new ExtensionDetailWidget(extension);
widget.id = 'extension:' + options.name;
widget.title.closable = true;
widget.title.label = options.name;
return widget;
}

}
19 changes: 8 additions & 11 deletions packages/extension-manager/src/browser/extension-widget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*/

import { Extension, ExtensionManager } from '../common';
import { injectable, inject } from 'inversify';
import { VirtualWidget, VirtualRenderer } from '@theia/core/lib/browser';
import { h, VirtualNode } from '@phosphor/virtualdom/lib';
import { DisposableCollection, Disposable } from '@theia/core';
import { ExtensionDetailWidgetService } from './extension-detail-widget-service';
import { VirtualWidget, VirtualRenderer, OpenerService, open } from '@theia/core/lib/browser';
import { Extension, ExtensionManager } from '../common';
import { ExtensionUri } from './extension-uri';

@injectable()
export class ExtensionWidget extends VirtualWidget {
Expand All @@ -21,8 +21,10 @@ export class ExtensionWidget extends VirtualWidget {
protected readonly toDisposedOnFetch = new DisposableCollection();
protected ready = false;

constructor( @inject(ExtensionManager) protected readonly extensionManager: ExtensionManager,
@inject(ExtensionDetailWidgetService) protected readonly detailWidgetService: ExtensionDetailWidgetService) {
constructor(
@inject(ExtensionManager) protected readonly extensionManager: ExtensionManager,
@inject(OpenerService) protected readonly openerService: OpenerService
) {
super();
this.id = 'extensions';
this.title.label = 'Extensions';
Expand Down Expand Up @@ -130,12 +132,7 @@ export class ExtensionWidget extends VirtualWidget {

const container = h.div({
className: 'extensionHeaderContainer',
onclick: event => {
extension.resolve().then(rawExt => {
this.detailWidgetService.openOrFocusDetailWidget(rawExt);
return false;
});
}
onclick: () => open(this.openerService, ExtensionUri.toUri(extension.name))
}, leftColumn);
return container;
}
Expand Down
17 changes: 14 additions & 3 deletions packages/extension-manager/src/common/extension-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ export class Extension extends protocol.Extension {
protected readonly onDidChangedEmitter = new Emitter<ExtensionChange>();

constructor(extension: protocol.Extension,
protected readonly server: protocol.ExtensionServer,
protected readonly manager: ExtensionManager) {
protected readonly server: protocol.ExtensionServer,
protected readonly manager: ExtensionManager) {
super();
Object.assign(this, extension);
manager.onDidChange(change => {
Expand Down Expand Up @@ -96,7 +96,9 @@ export class ExtensionManager implements Disposable {
protected readonly onDidStopInstallationEmitter = new Emitter<protocol.DidStopInstallationParam>();
protected readonly toDispose = new DisposableCollection();

constructor(@inject(protocol.ExtensionServer) protected readonly server: protocol.ExtensionServer) {
constructor(
@inject(protocol.ExtensionServer) protected readonly server: protocol.ExtensionServer
) {
this.toDispose.push(server);
this.toDispose.push(this.onChangedEmitter);
this.toDispose.push(this.onWillStartInstallationEmitter);
Expand All @@ -112,6 +114,15 @@ export class ExtensionManager implements Disposable {
this.toDispose.dispose();
}

/**
* Resolve the detailed extension for the given name.
*/
async resolve(name: string): Promise<ResolvedExtension> {
const raw = await this.server.resolve(name);
const extension = new Extension(raw, this.server, this);
return extension as ResolvedExtension;
}

/**
* List installed extensions if the given query is undefined or empty.
* Otherwise look up extensions from the repository matching the given query
Expand Down

0 comments on commit 6b4294d

Please sign in to comment.