diff --git a/arduino-ide-extension/src/browser/arduino-commands.ts b/arduino-ide-extension/src/browser/arduino-commands.ts index 00af4c47..9332c00c 100644 --- a/arduino-ide-extension/src/browser/arduino-commands.ts +++ b/arduino-ide-extension/src/browser/arduino-commands.ts @@ -12,4 +12,10 @@ export namespace ArduinoCommands { label: 'Upload Sketch' } + export const NEW_SKETCH: Command = { + id: "arduino-new-sketch", + label: 'New Sketch', + category: 'File' + } + } diff --git a/arduino-ide-extension/src/browser/arduino-file-menu.ts b/arduino-ide-extension/src/browser/arduino-file-menu.ts new file mode 100644 index 00000000..703d6547 --- /dev/null +++ b/arduino-ide-extension/src/browser/arduino-file-menu.ts @@ -0,0 +1,15 @@ +import { injectable } from "inversify"; +import { MenuContribution, MenuModelRegistry } from "@theia/core"; +import { CommonMenus } from "@theia/core/lib/browser"; +import { ArduinoCommands } from "./arduino-commands"; + +@injectable() +export class ArduinoFileMenuContribution implements MenuContribution { + + registerMenus(registry: MenuModelRegistry) { + registry.registerMenuAction([...CommonMenus.FILE, '0_new_sletch'], { + commandId: ArduinoCommands.NEW_SKETCH.id + }) + } + +} \ No newline at end of file diff --git a/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx b/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx index 85c733bc..5c175982 100644 --- a/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx +++ b/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx @@ -12,11 +12,14 @@ import { ConnectedBoards } from './components/connected-boards'; import { CoreService } from '../common/protocol/core-service'; import { WorkspaceServiceExt } from './workspace-service-ext'; import { ToolOutputServiceClient } from '../common/protocol/tool-output-service'; -import { ConfirmDialog } from '@theia/core/lib/browser'; +import { ConfirmDialog, OpenerService } from '@theia/core/lib/browser'; import { QuickPickService } from '@theia/core/lib/common/quick-pick-service'; import { BoardsListWidgetFrontendContribution } from './boards/boards-widget-frontend-contribution'; import { BoardsNotificationService } from './boards-notification-service'; - +import { FileSystem, FileStat } from '@theia/filesystem/lib/common'; +import { WorkspaceRootUriAwareCommandHandler } from '@theia/workspace/lib/browser/workspace-commands'; +import { SelectionService } from '@theia/core'; +import { WorkspaceService } from '@theia/workspace/lib/browser/workspace-service'; @injectable() export class ArduinoFrontendContribution extends DefaultFrontendApplicationContribution implements TabBarToolbarContribution, CommandContribution { @@ -45,6 +48,19 @@ export class ArduinoFrontendContribution extends DefaultFrontendApplicationContr @inject(BoardsNotificationService) protected readonly boardsNotificationService: BoardsNotificationService; + @inject(FileSystem) + protected readonly fileSystem: FileSystem; + + @inject(OpenerService) + protected readonly openerService: OpenerService; + + @inject(WorkspaceService) + protected readonly workspaceService: WorkspaceService; + + @inject(SelectionService) + protected readonly selectionService: SelectionService; + + @postConstruct() protected async init(): Promise { // This is a hack. Otherwise, the backend services won't bind. @@ -114,6 +130,64 @@ export class ArduinoFrontendContribution extends DefaultFrontendApplicationContr } } }); + registry.registerCommand(ArduinoCommands.NEW_SKETCH, new WorkspaceRootUriAwareCommandHandler(this.workspaceService, this.selectionService, { + execute: async uri => { + const parent = await this.getDirectory(uri) + if (!parent) { + return; + } + + const parentUri = new URI(parent.uri); + const monthNames = ["january", "february", "march", "april", "may", "june", + "july", "august", "september", "october", "november", "december" + ]; + const today = new Date(); + + const sketchBaseName = `sketch_${monthNames[today.getMonth()]}${today.getDay()}`; + let sketchName: string | undefined; + for (let i = 97; i < 97 + 26; i++) { + let sketchNameCandidate = `${sketchBaseName}${String.fromCharCode(i)}`; + if (await this.fileSystem.exists(parentUri.resolve(sketchNameCandidate).toString())) { + continue; + } + + sketchName = sketchNameCandidate; + break; + } + + if (!sketchName) { + new ConfirmDialog({ title: "New sketch", msg: "Cannot create a unique sketch name", ok: "Ok" }).open(); + return; + } + + try { + const sketchDir = parentUri.resolve(sketchName); + const sketchFile = sketchDir.resolve(`${sketchName}.ino`); + this.fileSystem.createFolder(sketchDir.toString()); + this.fileSystem.createFile(sketchFile.toString(), { content: ` +void setup() { + +} + +void loop() { + +} +` }); + const opener = await this.openerService.getOpener(sketchFile) + opener.open(sketchFile, { reveal: true }); + } catch (e) { + new ConfirmDialog({ title: "New sketch", msg: "Cannot create new sketch: " + e, ok: "Ok" }).open(); + } + } + })) + } + + protected async getDirectory(candidate: URI): Promise { + const stat = await this.fileSystem.getFileStat(candidate.toString()); + if (stat && stat.isDirectory) { + return stat; + } + return this.fileSystem.getFileStat(candidate.parent.toString()); } private async onNoBoardsInstalled() { diff --git a/arduino-ide-extension/src/browser/arduino-frontend-module.ts b/arduino-ide-extension/src/browser/arduino-frontend-module.ts index 31f70ba7..3aa571c0 100644 --- a/arduino-ide-extension/src/browser/arduino-frontend-module.ts +++ b/arduino-ide-extension/src/browser/arduino-frontend-module.ts @@ -26,12 +26,15 @@ import { WorkspaceService } from '@theia/workspace/lib/browser/workspace-service import { AWorkspaceService } from './arduino-workspace-service'; import { ThemeService } from '@theia/core/lib/browser/theming'; import { ArduinoTheme } from './arduino-theme'; +import { ArduinoFileMenuContribution } from './arduino-file-menu'; +import { MenuContribution } from '@theia/core'; export default new ContainerModule((bind: interfaces.Bind, unbind: interfaces.Unbind, isBound: interfaces.IsBound, rebind: interfaces.Rebind) => { // Commands and toolbar items bind(ArduinoFrontendContribution).toSelf().inSingletonScope(); bind(CommandContribution).toService(ArduinoFrontendContribution); bind(TabBarToolbarContribution).toService(ArduinoFrontendContribution); + bind(MenuContribution).to(ArduinoFileMenuContribution).inSingletonScope(); // `ino` TextMate grammar bind(LanguageGrammarDefinitionContribution).to(ArduinoLanguageGrammarContribution).inSingletonScope(); diff --git a/arduino-ide-extension/tslint.json b/arduino-ide-extension/tslint.json index 6f1bc509..09107cd5 100644 --- a/arduino-ide-extension/tslint.json +++ b/arduino-ide-extension/tslint.json @@ -6,7 +6,7 @@ "forin": false, "indent": [true, "spaces"], "max-line-length": [true, 180], - "no-trailing-whitespace": true, + "no-trailing-whitespace": false, "no-unused-expression": true, "no-use-before-declare": true, "no-var-keyword": true,