Skip to content

Commit 9d3cbf2

Browse files
committed
Use service to load sketches
Signed-off-by: jbicker <jan.bicker@typefox.io>
1 parent 0c93721 commit 9d3cbf2

File tree

6 files changed

+157
-36
lines changed

6 files changed

+157
-36
lines changed

arduino-ide-extension/src/browser/arduino-file-menu.ts

+29-29
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ import { injectable, inject } from "inversify";
22
import { MenuContribution, MenuModelRegistry, MenuPath, CommandRegistry, Command } from "@theia/core";
33
import { CommonMenus } from "@theia/core/lib/browser";
44
import { ArduinoCommands } from "./arduino-commands";
5-
import URI from "@theia/core/lib/common/uri";
5+
import { SketchesService, Sketch } from "../common/protocol/sketches-service";
6+
import { AWorkspaceService } from "./arduino-workspace-service";
67

78
export namespace ArduinoOpenSketchContextMenu {
89
export const PATH: MenuPath = ['arduino-open-sketch-context-menu'];
@@ -11,44 +12,28 @@ export namespace ArduinoOpenSketchContextMenu {
1112
export const EXAMPLE_SKETCHES_GROUP: MenuPath = [...PATH, '3_examples'];
1213
}
1314

14-
export interface SketchMenuEntry {
15-
name: string,
16-
uri: URI
17-
}
18-
1915
@injectable()
2016
export class ArduinoFileMenuContribution implements MenuContribution {
2117

2218
@inject(CommandRegistry)
2319
protected readonly commands: CommandRegistry;
2420

21+
@inject(SketchesService)
22+
protected readonly sketches: SketchesService;
2523

26-
protected async getWorkspaceSketches(): Promise<SketchMenuEntry[]> {
27-
return [
28-
{
29-
name: 'foo',
30-
uri: new URI('this/is/a/test/uri/foo')
31-
},
32-
{
33-
name: 'bar',
34-
uri: new URI('this/is/a/test/uri/bar')
24+
constructor(
25+
@inject(AWorkspaceService) protected readonly workspaceService: AWorkspaceService,
26+
@inject(MenuModelRegistry) protected readonly menuRegistry: MenuModelRegistry) {
27+
workspaceService.onWorkspaceChanged(() => {
28+
if (this.workspaceService.workspace) {
29+
this.registerSketchesInMenu(menuRegistry);
3530
}
36-
]
37-
}
38-
39-
registerMenus(registry: MenuModelRegistry) {
40-
registry.registerMenuAction([...CommonMenus.FILE, '0_new_sletch'], {
41-
commandId: ArduinoCommands.NEW_SKETCH.id
4231
})
32+
}
4333

44-
registry.registerMenuAction(ArduinoOpenSketchContextMenu.OPEN_GROUP, {
45-
commandId: ArduinoCommands.OPEN_FILE_NAVIGATOR.id,
46-
label: 'Open...'
47-
});
48-
34+
protected registerSketchesInMenu(registry: MenuModelRegistry) {
4935
this.getWorkspaceSketches().then(sketches => {
5036
sketches.forEach(sketch => {
51-
5237
const command: Command = {
5338
id: 'openSketch' + sketch.name
5439
}
@@ -58,10 +43,25 @@ export class ArduinoFileMenuContribution implements MenuContribution {
5843

5944
registry.registerMenuAction(ArduinoOpenSketchContextMenu.WS_SKETCHES_GROUP, {
6045
commandId: command.id,
61-
label: sketch.name
62-
});
46+
label: sketch.name
47+
});
6348
})
6449
})
50+
}
51+
52+
protected async getWorkspaceSketches(): Promise<Sketch[]> {
53+
const sketches = this.sketches.getSketches(this.workspaceService.workspace);
54+
return sketches;
55+
}
6556

57+
registerMenus(registry: MenuModelRegistry) {
58+
registry.registerMenuAction([...CommonMenus.FILE, '0_new_sletch'], {
59+
commandId: ArduinoCommands.NEW_SKETCH.id
60+
})
61+
62+
registry.registerMenuAction(ArduinoOpenSketchContextMenu.OPEN_GROUP, {
63+
commandId: ArduinoCommands.OPEN_FILE_NAVIGATOR.id,
64+
label: 'Open...'
65+
});
6666
}
6767
}

arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx

+32-7
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@ import { EditorManager } from '@theia/editor/lib/browser';
2424
import { open, ContextMenuRenderer, OpenerService, Widget } from '@theia/core/lib/browser';
2525
import { OpenFileDialogProps, FileDialogService } from '@theia/filesystem/lib/browser/file-dialog';
2626
import { FileSystem } from '@theia/filesystem/lib/common';
27-
import { ArduinoOpenSketchContextMenu, SketchMenuEntry } from './arduino-file-menu';
27+
import { ArduinoOpenSketchContextMenu } from './arduino-file-menu';
28+
import { Sketch, SketchesService } from '../common/protocol/sketches-service';
29+
import { WindowService } from '@theia/core/lib/browser/window/window-service';
2830

2931
@injectable()
3032
export class ArduinoFrontendContribution extends DefaultFrontendApplicationContribution implements TabBarToolbarContribution, CommandContribution {
@@ -68,15 +70,22 @@ export class ArduinoFrontendContribution extends DefaultFrontendApplicationContr
6870
@inject(ContextMenuRenderer)
6971
protected readonly contextMenuRenderer: ContextMenuRenderer;
7072

71-
@inject(FileDialogService)
73+
@inject(FileDialogService)
7274
protected readonly fileDialogService: FileDialogService;
7375

74-
@inject(FileSystem)
76+
@inject(FileSystem)
7577
protected readonly fileSystem: FileSystem;
7678

77-
@inject(OpenerService)
79+
@inject(OpenerService)
7880
protected readonly openerService: OpenerService;
7981

82+
@inject(SketchesService)
83+
protected readonly sketches: SketchesService;
84+
85+
@inject(WindowService)
86+
protected readonly windowService: WindowService;
87+
88+
8089
@postConstruct()
8190
protected async init(): Promise<void> {
8291
// This is a hack. Otherwise, the backend services won't bind.
@@ -165,7 +174,7 @@ export class ArduinoFrontendContribution extends DefaultFrontendApplicationContr
165174
isEnabled: widget => this.isArduinoToolbar(widget),
166175
execute: async (widget: Widget, event: React.MouseEvent<HTMLElement>) => {
167176
const el = (event.target as HTMLElement).parentElement;
168-
if(el) {
177+
if (el) {
169178
this.contextMenuRenderer.render(ArduinoOpenSketchContextMenu.PATH, {
170179
x: el.getBoundingClientRect().left,
171180
y: el.getBoundingClientRect().top + el.offsetHeight
@@ -179,8 +188,24 @@ export class ArduinoFrontendContribution extends DefaultFrontendApplicationContr
179188
})
180189
registry.registerCommand(ArduinoCommands.OPEN_SKETCH, {
181190
isEnabled: () => true,
182-
execute: (sketch: SketchMenuEntry) => {
183-
console.log("OPEN SOME SKETCH", sketch);
191+
execute: async (sketch: Sketch) => {
192+
// const url = new URL(window.location.href);
193+
// if (this.workspaceService.workspace) {
194+
// const wsUri = this.workspaceService.workspace.uri;
195+
// const path = new URI(wsUri).path;
196+
// url.hash = path + '?sketch=' + sketch.name
197+
// }
198+
// this.windowService.openNewWindow(url.toString());
199+
200+
const fileStat = await this.fileSystem.getFileStat(sketch.uri);
201+
if (fileStat) {
202+
const sketchFiles = await this.sketches.getSketchFiles(fileStat);
203+
sketchFiles.forEach(sketchFile => {
204+
const uri = new URI(sketchFile);
205+
this.editorManager.open(uri);
206+
});
207+
}
208+
184209
}
185210
})
186211
registry.registerCommand(ArduinoCommands.NEW_SKETCH, new WorkspaceRootUriAwareCommandHandler(this.workspaceService, this.selectionService, {

arduino-ide-extension/src/browser/arduino-frontend-module.ts

+6
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { ArduinoFrontendContribution } from './arduino-frontend-contribution';
1212
import { ArduinoLanguageGrammarContribution } from './language/arduino-language-grammar-contribution';
1313
import { LibraryService, LibraryServicePath } from '../common/protocol/library-service';
1414
import { BoardsService, BoardsServicePath } from '../common/protocol/boards-service';
15+
import { SketchesService, SketchesServicePath } from '../common/protocol/sketches-service';
1516
import { LibraryListWidgetFrontendContribution } from './library/list-widget-frontend-contribution';
1617
import { CoreService, CoreServicePath } from '../common/protocol/core-service';
1718
import { BoardsListWidget } from './boards/boards-list-widget';
@@ -70,6 +71,9 @@ export default new ContainerModule((bind: interfaces.Bind, unbind: interfaces.Un
7071
}));
7172
bind(FrontendApplicationContribution).toService(LibraryListWidgetFrontendContribution);
7273

74+
// Sketch list service
75+
bind(SketchesService).toDynamicValue(context => WebSocketConnectionProvider.createProxy(context.container, SketchesServicePath)).inSingletonScope();
76+
7377
// Boards Notification service for updating boards list
7478
// TODO (post-PoC): move this to boards service/backend
7579
bind(BoardsNotificationService).toSelf().inSingletonScope();
@@ -106,9 +110,11 @@ export default new ContainerModule((bind: interfaces.Bind, unbind: interfaces.Un
106110
container.get(CoreService);
107111
container.get(LibraryService);
108112
container.get(BoardsService);
113+
container.get(SketchesService);
109114
return workspaceServiceExt;
110115
});
111116

117+
bind(AWorkspaceService).toSelf().inSingletonScope();
112118
rebind(WorkspaceService).to(AWorkspaceService).inSingletonScope();
113119
bind(SketchFactory).toSelf().inSingletonScope();
114120

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { FileStat } from "@theia/filesystem/lib/common";
2+
3+
export const SketchesServicePath = '/services/sketches-service';
4+
export const SketchesService = Symbol('SketchesService');
5+
export interface SketchesService {
6+
getSketches(fileStat?: FileStat): Promise<Sketch[]>
7+
getSketchFiles(fileStat: FileStat): Promise<string[]>
8+
}
9+
10+
export interface Sketch {
11+
name: string;
12+
uri: string
13+
}

arduino-ide-extension/src/node/arduino-backend-module.ts

+10
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ import { ConnectionHandler, JsonRpcConnectionHandler } from '@theia/core';
1717
import { ToolOutputServiceServerImpl } from './tool-output-service-impl';
1818
import { DefaultWorkspaceServerExt } from './default-workspace-server-ext';
1919
import { WorkspaceServer } from '@theia/workspace/lib/common';
20+
import { SketchesServiceImpl } from './sketches-service-impl';
21+
import { SketchesService, SketchesServicePath } from '../common/protocol/sketches-service';
2022

2123
export default new ContainerModule((bind, unbind, isBound, rebind) => {
2224
bind(ArduinoDaemon).toSelf().inSingletonScope();
@@ -30,6 +32,14 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
3032
});
3133
bind(ConnectionContainerModule).toConstantValue(libraryServiceConnectionModule);
3234

35+
// Sketches service
36+
const sketchesServiceConnectionModule = ConnectionContainerModule.create(({ bind, bindBackendService }) => {
37+
bind(SketchesServiceImpl).toSelf().inSingletonScope();
38+
bind(SketchesService).toService(SketchesServiceImpl);
39+
bindBackendService(SketchesServicePath, SketchesService);
40+
});
41+
bind(ConnectionContainerModule).toConstantValue(sketchesServiceConnectionModule);
42+
3343
// Boards service
3444
const boardsServiceConnectionModule = ConnectionContainerModule.create(({ bind, bindBackendService }) => {
3545
bind(BoardsServiceImpl).toSelf().inSingletonScope();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import { injectable } from "inversify";
2+
import { SketchesService, Sketch } from "../common/protocol/sketches-service";
3+
import URI from "@theia/core/lib/common/uri";
4+
import { FileStat } from "@theia/filesystem/lib/common";
5+
import * as fs from 'fs';
6+
import * as path from 'path';
7+
8+
export const ALLOWED_FILE_EXTENSIONS = [".c", ".cpp", ".h", ".hh", ".hpp", ".s", ".pde", ".ino"];
9+
10+
@injectable()
11+
export class SketchesServiceImpl implements SketchesService {
12+
13+
async getSketches(fileStat?: FileStat): Promise<Sketch[]> {
14+
const sketches: Sketch[] = [];
15+
if (fileStat && fileStat.isDirectory) {
16+
const sketchFolderPath = this.getPath(fileStat);
17+
const files = fs.readdirSync(sketchFolderPath);
18+
files.forEach(file => {
19+
const filePath = path.join(sketchFolderPath, file);
20+
if (this.isSketchFolder(filePath, file)) {
21+
sketches.push({
22+
name: file,
23+
uri: filePath
24+
});
25+
}
26+
});
27+
}
28+
return sketches;
29+
}
30+
31+
/**
32+
* Return all allowed files.
33+
* File extensions: "c", "cpp", "h", "hh", "hpp", "s", "pde", "ino"
34+
*/
35+
async getSketchFiles(sketchDir: FileStat): Promise<string[]> {
36+
const files: string[] = [];
37+
const sketchDirPath = this.getPath(sketchDir);
38+
const sketchDirContents = fs.readdirSync(sketchDirPath);
39+
sketchDirContents.forEach(fileName => {
40+
const filePath = path.join(sketchDirPath, fileName);
41+
if (fs.existsSync(filePath) &&
42+
fs.lstatSync(filePath).isFile() &&
43+
ALLOWED_FILE_EXTENSIONS.indexOf(path.extname(filePath)) !== -1) {
44+
files.push(filePath);
45+
}
46+
});
47+
return files;
48+
}
49+
50+
protected isSketchFolder(path: string, name: string): boolean {
51+
if (fs.existsSync(path) && fs.lstatSync(path).isDirectory()) {
52+
const files = fs.readdirSync(path);
53+
for (let i = 0; i < files.length; i++) {
54+
if (files[i] === name + '.ino') {
55+
return true;
56+
}
57+
}
58+
}
59+
return false;
60+
}
61+
62+
protected getPath(fileStat: FileStat) {
63+
const fileStatUri = fileStat.uri;
64+
const uri = new URI(fileStatUri);
65+
return uri.path.toString();
66+
}
67+
}

0 commit comments

Comments
 (0)