ATL-73: Added library examples to the app.

Signed-off-by: Akos Kitta <kittaakos@typefox.io>
This commit is contained in:
Akos Kitta
2020-08-19 08:26:38 +02:00
committed by Akos Kitta
parent 1c9fcd0cdf
commit 56ff86629c
28 changed files with 2827 additions and 346 deletions

View File

@@ -2,9 +2,12 @@ import { inject, injectable, postConstruct } from 'inversify';
import { join, basename } from 'path';
import * as fs from './fs-extra';
import { FileUri } from '@theia/core/lib/node/file-uri';
import { notEmpty } from '@theia/core/lib/common/objects';
import { Sketch } from '../common/protocol/sketches-service';
import { SketchesServiceImpl } from './sketches-service-impl';
import { ExamplesService, ExampleContainer } from '../common/protocol/examples-service';
import { LibraryServiceServer, LibraryLocation, LibraryPackage } from '../common/protocol';
import { ConfigServiceImpl } from './config-service-impl';
@injectable()
export class ExamplesServiceImpl implements ExamplesService {
@@ -12,22 +15,100 @@ export class ExamplesServiceImpl implements ExamplesService {
@inject(SketchesServiceImpl)
protected readonly sketchesService: SketchesServiceImpl;
protected _all: ExampleContainer | undefined;
@inject(LibraryServiceServer)
protected readonly libraryService: LibraryServiceServer;
@inject(ConfigServiceImpl)
protected readonly configService: ConfigServiceImpl;
protected _all: ExampleContainer[] | undefined;
@postConstruct()
protected init(): void {
this.all();
this.builtIns();
}
async all(): Promise<ExampleContainer> {
async builtIns(): Promise<ExampleContainer[]> {
if (this._all) {
return this._all;
}
this._all = await this.load();
const exampleRootPath = join(__dirname, '..', '..', 'Examples');
const exampleNames = await fs.readdir(exampleRootPath);
this._all = await Promise.all(exampleNames.map(name => join(exampleRootPath, name)).map(path => this.load(path)));
return this._all;
}
protected async load(path: string = join(__dirname, '..', '..', 'Examples')): Promise<ExampleContainer> {
// TODO: decide whether it makes sense to cache them. Keys should be: `fqbn` + version of containing core/library.
async installed({ fqbn }: { fqbn: string }): Promise<{ user: ExampleContainer[], current: ExampleContainer[], any: ExampleContainer[] }> {
const user: ExampleContainer[] = [];
const current: ExampleContainer[] = [];
const any: ExampleContainer[] = [];
if (fqbn) {
const packages = await this.libraryService.list({ fqbn });
for (const pkg of packages) {
const container = await this.tryGroupExamples(pkg);
const { location } = pkg;
if (location === LibraryLocation.USER) {
user.push(container);
} else if (location === LibraryLocation.PLATFORM_BUILTIN || LibraryLocation.REFERENCED_PLATFORM_BUILTIN) {
current.push(container);
} else {
any.push(container);
}
}
}
return { user, current, any };
}
/**
* The CLI provides direct FS paths to the examples so that menus and menu groups cannot be built for the UI by traversing the
* folder hierarchy. This method tries to workaround it by falling back to the `installDirUri` and manually creating the
* location of the examples. Otherwise it creates the example container from the direct examples FS paths.
*/
protected async tryGroupExamples({ label, exampleUris, installDirUri }: LibraryPackage): Promise<ExampleContainer> {
const paths = exampleUris.map(uri => FileUri.fsPath(uri));
if (installDirUri) {
for (const example of ['example', 'Example', 'EXAMPLE', 'examples', 'Examples', 'EXAMPLES']) {
const examplesPath = join(FileUri.fsPath(installDirUri), example);
const exists = await fs.exists(examplesPath);
const isDir = exists && (await fs.lstat(examplesPath)).isDirectory();
if (isDir) {
const fileNames = await fs.readdir(examplesPath);
const children: ExampleContainer[] = [];
const sketches: Sketch[] = [];
for (const fileName of fileNames) {
const subPath = join(examplesPath, fileName);
const subIsDir = (await fs.lstat(subPath)).isDirectory();
if (subIsDir) {
const sketch = await this.tryLoadSketch(subPath);
if (!sketch) {
const container = await this.load(subPath);
if (container.children.length || container.sketches.length) {
children.push(container);
}
} else {
sketches.push(sketch);
}
}
}
return {
label,
children,
sketches
};
}
}
}
const sketches = await Promise.all(paths.map(path => this.tryLoadSketch(path)));
return {
label,
children: [],
sketches: sketches.filter(notEmpty)
};
}
// Built-ins are included inside the IDE.
protected async load(path: string): Promise<ExampleContainer> {
if (!await fs.exists(path)) {
throw new Error('Examples are not available');
}