mirror of
https://github.com/arduino/arduino-ide.git
synced 2025-06-06 12:16:33 +00:00
Merge pull request #66 from bcmi-labs/various-bug-fixes
Various bug fixes
This commit is contained in:
commit
065f9f042b
@ -31,6 +31,7 @@
|
||||
"p-queue": "^5.0.0",
|
||||
"ps-tree": "^1.2.0",
|
||||
"tree-kill": "^1.2.1",
|
||||
"upath": "^1.1.2",
|
||||
"which": "^1.3.1"
|
||||
},
|
||||
"scripts": {
|
||||
|
@ -1,25 +0,0 @@
|
||||
import { injectable, inject } from "inversify";
|
||||
import { MenuContribution, MenuModelRegistry, MenuPath } from "@theia/core";
|
||||
import { CommonMenus } from "@theia/core/lib/browser";
|
||||
import { ArduinoCommands } from "./arduino-commands";
|
||||
|
||||
export namespace ArduinoToolbarContextMenu {
|
||||
export const OPEN_SKETCH_PATH: MenuPath = ['arduino-open-sketch-context-menu'];
|
||||
export const OPEN_GROUP: MenuPath = [...OPEN_SKETCH_PATH, '1_open'];
|
||||
export const WS_SKETCHES_GROUP: MenuPath = [...OPEN_SKETCH_PATH, '2_sketches'];
|
||||
export const EXAMPLE_SKETCHES_GROUP: MenuPath = [...OPEN_SKETCH_PATH, '3_examples'];
|
||||
}
|
||||
|
||||
@injectable()
|
||||
export class ArduinoToolbarMenuContribution implements MenuContribution {
|
||||
|
||||
constructor(
|
||||
@inject(MenuModelRegistry) protected readonly menuRegistry: MenuModelRegistry) {
|
||||
}
|
||||
|
||||
registerMenus(registry: MenuModelRegistry) {
|
||||
registry.registerMenuAction([...CommonMenus.FILE, '0_new_sletch'], {
|
||||
commandId: ArduinoCommands.NEW_SKETCH.id
|
||||
})
|
||||
}
|
||||
}
|
@ -14,9 +14,7 @@ import { QuickPickService } from '@theia/core/lib/common/quick-pick-service';
|
||||
import { BoardsListWidgetFrontendContribution } from './boards/boards-widget-frontend-contribution';
|
||||
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 } from '@theia/core';
|
||||
import { WorkspaceService } from '@theia/workspace/lib/browser/workspace-service';
|
||||
import { SketchFactory } from './sketch-factory';
|
||||
import { SelectionService, MenuContribution, MenuModelRegistry, MAIN_MENU_BAR, MenuPath } from '@theia/core';
|
||||
import { ArduinoToolbar } from './toolbar/arduino-toolbar';
|
||||
import { EditorManager, EditorMainMenu } from '@theia/editor/lib/browser';
|
||||
import {
|
||||
@ -26,12 +24,10 @@ import {
|
||||
StatusBar,
|
||||
ShellLayoutRestorer,
|
||||
StatusBarAlignment,
|
||||
QuickOpenService,
|
||||
LabelProvider
|
||||
QuickOpenService
|
||||
} from '@theia/core/lib/browser';
|
||||
import { OpenFileDialogProps, FileDialogService } from '@theia/filesystem/lib/browser/file-dialog';
|
||||
import { FileSystem, FileStat } from '@theia/filesystem/lib/common';
|
||||
import { ArduinoToolbarContextMenu } from './arduino-file-menu';
|
||||
import { Sketch, SketchesService } from '../common/protocol/sketches-service';
|
||||
import { WindowService } from '@theia/core/lib/browser/window/window-service';
|
||||
import { CommonCommands, CommonMenus } from '@theia/core/lib/browser/common-frontend-contribution';
|
||||
@ -47,12 +43,20 @@ import { MonitorService } from '../common/protocol/monitor-service';
|
||||
import { ConfigService } from '../common/protocol/config-service';
|
||||
import { MonitorConnection } from './monitor/monitor-connection';
|
||||
import { MonitorViewContribution } from './monitor/monitor-view-contribution';
|
||||
import { ArduinoWorkspaceService } from './arduino-workspace-service';
|
||||
|
||||
export namespace ArduinoMenus {
|
||||
export const SKETCH = [...MAIN_MENU_BAR, '3_sketch'];
|
||||
export const TOOLS = [...MAIN_MENU_BAR, '4_tools'];
|
||||
}
|
||||
|
||||
export namespace ArduinoToolbarContextMenu {
|
||||
export const OPEN_SKETCH_PATH: MenuPath = ['arduino-open-sketch-context-menu'];
|
||||
export const OPEN_GROUP: MenuPath = [...OPEN_SKETCH_PATH, '1_open'];
|
||||
export const WS_SKETCHES_GROUP: MenuPath = [...OPEN_SKETCH_PATH, '2_sketches'];
|
||||
export const EXAMPLE_SKETCHES_GROUP: MenuPath = [...OPEN_SKETCH_PATH, '3_examples'];
|
||||
}
|
||||
|
||||
export namespace ArduinoAdvancedMode {
|
||||
export const LS_ID = 'arduino-advanced-mode';
|
||||
export const TOGGLED: boolean = (() => {
|
||||
@ -61,7 +65,6 @@ export namespace ArduinoAdvancedMode {
|
||||
})();
|
||||
}
|
||||
|
||||
|
||||
@injectable()
|
||||
export class ArduinoFrontendContribution implements TabBarToolbarContribution, CommandContribution, MenuContribution {
|
||||
|
||||
@ -95,9 +98,6 @@ export class ArduinoFrontendContribution implements TabBarToolbarContribution, C
|
||||
@inject(SelectionService)
|
||||
protected readonly selectionService: SelectionService;
|
||||
|
||||
@inject(SketchFactory)
|
||||
protected readonly sketchFactory: SketchFactory;
|
||||
|
||||
@inject(EditorManager)
|
||||
protected readonly editorManager: EditorManager;
|
||||
|
||||
@ -117,7 +117,7 @@ export class ArduinoFrontendContribution implements TabBarToolbarContribution, C
|
||||
protected readonly windowService: WindowService;
|
||||
|
||||
@inject(SketchesService)
|
||||
protected readonly sketches: SketchesService;
|
||||
protected readonly sketchService: SketchesService;
|
||||
|
||||
@inject(BoardsConfigDialog)
|
||||
protected readonly boardsConfigDialog: BoardsConfigDialog;
|
||||
@ -134,17 +134,15 @@ export class ArduinoFrontendContribution implements TabBarToolbarContribution, C
|
||||
@inject(ShellLayoutRestorer)
|
||||
protected readonly layoutRestorer: ShellLayoutRestorer;
|
||||
|
||||
@inject(LabelProvider)
|
||||
protected readonly labelProvider: LabelProvider;
|
||||
|
||||
@inject(QuickOpenService)
|
||||
protected readonly quickOpenService: QuickOpenService;
|
||||
|
||||
@inject(WorkspaceService)
|
||||
protected readonly workspaceService: WorkspaceService;
|
||||
@inject(ArduinoWorkspaceService)
|
||||
protected readonly workspaceService: ArduinoWorkspaceService;
|
||||
|
||||
@inject(ConfigService)
|
||||
protected readonly configService: ConfigService;
|
||||
|
||||
@inject(MonitorConnection)
|
||||
protected readonly monitorConnection: MonitorConnection;
|
||||
|
||||
@ -304,7 +302,7 @@ export class ArduinoFrontendContribution implements TabBarToolbarContribution, C
|
||||
registry.registerCommand(ArduinoCommands.OPEN_SKETCH, {
|
||||
isEnabled: () => true,
|
||||
execute: async (sketch: Sketch) => {
|
||||
this.openSketchFilesInNewWindow(sketch.uri);
|
||||
this.workspaceService.openSketchFilesInNewWindow(sketch.uri);
|
||||
}
|
||||
})
|
||||
registry.registerCommand(ArduinoCommands.SAVE_SKETCH, {
|
||||
@ -322,7 +320,8 @@ export class ArduinoFrontendContribution implements TabBarToolbarContribution, C
|
||||
uri = uri.withPath(uri.path.dir.dir);
|
||||
}
|
||||
|
||||
await this.sketchFactory.createNewSketch(uri);
|
||||
const sketch = await this.sketchService.createNewSketch(uri.toString());
|
||||
this.workspaceService.openSketchFilesInNewWindow(sketch.uri);
|
||||
} catch (e) {
|
||||
await this.messageService.error(e.toString());
|
||||
}
|
||||
@ -388,7 +387,11 @@ export class ArduinoFrontendContribution implements TabBarToolbarContribution, C
|
||||
registry.registerMenuAction(CommonMenus.HELP, {
|
||||
commandId: ArduinoCommands.TOGGLE_ADVANCED_MODE.id,
|
||||
label: 'Advanced Mode'
|
||||
})
|
||||
});
|
||||
|
||||
registry.registerMenuAction([...CommonMenus.FILE, '0_new_sketch'], {
|
||||
commandId: ArduinoCommands.NEW_SKETCH.id
|
||||
});
|
||||
}
|
||||
|
||||
protected getMenuId(menuPath: string[]): string {
|
||||
@ -397,8 +400,8 @@ export class ArduinoFrontendContribution implements TabBarToolbarContribution, C
|
||||
return menuId;
|
||||
}
|
||||
|
||||
protected registerSketchesInMenu(registry: MenuModelRegistry) {
|
||||
this.getWorkspaceSketches().then(sketches => {
|
||||
protected async registerSketchesInMenu(registry: MenuModelRegistry): Promise<void> {
|
||||
this.sketchService.getSketches().then(sketches => {
|
||||
this.wsSketchCount = sketches.length;
|
||||
sketches.forEach(sketch => {
|
||||
const command: Command = {
|
||||
@ -416,48 +419,12 @@ export class ArduinoFrontendContribution implements TabBarToolbarContribution, C
|
||||
})
|
||||
}
|
||||
|
||||
protected async getWorkspaceSketches(): Promise<Sketch[]> {
|
||||
|
||||
let sketches: Sketch[] = [];
|
||||
const config = await this.configService.getConfiguration();
|
||||
const stat = await this.fileSystem.getFileStat(config.sketchDirUri);
|
||||
if (!!stat) {
|
||||
sketches = await this.sketches.getSketches(stat);
|
||||
}
|
||||
return sketches;
|
||||
}
|
||||
|
||||
protected async openSketchFilesInNewWindow(uri: string) {
|
||||
const url = new URL(window.location.href);
|
||||
const currentSketch = url.searchParams.get('sketch');
|
||||
// Nothing to do if we want to open the same sketch which is already opened.
|
||||
const sketchUri = new URI(uri);
|
||||
if (!!currentSketch && new URI(currentSketch).toString() === sketchUri.toString()) {
|
||||
this.messageService.info(`The '${this.labelProvider.getLongName(sketchUri)}' is already opened.`);
|
||||
// NOOP.
|
||||
return;
|
||||
}
|
||||
// Preserve the current window if the `sketch` is not in the `searchParams`.
|
||||
url.searchParams.set('sketch', uri);
|
||||
const hash = await this.fileSystem.getFsPath(sketchUri.toString());
|
||||
if (hash) {
|
||||
url.hash = hash;
|
||||
}
|
||||
if (!currentSketch) {
|
||||
setTimeout(() => window.location.href = url.toString(), 100);
|
||||
return;
|
||||
}
|
||||
this.windowService.openNewWindow(url.toString());
|
||||
}
|
||||
|
||||
async openSketchFiles(uri: string) {
|
||||
const fileStat = await this.fileSystem.getFileStat(uri);
|
||||
if (fileStat) {
|
||||
const uris = await this.sketches.getSketchFiles(fileStat);
|
||||
async openSketchFiles(uri: string): Promise<void> {
|
||||
this.sketchService.getSketchFiles(uri).then(uris => {
|
||||
for (const uri of uris) {
|
||||
this.editorManager.open(new URI(uri));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -481,7 +448,7 @@ export class ArduinoFrontendContribution implements TabBarToolbarContribution, C
|
||||
if (destinationFile && !destinationFile.isDirectory) {
|
||||
const message = await this.validate(destinationFile);
|
||||
if (!message) {
|
||||
await this.openSketchFilesInNewWindow(destinationFileUri.toString());
|
||||
await this.workspaceService.openSketchFilesInNewWindow(destinationFileUri.toString());
|
||||
return destinationFileUri;
|
||||
} else {
|
||||
this.messageService.warn(message);
|
||||
|
@ -25,12 +25,10 @@ import { ToolOutputService } from '../common/protocol/tool-output-service';
|
||||
import { ToolOutputServiceClientImpl } from './tool-output/client-service-impl';
|
||||
import { BoardsServiceClientImpl } from './boards/boards-service-client-impl';
|
||||
import { WorkspaceService } from '@theia/workspace/lib/browser/workspace-service';
|
||||
import { AWorkspaceService } from './arduino-workspace-service';
|
||||
import { ArduinoWorkspaceService } from './arduino-workspace-service';
|
||||
import { ThemeService } from '@theia/core/lib/browser/theming';
|
||||
import { ArduinoTheme } from './arduino-theme';
|
||||
import { ArduinoToolbarMenuContribution } from './arduino-file-menu';
|
||||
import { MenuContribution } from '@theia/core';
|
||||
import { SketchFactory } from './sketch-factory';
|
||||
import { OutlineViewContribution } from '@theia/outline-view/lib/browser/outline-view-contribution';
|
||||
import { SilentOutlineViewContribution } from './customization/silent-outline-contribution';
|
||||
import { ProblemContribution } from '@theia/markers/lib/browser/problem/problem-contribution';
|
||||
@ -41,12 +39,12 @@ import { ArduinoToolbarContribution } from './toolbar/arduino-toolbar-contributi
|
||||
import { OutputToolbarContribution } from '@theia/output/lib/browser/output-toolbar-contribution';
|
||||
import { ArduinoOutputToolContribution } from './customization/silent-output-tool-contribution';
|
||||
import { EditorContribution } from '@theia/editor/lib/browser/editor-contribution';
|
||||
import { CustomEditorContribution } from './customization/custom-editor-contribution';
|
||||
import { ArduinoEditorContribution } from './customization/arduino-editor-contribution';
|
||||
import { MonacoStatusBarContribution } from '@theia/monaco/lib/browser/monaco-status-bar-contribution';
|
||||
import { SilentMonacoStatusBarContribution } from './customization/silent-monaco-status-bar-contribution';
|
||||
import { ArduinoMonacoStatusBarContribution } from './customization/arduino-monaco-status-bar-contribution';
|
||||
import { ApplicationShell } from '@theia/core/lib/browser';
|
||||
import { CustomApplicationShell } from './customization/custom-application-shell';
|
||||
import { CustomFrontendApplication } from './customization/custom-frontend-application';
|
||||
import { ArduinoApplicationShell } from './customization/arduino-application-shell';
|
||||
import { ArduinoFrontendApplication } from './customization/arduino-frontend-application';
|
||||
import { BoardsConfigDialog, BoardsConfigDialogProps } from './boards/boards-config-dialog';
|
||||
import { BoardsConfigDialogWidget } from './boards/boards-config-dialog-widget';
|
||||
import { ScmContribution } from '@theia/scm/lib/browser/scm-contribution';
|
||||
@ -63,6 +61,12 @@ import { MonitorWidget } from './monitor/monitor-widget';
|
||||
import { MonitorViewContribution } from './monitor/monitor-view-contribution';
|
||||
import { MonitorConnection } from './monitor/monitor-connection';
|
||||
import { MonitorModel } from './monitor/monitor-model';
|
||||
import { MonacoEditorProvider } from '@theia/monaco/lib/browser/monaco-editor-provider';
|
||||
import { ArduinoMonacoEditorProvider } from './editor/arduino-monaco-editor-provider';
|
||||
import { TabBarDecoratorService } from '@theia/core/lib/browser/shell/tab-bar-decorator';
|
||||
import { ArduinoTabBarDecoratorService } from './shell/arduino-tab-bar-decorator';
|
||||
import { ProblemManager } from '@theia/markers/lib/browser';
|
||||
import { ArduinoProblemManager } from './markers/arduino-problem-manager';
|
||||
const ElementQueries = require('css-element-queries/src/ElementQueries');
|
||||
|
||||
export default new ContainerModule((bind: interfaces.Bind, unbind: interfaces.Unbind, isBound: interfaces.IsBound, rebind: interfaces.Rebind) => {
|
||||
@ -75,7 +79,6 @@ export default new ContainerModule((bind: interfaces.Bind, unbind: interfaces.Un
|
||||
bind(MenuContribution).toService(ArduinoFrontendContribution);
|
||||
bind(TabBarToolbarContribution).toService(ArduinoFrontendContribution);
|
||||
bind(FrontendApplicationContribution).toService(ArduinoFrontendContribution);
|
||||
bind(MenuContribution).to(ArduinoToolbarMenuContribution).inSingletonScope();
|
||||
|
||||
bind(ArduinoToolbarContribution).toSelf().inSingletonScope();
|
||||
bind(FrontendApplicationContribution).toService(ArduinoToolbarContribution);
|
||||
@ -183,9 +186,8 @@ export default new ContainerModule((bind: interfaces.Bind, unbind: interfaces.Un
|
||||
return client;
|
||||
}).inSingletonScope();
|
||||
|
||||
bind(AWorkspaceService).toSelf().inSingletonScope();
|
||||
rebind(WorkspaceService).to(AWorkspaceService).inSingletonScope();
|
||||
bind(SketchFactory).toSelf().inSingletonScope();
|
||||
bind(ArduinoWorkspaceService).toSelf().inSingletonScope();
|
||||
rebind(WorkspaceService).to(ArduinoWorkspaceService).inSingletonScope();
|
||||
|
||||
const themeService = ThemeService.get();
|
||||
themeService.register(...ArduinoTheme.themes);
|
||||
@ -201,16 +203,34 @@ export default new ContainerModule((bind: interfaces.Bind, unbind: interfaces.Un
|
||||
unbind(OutputToolbarContribution);
|
||||
bind(OutputToolbarContribution).to(ArduinoOutputToolContribution).inSingletonScope();
|
||||
unbind(EditorContribution);
|
||||
bind(EditorContribution).to(CustomEditorContribution).inSingletonScope();
|
||||
bind(EditorContribution).to(ArduinoEditorContribution).inSingletonScope();
|
||||
unbind(MonacoStatusBarContribution);
|
||||
bind(MonacoStatusBarContribution).to(SilentMonacoStatusBarContribution).inSingletonScope();
|
||||
bind(MonacoStatusBarContribution).to(ArduinoMonacoStatusBarContribution).inSingletonScope();
|
||||
unbind(ApplicationShell);
|
||||
bind(ApplicationShell).to(CustomApplicationShell).inSingletonScope();
|
||||
bind(ApplicationShell).to(ArduinoApplicationShell).inSingletonScope();
|
||||
unbind(ScmContribution);
|
||||
bind(ScmContribution).to(SilentScmContribution).inSingletonScope();
|
||||
unbind(SearchInWorkspaceFrontendContribution);
|
||||
bind(SearchInWorkspaceFrontendContribution).to(SilentSearchInWorkspaceContribution).inSingletonScope();
|
||||
} else {
|
||||
// We use this CSS class on the body to modify the visibbility of the close button for the editors and views.
|
||||
document.body.classList.add(ArduinoAdvancedMode.LS_ID);
|
||||
}
|
||||
unbind(FrontendApplication);
|
||||
bind(FrontendApplication).to(CustomFrontendApplication).inSingletonScope();
|
||||
bind(FrontendApplication).to(ArduinoFrontendApplication).inSingletonScope();
|
||||
|
||||
// monaco customizations
|
||||
unbind(MonacoEditorProvider);
|
||||
bind(ArduinoMonacoEditorProvider).toSelf().inSingletonScope();
|
||||
bind(MonacoEditorProvider).toService(ArduinoMonacoEditorProvider);
|
||||
|
||||
// decorator customizations
|
||||
unbind(TabBarDecoratorService);
|
||||
bind(ArduinoTabBarDecoratorService).toSelf().inSingletonScope();
|
||||
bind(TabBarDecoratorService).toService(ArduinoTabBarDecoratorService);
|
||||
|
||||
// problem markers
|
||||
unbind(ProblemManager);
|
||||
bind(ArduinoProblemManager).toSelf().inSingletonScope();
|
||||
bind(ProblemManager).toService(ArduinoProblemManager);
|
||||
});
|
||||
|
@ -19,9 +19,10 @@ export class ArduinoTheme {
|
||||
deactivate() {
|
||||
ARDUINO_CSS.unuse();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static readonly themes: Theme[] = [
|
||||
ArduinoTheme.arduino
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
}
|
||||
|
@ -1,49 +1,129 @@
|
||||
import { WorkspaceService } from "@theia/workspace/lib/browser/workspace-service";
|
||||
import { injectable, inject } from "inversify";
|
||||
import { WorkspaceServer } from "@theia/workspace/lib/common";
|
||||
import { FileSystem, FileStat } from "@theia/filesystem/lib/common";
|
||||
import URI from "@theia/core/lib/common/uri";
|
||||
import { SketchFactory } from "./sketch-factory";
|
||||
import { ConfigService } from "../common/protocol/config-service";
|
||||
import { injectable, inject } from 'inversify';
|
||||
import { toUnix } from 'upath';
|
||||
import URI from '@theia/core/lib/common/uri';
|
||||
import { isWindows } from '@theia/core/lib/common/os';
|
||||
import { LabelProvider } from '@theia/core/lib/browser';
|
||||
import { WorkspaceService } from '@theia/workspace/lib/browser/workspace-service';
|
||||
import { ConfigService } from '../common/protocol/config-service';
|
||||
import { SketchesService } from '../common/protocol/sketches-service';
|
||||
import { ArduinoAdvancedMode } from './arduino-frontend-contribution';
|
||||
|
||||
/**
|
||||
* This is workaround to have custom frontend binding for the default workspace, although we
|
||||
* already have a custom binding for the backend.
|
||||
*/
|
||||
@injectable()
|
||||
export class AWorkspaceService extends WorkspaceService {
|
||||
export class ArduinoWorkspaceService extends WorkspaceService {
|
||||
|
||||
@inject(WorkspaceServer)
|
||||
protected readonly workspaceServer: WorkspaceServer;
|
||||
|
||||
@inject(FileSystem)
|
||||
protected readonly fileSystem: FileSystem;
|
||||
|
||||
@inject(SketchFactory)
|
||||
protected readonly sketchFactory: SketchFactory;
|
||||
@inject(SketchesService)
|
||||
protected readonly sketchService: SketchesService;
|
||||
|
||||
@inject(ConfigService)
|
||||
protected readonly configService: ConfigService;
|
||||
|
||||
protected async getDefaultWorkspacePath(): Promise<string | undefined> {
|
||||
let result = await super.getDefaultWorkspacePath();
|
||||
if (!result) {
|
||||
const config = await this.configService.getConfiguration();
|
||||
result = config.sketchDirUri;
|
||||
@inject(LabelProvider)
|
||||
protected readonly labelProvider: LabelProvider;
|
||||
|
||||
async getDefaultWorkspacePath(): Promise<string | undefined> {
|
||||
const url = new URL(window.location.href);
|
||||
// If `sketch` is set and valid, we use it as is.
|
||||
// `sketch` is set as an encoded URI string.
|
||||
const sketch = url.searchParams.get('sketch');
|
||||
if (sketch) {
|
||||
const sketchDirUri = new URI(sketch).toString();
|
||||
if (await this.sketchService.isSketchFolder(sketchDirUri)) {
|
||||
if (await this.configService.isInSketchDir(sketchDirUri)) {
|
||||
if (ArduinoAdvancedMode.TOGGLED) {
|
||||
return (await this.configService.getConfiguration()).sketchDirUri
|
||||
} else {
|
||||
return sketchDirUri;
|
||||
}
|
||||
}
|
||||
return (await this.configService.getConfiguration()).sketchDirUri
|
||||
}
|
||||
}
|
||||
|
||||
const stat = await this.fileSystem.getFileStat(result);
|
||||
const { hash } = window.location;
|
||||
// Note: here, the `uriPath` was defined as new `URI(yourValidFsPath).path` so we have to map it to a valid FS path first.
|
||||
// This is important for Windows only and a NOOP on UNIX.
|
||||
if (hash.length > 1 && hash.startsWith('#')) {
|
||||
let uri = this.toUri(hash.slice(1));
|
||||
if (uri && await this.sketchService.isSketchFolder(uri)) {
|
||||
return this.openSketchFilesInNewWindow(uri);
|
||||
}
|
||||
}
|
||||
|
||||
// If we cannot acquire the FS path from the `location.hash` we try to get the most recently used workspace that was a valid sketch folder.
|
||||
// XXX: Check if `WorkspaceServer#getRecentWorkspaces()` returns with inverse-chrolonolgical order.
|
||||
const candidateUris = await this.server.getRecentWorkspaces();
|
||||
for (const uri of candidateUris) {
|
||||
if (await this.sketchService.isSketchFolder(uri)) {
|
||||
return this.openSketchFilesInNewWindow(uri);
|
||||
}
|
||||
}
|
||||
|
||||
const config = await this.configService.getConfiguration();
|
||||
const { sketchDirUri } = config;
|
||||
const stat = await this.fileSystem.getFileStat(sketchDirUri);
|
||||
if (!stat) {
|
||||
// workspace does not exist yet, create it
|
||||
await this.fileSystem.createFolder(result);
|
||||
await this.sketchFactory.createNewSketch(new URI(result));
|
||||
// The folder for the workspace root does not exist yet, create it.
|
||||
await this.fileSystem.createFolder(sketchDirUri);
|
||||
await this.sketchService.createNewSketch(sketchDirUri);
|
||||
}
|
||||
|
||||
return result;
|
||||
const sketches = await this.sketchService.getSketches(sketchDirUri);
|
||||
if (!sketches.length) {
|
||||
const sketch = await this.sketchService.createNewSketch(sketchDirUri);
|
||||
sketches.unshift(sketch);
|
||||
}
|
||||
|
||||
const uri = sketches[0].uri;
|
||||
this.server.setMostRecentlyUsedWorkspace(uri);
|
||||
this.openSketchFilesInNewWindow(uri);
|
||||
if (ArduinoAdvancedMode.TOGGLED && await this.configService.isInSketchDir(uri)) {
|
||||
return (await this.configService.getConfiguration()).sketchDirUri;
|
||||
}
|
||||
return uri;
|
||||
}
|
||||
|
||||
protected async setWorkspace(workspaceStat: FileStat | undefined): Promise<void> {
|
||||
await super.setWorkspace(workspaceStat);
|
||||
private toUri(uriPath: string | undefined): string | undefined {
|
||||
if (uriPath) {
|
||||
return new URI(toUnix(uriPath.slice(isWindows && uriPath.startsWith('/') ? 1 : 0))).withScheme('file').toString();
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
}
|
||||
async openSketchFilesInNewWindow(uri: string): Promise<string> {
|
||||
const url = new URL(window.location.href);
|
||||
const currentSketch = url.searchParams.get('sketch');
|
||||
// Nothing to do if we want to open the same sketch which is already opened.
|
||||
const sketchUri = new URI(uri);
|
||||
if (!!currentSketch && new URI(currentSketch).toString() === sketchUri.toString()) {
|
||||
return uri;
|
||||
}
|
||||
|
||||
url.searchParams.set('sketch', uri);
|
||||
// If in advanced mode, we root folder of all sketch folders as the hash, so the default workspace will be opened on the root
|
||||
// Note: we set the `new URI(myValidUri).path.toString()` as the `hash`. See:
|
||||
// - https://github.com/eclipse-theia/theia/blob/8196e9dcf9c8de8ea0910efeb5334a974f426966/packages/workspace/src/browser/workspace-service.ts#L143 and
|
||||
// - https://github.com/eclipse-theia/theia/blob/8196e9dcf9c8de8ea0910efeb5334a974f426966/packages/workspace/src/browser/workspace-service.ts#L423
|
||||
if (ArduinoAdvancedMode.TOGGLED && await this.configService.isInSketchDir(uri)) {
|
||||
url.hash = new URI((await this.configService.getConfiguration()).sketchDirUri).path.toString();
|
||||
} else {
|
||||
// Otherwise, we set the hash as is
|
||||
const hash = await this.fileSystem.getFsPath(sketchUri.toString());
|
||||
if (hash) {
|
||||
url.hash = sketchUri.path.toString()
|
||||
}
|
||||
}
|
||||
|
||||
// Preserve the current window if the `sketch` is not in the `searchParams`.
|
||||
if (!currentSketch) {
|
||||
setTimeout(() => window.location.href = url.toString(), 100);
|
||||
return uri;
|
||||
}
|
||||
this.windowService.openNewWindow(url.toString());
|
||||
return uri;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ export abstract class ListItemRenderer<T> {
|
||||
protected onClick = (event: React.SyntheticEvent<HTMLAnchorElement, Event>) => {
|
||||
const { target } = event.nativeEvent;
|
||||
if (target instanceof HTMLAnchorElement) {
|
||||
this.windowService.openNewWindow(target.href);
|
||||
this.windowService.openNewWindow(target.href, { external: true });
|
||||
event.nativeEvent.preventDefault();
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { ApplicationShell, Widget, Saveable, FocusTracker, Message } from '@theia/core/lib/browser';
|
||||
import { EditorWidget } from '@theia/editor/lib/browser';
|
||||
|
||||
export class CustomApplicationShell extends ApplicationShell {
|
||||
export class ArduinoApplicationShell extends ApplicationShell {
|
||||
|
||||
protected refreshBottomPanelToggleButton() {
|
||||
}
|
||||
@ -30,4 +30,4 @@ export class CustomApplicationShell extends ApplicationShell {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,8 +1,9 @@
|
||||
import {EditorContribution} from '@theia/editor/lib/browser/editor-contribution';
|
||||
import { EditorContribution } from '@theia/editor/lib/browser/editor-contribution';
|
||||
import { TextEditor } from '@theia/editor/lib/browser';
|
||||
import { StatusBarAlignment } from '@theia/core/lib/browser';
|
||||
|
||||
export class CustomEditorContribution extends EditorContribution {
|
||||
export class ArduinoEditorContribution extends EditorContribution {
|
||||
|
||||
protected updateLanguageStatus(editor: TextEditor | undefined): void {
|
||||
}
|
||||
|
||||
@ -18,4 +19,5 @@ export class CustomEditorContribution extends EditorContribution {
|
||||
priority: 100
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
import { injectable } from 'inversify';
|
||||
import { FileMenuContribution } from '@theia/workspace/lib/browser';
|
||||
import { MenuModelRegistry } from '@theia/core';
|
||||
|
||||
@injectable()
|
||||
export class ArduinoFileMenuContribution extends FileMenuContribution {
|
||||
|
||||
registerMenus(registry: MenuModelRegistry) {
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
import { injectable, inject } from 'inversify';
|
||||
import { FileSystem } from '@theia/filesystem/lib/common';
|
||||
import { FrontendApplication } from '@theia/core/lib/browser';
|
||||
import { ArduinoFrontendContribution } from '../arduino-frontend-contribution';
|
||||
|
||||
@injectable()
|
||||
export class ArduinoFrontendApplication extends FrontendApplication {
|
||||
|
||||
@inject(ArduinoFrontendContribution)
|
||||
protected readonly frontendContribution: ArduinoFrontendContribution;
|
||||
|
||||
@inject(FileSystem)
|
||||
protected readonly fileSystem: FileSystem;
|
||||
|
||||
protected async initializeLayout(): Promise<void> {
|
||||
await super.initializeLayout();
|
||||
const location = new URL(window.location.href);
|
||||
const sketchPath = location.searchParams.get('sketch');
|
||||
if (sketchPath && await this.fileSystem.exists(sketchPath)) {
|
||||
this.frontendContribution.openSketchFiles(decodeURIComponent(sketchPath));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
import { MonacoStatusBarContribution } from '@theia/monaco/lib/browser/monaco-status-bar-contribution';
|
||||
|
||||
export class ArduinoMonacoStatusBarContribution extends MonacoStatusBarContribution {
|
||||
|
||||
protected setConfigTabSizeWidget() {
|
||||
}
|
||||
|
||||
protected setLineEndingWidget() {
|
||||
}
|
||||
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
import { injectable } from "inversify";
|
||||
import { CommonFrontendContribution, CommonMenus, CommonCommands } from "@theia/core/lib/browser";
|
||||
import { MenuModelRegistry } from "@theia/core";
|
||||
import { ArduinoAdvancedMode } from "../arduino-frontend-contribution";
|
||||
|
||||
@injectable()
|
||||
export class CustomCommonFrontendContribution extends CommonFrontendContribution {
|
||||
registerMenus(registry: MenuModelRegistry): void {
|
||||
if (!ArduinoAdvancedMode.TOGGLED) {
|
||||
registry.registerSubmenu(CommonMenus.FILE, 'File');
|
||||
registry.registerSubmenu(CommonMenus.EDIT, 'Edit');
|
||||
|
||||
registry.registerSubmenu(CommonMenus.FILE_SETTINGS_SUBMENU, 'Settings');
|
||||
|
||||
registry.registerMenuAction(CommonMenus.EDIT_UNDO, {
|
||||
commandId: CommonCommands.UNDO.id,
|
||||
order: '0'
|
||||
});
|
||||
registry.registerMenuAction(CommonMenus.EDIT_UNDO, {
|
||||
commandId: CommonCommands.REDO.id,
|
||||
order: '1'
|
||||
});
|
||||
|
||||
registry.registerMenuAction(CommonMenus.EDIT_FIND, {
|
||||
commandId: CommonCommands.FIND.id,
|
||||
order: '0'
|
||||
});
|
||||
registry.registerMenuAction(CommonMenus.EDIT_FIND, {
|
||||
commandId: CommonCommands.REPLACE.id,
|
||||
order: '1'
|
||||
});
|
||||
|
||||
registry.registerMenuAction(CommonMenus.EDIT_CLIPBOARD, {
|
||||
commandId: CommonCommands.CUT.id,
|
||||
order: '0'
|
||||
});
|
||||
registry.registerMenuAction(CommonMenus.EDIT_CLIPBOARD, {
|
||||
commandId: CommonCommands.COPY.id,
|
||||
order: '1'
|
||||
});
|
||||
registry.registerMenuAction(CommonMenus.EDIT_CLIPBOARD, {
|
||||
commandId: CommonCommands.PASTE.id,
|
||||
order: '2'
|
||||
});
|
||||
} else {
|
||||
super.registerMenus(registry);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
import { injectable } from "inversify";
|
||||
import { FileMenuContribution } from "@theia/workspace/lib/browser";
|
||||
import { MenuModelRegistry } from "@theia/core";
|
||||
|
||||
@injectable()
|
||||
export class CustomFileMenuContribution extends FileMenuContribution {
|
||||
registerMenus(registry: MenuModelRegistry) {
|
||||
|
||||
}
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
import { injectable, inject } from "inversify";
|
||||
import { FrontendApplication } from "@theia/core/lib/browser";
|
||||
import { ArduinoFrontendContribution } from "../arduino-frontend-contribution";
|
||||
|
||||
@injectable()
|
||||
export class CustomFrontendApplication extends FrontendApplication {
|
||||
|
||||
@inject(ArduinoFrontendContribution)
|
||||
protected readonly frontendContribution: ArduinoFrontendContribution;
|
||||
|
||||
protected async initializeLayout(): Promise<void> {
|
||||
await super.initializeLayout();
|
||||
const location = new URL(window.location.href);
|
||||
const sketchPath = location.searchParams.get('sketch');
|
||||
if (sketchPath) {
|
||||
this.frontendContribution.openSketchFiles(decodeURIComponent(sketchPath));
|
||||
}
|
||||
}
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
import {MonacoStatusBarContribution} from '@theia/monaco/lib/browser/monaco-status-bar-contribution';
|
||||
|
||||
export class SilentMonacoStatusBarContribution extends MonacoStatusBarContribution {
|
||||
protected setConfigTabSizeWidget() {
|
||||
|
||||
}
|
||||
|
||||
protected setLineEndingWidget() {
|
||||
|
||||
}
|
||||
}
|
@ -1,9 +1,11 @@
|
||||
import { injectable } from "inversify";
|
||||
import { FileNavigatorContribution } from "@theia/navigator/lib/browser/navigator-contribution";
|
||||
import { FrontendApplication } from "@theia/core/lib/browser";
|
||||
import { injectable } from 'inversify';
|
||||
import { FileNavigatorContribution } from '@theia/navigator/lib/browser/navigator-contribution';
|
||||
import { FrontendApplication } from '@theia/core/lib/browser';
|
||||
|
||||
@injectable()
|
||||
export class SilentNavigatorContribution extends FileNavigatorContribution {
|
||||
|
||||
async initializeLayout(app: FrontendApplication): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,19 +1,3 @@
|
||||
/********************************************************************************
|
||||
* Copyright (C) 2017 TypeFox and others.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License v. 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0.
|
||||
*
|
||||
* This Source Code may also be made available under the following Secondary
|
||||
* Licenses when the conditions for such availability set forth in the Eclipse
|
||||
* Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
||||
* with the GNU Classpath Exception which is available at
|
||||
* https://www.gnu.org/software/classpath/license.html.
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
|
||||
********************************************************************************/
|
||||
|
||||
import { injectable } from 'inversify';
|
||||
import { OutlineViewContribution } from '@theia/outline-view/lib/browser/outline-view-contribution';
|
||||
import { FrontendApplication } from '@theia/core/lib/browser';
|
||||
@ -23,4 +7,6 @@ export class SilentOutlineViewContribution extends OutlineViewContribution {
|
||||
|
||||
async initializeLayout(app: FrontendApplication): Promise<void> {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -1,9 +1,11 @@
|
||||
import { OutputToolbarContribution } from "@theia/output/lib/browser/output-toolbar-contribution";
|
||||
import { TabBarToolbarRegistry } from "@theia/core/lib/browser/shell/tab-bar-toolbar";
|
||||
import { injectable } from "inversify";
|
||||
import { OutputToolbarContribution } from '@theia/output/lib/browser/output-toolbar-contribution';
|
||||
import { TabBarToolbarRegistry } from '@theia/core/lib/browser/shell/tab-bar-toolbar';
|
||||
import { injectable } from 'inversify';
|
||||
|
||||
@injectable()
|
||||
export class ArduinoOutputToolContribution extends OutputToolbarContribution {
|
||||
|
||||
async registerToolbarItems(toolbarRegistry: TabBarToolbarRegistry): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -11,4 +11,5 @@ export class SilentProblemContribution extends ProblemContribution {
|
||||
|
||||
protected setStatusBarElement(problemStat: ProblemStat) {
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { injectable } from "inversify";
|
||||
import { ScmContribution } from "@theia/scm/lib/browser/scm-contribution";
|
||||
import { StatusBarEntry } from "@theia/core/lib/browser";
|
||||
import { injectable } from 'inversify';
|
||||
import { ScmContribution } from '@theia/scm/lib/browser/scm-contribution';
|
||||
import { StatusBarEntry } from '@theia/core/lib/browser';
|
||||
|
||||
@injectable()
|
||||
export class SilentScmContribution extends ScmContribution {
|
||||
@ -9,6 +9,6 @@ export class SilentScmContribution extends ScmContribution {
|
||||
}
|
||||
|
||||
protected setStatusBarEntry(id: string, entry: StatusBarEntry): void {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,10 +1,11 @@
|
||||
import { injectable } from "inversify";
|
||||
import { SearchInWorkspaceFrontendContribution } from "@theia/search-in-workspace/lib/browser/search-in-workspace-frontend-contribution";
|
||||
import { FrontendApplication } from "@theia/core/lib/browser";
|
||||
import { injectable } from 'inversify';
|
||||
import { SearchInWorkspaceFrontendContribution } from '@theia/search-in-workspace/lib/browser/search-in-workspace-frontend-contribution';
|
||||
import { FrontendApplication } from '@theia/core/lib/browser';
|
||||
|
||||
@injectable()
|
||||
export class SilentSearchInWorkspaceContribution extends SearchInWorkspaceFrontendContribution {
|
||||
async initializeLayout(app: FrontendApplication): Promise<void> {
|
||||
|
||||
async initializeLayout(app: FrontendApplication): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,34 @@
|
||||
import { inject, injectable } from 'inversify';
|
||||
import URI from '@theia/core/lib/common/uri';
|
||||
import { DisposableCollection } from '@theia/core/lib/common/disposable';
|
||||
import { MonacoEditor } from '@theia/monaco/lib/browser/monaco-editor';
|
||||
import { MonacoEditorModel } from '@theia/monaco/lib/browser/monaco-editor-model';
|
||||
import { MonacoEditorProvider } from '@theia/monaco/lib/browser/monaco-editor-provider';
|
||||
import { ConfigService } from '../../common/protocol/config-service';
|
||||
|
||||
@injectable()
|
||||
export class ArduinoMonacoEditorProvider extends MonacoEditorProvider {
|
||||
|
||||
@inject(ConfigService)
|
||||
protected readonly configService: ConfigService;
|
||||
protected dataDirUri: string | undefined;
|
||||
|
||||
protected async getModel(uri: URI, toDispose: DisposableCollection): Promise<MonacoEditorModel> {
|
||||
// `createMonacoEditorOptions` is not `async` so we ask the `dataDirUri` here.
|
||||
// https://github.com/eclipse-theia/theia/issues/6234
|
||||
const { dataDirUri } = await this.configService.getConfiguration()
|
||||
this.dataDirUri = dataDirUri;
|
||||
return super.getModel(uri, toDispose);
|
||||
}
|
||||
|
||||
protected createMonacoEditorOptions(model: MonacoEditorModel): MonacoEditor.IOptions {
|
||||
const options = this.createOptions(this.preferencePrefixes, model.uri, model.languageId);
|
||||
options.model = model.textEditorModel;
|
||||
options.readOnly = model.readOnly;
|
||||
if (this.dataDirUri) {
|
||||
options.readOnly = new URI(this.dataDirUri).isEqualOrParent(new URI(model.uri));
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
import { inject, injectable, postConstruct } from 'inversify';
|
||||
import { Diagnostic } from 'vscode-languageserver-types';
|
||||
import URI from '@theia/core/lib/common/uri';
|
||||
import { Marker } from '@theia/markers/lib/common/marker';
|
||||
import { ProblemManager } from '@theia/markers/lib/browser/problem/problem-manager';
|
||||
import { ConfigService } from '../../common/protocol/config-service';
|
||||
|
||||
@injectable()
|
||||
export class ArduinoProblemManager extends ProblemManager {
|
||||
|
||||
@inject(ConfigService)
|
||||
protected readonly configService: ConfigService;
|
||||
protected dataDirUri: URI | undefined;
|
||||
|
||||
@postConstruct()
|
||||
protected init(): void {
|
||||
super.init();
|
||||
this.configService.getConfiguration().then(({ dataDirUri }) => this.dataDirUri = new URI(dataDirUri));
|
||||
}
|
||||
|
||||
setMarkers(uri: URI, owner: string, data: Diagnostic[]): Marker<Diagnostic>[] {
|
||||
if (this.dataDirUri && this.dataDirUri.isEqualOrParent(uri)) {
|
||||
return [];
|
||||
}
|
||||
return super.setMarkers(uri, owner, data);
|
||||
}
|
||||
|
||||
}
|
@ -1,11 +1,13 @@
|
||||
import { injectable } from "inversify";
|
||||
import { BrowserMenuBarContribution } from "@theia/core/lib/browser/menu/browser-menu-plugin";
|
||||
import { FrontendApplication } from "@theia/core/lib/browser";
|
||||
import { injectable } from 'inversify';
|
||||
import { FrontendApplication } from '@theia/core/lib/browser';
|
||||
import { BrowserMenuBarContribution } from '@theia/core/lib/browser/menu/browser-menu-plugin';
|
||||
|
||||
@injectable()
|
||||
export class ArduinoMenuContribution extends BrowserMenuBarContribution {
|
||||
|
||||
onStart(app: FrontendApplication): void {
|
||||
const menu = this.factory.createMenuBar();
|
||||
app.shell.addWidget(menu, { area: 'top' });
|
||||
const menu = this.factory.createMenuBar();
|
||||
app.shell.addWidget(menu, { area: 'top' });
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -7,4 +7,4 @@ import '../../../src/browser/style/browser-menu.css'
|
||||
export default new ContainerModule((bind: interfaces.Bind, unbind: interfaces.Unbind) => {
|
||||
unbind(BrowserMenuBarContribution);
|
||||
bind(BrowserMenuBarContribution).to(ArduinoMenuContribution).inSingletonScope();
|
||||
})
|
||||
});
|
||||
|
@ -0,0 +1,32 @@
|
||||
import { inject, injectable, postConstruct } from 'inversify';
|
||||
import URI from '@theia/core/lib/common/uri';
|
||||
import { Title, Widget } from '@phosphor/widgets';
|
||||
import { WidgetDecoration } from '@theia/core/lib/browser/widget-decoration';
|
||||
import { TabBarDecoratorService } from '@theia/core/lib/browser/shell/tab-bar-decorator';
|
||||
import { ConfigService } from '../../common/protocol/config-service';
|
||||
import { EditorWidget } from '@theia/editor/lib/browser';
|
||||
|
||||
@injectable()
|
||||
export class ArduinoTabBarDecoratorService extends TabBarDecoratorService {
|
||||
|
||||
@inject(ConfigService)
|
||||
protected readonly configService: ConfigService;
|
||||
protected dataDirUri: URI | undefined;
|
||||
|
||||
@postConstruct()
|
||||
protected init(): void {
|
||||
super.init();
|
||||
this.configService.getConfiguration().then(({ dataDirUri }) => this.dataDirUri = new URI(dataDirUri));
|
||||
}
|
||||
|
||||
getDecorations(title: Title<Widget>): WidgetDecoration.Data[] {
|
||||
if (title.owner instanceof EditorWidget) {
|
||||
const editor = title.owner.editor;
|
||||
if (this.dataDirUri && this.dataDirUri.isEqualOrParent(editor.uri)) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
return super.getDecorations(title);
|
||||
}
|
||||
|
||||
}
|
@ -1,65 +0,0 @@
|
||||
import { injectable, inject } from "inversify";
|
||||
import URI from "@theia/core/lib/common/uri";
|
||||
import { FileSystem } from "@theia/filesystem/lib/common";
|
||||
import { WindowService } from "@theia/core/lib/browser/window/window-service";
|
||||
|
||||
@injectable()
|
||||
export class SketchFactory {
|
||||
|
||||
@inject(FileSystem)
|
||||
protected readonly fileSystem: FileSystem;
|
||||
|
||||
@inject(WindowService)
|
||||
protected readonly windowService: WindowService;
|
||||
|
||||
public async createNewSketch(parent: URI): Promise<void> {
|
||||
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(parent.resolve(sketchNameCandidate).toString())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
sketchName = sketchNameCandidate;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!sketchName) {
|
||||
throw new Error("Cannot create a unique sketch name");
|
||||
}
|
||||
|
||||
try {
|
||||
const sketchDir = parent.resolve(sketchName);
|
||||
const sketchFile = sketchDir.resolve(`${sketchName}.ino`);
|
||||
this.fileSystem.createFolder(sketchDir.toString());
|
||||
this.fileSystem.createFile(sketchFile.toString(), {
|
||||
content: `
|
||||
void setup() {
|
||||
// put your setup code here, to run once:
|
||||
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// put your main code here, to run repeatedly:
|
||||
|
||||
}
|
||||
` });
|
||||
const location = new URL(window.location.href);
|
||||
location.searchParams.set('sketch', sketchDir.toString());
|
||||
const hash = await this.fileSystem.getFsPath(sketchDir.toString());
|
||||
if (hash) {
|
||||
location.hash = hash;
|
||||
}
|
||||
this.windowService.openNewWindow(location.toString());
|
||||
} catch (e) {
|
||||
throw new Error("Cannot create new sketch: " + e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -193,10 +193,9 @@ button.theia-button.main {
|
||||
}
|
||||
|
||||
.arduino-boards-dropdown-list {
|
||||
background: var(--theia-layout-color1);
|
||||
border: 3px solid var(--theia-border-color2);
|
||||
margin: -3px;
|
||||
z-index: 1000;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.arduino-boards-dropdown-item {
|
||||
@ -205,6 +204,7 @@ button.theia-button.main {
|
||||
padding: 10px;
|
||||
cursor: pointer;
|
||||
color: var(--theia-ui-font-color1);
|
||||
background: var(--theia-layout-color1);
|
||||
}
|
||||
|
||||
.arduino-boards-dropdown-item .fa-check {
|
||||
|
@ -1,12 +1,12 @@
|
||||
/* Do not show the `close` icon for editor, but show the dirty state. */
|
||||
.p-TabBar.theia-app-centers .p-TabBar-tab.p-mod-closable:hover > .p-TabBar-tabCloseIcon,
|
||||
.p-TabBar.theia-app-centers .p-TabBar-tab.p-mod-current > .p-TabBar-tabCloseIcon {
|
||||
/* Do not show the `close` icon for editor, but show the dirty state if not in pro-mode. */
|
||||
body:not(.arduino-advanced-mode) .p-TabBar.theia-app-centers .p-TabBar-tab.p-mod-closable:hover > .p-TabBar-tabCloseIcon,
|
||||
body:not(.arduino-advanced-mode) .p-TabBar.theia-app-centers .p-TabBar-tab.p-mod-current > .p-TabBar-tabCloseIcon {
|
||||
background-image: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.p-TabBar.theia-app-centers .p-TabBar-tab.p-mod-closable.theia-mod-dirty:hover > .p-TabBar-tabCloseIcon,
|
||||
.p-TabBar.theia-app-centers .p-TabBar-tab.p-mod-closable.theia-mod-dirty > .p-TabBar-tabCloseIcon:hover {
|
||||
body:not(.arduino-advanced-mode) .p-TabBar.theia-app-centers .p-TabBar-tab.p-mod-closable.theia-mod-dirty:hover > .p-TabBar-tabCloseIcon,
|
||||
body:not(.arduino-advanced-mode) .p-TabBar.theia-app-centers .p-TabBar-tab.p-mod-closable.theia-mod-dirty > .p-TabBar-tabCloseIcon:hover {
|
||||
background-size: 10px;
|
||||
background-image: var(--theia-icon-circle);
|
||||
cursor: pointer;
|
||||
|
@ -2,4 +2,4 @@
|
||||
@import './board-select-dialog.css';
|
||||
@import './main.css';
|
||||
@import './editor.css';
|
||||
@import './serial-monitor.css';
|
||||
@import './monitor.css';
|
@ -72,7 +72,7 @@
|
||||
}
|
||||
|
||||
.p-TabBar-toolbar .item.arduino-monitor.toggled {
|
||||
background: var(--theia-secondary-brand-color0);
|
||||
background: var(--theia-secondary-brand-color1);
|
||||
}
|
||||
|
||||
.p-TabBar-toolbar .item .clear-all {
|
@ -3,6 +3,8 @@ export const ConfigService = Symbol('ConfigService');
|
||||
|
||||
export interface ConfigService {
|
||||
getConfiguration(): Promise<Config>;
|
||||
isInDataDir(uri: string): Promise<boolean>;
|
||||
isInSketchDir(uri: string): Promise<boolean>;
|
||||
}
|
||||
|
||||
export interface Config {
|
||||
|
@ -1,13 +1,17 @@
|
||||
import { FileStat } from "@theia/filesystem/lib/common";
|
||||
|
||||
export const SketchesServicePath = '/services/sketches-service';
|
||||
export const SketchesService = Symbol('SketchesService');
|
||||
export interface SketchesService {
|
||||
getSketches(fileStat?: FileStat): Promise<Sketch[]>
|
||||
getSketchFiles(fileStat: FileStat): Promise<string[]>
|
||||
/**
|
||||
* Returns with the direct sketch folders from the location of the `fileStat`.
|
||||
* The sketches returns with inverchronological order, the first item is the most recent one.
|
||||
*/
|
||||
getSketches(uri?: string): Promise<Sketch[]>
|
||||
getSketchFiles(uri: string): Promise<string[]>
|
||||
createNewSketch(parentUri: string): Promise<Sketch>
|
||||
isSketchFolder(uri: string): Promise<boolean>
|
||||
}
|
||||
|
||||
export interface Sketch {
|
||||
name: string;
|
||||
uri: string
|
||||
readonly name: string;
|
||||
readonly uri: string
|
||||
}
|
@ -1,31 +1,12 @@
|
||||
import * as electron from 'electron';
|
||||
import { injectable, inject } from "inversify";
|
||||
import { ElectronMenuContribution } from "@theia/core/lib/electron-browser/menu/electron-menu-contribution";
|
||||
import { FrontendApplication } from "@theia/core/lib/browser";
|
||||
import { isOSX } from '@theia/core';
|
||||
import { WorkspaceService } from '@theia/workspace/lib/browser';
|
||||
import { injectable } from 'inversify';
|
||||
import { ElectronMenuContribution } from '@theia/core/lib/electron-browser/menu/electron-menu-contribution';
|
||||
|
||||
@injectable()
|
||||
export class ElectronArduinoMenuContribution extends ElectronMenuContribution {
|
||||
|
||||
@inject(WorkspaceService) protected readonly workspaceService: WorkspaceService;
|
||||
|
||||
onStart(app: FrontendApplication): void {
|
||||
this.workspaceService.onWorkspaceChanged(() => {
|
||||
const createdMenuBar = this.factory.createMenuBar();
|
||||
const currentWindow = electron.remote.getCurrentWindow();
|
||||
if (isOSX) {
|
||||
electron.remote.Menu.setApplicationMenu(createdMenuBar);
|
||||
currentWindow.on('focus', () =>
|
||||
// OSX: Recreate the menus when changing windows.
|
||||
// OSX only has one menu bar for all windows, so we need to swap
|
||||
// between them as the user switch windows.
|
||||
electron.remote.Menu.setApplicationMenu(this.factory.createMenuBar())
|
||||
);
|
||||
} else {
|
||||
// Unix/Windows: Set the per-window menus
|
||||
currentWindow.setMenu(createdMenuBar);
|
||||
}
|
||||
});
|
||||
protected hideTopPanel(): void {
|
||||
// NOOP
|
||||
// We reuse the `div` for the Arduino toolbar.
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { ContainerModule } from "inversify";
|
||||
import { ElectronMenuContribution } from "@theia/core/lib/electron-browser/menu/electron-menu-contribution"
|
||||
import { ElectronArduinoMenuContribution } from "./electron-arduino-menu-contribution";
|
||||
import { ContainerModule } from 'inversify';
|
||||
import { ElectronMenuContribution } from '@theia/core/lib/electron-browser/menu/electron-menu-contribution'
|
||||
import { ElectronArduinoMenuContribution } from './electron-arduino-menu-contribution';
|
||||
|
||||
export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
bind(ElectronArduinoMenuContribution).toSelf().inSingletonScope();
|
||||
rebind(ElectronMenuContribution).to(ElectronArduinoMenuContribution);
|
||||
})
|
||||
});
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { injectable, inject } from "inversify";
|
||||
import { ConfigService, Config } from "../common/protocol/config-service";
|
||||
import { ArduinoCli } from "./arduino-cli";
|
||||
import { injectable, inject } from 'inversify';
|
||||
import URI from '@theia/core/lib/common/uri';
|
||||
import { ConfigService, Config } from '../common/protocol/config-service';
|
||||
import { ArduinoCli } from './arduino-cli';
|
||||
|
||||
@injectable()
|
||||
export class ConfigServiceImpl implements ConfigService {
|
||||
@ -11,4 +12,13 @@ export class ConfigServiceImpl implements ConfigService {
|
||||
async getConfiguration(): Promise<Config> {
|
||||
return this.cli.getDefaultConfig();
|
||||
}
|
||||
}
|
||||
|
||||
async isInDataDir(uri: string): Promise<boolean> {
|
||||
return this.getConfiguration().then(({ dataDirUri }) => new URI(dataDirUri).isEqualOrParent(new URI(uri)));
|
||||
}
|
||||
|
||||
async isInSketchDir(uri: string): Promise<boolean> {
|
||||
return this.getConfiguration().then(({ sketchDirUri }) => new URI(sketchDirUri).isEqualOrParent(new URI(uri)));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -109,7 +109,7 @@ export namespace DaemonLog {
|
||||
|
||||
export function toPrettyString(logMessages: string): string {
|
||||
const parsed = parse(logMessages);
|
||||
return parsed.map(toMessage).join('\n');
|
||||
return parsed.map(toMessage).join('\n') + '\n';
|
||||
}
|
||||
|
||||
function toMessage(log: DaemonLog): string {
|
||||
|
@ -1,80 +1,126 @@
|
||||
import { injectable, inject } from "inversify";
|
||||
import { SketchesService, Sketch } from "../common/protocol/sketches-service";
|
||||
import URI from "@theia/core/lib/common/uri";
|
||||
import { FileStat, FileSystem } from "@theia/filesystem/lib/common";
|
||||
import * as fs from 'fs';
|
||||
import { injectable, inject } from 'inversify';
|
||||
import * as path from 'path';
|
||||
import { FileUri } from "@theia/core/lib/node";
|
||||
import * as fs from 'fs-extra';
|
||||
import { FileUri } from '@theia/core/lib/node';
|
||||
import { ConfigService } from '../common/protocol/config-service';
|
||||
import { SketchesService, Sketch } from '../common/protocol/sketches-service';
|
||||
|
||||
export const ALLOWED_FILE_EXTENSIONS = [".c", ".cpp", ".h", ".hh", ".hpp", ".s", ".pde", ".ino"];
|
||||
export const ALLOWED_FILE_EXTENSIONS = ['.c', '.cpp', '.h', '.hh', '.hpp', '.s', '.pde', '.ino'];
|
||||
|
||||
// TODO: `fs`: use async API
|
||||
@injectable()
|
||||
export class SketchesServiceImpl implements SketchesService {
|
||||
|
||||
@inject(FileSystem)
|
||||
protected readonly filesystem: FileSystem;
|
||||
@inject(ConfigService)
|
||||
protected readonly configService: ConfigService;
|
||||
|
||||
async getSketches(fileStat?: FileStat): Promise<Sketch[]> {
|
||||
const sketches: Sketch[] = [];
|
||||
if (fileStat && fileStat.isDirectory) {
|
||||
const uri = new URI(fileStat.uri);
|
||||
const sketchFolderPath = await this.filesystem.getFsPath(uri.toString());
|
||||
if (sketchFolderPath) {
|
||||
const fileNames = fs.readdirSync(sketchFolderPath);
|
||||
for (const fileName of fileNames) {
|
||||
const filePath = path.join(sketchFolderPath, fileName);
|
||||
if (this.isSketchFolder(filePath, fileName)) {
|
||||
sketches.push({
|
||||
name: fileName,
|
||||
uri: FileUri.create(filePath).toString()
|
||||
});
|
||||
}
|
||||
}
|
||||
async getSketches(uri?: string): Promise<Sketch[]> {
|
||||
const sketches: Array<Sketch & { mtimeMs: number }> = [];
|
||||
const fsPath = FileUri.fsPath(uri ? uri : (await this.configService.getConfiguration()).sketchDirUri);
|
||||
const fileNames = fs.readdirSync(fsPath);
|
||||
for (const fileName of fileNames) {
|
||||
const filePath = path.join(fsPath, fileName);
|
||||
if (await this.isSketchFolder(FileUri.create(filePath).toString())) {
|
||||
const stat = fs.statSync(filePath);
|
||||
sketches.push({
|
||||
mtimeMs: stat.mtimeMs,
|
||||
name: fileName,
|
||||
uri: FileUri.create(filePath).toString()
|
||||
});
|
||||
}
|
||||
}
|
||||
return sketches;
|
||||
return sketches.sort((left, right) => right.mtimeMs - left.mtimeMs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all allowed files.
|
||||
* File extensions: "c", "cpp", "h", "hh", "hpp", "s", "pde", "ino"
|
||||
* File extensions: 'c', 'cpp', 'h', 'hh', 'hpp', 's', 'pde', 'ino'
|
||||
*/
|
||||
async getSketchFiles(sketchFileStat: FileStat): Promise<string[]> {
|
||||
async getSketchFiles(uri: string): Promise<string[]> {
|
||||
const uris: string[] = [];
|
||||
const sketchUri = new URI(sketchFileStat.uri);
|
||||
const sketchPath = await this.filesystem.getFsPath(sketchUri.toString());
|
||||
if (sketchPath) {
|
||||
if (sketchFileStat.isDirectory && this.isSketchFolder(sketchPath, sketchUri.displayName)) {
|
||||
const fileNames = fs.readdirSync(sketchPath);
|
||||
for (const fileName of fileNames) {
|
||||
const filePath = path.join(sketchPath, fileName);
|
||||
if (ALLOWED_FILE_EXTENSIONS.indexOf(path.extname(filePath)) !== -1
|
||||
&& fs.existsSync(filePath)
|
||||
&& fs.lstatSync(filePath).isFile()) {
|
||||
uris.push(FileUri.create(filePath).toString())
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const sketchDir = path.dirname(sketchPath);
|
||||
if (sketchDir && this.isSketchFolder(sketchDir, sketchUri.path.dir.name)) {
|
||||
const sketchFolderStat = await this.filesystem.getFileStat(sketchUri.path.dir.toString());
|
||||
if (sketchFolderStat) {
|
||||
const sketchDirContents = await this.getSketchFiles(sketchFolderStat);
|
||||
uris.push(...sketchDirContents);
|
||||
}
|
||||
const fsPath = FileUri.fsPath(uri);
|
||||
const stats = fs.lstatSync(fsPath);
|
||||
if (stats.isDirectory && await this.isSketchFolder(uri)) {
|
||||
const fileNames = fs.readdirSync(fsPath);
|
||||
for (const fileName of fileNames) {
|
||||
const filePath = path.join(fsPath, fileName);
|
||||
if (ALLOWED_FILE_EXTENSIONS.indexOf(path.extname(filePath)) !== -1
|
||||
&& fs.existsSync(filePath)
|
||||
&& fs.lstatSync(filePath).isFile()) {
|
||||
uris.push(FileUri.create(filePath).toString())
|
||||
}
|
||||
}
|
||||
return uris;
|
||||
}
|
||||
return uris;
|
||||
const sketchDir = path.dirname(fsPath);
|
||||
return this.getSketchFiles(FileUri.create(sketchDir).toString());
|
||||
}
|
||||
|
||||
protected isSketchFolder(path: string, name: string): boolean {
|
||||
if (fs.existsSync(path) && fs.lstatSync(path).isDirectory()) {
|
||||
const files = fs.readdirSync(path);
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
if (files[i] === name + '.ino') {
|
||||
return true;
|
||||
}
|
||||
async createNewSketch(parentUri: string): Promise<Sketch> {
|
||||
const monthNames = ['january', 'february', 'march', 'april', 'may', 'june',
|
||||
'july', 'august', 'september', 'october', 'november', 'december'
|
||||
];
|
||||
const today = new Date();
|
||||
const parent = FileUri.fsPath(parentUri);
|
||||
|
||||
const sketchBaseName = `sketch_${monthNames[today.getMonth()]}${today.getDate()}`;
|
||||
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))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
sketchName = sketchNameCandidate;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!sketchName) {
|
||||
throw new Error('Cannot create a unique sketch name');
|
||||
}
|
||||
|
||||
const sketchDir = path.join(parent, sketchName)
|
||||
const sketchFile = path.join(sketchDir, `${sketchName}.ino`);
|
||||
fs.mkdirSync(sketchDir);
|
||||
fs.writeFileSync(sketchFile, `
|
||||
void setup() {
|
||||
// put your setup code here, to run once:
|
||||
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// put your main code here, to run repeatedly:
|
||||
|
||||
}
|
||||
`, { encoding: 'utf8' });
|
||||
return {
|
||||
name: sketchName,
|
||||
uri: FileUri.create(sketchDir).toString()
|
||||
}
|
||||
}
|
||||
|
||||
async isSketchFolder(uri: string): Promise<boolean> {
|
||||
const fsPath = FileUri.fsPath(uri);
|
||||
const exists = await fs.pathExists(fsPath);
|
||||
if (exists) {
|
||||
const stats = await fs.lstat(fsPath);
|
||||
if (stats.isDirectory()) {
|
||||
const basename = path.basename(fsPath);
|
||||
return new Promise<boolean>((resolve, reject) => {
|
||||
fs.readdir(fsPath, (error, files) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
if (files[i] === basename + '.ino') {
|
||||
resolve(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
resolve(false);
|
||||
});
|
||||
})
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
@ -8,7 +8,6 @@
|
||||
"max-line-length": [true, 180],
|
||||
"no-trailing-whitespace": false,
|
||||
"no-unused-expression": true,
|
||||
"no-use-before-declare": true,
|
||||
"no-var-keyword": true,
|
||||
"one-line": [true,
|
||||
"check-open-brace",
|
||||
|
Loading…
x
Reference in New Issue
Block a user