Skip to content

Commit

Permalink
Merge pull request eclipsesource#64 from jonny3576/(eclipsesource#35)…
Browse files Browse the repository at this point in the history
…Ecore-File-Generation

Create a scafolding wizard to create new Ecore files / diagrams eclipsesource#35
  • Loading branch information
CamilleLetavernier authored Nov 29, 2019
2 parents 1e68385 + 6ae584d commit 78a0c3e
Show file tree
Hide file tree
Showing 83 changed files with 928 additions and 208 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
.idea
*.iml
server/ecore-backend-server/target/
server/target/
208 changes: 208 additions & 0 deletions client/theia-ecore/src/browser/EcoreCommandContribution.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
/********************************************************************************
* Copyright (c) 2019 EclipseSource 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 {
FrontendApplication,
open,
OpenerService,
QuickOpenItem,
QuickOpenMode,
QuickOpenModel,
QuickOpenOptions,
QuickOpenService
} from "@theia/core/lib/browser";
import { Command, CommandContribution, CommandRegistry } from "@theia/core/lib/common/command";
import { MessageService } from "@theia/core/lib/common/message-service";
import { ProgressService } from "@theia/core/lib/common/progress-service";
import { SelectionService } from "@theia/core/lib/common/selection-service";
import URI from "@theia/core/lib/common/uri";
import { UriAwareCommandHandler, UriCommandHandler } from "@theia/core/lib/common/uri-command-handler";
import { EDITOR_CONTEXT_MENU } from "@theia/editor/lib/browser";
import { FileDialogService } from "@theia/filesystem/lib/browser";
import { FileStat, FileSystem } from "@theia/filesystem/lib/common/filesystem";
import { NAVIGATOR_CONTEXT_MENU } from "@theia/navigator/lib/browser/navigator-contribution";
import { WorkspaceService } from "@theia/workspace/lib/browser";
import { inject, injectable } from "inversify";

import { FileGenServer } from "../common/generate-protocol";



export const EXAMPLE_NAVIGATOR = [...NAVIGATOR_CONTEXT_MENU, 'example'];
export const EXAMPLE_EDITOR = [...EDITOR_CONTEXT_MENU, 'example'];



export const NEW_ECORE_FILE: Command = {
id: 'file.newEcoreFile',
category: 'File',
label: 'New Ecore-File',
};

@injectable()
export class EcoreCommandContribution implements CommandContribution {

@inject(FileSystem) protected readonly fileSystem: FileSystem;
@inject(SelectionService) protected readonly selectionService: SelectionService;
@inject(OpenerService) protected readonly openerService: OpenerService;
@inject(FrontendApplication) protected readonly app: FrontendApplication;
@inject(MessageService) protected readonly messageService: MessageService;
@inject(FileDialogService) protected readonly fileDialogService: FileDialogService;
@inject(WorkspaceService) protected readonly workspaceService: WorkspaceService;
@inject(ProgressService) protected readonly progressService: ProgressService;
@inject(QuickOpenService) protected readonly quickOpenService: QuickOpenService;
@inject(FileGenServer) private readonly fileGenServer: FileGenServer;

registerCommands(registry: CommandRegistry): void {
registry.registerCommand(NEW_ECORE_FILE, this.newWorkspaceRootUriAwareCommandHandler({
execute: uri => this.getDirectory(uri).then(parent => {
if (parent) {
const parentUri = new URI(parent.uri);

const createEcore = (name: string, nsPrefix: string, nsURI: string) => {
if (name) {
this.fileGenServer.generateEcore(name, nsPrefix, nsURI, parentUri.path.toString()).then(() => {
const ecorePath = parentUri.toString() + '/' + name + '.ecore';
const fileUriEcore = new URI(ecorePath);
open(this.openerService, fileUriEcore);
});
}
};

const showInput = (hint: string, prefix: string, onEnter: (result: string) => void) => {
const quickOpenModel: QuickOpenModel = {
onType(lookFor: string, acceptor: (items: QuickOpenItem[]) => void): void {
const dynamicItems: QuickOpenItem[] = [];
const suffix = "Press 'Enter' to confirm or 'Escape' to cancel.";

dynamicItems.push(new SingleStringInputOpenItem(
`${prefix}: ${lookFor}. ${suffix}`,
() => onEnter(lookFor),
(mode: QuickOpenMode) => mode === QuickOpenMode.OPEN,
() => false
));

acceptor(dynamicItems);
}
};
this.quickOpenService.open(quickOpenModel, this.getOptions(hint, false));
};

showInput("Name", "Name of Ecore ", (nameOfEcore) => {
showInput("Prefix", "Prefix", (prefix) => {
showInput("URI", "URI:", (ecore_uri) => {
createEcore(nameOfEcore, prefix, ecore_uri);
});
});
});
}
})
}));
}

protected withProgress<T>(task: () => Promise<T>): Promise<T> {
return this.progressService.withProgress('', 'scm', task);
}

protected newUriAwareCommandHandler(handler: UriCommandHandler<URI>): UriAwareCommandHandler<URI> {
return new UriAwareCommandHandler(this.selectionService, handler);
}

protected newMultiUriAwareCommandHandler(handler: UriCommandHandler<URI[]>): UriAwareCommandHandler<URI[]> {
return new UriAwareCommandHandler(this.selectionService, handler, { multi: true });
}

protected newWorkspaceRootUriAwareCommandHandler(handler: UriCommandHandler<URI>): WorkspaceRootUriAwareCommandHandler {
return new WorkspaceRootUriAwareCommandHandler(this.workspaceService, this.selectionService, handler);
}

protected async getDirectory(candidate: URI): Promise<FileStat | undefined> {
const stat = await this.fileSystem.getFileStat(candidate.toString());
if (stat && stat.isDirectory) {
return stat;
}
return this.getParent(candidate);
}

protected getParent(candidate: URI): Promise<FileStat | undefined> {
return this.fileSystem.getFileStat(candidate.parent.toString());
}

private getOptions(placeholder: string, fuzzyMatchLabel: boolean = true, onClose: (canceled: boolean) => void = () => { }): QuickOpenOptions {
return QuickOpenOptions.resolve({
placeholder,
fuzzyMatchLabel,
fuzzySort: false,
onClose
});
}
}

export class WorkspaceRootUriAwareCommandHandler extends UriAwareCommandHandler<URI> {

constructor(
protected readonly workspaceService: WorkspaceService,
protected readonly selectionService: SelectionService,
protected readonly handler: UriCommandHandler<URI>
) {
super(selectionService, handler);
}

public isEnabled(...args: any[]): boolean {
return super.isEnabled(...args) && !!this.workspaceService.tryGetRoots().length;
}

public isVisible(...args: any[]): boolean {
return super.isVisible(...args) && !!this.workspaceService.tryGetRoots().length;
}

protected getUri(...args: any[]): URI | undefined {
const uri = super.getUri(...args);
// If the URI is available, return it immediately.
if (uri) {
return uri;
}
// Return the first root if available.
if (!!this.workspaceService.tryGetRoots().length) {
return new URI(this.workspaceService.tryGetRoots()[0].uri);
}
return undefined;
}
}

class SingleStringInputOpenItem extends QuickOpenItem {

constructor(
private readonly label: string,
private readonly execute: (item: QuickOpenItem) => void = () => { },
private readonly canRun: (mode: QuickOpenMode) => boolean = mode => mode === QuickOpenMode.OPEN,
private readonly canClose: (mode: QuickOpenMode) => boolean = mode => true) {

super();
}

getLabel(): string {
return this.label;
}

run(mode: QuickOpenMode): boolean {
if (!this.canRun(mode)) {
return false;
}
this.execute(this);
return this.canClose(mode);
}

}
17 changes: 14 additions & 3 deletions client/theia-ecore/src/browser/frontend-extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,28 @@
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/
import { GLSPClientContribution } from "@glsp/theia-integration/lib/browser";
import { FrontendApplicationContribution, OpenHandler, WidgetFactory } from "@theia/core/lib/browser";
import { CommandContribution } from "@theia/core";
import {
FrontendApplicationContribution,
OpenHandler,
WebSocketConnectionProvider,
WidgetFactory
} from "@theia/core/lib/browser";
import { ContainerModule, interfaces } from "inversify";
import { DiagramConfiguration, DiagramManager, DiagramManagerProvider } from "sprotty-theia/lib";

import { FILEGEN_SERVICE_PATH, FileGenServer } from "../common/generate-protocol";
import { EcoreDiagramConfiguration } from "./diagram/ecore-diagram-configuration";
import { EcoreDiagramManager } from "./diagram/ecore-diagram-manager.";
import { EcoreGLSPDiagramClient } from "./diagram/ecore-glsp-diagram-client";
import { EcoreGLSPClientContribution } from "./ecore-glsp--contribution";
import { EcoreCommandContribution } from "./EcoreCommandContribution";


export default new ContainerModule((bind: interfaces.Bind, unbind: interfaces.Unbind, isBound: interfaces.IsBound, rebind: interfaces.Rebind) => {
bind(EcoreGLSPClientContribution).toSelf().inSingletonScope();
bind(GLSPClientContribution).toService(EcoreGLSPClientContribution);

bind(EcoreGLSPDiagramClient).toSelf().inSingletonScope();

bind(DiagramConfiguration).to(EcoreDiagramConfiguration).inSingletonScope();
bind(EcoreDiagramManager).toSelf().inSingletonScope();
bind(FrontendApplicationContribution).toService(EcoreDiagramManager);
Expand All @@ -43,4 +49,9 @@ export default new ContainerModule((bind: interfaces.Bind, unbind: interfaces.Un
});
};
});
bind(CommandContribution).to(EcoreCommandContribution);
bind(FileGenServer).toDynamicValue(ctx => {
const connection = ctx.container.get(WebSocketConnectionProvider);
return connection.createProxy<FileGenServer>(FILEGEN_SERVICE_PATH);
}).inSingletonScope();
});
23 changes: 23 additions & 0 deletions client/theia-ecore/src/common/generate-protocol.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*!
* Copyright (C) 2019 EclipseSource 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 { JsonRpcServer } from "@theia/core/lib/common/messaging";

export const FileGenServer = Symbol('FileGenServer');
export const FILEGEN_SERVICE_PATH = '/services/codegen';

export interface FileGenServer extends JsonRpcServer<undefined> {
generateEcore(name: string, prefix: string, uri: string, path: string): Promise<string>
}
11 changes: 11 additions & 0 deletions client/theia-ecore/src/node/backend-extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,22 @@
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/
import { ConnectionHandler, JsonRpcConnectionHandler } from "@theia/core";
import { BackendApplicationContribution } from "@theia/core/lib/node";
import { LanguageServerContribution } from "@theia/languages/lib/node";
import { ContainerModule } from "inversify";

import { FILEGEN_SERVICE_PATH, FileGenServer } from "../common/generate-protocol";
import { EcoreFileGenServer } from "./ecore-file-generation";
import { EcoreGLServerContribution } from "./ecore-glsp-server-contribution";


export default new ContainerModule(bind => {
bind(LanguageServerContribution).to(EcoreGLServerContribution).inSingletonScope();
bind(EcoreFileGenServer).toSelf().inSingletonScope();
bind(BackendApplicationContribution).toService(EcoreFileGenServer);
bind(ConnectionHandler).toDynamicValue(ctx =>
new JsonRpcConnectionHandler(FILEGEN_SERVICE_PATH, () =>
ctx.container.get<FileGenServer>(EcoreFileGenServer))
).inSingletonScope();
});
Loading

0 comments on commit 78a0c3e

Please sign in to comment.