mirror of
https://github.com/arduino/arduino-ide.git
synced 2025-11-17 06:09:28 +00:00
ATL-1064: Support for nested sketchbook structure
Signed-off-by: Akos Kitta <kittaakos@typefox.io>
This commit is contained in:
@@ -4,9 +4,9 @@ import * as fs from 'fs';
|
||||
import { promisify } from 'util';
|
||||
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 { Sketch, SketchContainer } from '../common/protocol/sketches-service';
|
||||
import { SketchesServiceImpl } from './sketches-service-impl';
|
||||
import { ExamplesService, ExampleContainer } from '../common/protocol/examples-service';
|
||||
import { ExamplesService } from '../common/protocol/examples-service';
|
||||
import { LibraryLocation, LibraryPackage, LibraryService } from '../common/protocol';
|
||||
import { ConfigServiceImpl } from './config-service-impl';
|
||||
|
||||
@@ -22,14 +22,14 @@ export class ExamplesServiceImpl implements ExamplesService {
|
||||
@inject(ConfigServiceImpl)
|
||||
protected readonly configService: ConfigServiceImpl;
|
||||
|
||||
protected _all: ExampleContainer[] | undefined;
|
||||
protected _all: SketchContainer[] | undefined;
|
||||
|
||||
@postConstruct()
|
||||
protected init(): void {
|
||||
this.builtIns();
|
||||
}
|
||||
|
||||
async builtIns(): Promise<ExampleContainer[]> {
|
||||
async builtIns(): Promise<SketchContainer[]> {
|
||||
if (this._all) {
|
||||
return this._all;
|
||||
}
|
||||
@@ -40,10 +40,10 @@ export class ExamplesServiceImpl implements ExamplesService {
|
||||
}
|
||||
|
||||
// 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[] = [];
|
||||
async installed({ fqbn }: { fqbn: string }): Promise<{ user: SketchContainer[], current: SketchContainer[], any: SketchContainer[] }> {
|
||||
const user: SketchContainer[] = [];
|
||||
const current: SketchContainer[] = [];
|
||||
const any: SketchContainer[] = [];
|
||||
if (fqbn) {
|
||||
const packages: LibraryPackage[] = await this.libraryService.list({ fqbn });
|
||||
for (const pkg of packages) {
|
||||
@@ -66,7 +66,7 @@ export class ExamplesServiceImpl implements ExamplesService {
|
||||
* 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> {
|
||||
protected async tryGroupExamples({ label, exampleUris, installDirUri }: LibraryPackage): Promise<SketchContainer> {
|
||||
const paths = exampleUris.map(uri => FileUri.fsPath(uri));
|
||||
if (installDirUri) {
|
||||
for (const example of ['example', 'Example', 'EXAMPLE', 'examples', 'Examples', 'EXAMPLES']) {
|
||||
@@ -75,7 +75,7 @@ export class ExamplesServiceImpl implements ExamplesService {
|
||||
const isDir = exists && (await promisify(fs.lstat)(examplesPath)).isDirectory();
|
||||
if (isDir) {
|
||||
const fileNames = await promisify(fs.readdir)(examplesPath);
|
||||
const children: ExampleContainer[] = [];
|
||||
const children: SketchContainer[] = [];
|
||||
const sketches: Sketch[] = [];
|
||||
for (const fileName of fileNames) {
|
||||
const subPath = join(examplesPath, fileName);
|
||||
@@ -109,7 +109,7 @@ export class ExamplesServiceImpl implements ExamplesService {
|
||||
}
|
||||
|
||||
// Built-ins are included inside the IDE.
|
||||
protected async load(path: string): Promise<ExampleContainer> {
|
||||
protected async load(path: string): Promise<SketchContainer> {
|
||||
if (!await promisify(fs.exists)(path)) {
|
||||
throw new Error('Examples are not available');
|
||||
}
|
||||
@@ -119,7 +119,7 @@ export class ExamplesServiceImpl implements ExamplesService {
|
||||
}
|
||||
const names = await promisify(fs.readdir)(path);
|
||||
const sketches: Sketch[] = [];
|
||||
const children: ExampleContainer[] = [];
|
||||
const children: SketchContainer[] = [];
|
||||
for (const p of names.map(name => join(path, name))) {
|
||||
const stat = await promisify(fs.stat)(p);
|
||||
if (stat.isDirectory()) {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { injectable, inject } from 'inversify';
|
||||
import * as minimatch from 'minimatch';
|
||||
import * as fs from 'fs';
|
||||
import * as os from 'os';
|
||||
import * as temp from 'temp';
|
||||
@@ -10,7 +11,7 @@ import URI from '@theia/core/lib/common/uri';
|
||||
import { FileUri } from '@theia/core/lib/node';
|
||||
import { isWindows } from '@theia/core/lib/common/os';
|
||||
import { ConfigService } from '../common/protocol/config-service';
|
||||
import { SketchesService, Sketch } from '../common/protocol/sketches-service';
|
||||
import { SketchesService, Sketch, SketchContainer } from '../common/protocol/sketches-service';
|
||||
import { firstToLowerCase } from '../common/utils';
|
||||
import { NotificationServiceServerImpl } from './notification-service-server';
|
||||
import { EnvVariablesServer } from '@theia/core/lib/common/env-variables';
|
||||
@@ -32,8 +33,8 @@ export class SketchesServiceImpl extends CoreClientAware implements SketchesServ
|
||||
|
||||
@inject(EnvVariablesServer)
|
||||
protected readonly envVariableServer: EnvVariablesServer;
|
||||
|
||||
async getSketches(uri?: string): Promise<SketchWithDetails[]> {
|
||||
async getSketches({ uri, exclude }: { uri?: string, exclude?: string[] }): Promise<SketchContainerWithDetails> {
|
||||
const start = Date.now();
|
||||
let sketchbookPath: undefined | string;
|
||||
if (!uri) {
|
||||
const { sketchDirUri } = await this.configService.getConfiguration();
|
||||
@@ -44,33 +45,65 @@ export class SketchesServiceImpl extends CoreClientAware implements SketchesServ
|
||||
} else {
|
||||
sketchbookPath = FileUri.fsPath(uri);
|
||||
}
|
||||
const container: SketchContainerWithDetails = {
|
||||
label: uri ? path.basename(sketchbookPath) : 'Sketchbook',
|
||||
sketches: [],
|
||||
children: []
|
||||
};
|
||||
if (!await promisify(fs.exists)(sketchbookPath)) {
|
||||
return [];
|
||||
return container;
|
||||
}
|
||||
const stat = await promisify(fs.stat)(sketchbookPath);
|
||||
if (!stat.isDirectory()) {
|
||||
return [];
|
||||
return container;
|
||||
}
|
||||
|
||||
const sketches: Array<SketchWithDetails> = [];
|
||||
const filenames = await promisify(fs.readdir)(sketchbookPath);
|
||||
for (const fileName of filenames) {
|
||||
const filePath = path.join(sketchbookPath, fileName);
|
||||
const sketch = await this._isSketchFolder(FileUri.create(filePath).toString());
|
||||
if (sketch) {
|
||||
const recursivelyLoad = async (fsPath: string, containerToLoad: SketchContainerWithDetails) => {
|
||||
const filenames = await promisify(fs.readdir)(fsPath);
|
||||
for (const name of filenames) {
|
||||
const childFsPath = path.join(fsPath, name);
|
||||
let skip = false;
|
||||
for (const pattern of exclude || ['**/libraries/**', '**/hardware/**']) {
|
||||
if (!skip && minimatch(childFsPath, pattern)) {
|
||||
skip = true;
|
||||
}
|
||||
}
|
||||
if (skip) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
const stat = await promisify(fs.stat)(filePath);
|
||||
sketches.push({
|
||||
...sketch,
|
||||
mtimeMs: stat.mtimeMs
|
||||
});
|
||||
const stat = await promisify(fs.stat)(childFsPath);
|
||||
if (stat.isDirectory()) {
|
||||
const sketch = await this._isSketchFolder(FileUri.create(childFsPath).toString());
|
||||
if (sketch) {
|
||||
containerToLoad.sketches.push({
|
||||
...sketch,
|
||||
mtimeMs: stat.mtimeMs
|
||||
});
|
||||
} else {
|
||||
const childContainer: SketchContainerWithDetails = {
|
||||
label: name,
|
||||
children: [],
|
||||
sketches: []
|
||||
};
|
||||
await recursivelyLoad(childFsPath, childContainer);
|
||||
if (!SketchContainer.isEmpty(childContainer)) {
|
||||
containerToLoad.children.push(childContainer);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
console.warn(`Could not load sketch from ${filePath}.`);
|
||||
console.warn(`Could not load sketch from ${childFsPath}.`);
|
||||
}
|
||||
}
|
||||
containerToLoad.sketches.sort((left, right) => right.mtimeMs - left.mtimeMs);
|
||||
return containerToLoad;
|
||||
}
|
||||
sketches.sort((left, right) => right.mtimeMs - left.mtimeMs);
|
||||
return sketches;
|
||||
|
||||
await recursivelyLoad(sketchbookPath, container);
|
||||
SketchContainer.prune(container);
|
||||
console.debug(`Loading the sketches from ${sketchbookPath} took ${Date.now() - start} ms.`);
|
||||
return container;
|
||||
}
|
||||
|
||||
async loadSketch(uri: string): Promise<SketchWithDetails> {
|
||||
@@ -363,3 +396,8 @@ void loop() {
|
||||
interface SketchWithDetails extends Sketch {
|
||||
readonly mtimeMs: number;
|
||||
}
|
||||
interface SketchContainerWithDetails extends SketchContainer {
|
||||
readonly label: string;
|
||||
readonly children: SketchContainerWithDetails[];
|
||||
readonly sketches: SketchWithDetails[];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user