Skip to content

Commit b1c4f65

Browse files
authored
Add GLSP diagram editor extension (#153)
* Add GLSP diagram editor extension This extension can generate diagram editors based on https://github.com/eclipse-glsp/glsp-examples Co-authored-by: Max Elia <[email protected]> Co-authored-by: Florian Gareis <[email protected]> Closes #110
1 parent cc9d830 commit b1c4f65

File tree

4 files changed

+530
-55
lines changed

4 files changed

+530
-55
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ yo theia-extension --help
5151
| `tree-editor` | Creates a tree editor extension | [readme](https://github.com/eclipse-theia/generator-theia-extension/blob/master/templates/tree-editor/README.md) |
5252
| `empty` | Creates a simple, minimal extension | [readme](https://github.com/eclipse-theia/generator-theia-extension/blob/master/templates/empty/README.md) |
5353
| `backend` | Creates a backend communication extension | [readme](https://github.com/eclipse-theia/generator-theia-extension/blob/master/templates/backend/README.md) |
54+
| `diagram-editor` | Creates a diagram editor extension | [readme](https://github.com/eclipse-glsp/glsp-examples/blob/master/README.md) |
5455

5556

5657

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616
"author": "TypeFox",
1717
"license": "EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0",
1818
"dependencies": {
19+
"fs-extra": "^10.0.0",
20+
"request": "^2.88.2",
21+
"tar": "^6.1.1",
1922
"yeoman-generator": "^4.0.2"
2023
},
2124
"devDependencies": {

src/app/index.ts

Lines changed: 138 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,25 @@
1616

1717
import path = require('path');
1818
import Base = require('yeoman-generator');
19+
const request = require('request');
20+
const tar = require('tar');
21+
const fs = require('fs-extra');
22+
23+
const glspExamplesRepositoryTag = "generator-latest";
1924

2025
enum ExtensionType {
2126
HelloWorld = 'hello-world',
2227
Widget = 'widget',
2328
LabelProvider = 'labelprovider',
2429
TreeEditor = 'tree-editor',
2530
Empty = 'empty',
26-
Backend = 'backend'
31+
Backend = 'backend',
32+
DiagramEditor = 'diagram-editor'
33+
}
34+
35+
enum TemplateType {
36+
Java = 'java',
37+
Node = 'node',
2738
}
2839

2940
module.exports = class TheiaExtension extends Base {
@@ -34,6 +45,7 @@ module.exports = class TheiaExtension extends Base {
3445
license: string
3546
extensionName: string
3647
extensionType: string
48+
templateType: string
3749
unscopedExtensionName: string
3850
githubURL: string
3951
extensionPrefix: string
@@ -156,18 +168,42 @@ module.exports = class TheiaExtension extends Base {
156168
{ value: ExtensionType.LabelProvider, name: 'LabelProvider' },
157169
{ value: ExtensionType.TreeEditor, name: 'TreeEditor' },
158170
{ value: ExtensionType.Backend, name: 'Backend Communication' },
159-
{ value: ExtensionType.Empty, name: 'Empty' }
171+
{ value: ExtensionType.Empty, name: 'Empty' },
172+
{ value: ExtensionType.DiagramEditor, name: 'DiagramEditor' }
160173
]
161174
});
162175
(this.options as any).extensionType = answer.type;
176+
177+
if (answer.type === ExtensionType.DiagramEditor) {
178+
const answer = await this.prompt({
179+
type: 'list',
180+
name: 'backend',
181+
message: 'Which GLSP backend do you want to use, i.e. in which language do you prefer to develop your GLSP server?',
182+
choices: [
183+
{ value: TemplateType.Java, name: 'Java (requires maven!)' },
184+
{ value: TemplateType.Node, name: 'Node (TypeScript)' },
185+
]
186+
});
187+
let template = answer.backend;
188+
189+
(this.options as any).templateType = template;
190+
191+
if(template === TemplateType.Java) {
192+
this.log('\x1b[32m%s\x1b[0m', 'The template will use an EMF source model on the server and generate a Theia extension ✓')
193+
}
194+
if(template === TemplateType.Node) {
195+
this.log('\x1b[32m%s\x1b[0m', 'The template will use a JSON based source model, node as a server and generate a Theia extension ✓')
196+
}
197+
}
163198
}
164199

165200
let extensionName = (this.options as any).extensionName;
166-
if (!extensionName) {
201+
// extensionName is not used within the DiagramEditor
202+
if (!extensionName && this.options.extensionType !== ExtensionType.DiagramEditor) {
167203
const answer = await this.prompt({
168204
type: 'input',
169205
name: 'name',
170-
message: "The extension's name",
206+
message: 'The extension\'s name',
171207
default: (this.options as any).extensionType
172208
});
173209
(this.options as any).extensionName = answer.name;
@@ -177,12 +213,18 @@ module.exports = class TheiaExtension extends Base {
177213
configuring() {
178214
const options = this.options as any
179215
const extensionName = options.extensionName as string
180-
const unscopedExtensionName = extensionName[0] === '@' ?
181-
extensionName.substring(extensionName.indexOf('/') + 1) :
182-
extensionName;
183-
const extensionPath = path.normalize(unscopedExtensionName).replace('/', '-');
184-
const extensionPrefix = extensionPath.split('-').map(name => this._capitalize(name)).join('');
216+
let unscopedExtensionName = ''
217+
let extensionPath = ''
218+
let extensionPrefix = ''
219+
if(extensionName) {
220+
unscopedExtensionName = extensionName[0] === '@' ?
221+
extensionName.substring(extensionName.indexOf('/') + 1) :
222+
extensionName;
223+
extensionPath = path.normalize(unscopedExtensionName).replace('/', '-');
224+
extensionPrefix = extensionPath.split('-').map(name => this._capitalize(name)).join('');
225+
}
185226
const extensionType = options.extensionType;
227+
const templateType = options.templateType;
186228
const githubURL = options.githubURL;
187229
this.log(extensionPrefix);
188230
this.params = {
@@ -192,6 +234,7 @@ module.exports = class TheiaExtension extends Base {
192234
extensionPath,
193235
extensionPrefix,
194236
extensionType,
237+
templateType,
195238
githubURL,
196239
theiaVersion: options["theia-version"],
197240
lernaVersion: options["lerna-version"],
@@ -210,11 +253,13 @@ module.exports = class TheiaExtension extends Base {
210253
this.params.containsTests = true;
211254
}
212255
options.params = this.params
213-
if (!options.standalone) {
214-
if ((options).browser)
256+
if (!options.standalone && this.params.extensionType !== ExtensionType.DiagramEditor) {
257+
if (options.browser) {
215258
this.composeWith(require.resolve('../browser'), this.options);
216-
if ((options).electron)
259+
}
260+
if (options.electron) {
217261
this.composeWith(require.resolve('../electron'), this.options);
262+
}
218263
}
219264
if (options.standalone) {
220265
options.skipInstall = true;
@@ -223,46 +268,48 @@ module.exports = class TheiaExtension extends Base {
223268
}
224269

225270
writing() {
226-
if (!this.options.standalone) {
227-
/** common templates */
228-
this.fs.copyTpl(
229-
this.templatePath('root-package.json'),
230-
this.destinationPath('package.json'),
231-
{ params: this.params }
232-
);
271+
if (this.params.extensionType !== ExtensionType.DiagramEditor) {
272+
if (!this.options.standalone) {
273+
/** common templates */
274+
this.fs.copyTpl(
275+
this.templatePath('root-package.json'),
276+
this.destinationPath('package.json'),
277+
{ params: this.params }
278+
);
279+
this.fs.copyTpl(
280+
this.templatePath('lerna.json'),
281+
this.destinationPath('lerna.json'),
282+
{ params: this.params }
283+
);
284+
this.fs.copyTpl(
285+
this.templatePath('gitignore'),
286+
this.destinationPath('.gitignore'),
287+
{ params: this.params }
288+
);
289+
this.fs.copyTpl(
290+
this.templatePath('README.md'),
291+
this.destinationPath('README.md'),
292+
{ params: this.params }
293+
);
294+
if (this.params.vscode) {
295+
this.fs.copyTpl(
296+
this.templatePath('launch.json'),
297+
this.destinationPath('.vscode/launch.json'),
298+
{ params: this.params }
299+
);
300+
}
301+
}
233302
this.fs.copyTpl(
234-
this.templatePath('lerna.json'),
235-
this.destinationPath('lerna.json'),
303+
this.templatePath('extension-package.json'),
304+
this.extensionPath('package.json'),
236305
{ params: this.params }
237306
);
238307
this.fs.copyTpl(
239-
this.templatePath('gitignore'),
240-
this.destinationPath('.gitignore'),
308+
this.templatePath('tsconfig.json'),
309+
this.extensionPath('tsconfig.json'),
241310
{ params: this.params }
242311
);
243-
this.fs.copyTpl(
244-
this.templatePath('README.md'),
245-
this.destinationPath('README.md'),
246-
{ params: this.params }
247-
)
248-
if (this.params.vscode) {
249-
this.fs.copyTpl(
250-
this.templatePath('launch.json'),
251-
this.destinationPath('.vscode/launch.json'),
252-
{ params: this.params }
253-
)
254-
}
255312
}
256-
this.fs.copyTpl(
257-
this.templatePath('extension-package.json'),
258-
this.extensionPath('package.json'),
259-
{ params: this.params }
260-
);
261-
this.fs.copyTpl(
262-
this.templatePath('tsconfig.json'),
263-
this.extensionPath('tsconfig.json'),
264-
{ params: this.params }
265-
);
266313

267314
/** hello-world */
268315
if (this.params.extensionType === ExtensionType.HelloWorld) {
@@ -339,7 +386,7 @@ module.exports = class TheiaExtension extends Base {
339386
this.extensionPath(`configs/jest.config.ts`),
340387
{ params: this.params }
341388
);
342-
}
389+
}
343390

344391
/** backend */
345392
if (this.params.extensionType === ExtensionType.Backend) {
@@ -423,6 +470,27 @@ module.exports = class TheiaExtension extends Base {
423470
);
424471
}
425472

473+
/** DiagramEditor */
474+
if (this.params.extensionType === ExtensionType.DiagramEditor) {
475+
const baseDir = `./glsp-examples-${glspExamplesRepositoryTag}`;
476+
let templatePath = '';
477+
if(this.params.templateType == TemplateType.Java) {
478+
templatePath = '/project-templates/java-emf-theia';
479+
} else if (this.params.templateType == TemplateType.Node) {
480+
templatePath = '/project-templates/node-json-theia';
481+
} else {
482+
return;
483+
}
484+
485+
const done = this.async();
486+
request.get(`https://github.com/eclipse-glsp/glsp-examples/archive/refs/tags/${glspExamplesRepositoryTag}.tar.gz`).pipe(tar.x().on('close',() => {
487+
fs.copy(baseDir+'/README.md', './README.md');
488+
fs.copy(baseDir+templatePath, './').then(() => {
489+
fs.rm(baseDir, { recursive: true });
490+
done();
491+
});
492+
}));
493+
}
426494
}
427495

428496
protected extensionPath(...paths: string[]) {
@@ -431,13 +499,31 @@ module.exports = class TheiaExtension extends Base {
431499

432500
install() {
433501
if (!(this.options as any).skipInstall) {
434-
var command = this.spawnCommand('yarn', []);
502+
if (this.params.extensionType == ExtensionType.DiagramEditor) {
503+
this.log('Installing dependencies');
435504

436-
command.on('close', function(code: number){
437-
if (code !== 0 ) {
438-
process.exit(code);
439-
}
440-
})
505+
const command = this.spawnCommand('yarn', []);
506+
507+
command.on('close', (code:number) => {
508+
if (code === 0 ) {
509+
this.log(
510+
'\x1b[32m%s\x1b[0m',
511+
'\nThe DiagramEditor Example has been generated and all dependencies installed\n\nCheck the Readme to get started.'
512+
);
513+
} else {
514+
this.log('\x1b[31m%s\x1b[0m','Command "yarn" failed. Please see above for the reported error message.');
515+
process.exit(code);
516+
}
517+
});
518+
} else {
519+
const command = this.spawnCommand('yarn', []);
520+
521+
command.on('close', function(code: number){
522+
if (code !== 0 ) {
523+
process.exit(code);
524+
}
525+
})
526+
}
441527
}
442528
}
443529

0 commit comments

Comments
 (0)