Skip to content

Commit 2bd9eef

Browse files
author
Akos Kitta
committed
Aligned the New and Save As... with the Java IDE.
From now on, sketches are created in the temp folder, and will be moved to the `directories.user` location when the user performs a manual `Save`. A new sketch can be created with the `CtrlCmd+N` binding. Closes: arduino/arduino-pro-ide#260 Closes: arduino/arduino-pro-ide#261 Signed-off-by: Akos Kitta <kittaakos@typefox.io>
1 parent 3082b3d commit 2bd9eef

15 files changed

+341
-70
lines changed

arduino-debugger-extension/src/browser/arduino-debug-frontend-application-contribution.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ export class ArduinoDebugFrontendApplicationContribution extends DebugFrontendAp
114114
id: ArduinoDebugCommands.START_DEBUG.id,
115115
command: ArduinoDebugCommands.START_DEBUG.id,
116116
tooltip: 'Start Debugging',
117-
priority: 1
117+
priority: 3
118118
});
119119
}
120120

arduino-ide-extension/package.json

+5-4
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,11 @@
4040
"@types/google-protobuf": "^3.7.2",
4141
"@types/js-yaml": "^3.12.2",
4242
"@types/lodash.debounce": "^4.0.6",
43+
"@types/ncp": "^2.0.4",
4344
"@types/ps-tree": "^1.1.0",
4445
"@types/react-select": "^3.0.0",
4546
"@types/sinon": "^7.5.2",
47+
"@types/temp": "^0.8.34",
4648
"@types/which": "^1.3.1",
4749
"ajv": "^6.5.3",
4850
"css-element-queries": "^1.2.0",
@@ -53,11 +55,13 @@
5355
"google-protobuf": "^3.11.4",
5456
"lodash.debounce": "^4.0.8",
5557
"js-yaml": "^3.13.1",
58+
"ncp": "^2.0.0",
5659
"p-queue": "^5.0.0",
5760
"ps-tree": "^1.2.0",
5861
"react-select": "^3.0.4",
5962
"semver": "^6.3.0",
6063
"string-natural-compare": "^2.0.3",
64+
"temp": "^0.9.1",
6165
"tree-kill": "^1.2.1",
6266
"upath": "^1.1.2",
6367
"which": "^1.3.1"
@@ -66,7 +70,6 @@
6670
"@types/chai": "^4.2.7",
6771
"@types/chai-string": "^1.4.2",
6872
"@types/mocha": "^5.2.7",
69-
"@types/temp": "^0.8.34",
7073
"chai": "^4.2.0",
7174
"chai-string": "^1.5.0",
7275
"decompress": "^4.2.0",
@@ -76,11 +79,9 @@
7679
"grpc_tools_node_protoc_ts": "^4.1.0",
7780
"mocha": "^7.0.0",
7881
"moment": "^2.24.0",
79-
"ncp": "^2.0.0",
80-
"protoc": "^1.0.4",
82+
"protoc": "1.0.4",
8183
"shelljs": "^0.8.3",
8284
"sinon": "^9.0.1",
83-
"temp": "^0.9.1",
8485
"uuid": "^3.2.1",
8586
"yargs": "^11.1.0"
8687
},

arduino-ide-extension/src/browser/arduino-commands.ts

+23-16
Original file line numberDiff line numberDiff line change
@@ -7,22 +7,22 @@ export namespace ArduinoCommands {
77
export const VERIFY: Command = {
88
id: 'arduino-verify',
99
label: 'Verify Sketch'
10-
}
10+
};
1111
export const VERIFY_TOOLBAR: Command = {
1212
id: 'arduino-verify-toolbar',
13-
}
13+
};
1414

1515
export const UPLOAD: Command = {
1616
id: 'arduino-upload',
1717
label: 'Upload Sketch'
18-
}
18+
};
1919
export const UPLOAD_TOOLBAR: Command = {
2020
id: 'arduino-upload-toolbar',
21-
}
21+
};
2222

2323
export const TOGGLE_COMPILE_FOR_DEBUG: Command = {
24-
id: "arduino-toggle-compile-for-debug"
25-
}
24+
id: 'arduino-toggle-compile-for-debug'
25+
};
2626

