Skip to content

Commit 527a34d

Browse files
committed
wip
1 parent e10f0f1 commit 527a34d

File tree

5 files changed

+319
-31
lines changed

5 files changed

+319
-31
lines changed

arduino-ide-extension/src/browser/widgets/component-list/filterable-list-container.tsx

-1
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,6 @@ export class FilterableListContainer<
9494
}
9595

9696
protected sort(items: T[]): T[] {
97-
// debugger;
9897
const { itemLabel, itemDeprecated } = this.props;
9998
return items.sort((left, right) => {
10099
// always put deprecated items at the bottom of the list

arduino-ide-extension/src/browser/widgets/sketchbook/sketchbook-tree-model.ts

+218-9
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,29 @@
1-
import { inject, injectable } from 'inversify';
1+
import { inject, injectable, postConstruct } from 'inversify';
22
import URI from '@theia/core/lib/common/uri';
33
import { FileNode, FileTreeModel } from '@theia/filesystem/lib/browser';
44
import { FileService } from '@theia/filesystem/lib/browser/file-service';
55
import { ConfigService } from '../../../common/protocol';
66
import { SketchbookTree } from './sketchbook-tree';
77
import { ArduinoPreferences } from '../../arduino-preferences';
8-
import { SelectableTreeNode, TreeNode } from '@theia/core/lib/browser/tree';
8+
import {
9+
CompositeTreeNode,
10+
ExpandableTreeNode,
11+
SelectableTreeNode,
12+
TreeNode,
13+
} from '@theia/core/lib/browser/tree';
914
import { SketchbookCommands } from './sketchbook-commands';
1015
import { OpenerService, open } from '@theia/core/lib/browser';
1116
import { SketchesServiceClientImpl } from '../../../common/protocol/sketches-service-client-impl';
1217
import { CommandRegistry } from '@theia/core/lib/common/command';
18+
import { WorkspaceService } from '@theia/workspace/lib/browser/workspace-service';
19+
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
20+
import { ProgressService } from '@theia/core/lib/common/progress-service';
21+
import {
22+
WorkspaceNode,
23+
WorkspaceRootNode,
24+
} from '@theia/navigator/lib/browser/navigator-tree';
25+
import { Deferred } from '@theia/core/lib/common/promise-util';
26+
import { Disposable } from '@theia/core/lib/common/disposable';
1327

1428
@injectable()
1529
export class SketchbookTreeModel extends FileTreeModel {
@@ -31,14 +45,209 @@ export class SketchbookTreeModel extends FileTreeModel {
3145
@inject(SketchesServiceClientImpl)
3246
protected readonly sketchServiceClient: SketchesServiceClientImpl;
3347

34-
async updateRoot(): Promise<void> {
35-
const config = await this.configService.getConfiguration();
36-
const fileStat = await this.fileService.resolve(
37-
new URI(config.sketchDirUri)
48+
@inject(SketchbookTree) protected readonly tree: SketchbookTree;
49+
@inject(WorkspaceService)
50+
protected readonly workspaceService: WorkspaceService;
51+
@inject(FrontendApplicationStateService)
52+
protected readonly applicationState: FrontendApplicationStateService;
53+
54+
@inject(ProgressService)
55+
protected readonly progressService: ProgressService;
56+
57+
@postConstruct()
58+
protected init(): void {
59+
super.init();
60+
this.reportBusyProgress();
61+
this.initializeRoot();
62+
}
63+
64+
protected readonly pendingBusyProgress = new Map<string, Deferred<void>>();
65+
protected reportBusyProgress(): void {
66+
this.toDispose.push(
67+
this.onDidChangeBusy((node) => {
68+
const pending = this.pendingBusyProgress.get(node.id);
69+
if (pending) {
70+
if (!node.busy) {
71+
pending.resolve();
72+
this.pendingBusyProgress.delete(node.id);
73+
}
74+
return;
75+
}
76+
if (node.busy) {
77+
const progress = new Deferred<void>();
78+
this.pendingBusyProgress.set(node.id, progress);
79+
this.progressService.withProgress(
80+
'',
81+
'explorer',
82+
() => progress.promise
83+
);
84+
}
85+
})
86+
);
87+
this.toDispose.push(
88+
Disposable.create(() => {
89+
for (const pending of this.pendingBusyProgress.values()) {
90+
pending.resolve();
91+
}
92+
this.pendingBusyProgress.clear();
93+
})
3894
);
39-
const showAllFiles =
40-
this.arduinoPreferences['arduino.sketchbook.showAllFiles'];
41-
this.tree.root = SketchbookTree.RootNode.create(fileStat, showAllFiles);
95+
}
96+
97+
protected async initializeRoot(): Promise<void> {
98+
await Promise.all([
99+
this.applicationState.reachedState('initialized_layout'),
100+
this.workspaceService.roots,
101+
]);
102+
await this.updateRoot();
103+
if (this.toDispose.disposed) {
104+
return;
105+
}
106+
this.toDispose.push(
107+
this.workspaceService.onWorkspaceChanged(() => this.updateRoot())
108+
);
109+
this.toDispose.push(
110+
this.workspaceService.onWorkspaceLocationChanged(() => this.updateRoot())
111+
);
112+
if (this.selectedNodes.length) {
113+
return;
114+
}
115+
const root = this.root;
116+
if (CompositeTreeNode.is(root) && root.children.length === 1) {
117+
const child = root.children[0];
118+
if (
119+
SelectableTreeNode.is(child) &&
120+
!child.selected &&
121+
ExpandableTreeNode.is(child)
122+
) {
123+
this.selectNode(child);
124+
this.expandNode(child);
125+
}
126+
}
127+
}
128+
129+
previewNode(node: TreeNode): void {
130+
if (FileNode.is(node)) {
131+
open(this.openerService, node.uri, {
132+
mode: 'reveal',
133+
preview: true,
134+
});
135+
}
136+
}
137+
138+
*getNodesByUri(uri: URI): IterableIterator<TreeNode> {
139+
const workspace = this.root;
140+
if (WorkspaceNode.is(workspace)) {
141+
for (const root of workspace.children) {
142+
const id = this.tree.createId(root, uri);
143+
const node = this.getNode(id);
144+
if (node) {
145+
yield node;
146+
}
147+
}
148+
}
149+
}
150+
151+
public async updateRoot(): Promise<void> {
152+
this.root = await this.createRoot();
153+
}
154+
155+
protected async createRoot(): Promise<TreeNode | undefined> {
156+
const config = await this.configService.getConfiguration();
157+
const stat = await this.fileService.resolve(new URI(config.sketchDirUri));
158+
159+
if (this.workspaceService.opened) {
160+
const isMulti = stat ? !stat.isDirectory : false;
161+
const workspaceNode = isMulti
162+
? this.createMultipleRootNode()
163+
: WorkspaceNode.createRoot();
164+
workspaceNode.children.push(
165+
await this.tree.createWorkspaceRoot(stat, workspaceNode)
166+
);
167+
168+
return workspaceNode;
169+
}
170+
}
171+
172+
/**
173+
* Create multiple root node used to display
174+
* the multiple root workspace name.
175+
*
176+
* @returns `WorkspaceNode`
177+
*/
178+
protected createMultipleRootNode(): WorkspaceNode {
179+
const workspace = this.workspaceService.workspace;
180+
let name = workspace ? workspace.resource.path.name : 'untitled';
181+
name += ' (Workspace)';
182+
return WorkspaceNode.createRoot(name);
183+
}
184+
185+
/**
186+
* Move the given source file or directory to the given target directory.
187+
*/
188+
async move(source: TreeNode, target: TreeNode): Promise<URI | undefined> {
189+
if (source.parent && WorkspaceRootNode.is(source)) {
190+
// do not support moving a root folder
191+
return undefined;
192+
}
193+
return super.move(source, target);
194+
}
195+
196+
/**
197+
* Reveals node in the navigator by given file uri.
198+
*
199+
* @param uri uri to file which should be revealed in the navigator
200+
* @returns file tree node if the file with given uri was revealed, undefined otherwise
201+
*/
202+
async revealFile(uri: URI): Promise<TreeNode | undefined> {
203+
if (!uri.path.isAbsolute) {
204+
return undefined;
205+
}
206+
let node = this.getNodeClosestToRootByUri(uri);
207+
208+
// success stop condition
209+
// we have to reach workspace root because expanded node could be inside collapsed one
210+
if (WorkspaceRootNode.is(node)) {
211+
if (ExpandableTreeNode.is(node)) {
212+
if (!node.expanded) {
213+
node = await this.expandNode(node);
214+
}
215+
return node;
216+
}
217+
// shouldn't happen, root node is always directory, i.e. expandable
218+
return undefined;
219+
}
220+
221+
// fail stop condition
222+
if (uri.path.isRoot) {
223+
// file system root is reached but workspace root wasn't found, it means that
224+
// given uri is not in workspace root folder or points to not existing file.
225+
return undefined;
226+
}
227+
228+
if (await this.revealFile(uri.parent)) {
229+
if (node === undefined) {
230+
// get node if it wasn't mounted into navigator tree before expansion
231+
node = this.getNodeClosestToRootByUri(uri);
232+
}
233+
if (ExpandableTreeNode.is(node) && !node.expanded) {
234+
node = await this.expandNode(node);
235+
}
236+
return node;
237+
}
238+
return undefined;
239+
}
240+
241+
protected getNodeClosestToRootByUri(uri: URI): TreeNode | undefined {
242+
const nodes = [...this.getNodesByUri(uri)];
243+
return nodes.length > 0
244+
? nodes.reduce(
245+
(
246+
node1,
247+
node2 // return the node closest to the workspace root
248+
) => (node1.id.length >= node2.id.length ? node1 : node2)
249+
)
250+
: undefined;
42251
}
43252

44253
// selectNode gets called when the user single-clicks on an item

arduino-ide-extension/src/browser/widgets/sketchbook/sketchbook-tree.ts

+67-21
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,39 @@
11
import { inject, injectable } from 'inversify';
22
import { LabelProvider } from '@theia/core/lib/browser/label-provider';
33
import { Command } from '@theia/core/lib/common/command';
4-
import { TreeNode, CompositeTreeNode } from '@theia/core/lib/browser/tree';
5-
import {
6-
DirNode,
7-
FileStatNode,
8-
FileTree,
9-
} from '@theia/filesystem/lib/browser/file-tree';
4+
import { CompositeTreeNode, TreeNode } from '@theia/core/lib/browser/tree';
5+
import { DirNode, FileStatNode } from '@theia/filesystem/lib/browser/file-tree';
106
import { SketchesService } from '../../../common/protocol';
117
import { FileStat } from '@theia/filesystem/lib/common/files';
128
import { SketchbookCommands } from './sketchbook-commands';
9+
import {
10+
FileNavigatorTree,
11+
WorkspaceNode,
12+
} from '@theia/navigator/lib/browser/navigator-tree';
13+
import { ArduinoPreferences } from '../../arduino-preferences';
1314

1415
@injectable()
15-
export class SketchbookTree extends FileTree {
16+
export class SketchbookTree extends FileNavigatorTree {
1617
@inject(LabelProvider)
1718
protected readonly labelProvider: LabelProvider;
1819

1920
@inject(SketchesService)
2021
protected readonly sketchesService: SketchesService;
2122

23+
@inject(ArduinoPreferences)
24+
protected readonly arduinoPreferences: ArduinoPreferences;
25+
2226
async resolveChildren(parent: CompositeTreeNode): Promise<TreeNode[]> {
23-
if (!FileStatNode.is(parent)) {
24-
return super.resolveChildren(parent);
25-
}
26-
const { root } = this;
27-
if (!root) {
28-
return [];
29-
}
30-
if (!SketchbookTree.RootNode.is(root)) {
31-
return [];
32-
}
27+
const showAllFiles =
28+
this.arduinoPreferences['arduino.sketchbook.showAllFiles'];
29+
30+
console.log(`showAllFiles: ${showAllFiles}`);
31+
3332
const children = (
3433
await Promise.all(
3534
(
3635
await super.resolveChildren(parent)
37-
).map((node) => this.maybeDecorateNode(node, root.showAllFiles))
36+
).map((node) => this.maybeDecorateNode(node, showAllFiles))
3837
)
3938
).filter((node) => {
4039
// filter out hidden nodes
@@ -43,7 +42,9 @@ export class SketchbookTree extends FileTree {
4342
}
4443
return true;
4544
});
46-
if (SketchbookTree.RootNode.is(parent)) {
45+
46+
// filter out hardware and libraries
47+
if (WorkspaceNode.is(parent.parent)) {
4748
return children
4849
.filter(DirNode.is)
4950
.filter(
@@ -53,12 +54,57 @@ export class SketchbookTree extends FileTree {
5354
) === -1
5455
);
5556
}
56-
if (SketchbookTree.SketchDirNode.is(parent)) {
57-
return children.filter(FileStatNode.is);
57+
58+
// return the Arduino directory containing all user sketches
59+
if (WorkspaceNode.is(parent)) {
60+
return children;
5861
}
62+
5963
return children;
64+
65+
// return this.filter.filter(super.resolveChildren(parent));
6066
}
6167

68+
// async resolveChildren(parent: CompositeTreeNode): Promise<TreeNode[]> {
69+
// if (!FileStatNode.is(parent)) {
70+
// return super.resolveChildren(parent);
71+
// }
72+
// const { root } = this;
73+
// if (!root) {
74+
// return [];
75+
// }
76+
// if (!SketchbookTree.RootNode.is(root)) {
77+
// return [];
78+
// }
79+
// const children = (
80+
// await Promise.all(
81+
// (
82+
// await super.resolveChildren(parent)
83+
// ).map((node) => this.maybeDecorateNode(node, root.showAllFiles))
84+
// )
85+
// ).filter((node) => {
86+
// // filter out hidden nodes
87+
// if (DirNode.is(node) || FileStatNode.is(node)) {
88+
// return node.fileStat.name.indexOf('.') !== 0;
89+
// }
90+
// return true;
91+
// });
92+
// if (SketchbookTree.RootNode.is(parent)) {
93+
// return children
94+
// .filter(DirNode.is)
95+
// .filter(
96+
// (node) =>
97+
// ['libraries', 'hardware'].indexOf(
98+
// this.labelProvider.getName(node)
99+
// ) === -1
100+
// );
101+
// }
102+
// if (SketchbookTree.SketchDirNode.is(parent)) {
103+
// return children.filter(FileStatNode.is);
104+
// }
105+
// return children;
106+
// }
107+
62108
protected async maybeDecorateNode(
63109
node: TreeNode,
64110
showAllFiles: boolean

0 commit comments

Comments
 (0)