mirror of
https://github.com/arduino/arduino-ide.git
synced 2025-06-07 20:56:32 +00:00
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>
This commit is contained in:
parent
3082b3d643
commit
2bd9eef146
@ -114,7 +114,7 @@ export class ArduinoDebugFrontendApplicationContribution extends DebugFrontendAp
|
||||
id: ArduinoDebugCommands.START_DEBUG.id,
|
||||
command: ArduinoDebugCommands.START_DEBUG.id,
|
||||
tooltip: 'Start Debugging',
|
||||
priority: 1
|
||||
priority: 3
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -40,9 +40,11 @@
|
||||
"@types/google-protobuf": "^3.7.2",
|
||||
"@types/js-yaml": "^3.12.2",
|
||||
"@types/lodash.debounce": "^4.0.6",
|
||||
"@types/ncp": "^2.0.4",
|
||||
"@types/ps-tree": "^1.1.0",
|
||||
"@types/react-select": "^3.0.0",
|
||||
"@types/sinon": "^7.5.2",
|
||||
"@types/temp": "^0.8.34",
|
||||
"@types/which": "^1.3.1",
|
||||
"ajv": "^6.5.3",
|
||||
"css-element-queries": "^1.2.0",
|
||||
@ -53,11 +55,13 @@
|
||||
"google-protobuf": "^3.11.4",
|
||||
"lodash.debounce": "^4.0.8",
|
||||
"js-yaml": "^3.13.1",
|
||||
"ncp": "^2.0.0",
|
||||
"p-queue": "^5.0.0",
|
||||
"ps-tree": "^1.2.0",
|
||||
"react-select": "^3.0.4",
|
||||
"semver": "^6.3.0",
|
||||
"string-natural-compare": "^2.0.3",
|
||||
"temp": "^0.9.1",
|
||||
"tree-kill": "^1.2.1",
|
||||
"upath": "^1.1.2",
|
||||
"which": "^1.3.1"
|
||||
@ -66,7 +70,6 @@
|
||||
"@types/chai": "^4.2.7",
|
||||
"@types/chai-string": "^1.4.2",
|
||||
"@types/mocha": "^5.2.7",
|
||||
"@types/temp": "^0.8.34",
|
||||
"chai": "^4.2.0",
|
||||
"chai-string": "^1.5.0",
|
||||
"decompress": "^4.2.0",
|
||||
@ -76,11 +79,9 @@
|
||||
"grpc_tools_node_protoc_ts": "^4.1.0",
|
||||
"mocha": "^7.0.0",
|
||||
"moment": "^2.24.0",
|
||||
"ncp": "^2.0.0",
|
||||
"protoc": "^1.0.4",
|
||||
"protoc": "1.0.4",
|
||||
"shelljs": "^0.8.3",
|
||||
"sinon": "^9.0.1",
|
||||
"temp": "^0.9.1",
|
||||
"uuid": "^3.2.1",
|
||||
"yargs": "^11.1.0"
|
||||
},
|
||||
|
@ -7,22 +7,22 @@ export namespace ArduinoCommands {
|
||||
export const VERIFY: Command = {
|
||||
id: 'arduino-verify',
|
||||
label: 'Verify Sketch'
|
||||
}
|
||||
};
|
||||
export const VERIFY_TOOLBAR: Command = {
|
||||
id: 'arduino-verify-toolbar',
|
||||
}
|
||||
};
|
||||
|
||||
export const UPLOAD: Command = {
|
||||
id: 'arduino-upload',
|
||||
label: 'Upload Sketch'
|
||||
}
|
||||
};
|
||||
export const UPLOAD_TOOLBAR: Command = {
|
||||
id: 'arduino-upload-toolbar',
|
||||
}
|
||||
};
|
||||
|
||||
export const TOGGLE_COMPILE_FOR_DEBUG: Command = {
|
||||
id: "arduino-toggle-compile-for-debug"
|
||||
}
|
||||
id: 'arduino-toggle-compile-for-debug'
|
||||
};
|
||||
|
||||
export const SHOW_OPEN_CONTEXT_MENU: Command = {
|
||||
id: 'arduino-show-open-context-menu',
|
||||
@ -32,39 +32,46 @@ export namespace ArduinoCommands {
|
||||
|
||||
export const OPEN_FILE_NAVIGATOR: Command = {
|
||||
id: 'arduino-open-file-navigator'
|
||||
}
|
||||
};
|
||||
|
||||
export const OPEN_SKETCH: Command = {
|
||||
id: 'arduino-open-file'
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Unlike `OPEN_SKETCH`, it opens all files from a sketch folder. (ino, cpp, etc...)
|
||||
*/
|
||||
export const OPEN_SKETCH_FILES: Command = {
|
||||
id: 'arduino-open-sketch-files'
|
||||
}
|
||||
};
|
||||
|
||||
export const SAVE_SKETCH: Command = {
|
||||
id: 'arduino-save-file'
|
||||
}
|
||||
id: 'arduino-save-sketch'
|
||||
};
|
||||
|
||||
export const SAVE_SKETCH_AS: Command = {
|
||||
id: 'arduino-save-sketch-as'
|
||||
};
|
||||
|
||||
export const NEW_SKETCH: Command = {
|
||||
id: 'arduino-new-sketch',
|
||||
label: 'New Sketch',
|
||||
category
|
||||
}
|
||||
};
|
||||
export const NEW_SKETCH_TOOLBAR: Command = {
|
||||
id: 'arduino-new-sketch-toolbar'
|
||||
};
|
||||
|
||||
export const OPEN_BOARDS_DIALOG: Command = {
|
||||
id: 'arduino-open-boards-dialog'
|
||||
}
|
||||
};
|
||||
|
||||
export const TOGGLE_ADVANCED_MODE: Command = {
|
||||
id: 'arduino-toggle-advanced-mode'
|
||||
}
|
||||
};
|
||||
export const TOGGLE_ADVANCED_MODE_TOOLBAR: Command = {
|
||||
id: "arduino-toggle-advanced-mode-toolbar"
|
||||
}
|
||||
id: 'arduino-toggle-advanced-mode-toolbar'
|
||||
};
|
||||
|
||||
export const OPEN_CLI_CONFIG: Command = {
|
||||
id: 'arduino-open-cli-config',
|
||||
|
@ -1,4 +1,6 @@
|
||||
import * as React from 'react';
|
||||
import * as dateFormat from 'dateformat';
|
||||
import { remote } from 'electron';
|
||||
import { injectable, inject, postConstruct } from 'inversify';
|
||||
import URI from '@theia/core/lib/common/uri';
|
||||
import { EditorWidget } from '@theia/editor/lib/browser/editor-widget';
|
||||
@ -8,8 +10,8 @@ import { TabBarToolbarContribution, TabBarToolbarRegistry } from '@theia/core/li
|
||||
import { BoardsService, BoardsServiceClient, CoreService, Sketch, SketchesService, ToolOutputServiceClient } from '../common/protocol';
|
||||
import { ArduinoCommands } from './arduino-commands';
|
||||
import { BoardsServiceClientImpl } from './boards/boards-service-client-impl';
|
||||
import { WorkspaceRootUriAwareCommandHandler, WorkspaceCommands } from '@theia/workspace/lib/browser/workspace-commands';
|
||||
import { SelectionService, MenuContribution, MenuModelRegistry, MAIN_MENU_BAR, MenuPath } from '@theia/core';
|
||||
import { WorkspaceCommands } from '@theia/workspace/lib/browser/workspace-commands';
|
||||
import { SelectionService, MenuContribution, MenuModelRegistry, MAIN_MENU_BAR, MenuPath, notEmpty } from '@theia/core';
|
||||
import { ArduinoToolbar } from './toolbar/arduino-toolbar';
|
||||
import { EditorManager, EditorMainMenu } from '@theia/editor/lib/browser';
|
||||
import {
|
||||
@ -44,6 +46,7 @@ import { ArduinoDaemon } from '../common/protocol/arduino-daemon';
|
||||
import { ConfigService } from '../common/protocol/config-service';
|
||||
import { BoardsConfigStore } from './boards/boards-config-store';
|
||||
import { MainMenuManager } from './menu/main-menu-manager';
|
||||
import { FileSystemExt } from '../common/protocol/filesystem-ext';
|
||||
|
||||
export namespace ArduinoMenus {
|
||||
export const SKETCH = [...MAIN_MENU_BAR, '3_sketch'];
|
||||
@ -152,6 +155,9 @@ export class ArduinoFrontendContribution implements FrontendApplicationContribut
|
||||
@inject(MainMenuManager)
|
||||
protected readonly mainMenuManager: MainMenuManager;
|
||||
|
||||
@inject(FileSystemExt)
|
||||
protected readonly fileSystemExt: FileSystemExt;
|
||||
|
||||
protected application: FrontendApplication;
|
||||
protected wsSketchCount: number = 0; // TODO: this does not belong here, does it?
|
||||
|
||||
@ -194,24 +200,32 @@ export class ArduinoFrontendContribution implements FrontendApplicationContribut
|
||||
registry.registerItem({
|
||||
id: ArduinoCommands.VERIFY.id,
|
||||
command: ArduinoCommands.VERIFY_TOOLBAR.id,
|
||||
tooltip: 'Verify'
|
||||
tooltip: 'Verify',
|
||||
priority: 1
|
||||
});
|
||||
registry.registerItem({
|
||||
id: ArduinoCommands.UPLOAD.id,
|
||||
command: ArduinoCommands.UPLOAD_TOOLBAR.id,
|
||||
tooltip: 'Upload'
|
||||
tooltip: 'Upload',
|
||||
priority: 2
|
||||
});
|
||||
registry.registerItem({
|
||||
id: ArduinoCommands.NEW_SKETCH.id,
|
||||
command: ArduinoCommands.NEW_SKETCH_TOOLBAR.id,
|
||||
tooltip: 'New',
|
||||
priority: 4 // Note: priority 3 was reserved by debug.
|
||||
});
|
||||
registry.registerItem({
|
||||
id: ArduinoCommands.SHOW_OPEN_CONTEXT_MENU.id,
|
||||
command: ArduinoCommands.SHOW_OPEN_CONTEXT_MENU.id,
|
||||
tooltip: 'Open',
|
||||
priority: 2
|
||||
priority: 5
|
||||
});
|
||||
registry.registerItem({
|
||||
id: ArduinoCommands.SAVE_SKETCH.id,
|
||||
command: ArduinoCommands.SAVE_SKETCH.id,
|
||||
tooltip: 'Save',
|
||||
priority: 2
|
||||
priority: 6
|
||||
});
|
||||
registry.registerItem({
|
||||
id: BoardsToolBarItem.TOOLBAR_ID,
|
||||
@ -220,14 +234,13 @@ export class ArduinoFrontendContribution implements FrontendApplicationContribut
|
||||
commands={this.commandRegistry}
|
||||
boardsServiceClient={this.boardsServiceClientImpl} />,
|
||||
isVisible: widget => ArduinoToolbar.is(widget) && widget.side === 'left',
|
||||
priority: 2
|
||||
priority: 6
|
||||
});
|
||||
registry.registerItem({
|
||||
id: 'toggle-serial-monitor',
|
||||
command: MonitorViewContribution.TOGGLE_SERIAL_MONITOR_TOOLBAR,
|
||||
tooltip: 'Toggle Serial Monitor'
|
||||
tooltip: 'Serial Monitor'
|
||||
});
|
||||
|
||||
registry.registerItem({
|
||||
id: ArduinoCommands.TOGGLE_ADVANCED_MODE.id,
|
||||
command: ArduinoCommands.TOGGLE_ADVANCED_MODE_TOOLBAR.id,
|
||||
@ -335,21 +348,65 @@ export class ArduinoFrontendContribution implements FrontendApplicationContribut
|
||||
}
|
||||
});
|
||||
|
||||
registry.registerCommand(ArduinoCommands.NEW_SKETCH, new WorkspaceRootUriAwareCommandHandler(this.workspaceService, this.selectionService, {
|
||||
execute: async uri => {
|
||||
try {
|
||||
// hack: sometimes we don't get the workspace root, but the currently active file: correct for that
|
||||
if (uri.path.ext !== "") {
|
||||
uri = uri.withPath(uri.path.dir.dir);
|
||||
}
|
||||
registry.registerCommand(ArduinoCommands.SAVE_SKETCH_AS, {
|
||||
execute: async ({ execOnlyIfTemp }: { execOnlyIfTemp: boolean } = { execOnlyIfTemp: false }) => {
|
||||
const sketches = (await Promise.all(this.workspaceService.tryGetRoots().map(({ uri }) => this.sketchService.getSketchFolder(uri)))).filter(notEmpty);
|
||||
if (!sketches.length) {
|
||||
return;
|
||||
}
|
||||
if (sketches.length > 1) {
|
||||
console.log(`Multiple sketch folders were found in the workspace. Falling back to the first one. Sketch folders: ${JSON.stringify(sketches)}`);
|
||||
}
|
||||
const sketch = sketches[0];
|
||||
const isTemp = await this.sketchService.isTemp(sketch);
|
||||
if (!isTemp && !!execOnlyIfTemp) {
|
||||
return;
|
||||
}
|
||||
|
||||
const sketch = await this.sketchService.createNewSketch(uri.toString());
|
||||
// If target does not exist, propose a `directories.user`/${sketch.name} path
|
||||
// If target exists, propose `directories.user`/${sketch.name}_copy_${yyyymmddHHMMss}
|
||||
const sketchDirUri = new URI((await this.configService.getConfiguration()).sketchDirUri);
|
||||
const exists = await this.fileSystem.exists(sketchDirUri.resolve(sketch.name).toString());
|
||||
const defaultUri = exists
|
||||
? sketchDirUri.resolve(sketchDirUri.resolve(`${sketch.name}_copy_${dateFormat(new Date(), 'yyyymmddHHMMss')}`).toString())
|
||||
: sketchDirUri.resolve(sketch.name);
|
||||
const defaultPath = await this.fileSystem.getFsPath(defaultUri.toString())!;
|
||||
const fsPath = await new Promise<string | undefined>(resolve => {
|
||||
remote.dialog.showSaveDialog({
|
||||
title: 'Save sketch folder as...',
|
||||
defaultPath
|
||||
}, (filename) => resolve(filename));
|
||||
});
|
||||
if (!fsPath) { // Canceled
|
||||
return;
|
||||
}
|
||||
const destinationUri = await this.fileSystemExt.getUri(fsPath);
|
||||
if (!destinationUri) {
|
||||
return;
|
||||
}
|
||||
const workspaceUri = await this.sketchService.copy(sketch, { destinationUri });
|
||||
if (workspaceUri) {
|
||||
this.workspaceService.open(new URI(workspaceUri));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
registry.registerCommand(ArduinoCommands.NEW_SKETCH, {
|
||||
execute: async () => {
|
||||
try {
|
||||
const sketch = await this.sketchService.createNewSketch();
|
||||
this.workspaceService.open(new URI(sketch.uri));
|
||||
} catch (e) {
|
||||
await this.messageService.error(e.toString());
|
||||
}
|
||||
}
|
||||
}));
|
||||
});
|
||||
registry.registerCommand(ArduinoCommands.NEW_SKETCH_TOOLBAR, {
|
||||
isVisible: widget => ArduinoToolbar.is(widget) && widget.side === 'left',
|
||||
execute: async () => {
|
||||
return registry.executeCommand(ArduinoCommands.NEW_SKETCH.id);
|
||||
}
|
||||
});
|
||||
|
||||
registry.registerCommand(ArduinoCommands.OPEN_BOARDS_DIALOG, {
|
||||
execute: async () => {
|
||||
@ -481,7 +538,6 @@ export class ArduinoFrontendContribution implements FrontendApplicationContribut
|
||||
registry.getMenu(MAIN_MENU_BAR).removeNode(this.getMenuId(TerminalMenus.TERMINAL));
|
||||
registry.getMenu(MAIN_MENU_BAR).removeNode(this.getMenuId(CommonMenus.VIEW));
|
||||
}
|
||||
|
||||
registry.registerSubmenu(ArduinoMenus.SKETCH, 'Sketch');
|
||||
registry.registerMenuAction(ArduinoMenus.SKETCH, {
|
||||
commandId: ArduinoCommands.TOGGLE_COMPILE_FOR_DEBUG.id,
|
||||
@ -517,6 +573,11 @@ export class ArduinoFrontendContribution implements FrontendApplicationContribut
|
||||
registry.registerMenuAction([...CommonMenus.FILE_SETTINGS_SUBMENU, '3_settings_cli'], {
|
||||
commandId: ArduinoCommands.OPEN_CLI_CONFIG.id
|
||||
});
|
||||
|
||||
registry.registerMenuAction(CommonMenus.FILE_SAVE, {
|
||||
commandId: ArduinoCommands.SAVE_SKETCH_AS.id,
|
||||
label: 'Save As...'
|
||||
});
|
||||
}
|
||||
|
||||
protected getMenuId(menuPath: string[]): string {
|
||||
@ -526,13 +587,22 @@ export class ArduinoFrontendContribution implements FrontendApplicationContribut
|
||||
}
|
||||
|
||||
registerKeybindings(keybindings: KeybindingRegistry): void {
|
||||
keybindings.unregisterKeybinding('ctrlcmd+n'); // Unregister the keybinding for `New File`, will be used by `New Sketch`. (eclipse-theia/theia#8170)
|
||||
keybindings.registerKeybinding({
|
||||
command: ArduinoCommands.VERIFY.id,
|
||||
keybinding: 'ctrlcmd+alt+v'
|
||||
keybinding: 'CtrlCmd+Alt+V'
|
||||
});
|
||||
keybindings.registerKeybinding({
|
||||
command: ArduinoCommands.UPLOAD.id,
|
||||
keybinding: 'ctrlcmd+alt+u'
|
||||
keybinding: 'CtrlCmd+Alt+U'
|
||||
});
|
||||
keybindings.registerKeybinding({
|
||||
command: ArduinoCommands.NEW_SKETCH.id,
|
||||
keybinding: 'CtrlCmd+N'
|
||||
});
|
||||
keybindings.registerKeybinding({
|
||||
command: ArduinoCommands.SAVE_SKETCH_AS.id,
|
||||
keybinding: 'CtrlCmd+Shift+S'
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -78,6 +78,9 @@ import { CoreServiceClientImpl } from './core-service-client-impl';
|
||||
import { BoardsDetailsMenuUpdater } from './boards/boards-details-menu-updater';
|
||||
import { BoardsConfigStore } from './boards/boards-config-store';
|
||||
import { ILogger } from '@theia/core';
|
||||
import { FileSystemExt, FileSystemExtPath } from '../common/protocol/filesystem-ext';
|
||||
import { WorkspaceFrontendContribution } from '@theia/workspace/lib/browser';
|
||||
import { ArduinoWorkspaceFrontendContribution } from './customization/arduino-workspace-frontend-contribution';
|
||||
|
||||
const ElementQueries = require('css-element-queries/src/ElementQueries');
|
||||
|
||||
@ -254,6 +257,7 @@ export default new ContainerModule((bind: interfaces.Bind, unbind: interfaces.Un
|
||||
rebind(ScmContribution).to(ArduinoScmContribution).inSingletonScope();
|
||||
rebind(SearchInWorkspaceFrontendContribution).to(ArduinoSearchInWorkspaceContribution).inSingletonScope();
|
||||
rebind(FrontendApplication).to(ArduinoFrontendApplication).inSingletonScope();
|
||||
rebind(WorkspaceFrontendContribution).to(ArduinoWorkspaceFrontendContribution).inSingletonScope();
|
||||
|
||||
// Show a disconnected status bar, when the daemon is not available
|
||||
bind(ArduinoApplicationConnectionStatusContribution).toSelf().inSingletonScope();
|
||||
@ -293,4 +297,7 @@ export default new ContainerModule((bind: interfaces.Bind, unbind: interfaces.Un
|
||||
WebSocketConnectionProvider.createProxy(context.container, ArduinoDaemonPath, client);
|
||||
return client;
|
||||
}).inSingletonScope();
|
||||
|
||||
// File-system extension
|
||||
bind(FileSystemExt).toDynamicValue(context => WebSocketConnectionProvider.createProxy(context.container, FileSystemExtPath)).inSingletonScope();
|
||||
});
|
||||
|
@ -47,9 +47,7 @@ export class ArduinoWorkspaceService extends WorkspaceService {
|
||||
await this.server.setMostRecentlyUsedWorkspace(uri);
|
||||
return toOpen.uri;
|
||||
}
|
||||
const { sketchDirUri } = (await this.configService.getConfiguration());
|
||||
this.logger.info(`No valid workspace URI found. Creating new sketch in ${sketchDirUri}`)
|
||||
return (await this.sketchService.createNewSketch(sketchDirUri)).uri;
|
||||
return (await this.sketchService.createNewSketch()).uri;
|
||||
} catch (err) {
|
||||
this.logger.fatal(`Failed to determine the sketch directory: ${err}`)
|
||||
this.messageService.error(
|
||||
|
@ -1,7 +1,10 @@
|
||||
|
||||
import { injectable, inject } from 'inversify';
|
||||
import { ApplicationShell, Widget, Saveable, FocusTracker, Message } from '@theia/core/lib/browser';
|
||||
import { EditorWidget } from '@theia/editor/lib/browser';
|
||||
import { injectable, inject } from 'inversify';
|
||||
import { EditorMode } from '../editor-mode';
|
||||
import { CommandService } from '@theia/core';
|
||||
import { ArduinoCommands } from '../arduino-commands';
|
||||
|
||||
@injectable()
|
||||
export class ArduinoApplicationShell extends ApplicationShell {
|
||||
@ -9,6 +12,9 @@ export class ArduinoApplicationShell extends ApplicationShell {
|
||||
@inject(EditorMode)
|
||||
protected readonly editorMode: EditorMode;
|
||||
|
||||
@inject(CommandService)
|
||||
protected readonly commandService: CommandService;
|
||||
|
||||
protected refreshBottomPanelToggleButton() {
|
||||
if (this.editorMode.proMode) {
|
||||
super.refreshBottomPanelToggleButton();
|
||||
@ -34,6 +40,11 @@ export class ArduinoApplicationShell extends ApplicationShell {
|
||||
}
|
||||
}
|
||||
|
||||
async save(): Promise<void> {
|
||||
await super.save();
|
||||
await this.commandService.executeCommand(ArduinoCommands.SAVE_SKETCH_AS.id, { execOnlyIfTemp: true });
|
||||
}
|
||||
|
||||
private disableClose(widget: Widget | undefined): void {
|
||||
if (widget instanceof EditorWidget) {
|
||||
const onCloseRequest = (_: Message) => {
|
||||
|
@ -0,0 +1,62 @@
|
||||
import { injectable } from 'inversify';
|
||||
import { isOSX } from '@theia/core/lib/common/os';
|
||||
import { environment } from '@theia/application-package/lib/environment';
|
||||
import { CommonMenus } from '@theia/core/lib/browser';
|
||||
import { CommandRegistry } from '@theia/core/lib/common/command';
|
||||
import { MenuModelRegistry } from '@theia/core/lib/common/menu';
|
||||
import { WorkspaceCommands } from '@theia/workspace/lib/browser/workspace-commands';
|
||||
import { WorkspaceFrontendContribution } from '@theia/workspace/lib/browser/workspace-frontend-contribution';
|
||||
|
||||
// TODO: https://github.com/eclipse-theia/theia/issues/8175
|
||||
@injectable()
|
||||
export class ArduinoWorkspaceFrontendContribution extends WorkspaceFrontendContribution {
|
||||
|
||||
registerCommands(registry: CommandRegistry): void {
|
||||
super.registerCommands(registry);
|
||||
registry.unregisterCommand(WorkspaceCommands.SAVE_AS);
|
||||
registry.unregisterCommand(WorkspaceCommands.OPEN_FOLDER);
|
||||
}
|
||||
|
||||
registerMenus(registry: MenuModelRegistry): void {
|
||||
if (isOSX || !environment.electron.is()) {
|
||||
registry.registerMenuAction(CommonMenus.FILE_OPEN, {
|
||||
commandId: WorkspaceCommands.OPEN.id,
|
||||
order: 'a00'
|
||||
});
|
||||
}
|
||||
if (!isOSX && environment.electron.is()) {
|
||||
registry.registerMenuAction(CommonMenus.FILE_OPEN, {
|
||||
commandId: WorkspaceCommands.OPEN_FILE.id,
|
||||
label: `${WorkspaceCommands.OPEN_FILE.dialogLabel}...`,
|
||||
order: 'a01'
|
||||
});
|
||||
registry.registerMenuAction(CommonMenus.FILE_OPEN, {
|
||||
commandId: WorkspaceCommands.OPEN_FOLDER.id,
|
||||
label: `${WorkspaceCommands.OPEN_FOLDER.dialogLabel}...`,
|
||||
order: 'a02'
|
||||
});
|
||||
}
|
||||
registry.registerMenuAction(CommonMenus.FILE_OPEN, {
|
||||
commandId: WorkspaceCommands.OPEN_WORKSPACE.id,
|
||||
order: 'a10'
|
||||
});
|
||||
registry.registerMenuAction(CommonMenus.FILE_OPEN, {
|
||||
commandId: WorkspaceCommands.OPEN_RECENT_WORKSPACE.id,
|
||||
order: 'a20'
|
||||
});
|
||||
registry.registerMenuAction(CommonMenus.FILE_OPEN, {
|
||||
commandId: WorkspaceCommands.SAVE_WORKSPACE_AS.id,
|
||||
order: 'a30'
|
||||
});
|
||||
|
||||
registry.registerMenuAction(CommonMenus.FILE_CLOSE, {
|
||||
commandId: WorkspaceCommands.CLOSE.id
|
||||
});
|
||||
|
||||
// `Save As`
|
||||
// menus.registerMenuAction(CommonMenus.FILE_SAVE, {
|
||||
// commandId: WorkspaceCommands.SAVE_AS.id,
|
||||
// });
|
||||
}
|
||||
|
||||
}
|
@ -42,7 +42,7 @@
|
||||
mask-size: 800%;
|
||||
}
|
||||
|
||||
.arduino-save-file-icon {
|
||||
.arduino-save-sketch-icon {
|
||||
-webkit-mask-position: 59px -4px;
|
||||
mask-position: 59px -4px;
|
||||
}
|
||||
@ -57,6 +57,11 @@
|
||||
mask-position: 156px -4px;
|
||||
}
|
||||
|
||||
.arduino-new-sketch-icon {
|
||||
-webkit-mask-position: 124px -4px;
|
||||
mask-position: 124px -4px;
|
||||
}
|
||||
|
||||
.arduino-show-open-context-menu-icon {
|
||||
-webkit-mask-position: 92px -4px;
|
||||
mask-position: 92px -4px;
|
||||
|
@ -0,0 +1,5 @@
|
||||
export const FileSystemExtPath = '/services/file-system-ext';
|
||||
export const FileSystemExt = Symbol('FileSystemExt');
|
||||
export interface FileSystemExt {
|
||||
getUri(fsPath: string): Promise<string>;
|
||||
}
|
@ -1,22 +1,44 @@
|
||||
export const SketchesServicePath = '/services/sketches-service';
|
||||
export const SketchesService = Symbol('SketchesService');
|
||||
export interface SketchesService {
|
||||
|
||||
/**
|
||||
* Returns with the direct sketch folders from the location of the `fileStat`.
|
||||
* The sketches returns with inverse-chronological order, the first item is the most recent one.
|
||||
*/
|
||||
getSketches(uri?: string): Promise<Sketch[]>
|
||||
getSketchFiles(uri: string): Promise<string[]>
|
||||
getSketches(uri?: string): Promise<Sketch[]>;
|
||||
|
||||
getSketchFiles(uri: string): Promise<string[]>;
|
||||
|
||||
/**
|
||||
* Creates a new sketch folder in the `parentUri` location.
|
||||
* Normally, `parentUri` is the client's workspace root, or the default `sketchDirUri` from the CLI.
|
||||
* Note, `parentUri` and `sketchDirUri` can be the same.
|
||||
* Creates a new sketch folder in the temp location.
|
||||
*/
|
||||
createNewSketch(parentUri: string): Promise<Sketch>
|
||||
isSketchFolder(uri: string): Promise<boolean>
|
||||
createNewSketch(): Promise<Sketch>;
|
||||
|
||||
isSketchFolder(uri: string): Promise<boolean>;
|
||||
|
||||
/**
|
||||
* Sketches are created to the temp location by default and will be moved under `directories.user` on save.
|
||||
* This method resolves to `true` if the `sketch` is still in the temp location. Otherwise, `false`.
|
||||
*/
|
||||
isTemp(sketch: Sketch): Promise<boolean>;
|
||||
|
||||
/**
|
||||
* If `isTemp` is `true` for the `sketch`, you can call this method to move the sketch from the temp
|
||||
* location to `directories.user`. Resolves with the URI of the sketch after the move. Rejects, when the sketch
|
||||
* was not in the temp folder. This method always overrides. It's the callers responsibility to ask the user whether
|
||||
* the files at the destination can be overwritten or not.
|
||||
*/
|
||||
copy(sketch: Sketch, options: { destinationUri: string }): Promise<string>;
|
||||
|
||||
/**
|
||||
* Returns with the container sketch for the input `uri`. If the `uri` is not in a sketch folder, resolved `undefined`.
|
||||
*/
|
||||
getSketchFolder(uri: string): Promise<Sketch | undefined>;
|
||||
|
||||
}
|
||||
|
||||
export interface Sketch {
|
||||
readonly name: string;
|
||||
readonly uri: string
|
||||
readonly uri: string;
|
||||
}
|
@ -33,6 +33,8 @@ import { HostedPluginReader } from '@theia/plugin-ext/lib/hosted/node/plugin-rea
|
||||
import { ConfigFileValidator } from './config-file-validator';
|
||||
import { EnvVariablesServer } from '@theia/core/lib/common/env-variables';
|
||||
import { ArduinoEnvVariablesServer } from './arduino-env-variables-server';
|
||||
import { NodeFileSystemExt } from './node-filesystem-ext';
|
||||
import { FileSystemExt, FileSystemExtPath } from '../common/protocol/filesystem-ext';
|
||||
|
||||
export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
rebind(EnvVariablesServer).to(ArduinoEnvVariablesServer).inSingletonScope();
|
||||
@ -185,4 +187,9 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
|
||||
bind(ArduinoHostedPluginReader).toSelf().inSingletonScope();
|
||||
rebind(HostedPluginReader).toService(ArduinoHostedPluginReader);
|
||||
|
||||
// File-system extension for mapping paths to URIs
|
||||
bind(NodeFileSystemExt).toSelf().inSingletonScope();
|
||||
bind(FileSystemExt).toDynamicValue(context => context.container.get(NodeFileSystemExt));
|
||||
bind(ConnectionHandler).toDynamicValue(context => new JsonRpcConnectionHandler(FileSystemExtPath, () => context.container.get(FileSystemExt))).inSingletonScope();
|
||||
});
|
||||
|
12
arduino-ide-extension/src/node/node-filesystem-ext.ts
Normal file
12
arduino-ide-extension/src/node/node-filesystem-ext.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { injectable } from 'inversify';
|
||||
import { FileUri } from '@theia/core/lib/node/file-uri';
|
||||
import { FileSystemExt } from '../common/protocol/filesystem-ext';
|
||||
|
||||
@injectable()
|
||||
export class NodeFileSystemExt implements FileSystemExt {
|
||||
|
||||
async getUri(fsPath: string): Promise<string> {
|
||||
return FileUri.create(fsPath).toString()
|
||||
}
|
||||
|
||||
}
|
@ -1,19 +1,29 @@
|
||||
import { injectable, inject } from 'inversify';
|
||||
import * as os from 'os';
|
||||
import * as temp from 'temp';
|
||||
import * as path from 'path';
|
||||
import * as fs from './fs-extra';
|
||||
import { FileUri } from '@theia/core/lib/node';
|
||||
import { ncp } from 'ncp';
|
||||
import { FileUri, BackendApplicationContribution } from '@theia/core/lib/node';
|
||||
import { ConfigService } from '../common/protocol/config-service';
|
||||
import { SketchesService, Sketch } from '../common/protocol/sketches-service';
|
||||
import URI from '@theia/core/lib/common/uri';
|
||||
|
||||
export const ALLOWED_FILE_EXTENSIONS = ['.c', '.cpp', '.h', '.hh', '.hpp', '.s', '.pde', '.ino'];
|
||||
|
||||
// TODO: `fs`: use async API
|
||||
@injectable()
|
||||
export class SketchesServiceImpl implements SketchesService {
|
||||
export class SketchesServiceImpl implements SketchesService, BackendApplicationContribution {
|
||||
|
||||
protected readonly temp = temp.track();
|
||||
|
||||
@inject(ConfigService)
|
||||
protected readonly configService: ConfigService;
|
||||
|
||||
onStop(): void {
|
||||
this.temp.cleanupSync();
|
||||
}
|
||||
|
||||
async getSketches(uri?: string): Promise<Sketch[]> {
|
||||
const sketches: Array<Sketch & { mtimeMs: number }> = [];
|
||||
let fsPath: undefined | string;
|
||||
@ -69,19 +79,26 @@ export class SketchesServiceImpl implements SketchesService {
|
||||
return this.getSketchFiles(FileUri.create(sketchDir).toString());
|
||||
}
|
||||
|
||||
async createNewSketch(parentUri?: string): Promise<Sketch> {
|
||||
const monthNames = ['january', 'february', 'march', 'april', 'may', 'june',
|
||||
'july', 'august', 'september', 'october', 'november', 'december'
|
||||
];
|
||||
async createNewSketch(): Promise<Sketch> {
|
||||
const monthNames = ['jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec'];
|
||||
const today = new Date();
|
||||
const uri = !!parentUri ? parentUri : (await this.configService.getConfiguration()).sketchDirUri;
|
||||
const parent = FileUri.fsPath(uri);
|
||||
|
||||
const parent = await new Promise<string>((resolve, reject) => {
|
||||
this.temp.mkdir({ prefix: '.arduinoProIDE' }, (err, dirPath) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
return;
|
||||
}
|
||||
resolve(dirPath);
|
||||
})
|
||||
})
|
||||
const sketchBaseName = `sketch_${monthNames[today.getMonth()]}${today.getDate()}`;
|
||||
const config = await this.configService.getConfiguration();
|
||||
const user = FileUri.fsPath(config.sketchDirUri);
|
||||
let sketchName: string | undefined;
|
||||
for (let i = 97; i < 97 + 26; i++) {
|
||||
let sketchNameCandidate = `${sketchBaseName}${String.fromCharCode(i)}`;
|
||||
if (fs.existsSync(path.join(parent, sketchNameCandidate))) {
|
||||
// Note: we check the future destination folder (`directories.user`) for name collision and not the temp folder!
|
||||
if (fs.existsSync(path.join(user, sketchNameCandidate))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -96,14 +113,13 @@ export class SketchesServiceImpl implements SketchesService {
|
||||
const sketchDir = path.join(parent, sketchName)
|
||||
const sketchFile = path.join(sketchDir, `${sketchName}.ino`);
|
||||
await fs.mkdirp(sketchDir);
|
||||
await fs.writeFile(sketchFile, `
|
||||
void setup() {
|
||||
// put your setup code here, to run once:
|
||||
await fs.writeFile(sketchFile, `void setup() {
|
||||
// put your setup code here, to run once:
|
||||
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// put your main code here, to run repeatedly:
|
||||
// put your main code here, to run repeatedly:
|
||||
|
||||
}
|
||||
`, { encoding: 'utf8' });
|
||||
@ -113,6 +129,23 @@ void loop() {
|
||||
}
|
||||
}
|
||||
|
||||
async getSketchFolder(uri: string): Promise<Sketch | undefined> {
|
||||
if (!uri) {
|
||||
return undefined;
|
||||
}
|
||||
let currentUri = new URI(uri);
|
||||
while (currentUri && !currentUri.path.isRoot) {
|
||||
if (await this.isSketchFolder(currentUri.toString())) {
|
||||
return {
|
||||
name: currentUri.path.base,
|
||||
uri: currentUri.toString()
|
||||
};
|
||||
}
|
||||
currentUri = currentUri.parent;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
async isSketchFolder(uri: string): Promise<boolean> {
|
||||
const fsPath = FileUri.fsPath(uri);
|
||||
if (fs.existsSync(fsPath) && fs.lstatSync(fsPath).isDirectory()) {
|
||||
@ -126,4 +159,28 @@ void loop() {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
async isTemp(sketch: Sketch): Promise<boolean> {
|
||||
const sketchPath = FileUri.fsPath(sketch.uri);
|
||||
return sketchPath.indexOf('.arduinoProIDE') !== -1 && sketchPath.startsWith(os.tmpdir());
|
||||
}
|
||||
|
||||
async copy(sketch: Sketch, { destinationUri }: { destinationUri: string }): Promise<string> {
|
||||
const source = FileUri.fsPath(sketch.uri);
|
||||
if (await !fs.exists(source)) {
|
||||
throw new Error(`Sketch does not exist: ${sketch}`);
|
||||
}
|
||||
const destination = FileUri.fsPath(destinationUri);
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
ncp.ncp(source, destination, error => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
return FileUri.create(destination).toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -2690,6 +2690,13 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-5.2.7.tgz#315d570ccb56c53452ff8638738df60726d5b6ea"
|
||||
integrity sha512-NYrtPht0wGzhwe9+/idPaBB+TqkY9AhTvOLMkThm0IoEfLaiVQZwBwyJ5puCkO3AUCWrmcoePjp2mbFocKy4SQ==
|
||||
|
||||
"@types/ncp@^2.0.4":
|
||||
version "2.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/ncp/-/ncp-2.0.4.tgz#16c9e7fa2c849d429a1b142648987164b06bf490"
|
||||
integrity sha512-erpimpT1pH8QfeNg77ypnjwz6CGMqrnL4DewVbqFzD9FXzSULjmG3KzjZnLNe7bzTSZm2W9DpkHyqop1g1KmgQ==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/node-fetch@^2.5.7":
|
||||
version "2.5.7"
|
||||
resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.5.7.tgz#20a2afffa882ab04d44ca786449a276f9f6bbf3c"
|
||||
|
Loading…
x
Reference in New Issue
Block a user