2727
export const SHOW_OPEN_CONTEXT_MENU: Command = {
2828
id: 'arduino-show-open-context-menu',
@@ -32,39 +32,46 @@ export namespace ArduinoCommands {
3232

3333
export const OPEN_FILE_NAVIGATOR: Command = {
3434
id: 'arduino-open-file-navigator'
35-
}
35+
};
3636

3737
export const OPEN_SKETCH: Command = {
3838
id: 'arduino-open-file'
39-
}
39+
};
4040

4141
/**
4242
* Unlike `OPEN_SKETCH`, it opens all files from a sketch folder. (ino, cpp, etc...)
4343
*/
4444
export const OPEN_SKETCH_FILES: Command = {
4545
id: 'arduino-open-sketch-files'
46-
}
46+
};
4747

4848
export const SAVE_SKETCH: Command = {
49-
id: 'arduino-save-file'
50-
}
49+
id: 'arduino-save-sketch'
50+
};
51+
52+
export const SAVE_SKETCH_AS: Command = {
53+
id: 'arduino-save-sketch-as'
54+
};
5155

5256
export const NEW_SKETCH: Command = {
5357
id: 'arduino-new-sketch',
5458
label: 'New Sketch',
5559
category
56-
}
60+
};
61+
export const NEW_SKETCH_TOOLBAR: Command = {
62+
id: 'arduino-new-sketch-toolbar'
63+
};
5764

5865
export const OPEN_BOARDS_DIALOG: Command = {
5966
id: 'arduino-open-boards-dialog'
60-
}
67+
};
6168

6269
export const TOGGLE_ADVANCED_MODE: Command = {
6370
id: 'arduino-toggle-advanced-mode'
64-
}
71+
};
6572
export const TOGGLE_ADVANCED_MODE_TOOLBAR: Command = {
66-
id: "arduino-toggle-advanced-mode-toolbar"
67-
}
73+
id: 'arduino-toggle-advanced-mode-toolbar'
74+
};
6875

