feat: Create remote sketch

Closes #1580

Signed-off-by: Akos Kitta <a.kitta@arduino.cc>
This commit is contained in:
Akos Kitta
2022-10-25 17:13:43 +02:00
committed by Akos Kitta
parent 6984c52b92
commit 7d6a2d5e33
21 changed files with 683 additions and 111 deletions

View File

@@ -1,20 +1,26 @@
import { inject, injectable, postConstruct } from '@theia/core/shared/inversify';
import { TreeNode } from '@theia/core/lib/browser/tree';
import {
inject,
injectable,
postConstruct,
} from '@theia/core/shared/inversify';
import { CompositeTreeNode, TreeNode } from '@theia/core/lib/browser/tree';
import { posixSegments, splitSketchPath } from '../../create/create-paths';
import { CreateApi } from '../../create/create-api';
import { CloudSketchbookTree } from './cloud-sketchbook-tree';
import { AuthenticationClientService } from '../../auth/authentication-client-service';
import { SketchbookTreeModel } from '../sketchbook/sketchbook-tree-model';
import { ArduinoPreferences } from '../../arduino-preferences';
import { WorkspaceNode } from '@theia/navigator/lib/browser/navigator-tree';
import { CreateUri } from '../../create/create-uri';
import { FileStat } from '@theia/filesystem/lib/common/files';
import { LocalCacheFsProvider } from '../../local-cache/local-cache-fs-provider';
import { FileService } from '@theia/filesystem/lib/browser/file-service';
import { FileChangesEvent, FileStat } from '@theia/filesystem/lib/common/files';
import {
LocalCacheFsProvider,
LocalCacheUri,
} from '../../local-cache/local-cache-fs-provider';
import URI from '@theia/core/lib/common/uri';
import { SketchCache } from './cloud-sketch-cache';
import { Create } from '../../create/typings';
import { nls } from '@theia/core/lib/common';
import { nls } from '@theia/core/lib/common/nls';
import { Deferred } from '@theia/core/lib/common/promise-util';
export function sketchBaseDir(sketch: Create.Sketch): FileStat {
// extract the sketch path
@@ -52,26 +58,16 @@ export function sketchesToFileStats(sketches: Create.Sketch[]): FileStat[] {
@injectable()
export class CloudSketchbookTreeModel extends SketchbookTreeModel {
@inject(FileService)
protected override readonly fileService: FileService;
@inject(AuthenticationClientService)
protected readonly authenticationService: AuthenticationClientService;
@inject(CreateApi)
protected readonly createApi: CreateApi;
@inject(CloudSketchbookTree)
protected readonly cloudSketchbookTree: CloudSketchbookTree;
@inject(ArduinoPreferences)
protected override readonly arduinoPreferences: ArduinoPreferences;
private readonly createApi: CreateApi;
@inject(AuthenticationClientService)
private readonly authenticationService: AuthenticationClientService;
@inject(LocalCacheFsProvider)
protected readonly localCacheFsProvider: LocalCacheFsProvider;
private readonly localCacheFsProvider: LocalCacheFsProvider;
@inject(SketchCache)
protected readonly sketchCache: SketchCache;
private readonly sketchCache: SketchCache;
private _localCacheFsProviderReady: Deferred<void> | undefined;
@postConstruct()
protected override init(): void {
@@ -81,6 +77,50 @@ export class CloudSketchbookTreeModel extends SketchbookTreeModel {
);
}
override *getNodesByUri(uri: URI): IterableIterator<TreeNode> {
if (uri.scheme === LocalCacheUri.scheme) {
const workspace = this.root;
const { session } = this.authenticationService;
if (session && WorkspaceNode.is(workspace)) {
const currentUri = this.localCacheFsProvider.to(uri);
if (currentUri) {
const rootPath = this.localCacheFsProvider
.toUri(session)
.path.toString();
const currentPath = currentUri.path.toString();
if (rootPath === currentPath) {
return workspace;
}
if (currentPath.startsWith(rootPath)) {
const id = currentPath.substring(rootPath.length);
const node = this.getNode(id);
if (node) {
yield node;
}
}
}
}
}
}
protected override isRootAffected(changes: FileChangesEvent): boolean {
return changes.changes
.map(({ resource }) => resource)
.some(
(uri) => uri.parent.toString().startsWith(LocalCacheUri.root.toString()) // all files under the root might affect the tree
);
}
override async refresh(
parent?: Readonly<CompositeTreeNode>
): Promise<CompositeTreeNode | undefined> {
if (parent) {
return super.refresh(parent);
}
await this.updateRoot();
return super.refresh();
}
override async createRoot(): Promise<TreeNode | undefined> {
const { session } = this.authenticationService;
if (!session) {
@@ -89,7 +129,10 @@ export class CloudSketchbookTreeModel extends SketchbookTreeModel {
}
this.createApi.init(this.authenticationService, this.arduinoPreferences);
this.sketchCache.init();
const sketches = await this.createApi.sketches();
const [sketches] = await Promise.all([
this.createApi.sketches(),
this.ensureLocalFsProviderReady(),
]);
const rootFileStats = sketchesToFileStats(sketches);
if (this.workspaceService.opened) {
const workspaceNode = WorkspaceNode.createRoot(
@@ -108,7 +151,9 @@ export class CloudSketchbookTreeModel extends SketchbookTreeModel {
return this.tree as CloudSketchbookTree;
}
protected override recursivelyFindSketchRoot(node: TreeNode): any {
protected override recursivelyFindSketchRoot(
node: TreeNode
): TreeNode | false {
if (node && CloudSketchbookTree.CloudSketchDirNode.is(node)) {
return node;
}
@@ -122,13 +167,25 @@ export class CloudSketchbookTreeModel extends SketchbookTreeModel {
}
override async revealFile(uri: URI): Promise<TreeNode | undefined> {
await this.localCacheFsProvider.ready.promise;
// we use remote uris as keys for the tree
// convert local URIs
const remoteuri = this.localCacheFsProvider.from(uri);
if (remoteuri) {
return super.revealFile(remoteuri);
const remoteUri = this.localCacheFsProvider.from(uri);
if (remoteUri) {
return super.revealFile(remoteUri);
} else {
return super.revealFile(uri);
}
}
private async ensureLocalFsProviderReady(): Promise<void> {
if (this._localCacheFsProviderReady) {
return this._localCacheFsProviderReady.promise;
}
this._localCacheFsProviderReady = new Deferred();
this.fileService
.access(LocalCacheUri.root)
.then(() => this._localCacheFsProviderReady?.resolve());
return this._localCacheFsProviderReady.promise;
}
}