6976
export const OPEN_CLI_CONFIG: Command = {
7077
id: 'arduino-open-cli-config',

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

+91-21
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import * as React from 'react';
2+
import * as dateFormat from 'dateformat';
3+
import { remote } from 'electron';
24
import { injectable, inject, postConstruct } from 'inversify';
35
import URI from '@theia/core/lib/common/uri';
46
import { EditorWidget } from '@theia/editor/lib/browser/editor-widget';
@@ -8,8 +10,8 @@ import { TabBarToolbarContribution, TabBarToolbarRegistry } from '@theia/core/li
810
import { BoardsService, BoardsServiceClient, CoreService, Sketch, SketchesService, ToolOutputServiceClient } from '../common/protocol';
911
import { ArduinoCommands } from './arduino-commands';
1012
import { BoardsServiceClientImpl } from './boards/boards-service-client-impl';
11-
import { WorkspaceRootUriAwareCommandHandler, WorkspaceCommands } from '@theia/workspace/lib/browser/workspace-commands';
12-
import { SelectionService, MenuContribution, MenuModelRegistry, MAIN_MENU_BAR, MenuPath } from '@theia/core';
13+
import { WorkspaceCommands } from '@theia/workspace/lib/browser/workspace-commands';
14+
import { SelectionService, MenuContribution, MenuModelRegistry, MAIN_MENU_BAR, MenuPath, notEmpty } from '@theia/core';
1315
import { ArduinoToolbar } from './toolbar/arduino-toolbar';
1416
import { EditorManager, EditorMainMenu } from '@theia/editor/lib/browser';
1517
import {
@@ -44,6 +46,7 @@ import { ArduinoDaemon } from '../common/protocol/arduino-daemon';
4446
import { ConfigService } from '../common/protocol/config-service';
4547
import { BoardsConfigStore } from './boards/boards-config-store';
4648
import { MainMenuManager } from './menu/main-menu-manager';
49+
import { FileSystemExt } from '../common/protocol/filesystem-ext';
4750

4851
export namespace ArduinoMenus {
4952
export const SKETCH = [...MAIN_MENU_BAR, '3_sketch'];
@@ -152,6 +155,9 @@ export class ArduinoFrontendContribution implements FrontendApplicationContribut
152155
@inject(MainMenuManager)
153156
protected readonly mainMenuManager: MainMenuManager;
154157

158+
@inject(FileSystemExt)
159+
protected readonly fileSystemExt: FileSystemExt;
160+
155161
protected application: FrontendApplication;
156162
protected wsSketchCount: number = 0; // TODO: this does not belong here, does it?
157163

@@ -194,24 +200,32 @@ export class ArduinoFrontendContribution implements FrontendApplicationContribut
194200
registry.registerItem({
195201
id: ArduinoCommands.VERIFY.id,
196202
command: ArduinoCommands.VERIFY_TOOLBAR.id,
197-
tooltip: 'Verify'
203+
tooltip: 'Verify',
204+
priority: 1
198205
});
199206
registry.registerItem({
200207
id: ArduinoCommands.UPLOAD.id,
201208
command: ArduinoCommands.UPLOAD_TOOLBAR.id,
202-
tooltip: 'Upload'
209+
tooltip: 'Upload',
210+
priority: 2
211+
});
212+
registry.registerItem({
213+
id: ArduinoCommands.NEW_SKETCH.id,
214+
command: ArduinoCommands.NEW_SKETCH_TOOLBAR.id,
215+
tooltip: 'New',
216+
priority: 4 // Note: priority 3 was reserved by debug.
203217
});
204218
registry.registerItem({
205219
id: ArduinoCommands.SHOW_OPEN_CONTEXT_MENU.id,
206220
command: ArduinoCommands.SHOW_OPEN_CONTEXT_MENU.id,
207221
tooltip: 'Open',
208-
priority: 2
222+
priority: 5
209223
});
210224
registry.registerItem({
211225
id: ArduinoCommands.SAVE_SKETCH.id,
212226
command: ArduinoCommands.SAVE_SKETCH.id,
213227
tooltip: 'Save',
214-
priority: 2
228+
priority: 6
215229
});
216230
registry.registerItem({
217231
id: BoardsToolBarItem.TOOLBAR_ID,
@@ -220,14 +234,13 @@ export class ArduinoFrontendContribution implements FrontendApplicationContribut
220234
commands={this.commandRegistry}
221235
boardsServiceClient={this.boardsServiceClientImpl} />,
222236
isVisible: widget => ArduinoToolbar.is(widget) && widget.side === 'left',
223-
priority: 2
237+
priority: 6
224238
});
225239
registry.registerItem({
226240
id: 'toggle-serial-monitor',
227241
command: MonitorViewContribution.TOGGLE_SERIAL_MONITOR_TOOLBAR,
228-
tooltip: 'Toggle Serial Monitor'
242+
tooltip: 'Serial Monitor'
229243
});
230-
231244
registry.registerItem({
232245
id: ArduinoCommands.TOGGLE_ADVANCED_MODE.id,
233246
command: ArduinoCommands.TOGGLE_ADVANCED_MODE_TOOLBAR.id,
@@ -335,21 +348,65 @@ export class ArduinoFrontendContribution implements FrontendApplicationContribut
335348
}
336349
});
337350

338-
registry.registerCommand(ArduinoCommands.NEW_SKETCH, new WorkspaceRootUriAwareCommandHandler(this.workspaceService, this.selectionService, {
339-
execute: async uri => {
340-
try {
341-
// hack: sometimes we don't get the workspace root, but the currently active file: correct for that
342-
if (uri.path.ext !== "") {
343-
uri = uri.withPath(uri.path.dir.dir);
344-
}
351+
registry.registerCommand(ArduinoCommands.SAVE_SKETCH_AS, {
352+
execute: async ({ execOnlyIfTemp }: { execOnlyIfTemp: boolean } = { execOnlyIfTemp: false }) => {
353+
const sketches = (await Promise.all(this.workspaceService.tryGetRoots().map(({ uri }) => this.sketchService.getSketchFolder(uri)))).filter(notEmpty);
354+
if (!sketches.length) {
355+
return;
356+
}
357+
if (sketches.length > 1) {
358+
console.log(`Multiple sketch folders were found in the workspace. Falling back to the first one. Sketch folders: ${JSON.stringify(sketches)}`);
359+
}
360+
const sketch = sketches[0];
361+
const isTemp = await this.sketchService.isTemp(sketch);
362+
if (!isTemp && !!execOnlyIfTemp) {
363+
return;
364+
}
345365

346-
const sketch = await this.sketchService.createNewSketch(uri.toString());
366+
// If target does not exist, propose a `directories.user`/${sketch.name} path
367+
// If target exists, propose `directories.user`/${sketch.name}_copy_${yyyymmddHHMMss}
368+
const sketchDirUri = new URI((await this.configService.getConfiguration()).sketchDirUri);
369+
const exists = await this.fileSystem.exists(sketchDirUri.resolve(sketch.name).toString());
370+
const defaultUri = exists
371+
? sketchDirUri.resolve(sketchDirUri.resolve(`${sketch.name}_copy_${dateFormat(new Date(), 'yyyymmddHHMMss')}`).toString())
372+
: sketchDirUri.resolve(sketch.name);
373+
const defaultPath = await this.fileSystem.getFsPath(defaultUri.toString())!;
374+
const fsPath = await new Promise<string | undefined>(resolve => {
375+
remote.dialog.showSaveDialog({
376+
title: 'Save sketch folder as...',
377+
defaultPath
378+
}, (filename) => resolve(filename));
379+
});
380+
if (!fsPath) { // Canceled
381+
return;
382+
}
383+
const destinationUri = await this.fileSystemExt.getUri(fsPath);
384+
if (!destinationUri) {
385+
return;
386+
}
387+
const workspaceUri = await this.sketchService.copy(sketch, { destinationUri });
388+
if (workspaceUri) {
389+
this.workspaceService.open(new URI(workspaceUri));
390+
}
391+
}
392+
});
393+
394+
registry.registerCommand(ArduinoCommands.NEW_SKETCH, {
395+
execute: async () => {
396+
try {
397+
const sketch = await this.sketchService.createNewSketch();
347398
this.workspaceService.open(new URI(sketch.uri));
348399
} catch (e) {
349400
await this.messageService.error(e.toString());
350401
}
351402
}
352-
}));
403+
});
404+
registry.registerCommand(ArduinoCommands.NEW_SKETCH_TOOLBAR, {
405+
isVisible: widget => ArduinoToolbar.is(widget) && widget.side === 'left',
406+
execute: async () => {
407+
return registry.executeCommand(ArduinoCommands.NEW_SKETCH.id);
408+
}
409+
});
353410

354411
registry.registerCommand(ArduinoCommands.OPEN_BOARDS_DIALOG, {
355412
execute: async () => {
@@ -481,7 +538,6 @@ export class ArduinoFrontendContribution implements FrontendApplicationContribut
481538
registry.getMenu(MAIN_MENU_BAR).removeNode(this.getMenuId(TerminalMenus.TERMINAL));
482539
registry.getMenu(MAIN_MENU_BAR).removeNode(this.getMenuId(CommonMenus.VIEW));
483540
}
484-
485541
registry.registerSubmenu(ArduinoMenus.SKETCH, 'Sketch');
486542
registry.registerMenuAction(ArduinoMenus.SKETCH, {
487543
commandId: ArduinoCommands.TOGGLE_COMPILE_FOR_DEBUG.id,
@@ -517,6 +573,11 @@ export class ArduinoFrontendContribution implements FrontendApplicationContribut
517573
registry.registerMenuAction([...CommonMenus.FILE_SETTINGS_SUBMENU, '3_settings_cli'], {
518574
commandId: ArduinoCommands.OPEN_CLI_CONFIG.id
519575
});
576+
577+
registry.registerMenuAction(CommonMenus.FILE_SAVE, {
578+
commandId: ArduinoCommands.SAVE_SKETCH_AS.id,
579+
label: 'Save As...'
580+
});
520581
}
521582

522583
protected getMenuId(menuPath: string[]): string {
@@ -526,13 +587,22 @@ export class ArduinoFrontendContribution implements FrontendApplicationContribut
526587
}
527588

528589
registerKeybindings(keybindings: KeybindingRegistry): void {
590+
keybindings.unregisterKeybinding('ctrlcmd+n'); // Unregister the keybinding for `New File`, will be used by `New Sketch`. (eclipse-theia/theia#8170)
529591
keybindings.registerKeybinding({
530592
command: ArduinoCommands.VERIFY.id,
531-
keybinding: 'ctrlcmd+alt+v'
593+
keybinding: 'CtrlCmd+Alt+V'
532594
});
533595
keybindings.registerKeybinding({
534596
command: ArduinoCommands.UPLOAD.id,
535-
keybinding: 'ctrlcmd+alt+u'
597+
keybinding: 'CtrlCmd+Alt+U'
598+
});
599+
keybindings.registerKeybinding({
600+
command: ArduinoCommands.NEW_SKETCH.id,
601+
keybinding: 'CtrlCmd+N'
602+
});
603+
keybindings.registerKeybinding({
604+
command: ArduinoCommands.SAVE_SKETCH_AS.id,
605+
keybinding: 'CtrlCmd+Shift+S'
536606
});
537607
}
538608

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

+7
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,9 @@ import { CoreServiceClientImpl } from './core-service-client-impl';
7878
import { BoardsDetailsMenuUpdater } from './boards/boards-details-menu-updater';
7979
import { BoardsConfigStore } from './boards/boards-config-store';
8080
import { ILogger } from '@theia/core';
81+
import { FileSystemExt, FileSystemExtPath } from '../common/protocol/filesystem-ext';
82+
import { WorkspaceFrontendContribution } from '@theia/workspace/lib/browser';
83+
import { ArduinoWorkspaceFrontendContribution } from './customization/arduino-workspace-frontend-contribution';
8184

8285
const ElementQueries = require('css-element-queries/src/ElementQueries');
8386

@@ -254,6 +257,7 @@ export default new ContainerModule((bind: interfaces.Bind, unbind: interfaces.Un
254257
rebind(ScmContribution).to(ArduinoScmContribution).inSingletonScope();
255258
rebind(SearchInWorkspaceFrontendContribution).to(ArduinoSearchInWorkspaceContribution).inSingletonScope();
256259
rebind(FrontendApplication).to(ArduinoFrontendApplication).inSingletonScope();
260+
rebind(WorkspaceFrontendContribution).to(ArduinoWorkspaceFrontendContribution).inSingletonScope();
257261

258262
// Show a disconnected status bar, when the daemon is not available
259263
bind(ArduinoApplicationConnectionStatusContribution).toSelf().inSingletonScope();
@@ -293,4 +297,7 @@ export default new ContainerModule((bind: interfaces.Bind, unbind: interfaces.Un
293297
WebSocketConnectionProvider.createProxy(context.container, ArduinoDaemonPath, client);
294298
return client;
295299
}).inSingletonScope();
300+
301+
// File-system extension
302+
bind(FileSystemExt).toDynamicValue(context => WebSocketConnectionProvider.createProxy(context.container, FileSystemExtPath)).inSingletonScope();
296303
});

arduino-ide-extension/src/browser/arduino-workspace-service.ts

+1-3
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,7 @@ export class ArduinoWorkspaceService extends WorkspaceService {
4747
await this.server.setMostRecentlyUsedWorkspace(uri);
4848
return toOpen.uri;
4949
}
50-
const { sketchDirUri } = (await this.configService.getConfiguration());
51-
this.logger.info(`No valid workspace URI found. Creating new sketch in ${sketchDirUri}`)
52-
return (await this.sketchService.createNewSketch(sketchDirUri)).uri;
50+
return (await this.sketchService.createNewSketch()).uri;
5351
} catch (err) {
5452
this.logger.fatal(`Failed to determine the sketch directory: ${err}`)
5553
this.messageService.error(

0 commit comments

Comments
 (0)