mirror of
https://github.com/arduino/arduino-ide.git
synced 2025-07-10 12:56:32 +00:00
commit
94233a1a19
22
.vscode/settings.json
vendored
22
.vscode/settings.json
vendored
@ -1,3 +1,21 @@
|
|||||||
{
|
{
|
||||||
"typescript.tsdk": "node_modules/typescript/lib"
|
"tslint.enable": true,
|
||||||
}
|
"tslint.configFile": "./tslint.json",
|
||||||
|
"editor.formatOnSave": true,
|
||||||
|
"files.exclude": {
|
||||||
|
"**/lib": false
|
||||||
|
},
|
||||||
|
"editor.insertSpaces": true,
|
||||||
|
"editor.detectIndentation": false,
|
||||||
|
"[typescript]": {
|
||||||
|
"editor.tabSize": 4
|
||||||
|
},
|
||||||
|
"[json]": {
|
||||||
|
"editor.tabSize": 2
|
||||||
|
},
|
||||||
|
"[jsonc]": {
|
||||||
|
"editor.tabSize": 2
|
||||||
|
},
|
||||||
|
"files.insertFinalNewline": true,
|
||||||
|
"typescript.tsdk": "node_modules/typescript/lib"
|
||||||
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "arduino-ide-extension",
|
"name": "arduino-ide-extension",
|
||||||
"version": "0.0.2",
|
"version": "0.0.3",
|
||||||
"description": "An extension for Theia building the Arduino IDE",
|
"description": "An extension for Theia building the Arduino IDE",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
@ -58,7 +58,7 @@
|
|||||||
"rimraf": "^2.6.1",
|
"rimraf": "^2.6.1",
|
||||||
"shelljs": "^0.8.3",
|
"shelljs": "^0.8.3",
|
||||||
"tslint": "^5.5.0",
|
"tslint": "^5.5.0",
|
||||||
"typescript": "2.9.1",
|
"typescript": "3.5.3",
|
||||||
"uuid": "^3.2.1",
|
"uuid": "^3.2.1",
|
||||||
"yargs": "^11.1.0"
|
"yargs": "^11.1.0"
|
||||||
},
|
},
|
||||||
|
@ -3,7 +3,7 @@ import { injectable, inject, postConstruct } from 'inversify';
|
|||||||
import URI from '@theia/core/lib/common/uri';
|
import URI from '@theia/core/lib/common/uri';
|
||||||
import { EditorWidget } from '@theia/editor/lib/browser/editor-widget';
|
import { EditorWidget } from '@theia/editor/lib/browser/editor-widget';
|
||||||
import { MessageService } from '@theia/core/lib/common/message-service';
|
import { MessageService } from '@theia/core/lib/common/message-service';
|
||||||
import { CommandContribution, CommandRegistry, Command } from '@theia/core/lib/common/command';
|
import { CommandContribution, CommandRegistry, Command, CommandHandler } from '@theia/core/lib/common/command';
|
||||||
import { TabBarToolbarContribution, TabBarToolbarRegistry } from '@theia/core/lib/browser/shell/tab-bar-toolbar';
|
import { TabBarToolbarContribution, TabBarToolbarRegistry } from '@theia/core/lib/browser/shell/tab-bar-toolbar';
|
||||||
import { BoardsService } from '../common/protocol/boards-service';
|
import { BoardsService } from '../common/protocol/boards-service';
|
||||||
import { ArduinoCommands } from './arduino-commands';
|
import { ArduinoCommands } from './arduino-commands';
|
||||||
@ -22,9 +22,11 @@ import {
|
|||||||
OpenerService,
|
OpenerService,
|
||||||
Widget,
|
Widget,
|
||||||
StatusBar,
|
StatusBar,
|
||||||
ShellLayoutRestorer,
|
|
||||||
StatusBarAlignment,
|
StatusBarAlignment,
|
||||||
QuickOpenService
|
QuickOpenService,
|
||||||
|
ApplicationShell,
|
||||||
|
FrontendApplicationContribution,
|
||||||
|
FrontendApplication
|
||||||
} from '@theia/core/lib/browser';
|
} from '@theia/core/lib/browser';
|
||||||
import { OpenFileDialogProps, FileDialogService } from '@theia/filesystem/lib/browser/file-dialog';
|
import { OpenFileDialogProps, FileDialogService } from '@theia/filesystem/lib/browser/file-dialog';
|
||||||
import { FileSystem, FileStat } from '@theia/filesystem/lib/common';
|
import { FileSystem, FileStat } from '@theia/filesystem/lib/common';
|
||||||
@ -44,6 +46,14 @@ import { ConfigService } from '../common/protocol/config-service';
|
|||||||
import { MonitorConnection } from './monitor/monitor-connection';
|
import { MonitorConnection } from './monitor/monitor-connection';
|
||||||
import { MonitorViewContribution } from './monitor/monitor-view-contribution';
|
import { MonitorViewContribution } from './monitor/monitor-view-contribution';
|
||||||
import { ArduinoWorkspaceService } from './arduino-workspace-service';
|
import { ArduinoWorkspaceService } from './arduino-workspace-service';
|
||||||
|
import { FileNavigatorContribution } from '@theia/navigator/lib/browser/navigator-contribution';
|
||||||
|
import { OutlineViewContribution } from '@theia/outline-view/lib/browser/outline-view-contribution';
|
||||||
|
import { ProblemContribution } from '@theia/markers/lib/browser/problem/problem-contribution';
|
||||||
|
import { ScmContribution } from '@theia/scm/lib/browser/scm-contribution';
|
||||||
|
import { SearchInWorkspaceFrontendContribution } from '@theia/search-in-workspace/lib/browser/search-in-workspace-frontend-contribution';
|
||||||
|
import { FileNavigatorCommands } from '@theia/navigator/lib/browser/navigator-contribution';
|
||||||
|
import { ArduinoShellLayoutRestorer } from './shell/arduino-shell-layout-restorer';
|
||||||
|
import { EditorMode } from './editor-mode';
|
||||||
|
|
||||||
export namespace ArduinoMenus {
|
export namespace ArduinoMenus {
|
||||||
export const SKETCH = [...MAIN_MENU_BAR, '3_sketch'];
|
export const SKETCH = [...MAIN_MENU_BAR, '3_sketch'];
|
||||||
@ -57,16 +67,8 @@ export namespace ArduinoToolbarContextMenu {
|
|||||||
export const EXAMPLE_SKETCHES_GROUP: MenuPath = [...OPEN_SKETCH_PATH, '3_examples'];
|
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 = (() => {
|
|
||||||
const advancedModeStr = window.localStorage.getItem(LS_ID);
|
|
||||||
return advancedModeStr === 'true';
|
|
||||||
})();
|
|
||||||
}
|
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class ArduinoFrontendContribution implements TabBarToolbarContribution, CommandContribution, MenuContribution {
|
export class ArduinoFrontendContribution implements FrontendApplicationContribution, TabBarToolbarContribution, CommandContribution, MenuContribution {
|
||||||
|
|
||||||
@inject(MessageService)
|
@inject(MessageService)
|
||||||
protected readonly messageService: MessageService;
|
protected readonly messageService: MessageService;
|
||||||
@ -126,13 +128,13 @@ export class ArduinoFrontendContribution implements TabBarToolbarContribution, C
|
|||||||
protected readonly menuRegistry: MenuModelRegistry;
|
protected readonly menuRegistry: MenuModelRegistry;
|
||||||
|
|
||||||
@inject(CommandRegistry)
|
@inject(CommandRegistry)
|
||||||
protected readonly commands: CommandRegistry;
|
protected readonly commandRegistry: CommandRegistry;
|
||||||
|
|
||||||
@inject(StatusBar)
|
@inject(StatusBar)
|
||||||
protected readonly statusBar: StatusBar;
|
protected readonly statusBar: StatusBar;
|
||||||
|
|
||||||
@inject(ShellLayoutRestorer)
|
@inject(ArduinoShellLayoutRestorer)
|
||||||
protected readonly layoutRestorer: ShellLayoutRestorer;
|
protected readonly layoutRestorer: ArduinoShellLayoutRestorer;
|
||||||
|
|
||||||
@inject(QuickOpenService)
|
@inject(QuickOpenService)
|
||||||
protected readonly quickOpenService: QuickOpenService;
|
protected readonly quickOpenService: QuickOpenService;
|
||||||
@ -146,8 +148,29 @@ export class ArduinoFrontendContribution implements TabBarToolbarContribution, C
|
|||||||
@inject(MonitorConnection)
|
@inject(MonitorConnection)
|
||||||
protected readonly monitorConnection: MonitorConnection;
|
protected readonly monitorConnection: MonitorConnection;
|
||||||
|
|
||||||
protected boardsToolbarItem: BoardsToolBarItem | null;
|
@inject(ApplicationShell)
|
||||||
protected wsSketchCount: number = 0;
|
protected readonly shell: ApplicationShell;
|
||||||
|
|
||||||
|
@inject(FileNavigatorContribution)
|
||||||
|
protected readonly fileNavigatorContributions: FileNavigatorContribution;
|
||||||
|
|
||||||
|
@inject(OutlineViewContribution)
|
||||||
|
protected readonly outlineContribution: OutlineViewContribution;
|
||||||
|
|
||||||
|
@inject(ProblemContribution)
|
||||||
|
protected readonly problemContribution: ProblemContribution;
|
||||||
|
|
||||||
|
@inject(ScmContribution)
|
||||||
|
protected readonly scmContribution: ScmContribution;
|
||||||
|
|
||||||
|
@inject(SearchInWorkspaceFrontendContribution)
|
||||||
|
protected readonly siwContribution: SearchInWorkspaceFrontendContribution;
|
||||||
|
|
||||||
|
@inject(EditorMode)
|
||||||
|
protected readonly editorMode: EditorMode;
|
||||||
|
|
||||||
|
protected application: FrontendApplication;
|
||||||
|
protected wsSketchCount: number = 0; // TODO: this does not belong here, does it?
|
||||||
|
|
||||||
@postConstruct()
|
@postConstruct()
|
||||||
protected async init(): Promise<void> {
|
protected async init(): Promise<void> {
|
||||||
@ -171,6 +194,22 @@ export class ArduinoFrontendContribution implements TabBarToolbarContribution, C
|
|||||||
]).then(([{ boards }, { ports }]) => this.boardsServiceClient.tryReconnect(boards, ports));
|
]).then(([{ boards }, { ports }]) => this.boardsServiceClient.tryReconnect(boards, ports));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onStart(app: FrontendApplication): void {
|
||||||
|
this.application = app;
|
||||||
|
// Initialize all `pro-mode` widgets. This is a NOOP if in normal mode.
|
||||||
|
for (const viewContribution of [
|
||||||
|
this.fileNavigatorContributions,
|
||||||
|
this.outlineContribution,
|
||||||
|
this.problemContribution,
|
||||||
|
this.scmContribution,
|
||||||
|
this.siwContribution] as Array<FrontendApplicationContribution>) {
|
||||||
|
|
||||||
|
if (viewContribution.initializeLayout) {
|
||||||
|
viewContribution.initializeLayout(this.application);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
registerToolbarItems(registry: TabBarToolbarRegistry): void {
|
registerToolbarItems(registry: TabBarToolbarRegistry): void {
|
||||||
registry.registerItem({
|
registry.registerItem({
|
||||||
id: ArduinoCommands.VERIFY.id,
|
id: ArduinoCommands.VERIFY.id,
|
||||||
@ -196,8 +235,7 @@ export class ArduinoFrontendContribution implements TabBarToolbarContribution, C
|
|||||||
id: BoardsToolBarItem.TOOLBAR_ID,
|
id: BoardsToolBarItem.TOOLBAR_ID,
|
||||||
render: () => <BoardsToolBarItem
|
render: () => <BoardsToolBarItem
|
||||||
key='boardsToolbarItem'
|
key='boardsToolbarItem'
|
||||||
ref={ref => this.boardsToolbarItem = ref}
|
commands={this.commandRegistry}
|
||||||
commands={this.commands}
|
|
||||||
boardsServiceClient={this.boardsServiceClient}
|
boardsServiceClient={this.boardsServiceClient}
|
||||||
boardService={this.boardsService} />,
|
boardService={this.boardsService} />,
|
||||||
isVisible: widget => ArduinoToolbar.is(widget) && widget.side === 'left'
|
isVisible: widget => ArduinoToolbar.is(widget) && widget.side === 'left'
|
||||||
@ -213,12 +251,48 @@ export class ArduinoFrontendContribution implements TabBarToolbarContribution, C
|
|||||||
id: ArduinoCommands.TOGGLE_ADVANCED_MODE.id,
|
id: ArduinoCommands.TOGGLE_ADVANCED_MODE.id,
|
||||||
command: ArduinoCommands.TOGGLE_ADVANCED_MODE.id,
|
command: ArduinoCommands.TOGGLE_ADVANCED_MODE.id,
|
||||||
tooltip: 'Toggle Advanced Mode',
|
tooltip: 'Toggle Advanced Mode',
|
||||||
text: (ArduinoAdvancedMode.TOGGLED ? '$(toggle-on)' : '$(toggle-off)'),
|
text: (this.editorMode.proMode ? '$(toggle-on)' : '$(toggle-off)'),
|
||||||
isVisible: widget => ArduinoToolbar.is(widget) && widget.side === 'right'
|
isVisible: widget => ArduinoToolbar.is(widget) && widget.side === 'right'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
registerCommands(registry: CommandRegistry): void {
|
registerCommands(registry: CommandRegistry): void {
|
||||||
|
// TODO: use proper API https://github.com/eclipse-theia/theia/pull/6599
|
||||||
|
const allHandlers: { [id: string]: CommandHandler[] } = (registry as any)._handlers;
|
||||||
|
// Make sure to reveal the `Explorer` before executing `New File` and `New Folder`.
|
||||||
|
for (const command of [WorkspaceCommands.NEW_FILE, WorkspaceCommands.NEW_FOLDER]) {
|
||||||
|
const { id } = command;
|
||||||
|
const handlers = allHandlers[id].slice();
|
||||||
|
registry.unregisterCommand(id);
|
||||||
|
registry.registerCommand(command);
|
||||||
|
for (const handler of handlers) {
|
||||||
|
const wrapper: CommandHandler = {
|
||||||
|
execute: (...args: any[]) => {
|
||||||
|
this.fileNavigatorContributions.openView({ reveal: true }).then(() => handler.execute(args));
|
||||||
|
},
|
||||||
|
isVisible: (...args: any[]) => {
|
||||||
|
return handler.isVisible!(args);
|
||||||
|
},
|
||||||
|
isEnabled: (args: any[]) => {
|
||||||
|
return handler.isEnabled!(args);
|
||||||
|
},
|
||||||
|
isToggled: (args: any[]) => {
|
||||||
|
return handler.isToggled!(args);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (!handler.isEnabled) {
|
||||||
|
delete wrapper.isEnabled;
|
||||||
|
}
|
||||||
|
if (!handler.isToggled) {
|
||||||
|
delete wrapper.isToggled;
|
||||||
|
}
|
||||||
|
if (!handler.isVisible) {
|
||||||
|
delete wrapper.isVisible;
|
||||||
|
}
|
||||||
|
registry.registerHandler(id, wrapper);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
registry.registerCommand(ArduinoCommands.VERIFY, {
|
registry.registerCommand(ArduinoCommands.VERIFY, {
|
||||||
isVisible: widget => ArduinoToolbar.is(widget) && widget.side === 'left',
|
isVisible: widget => ArduinoToolbar.is(widget) && widget.side === 'left',
|
||||||
isEnabled: widget => true,
|
isEnabled: widget => true,
|
||||||
@ -296,7 +370,7 @@ export class ArduinoFrontendContribution implements TabBarToolbarContribution, C
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.commands.executeCommand(ArduinoCommands.OPEN_FILE_NAVIGATOR.id);
|
this.commandRegistry.executeCommand(ArduinoCommands.OPEN_FILE_NAVIGATOR.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -342,18 +416,14 @@ export class ArduinoFrontendContribution implements TabBarToolbarContribution, C
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
registry.registerCommand(ArduinoCommands.TOGGLE_ADVANCED_MODE, {
|
registry.registerCommand(ArduinoCommands.TOGGLE_ADVANCED_MODE, {
|
||||||
execute: () => {
|
execute: () => this.editorMode.toggle(),
|
||||||
const oldModeState = ArduinoAdvancedMode.TOGGLED;
|
|
||||||
window.localStorage.setItem(ArduinoAdvancedMode.LS_ID, oldModeState ? 'false' : 'true');
|
|
||||||
registry.executeCommand('reset.layout');
|
|
||||||
},
|
|
||||||
isVisible: widget => ArduinoToolbar.is(widget) && widget.side === 'right',
|
isVisible: widget => ArduinoToolbar.is(widget) && widget.side === 'right',
|
||||||
isToggled: () => ArduinoAdvancedMode.TOGGLED
|
isToggled: () => this.editorMode.proMode
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
registerMenus(registry: MenuModelRegistry) {
|
registerMenus(registry: MenuModelRegistry) {
|
||||||
if (!ArduinoAdvancedMode.TOGGLED) {
|
if (!this.editorMode.proMode) {
|
||||||
// If are not in pro-mode, we have to disable the context menu for the tabs.
|
// If are not in pro-mode, we have to disable the context menu for the tabs.
|
||||||
// Such as `Close`, `Close All`, etc.
|
// Such as `Close`, `Close All`, etc.
|
||||||
for (const command of [
|
for (const command of [
|
||||||
@ -362,7 +432,8 @@ export class ArduinoFrontendContribution implements TabBarToolbarContribution, C
|
|||||||
CommonCommands.CLOSE_RIGHT_TABS,
|
CommonCommands.CLOSE_RIGHT_TABS,
|
||||||
CommonCommands.CLOSE_ALL_TABS,
|
CommonCommands.CLOSE_ALL_TABS,
|
||||||
CommonCommands.COLLAPSE_PANEL,
|
CommonCommands.COLLAPSE_PANEL,
|
||||||
CommonCommands.TOGGLE_MAXIMIZED
|
CommonCommands.TOGGLE_MAXIMIZED,
|
||||||
|
FileNavigatorCommands.REVEAL_IN_NAVIGATOR
|
||||||
]) {
|
]) {
|
||||||
registry.unregisterMenuAction(command);
|
registry.unregisterMenuAction(command);
|
||||||
}
|
}
|
||||||
@ -370,8 +441,6 @@ export class ArduinoFrontendContribution implements TabBarToolbarContribution, C
|
|||||||
registry.unregisterMenuAction(FileSystemCommands.UPLOAD);
|
registry.unregisterMenuAction(FileSystemCommands.UPLOAD);
|
||||||
registry.unregisterMenuAction(FileDownloadCommands.DOWNLOAD);
|
registry.unregisterMenuAction(FileDownloadCommands.DOWNLOAD);
|
||||||
|
|
||||||
registry.unregisterMenuAction(WorkspaceCommands.NEW_FOLDER);
|
|
||||||
|
|
||||||
registry.unregisterMenuAction(WorkspaceCommands.OPEN_FOLDER);
|
registry.unregisterMenuAction(WorkspaceCommands.OPEN_FOLDER);
|
||||||
registry.unregisterMenuAction(WorkspaceCommands.OPEN_WORKSPACE);
|
registry.unregisterMenuAction(WorkspaceCommands.OPEN_WORKSPACE);
|
||||||
registry.unregisterMenuAction(WorkspaceCommands.OPEN_RECENT_WORKSPACE);
|
registry.unregisterMenuAction(WorkspaceCommands.OPEN_RECENT_WORKSPACE);
|
||||||
@ -425,8 +494,8 @@ export class ArduinoFrontendContribution implements TabBarToolbarContribution, C
|
|||||||
const command: Command = {
|
const command: Command = {
|
||||||
id: 'openSketch' + sketch.name
|
id: 'openSketch' + sketch.name
|
||||||
}
|
}
|
||||||
this.commands.registerCommand(command, {
|
this.commandRegistry.registerCommand(command, {
|
||||||
execute: () => this.commands.executeCommand(ArduinoCommands.OPEN_SKETCH.id, sketch)
|
execute: () => this.commandRegistry.executeCommand(ArduinoCommands.OPEN_SKETCH.id, sketch)
|
||||||
});
|
});
|
||||||
|
|
||||||
registry.registerMenuAction(ArduinoToolbarContextMenu.WS_SKETCHES_GROUP, {
|
registry.registerMenuAction(ArduinoToolbarContextMenu.WS_SKETCHES_GROUP, {
|
||||||
@ -466,7 +535,7 @@ export class ArduinoFrontendContribution implements TabBarToolbarContribution, C
|
|||||||
if (destinationFile && !destinationFile.isDirectory) {
|
if (destinationFile && !destinationFile.isDirectory) {
|
||||||
const message = await this.validate(destinationFile);
|
const message = await this.validate(destinationFile);
|
||||||
if (!message) {
|
if (!message) {
|
||||||
await this.workspaceService.open(destinationFileUri);
|
this.workspaceService.open(destinationFileUri);
|
||||||
return destinationFileUri;
|
return destinationFileUri;
|
||||||
} else {
|
} else {
|
||||||
this.messageService.warn(message);
|
this.messageService.warn(message);
|
||||||
|
@ -10,7 +10,7 @@ import { LanguageGrammarDefinitionContribution } from '@theia/monaco/lib/browser
|
|||||||
import { LanguageClientContribution } from '@theia/languages/lib/browser';
|
import { LanguageClientContribution } from '@theia/languages/lib/browser';
|
||||||
import { ArduinoLanguageClientContribution } from './language/arduino-language-client-contribution';
|
import { ArduinoLanguageClientContribution } from './language/arduino-language-client-contribution';
|
||||||
import { LibraryListWidget } from './library/library-list-widget';
|
import { LibraryListWidget } from './library/library-list-widget';
|
||||||
import { ArduinoFrontendContribution, ArduinoAdvancedMode } from './arduino-frontend-contribution';
|
import { ArduinoFrontendContribution } from './arduino-frontend-contribution';
|
||||||
import { ArduinoLanguageGrammarContribution } from './language/arduino-language-grammar-contribution';
|
import { ArduinoLanguageGrammarContribution } from './language/arduino-language-grammar-contribution';
|
||||||
import { LibraryService, LibraryServicePath } from '../common/protocol/library-service';
|
import { LibraryService, LibraryServicePath } from '../common/protocol/library-service';
|
||||||
import { BoardsService, BoardsServicePath, BoardsServiceClient } from '../common/protocol/boards-service';
|
import { BoardsService, BoardsServicePath, BoardsServiceClient } from '../common/protocol/boards-service';
|
||||||
@ -30,30 +30,28 @@ import { ThemeService } from '@theia/core/lib/browser/theming';
|
|||||||
import { ArduinoTheme } from './arduino-theme';
|
import { ArduinoTheme } from './arduino-theme';
|
||||||
import { MenuContribution } from '@theia/core';
|
import { MenuContribution } from '@theia/core';
|
||||||
import { OutlineViewContribution } from '@theia/outline-view/lib/browser/outline-view-contribution';
|
import { OutlineViewContribution } from '@theia/outline-view/lib/browser/outline-view-contribution';
|
||||||
import { SilentOutlineViewContribution } from './customization/silent-outline-contribution';
|
import { ArduinoOutlineViewContribution } from './customization/arduino-outline-contribution';
|
||||||
import { ProblemContribution } from '@theia/markers/lib/browser/problem/problem-contribution';
|
import { ProblemContribution } from '@theia/markers/lib/browser/problem/problem-contribution';
|
||||||
import { SilentProblemContribution } from './customization/silent-problem-contribution';
|
import { ArduinoProblemContribution } from './customization/arduino-problem-contribution';
|
||||||
import { SilentNavigatorContribution } from './customization/silent-navigator-contribution';
|
import { ArduinoNavigatorContribution } from './customization/arduino-navigator-contribution';
|
||||||
import { FileNavigatorContribution } from '@theia/navigator/lib/browser/navigator-contribution';
|
import { FileNavigatorContribution } from '@theia/navigator/lib/browser/navigator-contribution';
|
||||||
import { ArduinoToolbarContribution } from './toolbar/arduino-toolbar-contribution';
|
import { ArduinoToolbarContribution } from './toolbar/arduino-toolbar-contribution';
|
||||||
import { OutputToolbarContribution } from '@theia/output/lib/browser/output-toolbar-contribution';
|
import { OutputToolbarContribution } from '@theia/output/lib/browser/output-toolbar-contribution';
|
||||||
import { ArduinoOutputToolContribution } from './customization/silent-output-tool-contribution';
|
import { ArduinoOutputToolContribution } from './customization/arduino-output-tool-contribution';
|
||||||
import { EditorContribution } from '@theia/editor/lib/browser/editor-contribution';
|
import { EditorContribution } from '@theia/editor/lib/browser/editor-contribution';
|
||||||
import { ArduinoEditorContribution } from './customization/arduino-editor-contribution';
|
import { ArduinoEditorContribution } from './customization/arduino-editor-contribution';
|
||||||
import { MonacoStatusBarContribution } from '@theia/monaco/lib/browser/monaco-status-bar-contribution';
|
import { MonacoStatusBarContribution } from '@theia/monaco/lib/browser/monaco-status-bar-contribution';
|
||||||
import { ArduinoMonacoStatusBarContribution } from './customization/arduino-monaco-status-bar-contribution';
|
import { ArduinoMonacoStatusBarContribution } from './customization/arduino-monaco-status-bar-contribution';
|
||||||
import { ApplicationShell } from '@theia/core/lib/browser';
|
import { ApplicationShell, ShellLayoutRestorer } from '@theia/core/lib/browser';
|
||||||
import { ArduinoApplicationShell } from './customization/arduino-application-shell';
|
import { ArduinoApplicationShell } from './customization/arduino-application-shell';
|
||||||
import { ArduinoFrontendApplication } from './customization/arduino-frontend-application';
|
import { ArduinoFrontendApplication } from './customization/arduino-frontend-application';
|
||||||
import { BoardsConfigDialog, BoardsConfigDialogProps } from './boards/boards-config-dialog';
|
import { BoardsConfigDialog, BoardsConfigDialogProps } from './boards/boards-config-dialog';
|
||||||
import { BoardsConfigDialogWidget } from './boards/boards-config-dialog-widget';
|
import { BoardsConfigDialogWidget } from './boards/boards-config-dialog-widget';
|
||||||
import { ScmContribution } from '@theia/scm/lib/browser/scm-contribution';
|
import { ScmContribution } from '@theia/scm/lib/browser/scm-contribution';
|
||||||
import { SilentScmContribution } from './customization/silent-scm-contribution';
|
import { ArduinoScmContribution } from './customization/arduino-scm-contribution';
|
||||||
import { SearchInWorkspaceFrontendContribution } from '@theia/search-in-workspace/lib/browser/search-in-workspace-frontend-contribution';
|
import { SearchInWorkspaceFrontendContribution } from '@theia/search-in-workspace/lib/browser/search-in-workspace-frontend-contribution';
|
||||||
import { SilentSearchInWorkspaceContribution } from './customization/silent-search-in-workspace-contribution';
|
import { ArduinoSearchInWorkspaceContribution } from './customization/arduino-search-in-workspace-contribution';
|
||||||
import { LibraryListWidgetFrontendContribution } from './library/library-widget-frontend-contribution';
|
import { LibraryListWidgetFrontendContribution } from './library/library-widget-frontend-contribution';
|
||||||
import { LibraryItemRenderer } from './library/library-item-renderer';
|
|
||||||
import { BoardItemRenderer } from './boards/boards-item-renderer';
|
|
||||||
import { MonitorServiceClientImpl } from './monitor/monitor-service-client-impl';
|
import { MonitorServiceClientImpl } from './monitor/monitor-service-client-impl';
|
||||||
import { MonitorServicePath, MonitorService, MonitorServiceClient } from '../common/protocol/monitor-service';
|
import { MonitorServicePath, MonitorService, MonitorServiceClient } from '../common/protocol/monitor-service';
|
||||||
import { ConfigService, ConfigServicePath } from '../common/protocol/config-service';
|
import { ConfigService, ConfigServicePath } from '../common/protocol/config-service';
|
||||||
@ -70,6 +68,9 @@ import { ArduinoProblemManager } from './markers/arduino-problem-manager';
|
|||||||
import { BoardsAutoInstaller } from './boards/boards-auto-installer';
|
import { BoardsAutoInstaller } from './boards/boards-auto-installer';
|
||||||
import { AboutDialog } from '@theia/core/lib/browser/about-dialog';
|
import { AboutDialog } from '@theia/core/lib/browser/about-dialog';
|
||||||
import { ArduinoAboutDialog } from './customization/arduino-about-dialog';
|
import { ArduinoAboutDialog } from './customization/arduino-about-dialog';
|
||||||
|
import { ArduinoShellLayoutRestorer } from './shell/arduino-shell-layout-restorer';
|
||||||
|
import { EditorMode } from './editor-mode';
|
||||||
|
import { ListItemRenderer } from './components/component-list/list-item-renderer';
|
||||||
const ElementQueries = require('css-element-queries/src/ElementQueries');
|
const ElementQueries = require('css-element-queries/src/ElementQueries');
|
||||||
|
|
||||||
export default new ContainerModule((bind: interfaces.Bind, unbind: interfaces.Unbind, isBound: interfaces.IsBound, rebind: interfaces.Rebind) => {
|
export default new ContainerModule((bind: interfaces.Bind, unbind: interfaces.Unbind, isBound: interfaces.IsBound, rebind: interfaces.Rebind) => {
|
||||||
@ -90,9 +91,11 @@ export default new ContainerModule((bind: interfaces.Bind, unbind: interfaces.Un
|
|||||||
bind(LanguageGrammarDefinitionContribution).to(ArduinoLanguageGrammarContribution).inSingletonScope();
|
bind(LanguageGrammarDefinitionContribution).to(ArduinoLanguageGrammarContribution).inSingletonScope();
|
||||||
bind(LanguageClientContribution).to(ArduinoLanguageClientContribution).inSingletonScope();
|
bind(LanguageClientContribution).to(ArduinoLanguageClientContribution).inSingletonScope();
|
||||||
|
|
||||||
|
// Renderer for both the library and the core widgets.
|
||||||
|
bind(ListItemRenderer).toSelf().inSingletonScope();
|
||||||
|
|
||||||
// Library service
|
// Library service
|
||||||
bind(LibraryService).toDynamicValue(context => WebSocketConnectionProvider.createProxy(context.container, LibraryServicePath)).inSingletonScope();
|
bind(LibraryService).toDynamicValue(context => WebSocketConnectionProvider.createProxy(context.container, LibraryServicePath)).inSingletonScope();
|
||||||
|
|
||||||
// Library list widget
|
// Library list widget
|
||||||
bind(LibraryListWidget).toSelf();
|
bind(LibraryListWidget).toSelf();
|
||||||
bindViewContribution(bind, LibraryListWidgetFrontendContribution);
|
bindViewContribution(bind, LibraryListWidgetFrontendContribution);
|
||||||
@ -101,7 +104,6 @@ export default new ContainerModule((bind: interfaces.Bind, unbind: interfaces.Un
|
|||||||
createWidget: () => context.container.get(LibraryListWidget)
|
createWidget: () => context.container.get(LibraryListWidget)
|
||||||
}));
|
}));
|
||||||
bind(FrontendApplicationContribution).toService(LibraryListWidgetFrontendContribution);
|
bind(FrontendApplicationContribution).toService(LibraryListWidgetFrontendContribution);
|
||||||
bind(LibraryItemRenderer).toSelf().inSingletonScope();
|
|
||||||
|
|
||||||
// Sketch list service
|
// Sketch list service
|
||||||
bind(SketchesService).toDynamicValue(context => WebSocketConnectionProvider.createProxy(context.container, SketchesServicePath)).inSingletonScope();
|
bind(SketchesService).toDynamicValue(context => WebSocketConnectionProvider.createProxy(context.container, SketchesServicePath)).inSingletonScope();
|
||||||
@ -135,7 +137,6 @@ export default new ContainerModule((bind: interfaces.Bind, unbind: interfaces.Un
|
|||||||
createWidget: () => context.container.get(BoardsListWidget)
|
createWidget: () => context.container.get(BoardsListWidget)
|
||||||
}));
|
}));
|
||||||
bind(FrontendApplicationContribution).toService(BoardsListWidgetFrontendContribution);
|
bind(FrontendApplicationContribution).toService(BoardsListWidgetFrontendContribution);
|
||||||
bind(BoardItemRenderer).toSelf().inSingletonScope();
|
|
||||||
|
|
||||||
// Board select dialog
|
// Board select dialog
|
||||||
bind(BoardsConfigDialogWidget).toSelf().inSingletonScope();
|
bind(BoardsConfigDialogWidget).toSelf().inSingletonScope();
|
||||||
@ -158,7 +159,7 @@ export default new ContainerModule((bind: interfaces.Bind, unbind: interfaces.Un
|
|||||||
}).inSingletonScope();
|
}).inSingletonScope();
|
||||||
|
|
||||||
// The workspace service extension
|
// The workspace service extension
|
||||||
bind(WorkspaceServiceExt).to(WorkspaceServiceExtImpl).inSingletonScope().onActivation(({ container }, workspaceServiceExt) => {
|
bind(WorkspaceServiceExt).to(WorkspaceServiceExtImpl).inSingletonScope().onActivation(({ container }, workspaceServiceExt: WorkspaceServiceExt) => {
|
||||||
WebSocketConnectionProvider.createProxy(container, WorkspaceServiceExtPath, workspaceServiceExt);
|
WebSocketConnectionProvider.createProxy(container, WorkspaceServiceExtPath, workspaceServiceExt);
|
||||||
// Eagerly active the core, library, and boards services.
|
// Eagerly active the core, library, and boards services.
|
||||||
container.get(CoreService);
|
container.get(CoreService);
|
||||||
@ -198,50 +199,37 @@ export default new ContainerModule((bind: interfaces.Bind, unbind: interfaces.Un
|
|||||||
const themeService = ThemeService.get();
|
const themeService = ThemeService.get();
|
||||||
themeService.register(...ArduinoTheme.themes);
|
themeService.register(...ArduinoTheme.themes);
|
||||||
|
|
||||||
// Customizing default Theia layout
|
// Customizing default Theia layout based on the editor mode: `pro-mode` or `classic`.
|
||||||
if (!ArduinoAdvancedMode.TOGGLED) {
|
bind(EditorMode).toSelf().inSingletonScope();
|
||||||
unbind(OutlineViewContribution);
|
bind(FrontendApplicationContribution).toService(EditorMode);
|
||||||
bind(OutlineViewContribution).to(SilentOutlineViewContribution).inSingletonScope();
|
rebind(OutlineViewContribution).to(ArduinoOutlineViewContribution).inSingletonScope();
|
||||||
unbind(ProblemContribution);
|
rebind(ProblemContribution).to(ArduinoProblemContribution).inSingletonScope();
|
||||||
bind(ProblemContribution).to(SilentProblemContribution).inSingletonScope();
|
rebind(FileNavigatorContribution).to(ArduinoNavigatorContribution).inSingletonScope();
|
||||||
unbind(FileNavigatorContribution);
|
rebind(OutputToolbarContribution).to(ArduinoOutputToolContribution).inSingletonScope();
|
||||||
bind(FileNavigatorContribution).to(SilentNavigatorContribution).inSingletonScope();
|
rebind(EditorContribution).to(ArduinoEditorContribution).inSingletonScope();
|
||||||
unbind(OutputToolbarContribution);
|
rebind(MonacoStatusBarContribution).to(ArduinoMonacoStatusBarContribution).inSingletonScope();
|
||||||
bind(OutputToolbarContribution).to(ArduinoOutputToolContribution).inSingletonScope();
|
rebind(ApplicationShell).to(ArduinoApplicationShell).inSingletonScope();
|
||||||
unbind(EditorContribution);
|
rebind(ScmContribution).to(ArduinoScmContribution).inSingletonScope();
|
||||||
bind(EditorContribution).to(ArduinoEditorContribution).inSingletonScope();
|
rebind(SearchInWorkspaceFrontendContribution).to(ArduinoSearchInWorkspaceContribution).inSingletonScope();
|
||||||
unbind(MonacoStatusBarContribution);
|
rebind(FrontendApplication).to(ArduinoFrontendApplication).inSingletonScope();
|
||||||
bind(MonacoStatusBarContribution).to(ArduinoMonacoStatusBarContribution).inSingletonScope();
|
|
||||||
unbind(ApplicationShell);
|
|
||||||
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 visibility of the close button for the editors and views.
|
|
||||||
document.body.classList.add(ArduinoAdvancedMode.LS_ID);
|
|
||||||
}
|
|
||||||
unbind(FrontendApplication);
|
|
||||||
bind(FrontendApplication).to(ArduinoFrontendApplication).inSingletonScope();
|
|
||||||
|
|
||||||
// Monaco customizations
|
// Monaco customizations
|
||||||
unbind(MonacoEditorProvider);
|
|
||||||
bind(ArduinoMonacoEditorProvider).toSelf().inSingletonScope();
|
bind(ArduinoMonacoEditorProvider).toSelf().inSingletonScope();
|
||||||
bind(MonacoEditorProvider).toService(ArduinoMonacoEditorProvider);
|
rebind(MonacoEditorProvider).toService(ArduinoMonacoEditorProvider);
|
||||||
|
|
||||||
// Decorator customizations
|
// Decorator customizations
|
||||||
unbind(TabBarDecoratorService);
|
|
||||||
bind(ArduinoTabBarDecoratorService).toSelf().inSingletonScope();
|
bind(ArduinoTabBarDecoratorService).toSelf().inSingletonScope();
|
||||||
bind(TabBarDecoratorService).toService(ArduinoTabBarDecoratorService);
|
rebind(TabBarDecoratorService).toService(ArduinoTabBarDecoratorService);
|
||||||
|
|
||||||
// Problem markers
|
// Problem markers
|
||||||
unbind(ProblemManager);
|
|
||||||
bind(ArduinoProblemManager).toSelf().inSingletonScope();
|
bind(ArduinoProblemManager).toSelf().inSingletonScope();
|
||||||
bind(ProblemManager).toService(ArduinoProblemManager);
|
rebind(ProblemManager).toService(ArduinoProblemManager);
|
||||||
|
|
||||||
// About dialog to show the CLI version
|
// About dialog to show the CLI version
|
||||||
unbind(AboutDialog);
|
|
||||||
bind(ArduinoAboutDialog).toSelf().inSingletonScope();
|
bind(ArduinoAboutDialog).toSelf().inSingletonScope();
|
||||||
bind(AboutDialog).toService(ArduinoAboutDialog);
|
rebind(AboutDialog).toService(ArduinoAboutDialog);
|
||||||
|
|
||||||
|
// Customized layout restorer that can restore the state in async way: https://github.com/eclipse-theia/theia/issues/6579
|
||||||
|
bind(ArduinoShellLayoutRestorer).toSelf().inSingletonScope();
|
||||||
|
rebind(ShellLayoutRestorer).toService(ArduinoShellLayoutRestorer);
|
||||||
});
|
});
|
||||||
|
@ -9,6 +9,7 @@ const ARDUINO_JSON = MonacoThemeRegistry.SINGLETON.register(
|
|||||||
export class ArduinoTheme {
|
export class ArduinoTheme {
|
||||||
|
|
||||||
static readonly arduino: Theme = {
|
static readonly arduino: Theme = {
|
||||||
|
type: 'light',
|
||||||
id: 'arduino-theme',
|
id: 'arduino-theme',
|
||||||
label: 'Arduino Light Theme',
|
label: 'Arduino Light Theme',
|
||||||
description: 'Arduino Light Theme',
|
description: 'Arduino Light Theme',
|
||||||
|
@ -4,7 +4,7 @@ import { WorkspaceService } from '@theia/workspace/lib/browser/workspace-service
|
|||||||
import { ConfigService } from '../common/protocol/config-service';
|
import { ConfigService } from '../common/protocol/config-service';
|
||||||
import { SketchesService } from '../common/protocol/sketches-service';
|
import { SketchesService } from '../common/protocol/sketches-service';
|
||||||
import { ArduinoWorkspaceRootResolver } from './arduino-workspace-resolver';
|
import { ArduinoWorkspaceRootResolver } from './arduino-workspace-resolver';
|
||||||
import { ArduinoAdvancedMode } from './arduino-frontend-contribution';
|
import { EditorMode } from './editor-mode';
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class ArduinoWorkspaceService extends WorkspaceService {
|
export class ArduinoWorkspaceService extends WorkspaceService {
|
||||||
@ -18,7 +18,10 @@ export class ArduinoWorkspaceService extends WorkspaceService {
|
|||||||
@inject(LabelProvider)
|
@inject(LabelProvider)
|
||||||
protected readonly labelProvider: LabelProvider;
|
protected readonly labelProvider: LabelProvider;
|
||||||
|
|
||||||
async getDefaultWorkspacePath(): Promise<string | undefined> {
|
@inject(EditorMode)
|
||||||
|
protected readonly editorMode: EditorMode;
|
||||||
|
|
||||||
|
async getDefaultWorkspaceUri(): Promise<string | undefined> {
|
||||||
const [hash, recentWorkspaces, recentSketches] = await Promise.all([
|
const [hash, recentWorkspaces, recentSketches] = await Promise.all([
|
||||||
window.location.hash,
|
window.location.hash,
|
||||||
this.sketchService.getSketches().then(sketches => sketches.map(({ uri }) => uri)),
|
this.sketchService.getSketches().then(sketches => sketches.map(({ uri }) => uri)),
|
||||||
@ -36,7 +39,8 @@ export class ArduinoWorkspaceService extends WorkspaceService {
|
|||||||
await this.server.setMostRecentlyUsedWorkspace(uri);
|
await this.server.setMostRecentlyUsedWorkspace(uri);
|
||||||
return toOpen.uri;
|
return toOpen.uri;
|
||||||
}
|
}
|
||||||
return (await this.sketchService.createNewSketch()).uri;
|
const { sketchDirUri } = (await this.configService.getConfiguration());
|
||||||
|
return (await this.sketchService.createNewSketch(sketchDirUri)).uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async isValid(uri: string): Promise<boolean> {
|
private async isValid(uri: string): Promise<boolean> {
|
||||||
@ -46,7 +50,7 @@ export class ArduinoWorkspaceService extends WorkspaceService {
|
|||||||
}
|
}
|
||||||
// The workspace root location must exist. However, when opening a workspace root in pro-mode,
|
// The workspace root location must exist. However, when opening a workspace root in pro-mode,
|
||||||
// the workspace root must not be a sketch folder. It can be the default sketch directory, or any other directories, for instance.
|
// the workspace root must not be a sketch folder. It can be the default sketch directory, or any other directories, for instance.
|
||||||
if (!ArduinoAdvancedMode.TOGGLED) {
|
if (this.editorMode.proMode) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
const sketchFolder = await this.sketchService.isSketchFolder(uri);
|
const sketchFolder = await this.sketchService.isSketchFolder(uri);
|
||||||
|
@ -4,7 +4,7 @@ import { FrontendApplicationContribution } from '@theia/core/lib/browser/fronten
|
|||||||
import { BoardsService, Board } from '../../common/protocol/boards-service';
|
import { BoardsService, Board } from '../../common/protocol/boards-service';
|
||||||
import { BoardsServiceClientImpl } from './boards-service-client-impl';
|
import { BoardsServiceClientImpl } from './boards-service-client-impl';
|
||||||
import { BoardsListWidgetFrontendContribution } from './boards-widget-frontend-contribution';
|
import { BoardsListWidgetFrontendContribution } from './boards-widget-frontend-contribution';
|
||||||
import { InstallationProgressDialog } from '../components/installation-progress-dialog';
|
import { InstallationProgressDialog } from '../components/progress-dialog';
|
||||||
import { BoardsConfig } from './boards-config';
|
import { BoardsConfig } from './boards-config';
|
||||||
|
|
||||||
|
|
||||||
@ -43,21 +43,21 @@ export class BoardsAutoInstaller implements FrontendApplicationContribution {
|
|||||||
// tslint:disable-next-line:max-line-length
|
// tslint:disable-next-line:max-line-length
|
||||||
this.messageService.info(`The \`"${candidate.name}"\` core has to be installed for the currently selected \`"${selectedBoard.name}"\` board. Do you want to install it now?`, 'Yes', 'Install Manually').then(async answer => {
|
this.messageService.info(`The \`"${candidate.name}"\` core has to be installed for the currently selected \`"${selectedBoard.name}"\` board. Do you want to install it now?`, 'Yes', 'Install Manually').then(async answer => {
|
||||||
if (answer === 'Yes') {
|
if (answer === 'Yes') {
|
||||||
const dialog = new InstallationProgressDialog(candidate.name);
|
const dialog = new InstallationProgressDialog(candidate.name, candidate.availableVersions[0]);
|
||||||
dialog.open();
|
dialog.open();
|
||||||
try {
|
try {
|
||||||
await this.boardsService.install(candidate);
|
await this.boardsService.install({ item: candidate });
|
||||||
} finally {
|
} finally {
|
||||||
dialog.close();
|
dialog.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (answer) {
|
if (answer) {
|
||||||
this.boardsManagerFrontendContribution.openView({ reveal: true }).then(widget => widget.refresh(candidate.name.toLocaleLowerCase()));
|
this.boardsManagerFrontendContribution.openView({ reveal: true }).then(widget => widget.refresh(candidate.name.toLocaleLowerCase()));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,54 +0,0 @@
|
|||||||
import * as React from 'react';
|
|
||||||
import { injectable } from 'inversify';
|
|
||||||
import { ListItemRenderer } from '../components/component-list/list-item-renderer';
|
|
||||||
import { BoardPackage } from '../../common/protocol/boards-service';
|
|
||||||
|
|
||||||
@injectable()
|
|
||||||
export class BoardItemRenderer extends ListItemRenderer<BoardPackage> {
|
|
||||||
|
|
||||||
renderItem(item: BoardPackage, install: (item: BoardPackage) => Promise<void>): React.ReactNode {
|
|
||||||
const name = <span className='name'>{item.name}</span>;
|
|
||||||
const author = <span className='author'>{item.author}</span>;
|
|
||||||
const installedVersion = !!item.installedVersion && <div className='version-info'>
|
|
||||||
<span className='version'>Version {item.installedVersion}</span>
|
|
||||||
<span className='installed'>INSTALLED</span>
|
|
||||||
</div>;
|
|
||||||
|
|
||||||
const summary = <div className='summary'>{item.summary}</div>;
|
|
||||||
const description = <div className='summary'>{item.description}</div>;
|
|
||||||
|
|
||||||
const moreInfo = !!item.moreInfoLink && <a href={item.moreInfoLink} onClick={this.onClick}>More info</a>;
|
|
||||||
const installButton = item.installable && !item.installedVersion &&
|
|
||||||
<button className='install' onClick={install.bind(this, item)}>INSTALL</button>;
|
|
||||||
|
|
||||||
const versions = (() => {
|
|
||||||
const { availableVersions } = item;
|
|
||||||
if (!!item.installedVersion || availableVersions.length === 0) {
|
|
||||||
return undefined;
|
|
||||||
} else if (availableVersions.length === 1) {
|
|
||||||
return <label>{availableVersions[0]}</label>
|
|
||||||
} else {
|
|
||||||
return <select>{item.availableVersions.map(version => <option value={version} key={version}>{version}</option>)}</select>;
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
|
|
||||||
return <div className='component-list-item noselect'>
|
|
||||||
<div className='header'>
|
|
||||||
<span>{name} by {author}</span>
|
|
||||||
{installedVersion}
|
|
||||||
</div>
|
|
||||||
<div className='content'>
|
|
||||||
{summary}
|
|
||||||
{description}
|
|
||||||
</div>
|
|
||||||
<div className='info'>
|
|
||||||
{moreInfo}
|
|
||||||
</div>
|
|
||||||
<div className='footer'>
|
|
||||||
{installButton}
|
|
||||||
{versions}
|
|
||||||
</div>
|
|
||||||
</div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,7 +1,7 @@
|
|||||||
import { inject, injectable } from 'inversify';
|
import { inject, injectable } from 'inversify';
|
||||||
import { BoardPackage, BoardsService } from '../../common/protocol/boards-service';
|
import { BoardPackage, BoardsService } from '../../common/protocol/boards-service';
|
||||||
import { ListWidget } from '../components/component-list/list-widget';
|
import { ListWidget } from '../components/component-list/list-widget';
|
||||||
import { BoardItemRenderer } from './boards-item-renderer';
|
import { ListItemRenderer } from '../components/component-list/list-item-renderer';
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class BoardsListWidget extends ListWidget<BoardPackage> {
|
export class BoardsListWidget extends ListWidget<BoardPackage> {
|
||||||
@ -11,7 +11,7 @@ export class BoardsListWidget extends ListWidget<BoardPackage> {
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@inject(BoardsService) protected service: BoardsService,
|
@inject(BoardsService) protected service: BoardsService,
|
||||||
@inject(BoardItemRenderer) protected itemRenderer: BoardItemRenderer) {
|
@inject(ListItemRenderer) protected itemRenderer: ListItemRenderer<BoardPackage>) {
|
||||||
|
|
||||||
super({
|
super({
|
||||||
id: BoardsListWidget.WIDGET_ID,
|
id: BoardsListWidget.WIDGET_ID,
|
||||||
|
@ -3,7 +3,7 @@ import { Emitter } from '@theia/core/lib/common/event';
|
|||||||
import { ILogger } from '@theia/core/lib/common/logger';
|
import { ILogger } from '@theia/core/lib/common/logger';
|
||||||
import { LocalStorageService } from '@theia/core/lib/browser/storage-service';
|
import { LocalStorageService } from '@theia/core/lib/browser/storage-service';
|
||||||
import { RecursiveRequired } from '../../common/types';
|
import { RecursiveRequired } from '../../common/types';
|
||||||
import { BoardsServiceClient, AttachedBoardsChangeEvent, BoardInstalledEvent, AttachedSerialBoard, Board, Port } from '../../common/protocol/boards-service';
|
import { BoardsServiceClient, AttachedBoardsChangeEvent, BoardInstalledEvent, AttachedSerialBoard, Board, Port, BoardUninstalledEvent } from '../../common/protocol/boards-service';
|
||||||
import { BoardsConfig } from './boards-config';
|
import { BoardsConfig } from './boards-config';
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
@ -16,6 +16,7 @@ export class BoardsServiceClientImpl implements BoardsServiceClient {
|
|||||||
protected storageService: LocalStorageService;
|
protected storageService: LocalStorageService;
|
||||||
|
|
||||||
protected readonly onBoardInstalledEmitter = new Emitter<BoardInstalledEvent>();
|
protected readonly onBoardInstalledEmitter = new Emitter<BoardInstalledEvent>();
|
||||||
|
protected readonly onBoardUninstalledEmitter = new Emitter<BoardUninstalledEvent>();
|
||||||
protected readonly onAttachedBoardsChangedEmitter = new Emitter<AttachedBoardsChangeEvent>();
|
protected readonly onAttachedBoardsChangedEmitter = new Emitter<AttachedBoardsChangeEvent>();
|
||||||
protected readonly onSelectedBoardsConfigChangedEmitter = new Emitter<BoardsConfig.Config>();
|
protected readonly onSelectedBoardsConfigChangedEmitter = new Emitter<BoardsConfig.Config>();
|
||||||
|
|
||||||
@ -31,6 +32,7 @@ export class BoardsServiceClientImpl implements BoardsServiceClient {
|
|||||||
|
|
||||||
readonly onBoardsChanged = this.onAttachedBoardsChangedEmitter.event;
|
readonly onBoardsChanged = this.onAttachedBoardsChangedEmitter.event;
|
||||||
readonly onBoardInstalled = this.onBoardInstalledEmitter.event;
|
readonly onBoardInstalled = this.onBoardInstalledEmitter.event;
|
||||||
|
readonly onBoardUninstalled = this.onBoardUninstalledEmitter.event;
|
||||||
readonly onBoardsConfigChanged = this.onSelectedBoardsConfigChangedEmitter.event;
|
readonly onBoardsConfigChanged = this.onSelectedBoardsConfigChangedEmitter.event;
|
||||||
|
|
||||||
@postConstruct()
|
@postConstruct()
|
||||||
@ -87,6 +89,11 @@ export class BoardsServiceClientImpl implements BoardsServiceClient {
|
|||||||
this.onBoardInstalledEmitter.fire(event);
|
this.onBoardInstalledEmitter.fire(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
notifyBoardUninstalled(event: BoardUninstalledEvent): void {
|
||||||
|
this.logger.info('Board uninstalled: ', JSON.stringify(event));
|
||||||
|
this.onBoardUninstalledEmitter.fire(event);
|
||||||
|
}
|
||||||
|
|
||||||
set boardsConfig(config: BoardsConfig.Config) {
|
set boardsConfig(config: BoardsConfig.Config) {
|
||||||
this.logger.info('Board config changed: ', JSON.stringify(config));
|
this.logger.info('Board config changed: ', JSON.stringify(config));
|
||||||
this._boardsConfig = config;
|
this._boardsConfig = config;
|
||||||
|
@ -1,25 +1,66 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
import { Installable } from '../../../common/protocol/installable';
|
||||||
|
import { ArduinoComponent } from '../../../common/protocol/arduino-component';
|
||||||
import { ListItemRenderer } from './list-item-renderer';
|
import { ListItemRenderer } from './list-item-renderer';
|
||||||
|
|
||||||
export class ComponentListItem<T> extends React.Component<ComponentListItem.Props<T>> {
|
export class ComponentListItem<T extends ArduinoComponent> extends React.Component<ComponentListItem.Props<T>, ComponentListItem.State> {
|
||||||
|
|
||||||
|
constructor(props: ComponentListItem.Props<T>) {
|
||||||
|
super(props);
|
||||||
|
if (props.item.installable) {
|
||||||
|
const version = props.item.availableVersions.filter(version => version !== props.item.installedVersion)[0];
|
||||||
|
this.state = {
|
||||||
|
selectedVersion: version
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected async install(item: T): Promise<void> {
|
protected async install(item: T): Promise<void> {
|
||||||
await this.props.install(item);
|
const toInstall = this.state.selectedVersion;
|
||||||
|
const version = this.props.item.availableVersions.filter(version => version !== this.state.selectedVersion)[0];
|
||||||
|
this.setState({
|
||||||
|
selectedVersion: version
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
await this.props.install(item, toInstall);
|
||||||
|
} catch {
|
||||||
|
this.setState({
|
||||||
|
selectedVersion: toInstall
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async uninstall(item: T): Promise<void> {
|
||||||
|
await this.props.uninstall(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected onVersionChange(version: Installable.Version) {
|
||||||
|
this.setState({ selectedVersion: version });
|
||||||
}
|
}
|
||||||
|
|
||||||
render(): React.ReactNode {
|
render(): React.ReactNode {
|
||||||
const { item, itemRenderer, install } = this.props;
|
const { item, itemRenderer } = this.props;
|
||||||
return itemRenderer.renderItem(item, install.bind(this));
|
return itemRenderer.renderItem(
|
||||||
|
Object.assign(this.state, { item }),
|
||||||
|
this.install.bind(this),
|
||||||
|
this.uninstall.bind(this),
|
||||||
|
this.onVersionChange.bind(this)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export namespace ComponentListItem {
|
export namespace ComponentListItem {
|
||||||
|
|
||||||
export interface Props<T> {
|
export interface Props<T extends ArduinoComponent> {
|
||||||
readonly item: T;
|
readonly item: T;
|
||||||
readonly install: (item: T) => Promise<void>;
|
readonly install: (item: T, version?: Installable.Version) => Promise<void>;
|
||||||
|
readonly uninstall: (item: T) => Promise<void>;
|
||||||
readonly itemRenderer: ListItemRenderer<T>;
|
readonly itemRenderer: ListItemRenderer<T>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface State {
|
||||||
|
selectedVersion?: Installable.Version;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
import { Installable } from '../../../common/protocol/installable';
|
||||||
|
import { ArduinoComponent } from '../../../common/protocol/arduino-component';
|
||||||
import { ComponentListItem } from './component-list-item';
|
import { ComponentListItem } from './component-list-item';
|
||||||
import { ListItemRenderer } from './list-item-renderer';
|
import { ListItemRenderer } from './list-item-renderer';
|
||||||
|
|
||||||
export class ComponentList<T> extends React.Component<ComponentList.Props<T>> {
|
export class ComponentList<T extends ArduinoComponent> extends React.Component<ComponentList.Props<T>> {
|
||||||
|
|
||||||
protected container?: HTMLElement;
|
protected container?: HTMLElement;
|
||||||
|
|
||||||
@ -29,18 +31,20 @@ export class ComponentList<T> extends React.Component<ComponentList.Props<T>> {
|
|||||||
key={this.props.itemLabel(item)}
|
key={this.props.itemLabel(item)}
|
||||||
item={item}
|
item={item}
|
||||||
itemRenderer={this.props.itemRenderer}
|
itemRenderer={this.props.itemRenderer}
|
||||||
install={this.props.install} />
|
install={this.props.install}
|
||||||
|
uninstall={this.props.uninstall} />
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export namespace ComponentList {
|
export namespace ComponentList {
|
||||||
|
|
||||||
export interface Props<T> {
|
export interface Props<T extends ArduinoComponent> {
|
||||||
readonly items: T[];
|
readonly items: T[];
|
||||||
readonly itemLabel: (item: T) => string;
|
readonly itemLabel: (item: T) => string;
|
||||||
readonly itemRenderer: ListItemRenderer<T>;
|
readonly itemRenderer: ListItemRenderer<T>;
|
||||||
readonly install: (item: T) => Promise<void>;
|
readonly install: (item: T, version?: Installable.Version) => Promise<void>;
|
||||||
|
readonly uninstall: (item: T) => Promise<void>;
|
||||||
readonly resolveContainer: (element: HTMLElement) => void;
|
readonly resolveContainer: (element: HTMLElement) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,14 +1,17 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import debounce = require('lodash.debounce');
|
import debounce = require('lodash.debounce');
|
||||||
import { Event } from '@theia/core/lib/common/event';
|
import { Event } from '@theia/core/lib/common/event';
|
||||||
|
import { ConfirmDialog } from '@theia/core/lib/browser/dialogs';
|
||||||
import { Searchable } from '../../../common/protocol/searchable';
|
import { Searchable } from '../../../common/protocol/searchable';
|
||||||
import { Installable } from '../../../common/protocol/installable';
|
import { Installable } from '../../../common/protocol/installable';
|
||||||
import { InstallationProgressDialog } from '../installation-progress-dialog';
|
import { ArduinoComponent } from '../../../common/protocol/arduino-component';
|
||||||
|
import { InstallationProgressDialog, UninstallationProgressDialog } from '../progress-dialog';
|
||||||
import { SearchBar } from './search-bar';
|
import { SearchBar } from './search-bar';
|
||||||
|
import { ListWidget } from './list-widget';
|
||||||
import { ComponentList } from './component-list';
|
import { ComponentList } from './component-list';
|
||||||
import { ListItemRenderer } from './list-item-renderer';
|
import { ListItemRenderer } from './list-item-renderer';
|
||||||
|
|
||||||
export class FilterableListContainer<T> extends React.Component<FilterableListContainer.Props<T>, FilterableListContainer.State<T>> {
|
export class FilterableListContainer<T extends ArduinoComponent> extends React.Component<FilterableListContainer.Props<T>, FilterableListContainer.State<T>> {
|
||||||
|
|
||||||
constructor(props: Readonly<FilterableListContainer.Props<T>>) {
|
constructor(props: Readonly<FilterableListContainer.Props<T>>) {
|
||||||
super(props);
|
super(props);
|
||||||
@ -18,12 +21,18 @@ export class FilterableListContainer<T> extends React.Component<FilterableListCo
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillMount(): void {
|
componentDidMount(): void {
|
||||||
this.search = debounce(this.search, 500);
|
this.search = debounce(this.search, 500);
|
||||||
this.handleFilterTextChange('');
|
this.handleFilterTextChange('');
|
||||||
this.props.filterTextChangeEvent(this.handleFilterTextChange.bind(this));
|
this.props.filterTextChangeEvent(this.handleFilterTextChange.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentDidUpdate(): void {
|
||||||
|
// See: arduino/arduino-pro-ide#101
|
||||||
|
// Resets the top of the perfect scroll-bar's thumb.
|
||||||
|
this.props.container.updateScrollBar();
|
||||||
|
}
|
||||||
|
|
||||||
render(): React.ReactNode {
|
render(): React.ReactNode {
|
||||||
return <div className={'filterable-list-container'}>
|
return <div className={'filterable-list-container'}>
|
||||||
{this.renderSearchFilter()}
|
{this.renderSearchFilter()}
|
||||||
@ -51,6 +60,7 @@ export class FilterableListContainer<T> extends React.Component<FilterableListCo
|
|||||||
itemLabel={itemLabel}
|
itemLabel={itemLabel}
|
||||||
itemRenderer={itemRenderer}
|
itemRenderer={itemRenderer}
|
||||||
install={this.install.bind(this)}
|
install={this.install.bind(this)}
|
||||||
|
uninstall={this.uninstall.bind(this)}
|
||||||
resolveContainer={resolveContainer}
|
resolveContainer={resolveContainer}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
@ -75,12 +85,34 @@ export class FilterableListContainer<T> extends React.Component<FilterableListCo
|
|||||||
return items.sort((left, right) => itemLabel(left).localeCompare(itemLabel(right)));
|
return items.sort((left, right) => itemLabel(left).localeCompare(itemLabel(right)));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async install(item: T): Promise<void> {
|
protected async install(item: T, version: Installable.Version): Promise<void> {
|
||||||
const { installable, searchable, itemLabel } = this.props;
|
const { installable, searchable, itemLabel } = this.props;
|
||||||
const dialog = new InstallationProgressDialog(itemLabel(item));
|
const dialog = new InstallationProgressDialog(itemLabel(item), version);
|
||||||
dialog.open();
|
dialog.open();
|
||||||
try {
|
try {
|
||||||
await installable.install(item);
|
await installable.install({ item, version });
|
||||||
|
const { items } = await searchable.search({ query: this.state.filterText });
|
||||||
|
this.setState({ items: this.sort(items) });
|
||||||
|
} finally {
|
||||||
|
dialog.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async uninstall(item: T): Promise<void> {
|
||||||
|
const uninstall = await new ConfirmDialog({
|
||||||
|
title: 'Uninstall',
|
||||||
|
msg: `Do you want to uninstall ${item.name}?`,
|
||||||
|
ok: 'Yes',
|
||||||
|
cancel: 'No'
|
||||||
|
}).open();
|
||||||
|
if (!uninstall) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const { installable, searchable, itemLabel } = this.props;
|
||||||
|
const dialog = new UninstallationProgressDialog(itemLabel(item));
|
||||||
|
dialog.open();
|
||||||
|
try {
|
||||||
|
await installable.uninstall({ item });
|
||||||
const { items } = await searchable.search({ query: this.state.filterText });
|
const { items } = await searchable.search({ query: this.state.filterText });
|
||||||
this.setState({ items: this.sort(items) });
|
this.setState({ items: this.sort(items) });
|
||||||
} finally {
|
} finally {
|
||||||
@ -92,7 +124,8 @@ export class FilterableListContainer<T> extends React.Component<FilterableListCo
|
|||||||
|
|
||||||
export namespace FilterableListContainer {
|
export namespace FilterableListContainer {
|
||||||
|
|
||||||
export interface Props<T> {
|
export interface Props<T extends ArduinoComponent> {
|
||||||
|
readonly container: ListWidget<T>;
|
||||||
readonly installable: Installable<T>;
|
readonly installable: Installable<T>;
|
||||||
readonly searchable: Searchable<T>;
|
readonly searchable: Searchable<T>;
|
||||||
readonly itemLabel: (item: T) => string;
|
readonly itemLabel: (item: T) => string;
|
||||||
|
@ -1,14 +1,17 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { inject, injectable } from 'inversify';
|
import { inject, injectable } from 'inversify';
|
||||||
import { WindowService } from '@theia/core/lib/browser/window/window-service';
|
import { WindowService } from '@theia/core/lib/browser/window/window-service';
|
||||||
|
import { Installable } from '../../../common/protocol/installable';
|
||||||
|
import { ArduinoComponent } from '../../../common/protocol/arduino-component';
|
||||||
|
import { ComponentListItem } from './component-list-item';
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export abstract class ListItemRenderer<T> {
|
export class ListItemRenderer<T extends ArduinoComponent> {
|
||||||
|
|
||||||
@inject(WindowService)
|
@inject(WindowService)
|
||||||
protected windowService: WindowService;
|
protected windowService: WindowService;
|
||||||
|
|
||||||
protected onClick = (event: React.SyntheticEvent<HTMLAnchorElement, Event>) => {
|
protected onMoreInfoClick = (event: React.SyntheticEvent<HTMLAnchorElement, Event>) => {
|
||||||
const { target } = event.nativeEvent;
|
const { target } = event.nativeEvent;
|
||||||
if (target instanceof HTMLAnchorElement) {
|
if (target instanceof HTMLAnchorElement) {
|
||||||
this.windowService.openNewWindow(target.href, { external: true });
|
this.windowService.openNewWindow(target.href, { external: true });
|
||||||
@ -16,6 +19,73 @@ export abstract class ListItemRenderer<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract renderItem(item: T, install: (item: T) => Promise<void>): React.ReactNode;
|
renderItem(
|
||||||
|
input: ComponentListItem.State & { item: T },
|
||||||
|
install: (item: T) => Promise<void>,
|
||||||
|
uninstall: (item: T) => Promise<void>,
|
||||||
|
onVersionChange: (version: Installable.Version) => void
|
||||||
|
): React.ReactNode {
|
||||||
|
|
||||||
}
|
const { item } = input;
|
||||||
|
const name = <span className='name'>{item.name}</span>;
|
||||||
|
const author = <span className='author'>{item.author}</span>;
|
||||||
|
const onClickUninstall = () => uninstall(item);
|
||||||
|
const installedVersion = !!item.installedVersion && <div className='version-info'>
|
||||||
|
<span className='version'>Version {item.installedVersion}</span>
|
||||||
|
<span className='installed' onClick={onClickUninstall} />
|
||||||
|
</div>;
|
||||||
|
|
||||||
|
const summary = <div className='summary'>{item.summary}</div>;
|
||||||
|
const description = <div className='summary'>{item.description}</div>;
|
||||||
|
|
||||||
|
const moreInfo = !!item.moreInfoLink && <a href={item.moreInfoLink} onClick={this.onMoreInfoClick}>More info</a>;
|
||||||
|
const onClickInstall = () => install(item);
|
||||||
|
const installButton = item.installable &&
|
||||||
|
<button className='install' onClick={onClickInstall}>INSTALL</button>;
|
||||||
|
|
||||||
|
const onSelectChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
|
||||||
|
const version = event.target.value;
|
||||||
|
if (version) {
|
||||||
|
onVersionChange(version);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const versions = (() => {
|
||||||
|
const { availableVersions } = item;
|
||||||
|
if (availableVersions.length === 0) {
|
||||||
|
return undefined;
|
||||||
|
} else if (availableVersions.length === 1) {
|
||||||
|
return <label>{availableVersions[0]}</label>
|
||||||
|
} else {
|
||||||
|
return <select
|
||||||
|
value={input.selectedVersion}
|
||||||
|
onChange={onSelectChange}>
|
||||||
|
{
|
||||||
|
item.availableVersions
|
||||||
|
.filter(version => version !== item.installedVersion) // Filter the version that is currently installed.
|
||||||
|
.map(version => <option value={version} key={version}>{version}</option>)
|
||||||
|
}
|
||||||
|
</select>;
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
|
return <div className='component-list-item noselect'>
|
||||||
|
<div className='header'>
|
||||||
|
<span>{name} by {author}</span>
|
||||||
|
{installedVersion}
|
||||||
|
</div>
|
||||||
|
<div className='content'>
|
||||||
|
{summary}
|
||||||
|
{description}
|
||||||
|
</div>
|
||||||
|
<div className='info'>
|
||||||
|
{moreInfo}
|
||||||
|
</div>
|
||||||
|
<div className='footer'>
|
||||||
|
{installButton}
|
||||||
|
{versions}
|
||||||
|
</div>
|
||||||
|
</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import { injectable } from 'inversify';
|
import { injectable } from 'inversify';
|
||||||
import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application';
|
import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application';
|
||||||
import { AbstractViewContribution } from '@theia/core/lib/browser/shell/view-contribution';
|
import { AbstractViewContribution } from '@theia/core/lib/browser/shell/view-contribution';
|
||||||
|
import { ArduinoComponent } from '../../../common/protocol/arduino-component';
|
||||||
import { ListWidget } from './list-widget';
|
import { ListWidget } from './list-widget';
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export abstract class ListWidgetFrontendContribution<T> extends AbstractViewContribution<ListWidget<T>> implements FrontendApplicationContribution {
|
export abstract class ListWidgetFrontendContribution<T extends ArduinoComponent> extends AbstractViewContribution<ListWidget<T>> implements FrontendApplicationContribution {
|
||||||
|
|
||||||
async initializeLayout(): Promise<void> {
|
async initializeLayout(): Promise<void> {
|
||||||
}
|
}
|
||||||
|
@ -7,11 +7,12 @@ import { MaybePromise } from '@theia/core/lib/common/types';
|
|||||||
import { ReactWidget } from '@theia/core/lib/browser/widgets/react-widget';
|
import { ReactWidget } from '@theia/core/lib/browser/widgets/react-widget';
|
||||||
import { Installable } from '../../../common/protocol/installable';
|
import { Installable } from '../../../common/protocol/installable';
|
||||||
import { Searchable } from '../../../common/protocol/searchable';
|
import { Searchable } from '../../../common/protocol/searchable';
|
||||||
|
import { ArduinoComponent } from '../../../common/protocol/arduino-component';
|
||||||
import { FilterableListContainer } from './filterable-list-container';
|
import { FilterableListContainer } from './filterable-list-container';
|
||||||
import { ListItemRenderer } from './list-item-renderer';
|
import { ListItemRenderer } from './list-item-renderer';
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export abstract class ListWidget<T> extends ReactWidget {
|
export abstract class ListWidget<T extends ArduinoComponent> extends ReactWidget {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Do not touch or use it. It is for setting the focus on the `input` after the widget activation.
|
* Do not touch or use it. It is for setting the focus on the `input` after the widget activation.
|
||||||
@ -61,6 +62,7 @@ export abstract class ListWidget<T> extends ReactWidget {
|
|||||||
|
|
||||||
render(): React.ReactNode {
|
render(): React.ReactNode {
|
||||||
return <FilterableListContainer<T>
|
return <FilterableListContainer<T>
|
||||||
|
container={this}
|
||||||
resolveContainer={this.deferredContainer.resolve}
|
resolveContainer={this.deferredContainer.resolve}
|
||||||
resolveFocus={this.onFocusResolved}
|
resolveFocus={this.onFocusResolved}
|
||||||
searchable={this.options.searchable}
|
searchable={this.options.searchable}
|
||||||
@ -74,10 +76,16 @@ export abstract class ListWidget<T> extends ReactWidget {
|
|||||||
this.deferredContainer.promise.then(() => this.filterTextChangeEmitter.fire(filterText));
|
this.deferredContainer.promise.then(() => this.filterTextChangeEmitter.fire(filterText));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateScrollBar(): void {
|
||||||
|
if (this.scrollBar) {
|
||||||
|
this.scrollBar.update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export namespace ListWidget {
|
export namespace ListWidget {
|
||||||
export interface Options<T> {
|
export interface Options<T extends ArduinoComponent> {
|
||||||
readonly id: string;
|
readonly id: string;
|
||||||
readonly label: string;
|
readonly label: string;
|
||||||
readonly iconClass: string;
|
readonly iconClass: string;
|
||||||
|
@ -1,12 +0,0 @@
|
|||||||
import { AbstractDialog } from '@theia/core/lib/browser';
|
|
||||||
|
|
||||||
export class InstallationProgressDialog extends AbstractDialog<undefined> {
|
|
||||||
|
|
||||||
readonly value = undefined;
|
|
||||||
|
|
||||||
constructor(componentName: string) {
|
|
||||||
super({ title: 'Installation in progress' });
|
|
||||||
this.contentNode.textContent = `Installing ${componentName}. Please wait.`;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -0,0 +1,23 @@
|
|||||||
|
import { AbstractDialog } from '@theia/core/lib/browser';
|
||||||
|
|
||||||
|
export class InstallationProgressDialog extends AbstractDialog<undefined> {
|
||||||
|
|
||||||
|
readonly value = undefined;
|
||||||
|
|
||||||
|
constructor(componentName: string, version: string) {
|
||||||
|
super({ title: 'Installation in progress' });
|
||||||
|
this.contentNode.textContent = `Installing ${componentName} [${version}]. Please wait...`;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export class UninstallationProgressDialog extends AbstractDialog<undefined> {
|
||||||
|
|
||||||
|
readonly value = undefined;
|
||||||
|
|
||||||
|
constructor(componentName: string) {
|
||||||
|
super({ title: 'Uninstallation in progress' });
|
||||||
|
this.contentNode.textContent = `Uninstalling ${componentName}. Please wait...`;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,22 +1,35 @@
|
|||||||
import { ApplicationShell, Widget, Saveable, FocusTracker, Message } from '@theia/core/lib/browser';
|
import { ApplicationShell, Widget, Saveable, FocusTracker, Message } from '@theia/core/lib/browser';
|
||||||
import { EditorWidget } from '@theia/editor/lib/browser';
|
import { EditorWidget } from '@theia/editor/lib/browser';
|
||||||
|
import { injectable, inject } from 'inversify';
|
||||||
|
import { EditorMode } from '../editor-mode';
|
||||||
|
|
||||||
|
@injectable()
|
||||||
export class ArduinoApplicationShell extends ApplicationShell {
|
export class ArduinoApplicationShell extends ApplicationShell {
|
||||||
|
|
||||||
|
@inject(EditorMode)
|
||||||
|
protected readonly editorMode: EditorMode;
|
||||||
|
|
||||||
protected refreshBottomPanelToggleButton() {
|
protected refreshBottomPanelToggleButton() {
|
||||||
|
if (this.editorMode.proMode) {
|
||||||
|
super.refreshBottomPanelToggleButton();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async track(widget: Widget): Promise<void> {
|
protected async track(widget: Widget): Promise<void> {
|
||||||
const tracker = (this as any).tracker as FocusTracker<Widget>;
|
if (this.editorMode.proMode) {
|
||||||
tracker.add(widget);
|
super.track(widget);
|
||||||
this.disableClose(Saveable.apply(widget));
|
} else {
|
||||||
if (ApplicationShell.TrackableWidgetProvider.is(widget)) {
|
const tracker = (this as any).tracker as FocusTracker<Widget>;
|
||||||
for (const toTrack of await widget.getTrackableWidgets()) {
|
tracker.add(widget);
|
||||||
tracker.add(toTrack);
|
this.disableClose(Saveable.apply(widget));
|
||||||
this.disableClose(Saveable.apply(toTrack));
|
if (ApplicationShell.TrackableWidgetProvider.is(widget)) {
|
||||||
}
|
for (const toTrack of await widget.getTrackableWidgets()) {
|
||||||
if (widget.onDidChangeTrackableWidgets) {
|
tracker.add(toTrack);
|
||||||
widget.onDidChangeTrackableWidgets(widgets => widgets.forEach(w => this.track(w)));
|
this.disableClose(Saveable.apply(toTrack));
|
||||||
|
}
|
||||||
|
if (widget.onDidChangeTrackableWidgets) {
|
||||||
|
widget.onDidChangeTrackableWidgets(widgets => widgets.forEach(w => this.track(w)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +0,0 @@
|
|||||||
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) {
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -2,7 +2,8 @@ import { injectable, inject } from 'inversify';
|
|||||||
import { FileSystem } from '@theia/filesystem/lib/common/filesystem';
|
import { FileSystem } from '@theia/filesystem/lib/common/filesystem';
|
||||||
import { WorkspaceService } from '@theia/workspace/lib/browser/workspace-service';
|
import { WorkspaceService } from '@theia/workspace/lib/browser/workspace-service';
|
||||||
import { FrontendApplication } from '@theia/core/lib/browser/frontend-application';
|
import { FrontendApplication } from '@theia/core/lib/browser/frontend-application';
|
||||||
import { ArduinoFrontendContribution, ArduinoAdvancedMode } from '../arduino-frontend-contribution';
|
import { EditorMode } from '../editor-mode';
|
||||||
|
import { ArduinoFrontendContribution } from '../arduino-frontend-contribution';
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class ArduinoFrontendApplication extends FrontendApplication {
|
export class ArduinoFrontendApplication extends FrontendApplication {
|
||||||
@ -16,12 +17,15 @@ export class ArduinoFrontendApplication extends FrontendApplication {
|
|||||||
@inject(ArduinoFrontendContribution)
|
@inject(ArduinoFrontendContribution)
|
||||||
protected readonly frontendContribution: ArduinoFrontendContribution;
|
protected readonly frontendContribution: ArduinoFrontendContribution;
|
||||||
|
|
||||||
|
@inject(EditorMode)
|
||||||
|
protected readonly editorMode: EditorMode;
|
||||||
|
|
||||||
protected async initializeLayout(): Promise<void> {
|
protected async initializeLayout(): Promise<void> {
|
||||||
super.initializeLayout().then(() => {
|
super.initializeLayout().then(() => {
|
||||||
// If not in PRO mode, we open the sketch file with all the related files.
|
// If not in PRO mode, we open the sketch file with all the related files.
|
||||||
// Otherwise, we reuse the workbench's restore functionality and we do not open anything at all.
|
// Otherwise, we reuse the workbench's restore functionality and we do not open anything at all.
|
||||||
// TODO: check `otherwise`. Also, what if we check for opened editors, instead of blindly opening them?
|
// TODO: check `otherwise`. Also, what if we check for opened editors, instead of blindly opening them?
|
||||||
if (!ArduinoAdvancedMode.TOGGLED) {
|
if (!this.editorMode.proMode) {
|
||||||
this.workspaceService.roots.then(roots => {
|
this.workspaceService.roots.then(roots => {
|
||||||
for (const root of roots) {
|
for (const root of roots) {
|
||||||
this.fileSystem.exists(root.uri).then(exists => {
|
this.fileSystem.exists(root.uri).then(exists => {
|
||||||
|
@ -0,0 +1,18 @@
|
|||||||
|
import { injectable, inject } from 'inversify';
|
||||||
|
import { FrontendApplication } from '@theia/core/lib/browser/frontend-application';
|
||||||
|
import { FileNavigatorContribution } from '@theia/navigator/lib/browser/navigator-contribution';
|
||||||
|
import { EditorMode } from '../editor-mode';
|
||||||
|
|
||||||
|
@injectable()
|
||||||
|
export class ArduinoNavigatorContribution extends FileNavigatorContribution {
|
||||||
|
|
||||||
|
@inject(EditorMode)
|
||||||
|
protected readonly editorMode: EditorMode;
|
||||||
|
|
||||||
|
async initializeLayout(app: FrontendApplication): Promise<void> {
|
||||||
|
if (this.editorMode.proMode) {
|
||||||
|
return super.initializeLayout(app);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
import { injectable, inject } from 'inversify';
|
||||||
|
import { FrontendApplication } from '@theia/core/lib/browser/frontend-application';
|
||||||
|
import { OutlineViewContribution } from '@theia/outline-view/lib/browser/outline-view-contribution';
|
||||||
|
import { EditorMode } from '../editor-mode';
|
||||||
|
|
||||||
|
@injectable()
|
||||||
|
export class ArduinoOutlineViewContribution extends OutlineViewContribution {
|
||||||
|
|
||||||
|
@inject(EditorMode)
|
||||||
|
protected readonly editorMode: EditorMode;
|
||||||
|
|
||||||
|
async initializeLayout(app: FrontendApplication): Promise<void> {
|
||||||
|
if (this.editorMode.proMode) {
|
||||||
|
return super.initializeLayout(app);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -1,11 +1,18 @@
|
|||||||
import { OutputToolbarContribution } from '@theia/output/lib/browser/output-toolbar-contribution';
|
import { inject, injectable } from 'inversify';
|
||||||
import { TabBarToolbarRegistry } from '@theia/core/lib/browser/shell/tab-bar-toolbar';
|
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 { EditorMode } from '../editor-mode';
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class ArduinoOutputToolContribution extends OutputToolbarContribution {
|
export class ArduinoOutputToolContribution extends OutputToolbarContribution {
|
||||||
|
|
||||||
|
@inject(EditorMode)
|
||||||
|
protected readonly editorMode: EditorMode;
|
||||||
|
|
||||||
async registerToolbarItems(toolbarRegistry: TabBarToolbarRegistry): Promise<void> {
|
async registerToolbarItems(toolbarRegistry: TabBarToolbarRegistry): Promise<void> {
|
||||||
|
if (this.editorMode.proMode) {
|
||||||
|
super.registerToolbarItems(toolbarRegistry);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
import { inject, injectable } from 'inversify';
|
||||||
|
import { ProblemStat } from '@theia/markers/lib/browser/problem/problem-manager';
|
||||||
|
import { FrontendApplication } from '@theia/core/lib/browser/frontend-application';
|
||||||
|
import { ProblemContribution } from '@theia/markers/lib/browser/problem/problem-contribution';
|
||||||
|
import { EditorMode } from '../editor-mode';
|
||||||
|
|
||||||
|
@injectable()
|
||||||
|
export class ArduinoProblemContribution extends ProblemContribution {
|
||||||
|
|
||||||
|
@inject(EditorMode)
|
||||||
|
protected readonly editorMode: EditorMode;
|
||||||
|
|
||||||
|
async initializeLayout(app: FrontendApplication): Promise<void> {
|
||||||
|
if (this.editorMode.proMode) {
|
||||||
|
return super.initializeLayout(app);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected setStatusBarElement(problemStat: ProblemStat): void {
|
||||||
|
if (this.editorMode.proMode) {
|
||||||
|
super.setStatusBarElement(problemStat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
import { inject, injectable } from 'inversify';
|
||||||
|
import { ScmContribution } from '@theia/scm/lib/browser/scm-contribution';
|
||||||
|
import { StatusBarEntry } from '@theia/core/lib/browser/status-bar/status-bar';
|
||||||
|
import { EditorMode } from '../editor-mode';
|
||||||
|
|
||||||
|
@injectable()
|
||||||
|
export class ArduinoScmContribution extends ScmContribution {
|
||||||
|
|
||||||
|
@inject(EditorMode)
|
||||||
|
protected readonly editorMode: EditorMode;
|
||||||
|
|
||||||
|
async initializeLayout(): Promise<void> {
|
||||||
|
if (this.editorMode.proMode) {
|
||||||
|
return super.initializeLayout();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected setStatusBarEntry(id: string, entry: StatusBarEntry): void {
|
||||||
|
if (this.editorMode.proMode) {
|
||||||
|
super.setStatusBarEntry(id, entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
import { inject, injectable } from 'inversify';
|
||||||
|
import { FrontendApplication } from '@theia/core/lib/browser/frontend-application';
|
||||||
|
import { SearchInWorkspaceFrontendContribution } from '@theia/search-in-workspace/lib/browser/search-in-workspace-frontend-contribution';
|
||||||
|
import { EditorMode } from '../editor-mode';
|
||||||
|
|
||||||
|
@injectable()
|
||||||
|
export class ArduinoSearchInWorkspaceContribution extends SearchInWorkspaceFrontendContribution {
|
||||||
|
|
||||||
|
@inject(EditorMode)
|
||||||
|
protected readonly editorMode: EditorMode;
|
||||||
|
|
||||||
|
async initializeLayout(app: FrontendApplication): Promise<void> {
|
||||||
|
if (this.editorMode.proMode) {
|
||||||
|
return super.initializeLayout(app);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,11 +0,0 @@
|
|||||||
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,12 +0,0 @@
|
|||||||
import { injectable } from 'inversify';
|
|
||||||
import { OutlineViewContribution } from '@theia/outline-view/lib/browser/outline-view-contribution';
|
|
||||||
import { FrontendApplication } from '@theia/core/lib/browser';
|
|
||||||
|
|
||||||
@injectable()
|
|
||||||
export class SilentOutlineViewContribution extends OutlineViewContribution {
|
|
||||||
|
|
||||||
async initializeLayout(app: FrontendApplication): Promise<void> {
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
|||||||
import { injectable } from 'inversify';
|
|
||||||
import { ProblemContribution } from '@theia/markers/lib/browser/problem/problem-contribution';
|
|
||||||
import { ProblemStat } from '@theia/markers/lib/browser/problem/problem-manager';
|
|
||||||
import { FrontendApplication } from '@theia/core/lib/browser';
|
|
||||||
|
|
||||||
@injectable()
|
|
||||||
export class SilentProblemContribution extends ProblemContribution {
|
|
||||||
|
|
||||||
async initializeLayout(app: FrontendApplication): Promise<void> {
|
|
||||||
}
|
|
||||||
|
|
||||||
protected setStatusBarElement(problemStat: ProblemStat) {
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
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 {
|
|
||||||
|
|
||||||
async initializeLayout(): Promise<void> {
|
|
||||||
}
|
|
||||||
|
|
||||||
protected setStatusBarEntry(id: string, entry: StatusBarEntry): void {
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
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> {
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
48
arduino-ide-extension/src/browser/editor-mode.ts
Normal file
48
arduino-ide-extension/src/browser/editor-mode.ts
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import { injectable } from 'inversify';
|
||||||
|
import { ApplicationShell, FrontendApplicationContribution, FrontendApplication } from '@theia/core/lib/browser';
|
||||||
|
import { ArduinoShellLayoutRestorer } from './shell/arduino-shell-layout-restorer';
|
||||||
|
import { OutputWidget } from '@theia/output/lib/browser/output-widget';
|
||||||
|
import { EditorWidget } from '@theia/editor/lib/browser';
|
||||||
|
|
||||||
|
@injectable()
|
||||||
|
export class EditorMode implements FrontendApplicationContribution {
|
||||||
|
|
||||||
|
protected app: FrontendApplication;
|
||||||
|
|
||||||
|
onStart(app: FrontendApplication): void {
|
||||||
|
this.app = app;
|
||||||
|
if (this.proMode) {
|
||||||
|
// We use this CSS class on the body to modify the visibility of the close button for the editors and views.
|
||||||
|
document.body.classList.add(EditorMode.PRO_MODE_KEY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get proMode(): boolean {
|
||||||
|
const value = window.localStorage.getItem(EditorMode.PRO_MODE_KEY);
|
||||||
|
return value === 'true';
|
||||||
|
}
|
||||||
|
|
||||||
|
async toggle(): Promise<void> {
|
||||||
|
const oldState = this.proMode;
|
||||||
|
const inAdvancedMode = !oldState;
|
||||||
|
window.localStorage.setItem(EditorMode.PRO_MODE_KEY, String(inAdvancedMode));
|
||||||
|
if (!inAdvancedMode) {
|
||||||
|
const { shell } = this.app;
|
||||||
|
// Close all widget that is neither editor nor `Output`.
|
||||||
|
for (const area of ['left', 'right', 'bottom', 'main'] as Array<ApplicationShell.Area>) {
|
||||||
|
shell.closeTabs(area, ({ owner }) => !(owner instanceof EditorWidget || owner instanceof OutputWidget));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// `storeLayout` has a sync API but the implementation is async, we store the layout manually before we reload the page.
|
||||||
|
// See: https://github.com/eclipse-theia/theia/issues/6579
|
||||||
|
// XXX: hack instead of injecting the `ArduinoShellLayoutRestorer` we have to retrieve it from the application to avoid DI cycle.
|
||||||
|
const layoutRestorer = (this.app as any).layoutRestorer as ArduinoShellLayoutRestorer
|
||||||
|
await layoutRestorer.storeLayoutAsync(this.app);
|
||||||
|
window.location.reload(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export namespace EditorMode {
|
||||||
|
export const PRO_MODE_KEY = 'arduino-advanced-mode';
|
||||||
|
}
|
@ -2,6 +2,7 @@ import { injectable, inject, postConstruct } from 'inversify';
|
|||||||
import { BaseLanguageClientContribution } from '@theia/languages/lib/browser';
|
import { BaseLanguageClientContribution } from '@theia/languages/lib/browser';
|
||||||
import { BoardsServiceClientImpl } from '../boards/boards-service-client-impl';
|
import { BoardsServiceClientImpl } from '../boards/boards-service-client-impl';
|
||||||
import { BoardsConfig } from '../boards/boards-config';
|
import { BoardsConfig } from '../boards/boards-config';
|
||||||
|
import { Board, BoardPackage } from '../../common/protocol/boards-service';
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class ArduinoLanguageClientContribution extends BaseLanguageClientContribution {
|
export class ArduinoLanguageClientContribution extends BaseLanguageClientContribution {
|
||||||
@ -25,6 +26,18 @@ export class ArduinoLanguageClientContribution extends BaseLanguageClientContrib
|
|||||||
@postConstruct()
|
@postConstruct()
|
||||||
protected init() {
|
protected init() {
|
||||||
this.boardsServiceClient.onBoardsConfigChanged(this.selectBoard.bind(this));
|
this.boardsServiceClient.onBoardsConfigChanged(this.selectBoard.bind(this));
|
||||||
|
const restartIfAffected = (pkg: BoardPackage) => {
|
||||||
|
if (!this.boardConfig) {
|
||||||
|
this.restart();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const { selectedBoard } = this.boardConfig;
|
||||||
|
if (selectedBoard && pkg.boards.some(board => Board.sameAs(board, selectedBoard))) {
|
||||||
|
this.restart();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.boardsServiceClient.onBoardInstalled(({ pkg }) => restartIfAffected(pkg));
|
||||||
|
this.boardsServiceClient.onBoardUninstalled(({ pkg }) => restartIfAffected(pkg));
|
||||||
}
|
}
|
||||||
|
|
||||||
selectBoard(config: BoardsConfig.Config): void {
|
selectBoard(config: BoardsConfig.Config): void {
|
||||||
|
@ -1,52 +0,0 @@
|
|||||||
import * as React from 'react';
|
|
||||||
import { injectable } from 'inversify';
|
|
||||||
import { Library } from '../../common/protocol/library-service';
|
|
||||||
import { ListItemRenderer } from '../components/component-list/list-item-renderer';
|
|
||||||
|
|
||||||
@injectable()
|
|
||||||
export class LibraryItemRenderer extends ListItemRenderer<Library> {
|
|
||||||
|
|
||||||
renderItem(item: Library, install: (item: Library) => Promise<void>): React.ReactNode {
|
|
||||||
const name = <span className='name'>{item.name}</span>;
|
|
||||||
const author = <span className='author'>by {item.author}</span>;
|
|
||||||
const installedVersion = !!item.installedVersion && <div className='version-info'>
|
|
||||||
<span className='version'>Version {item.installedVersion}</span>
|
|
||||||
<span className='installed'>INSTALLED</span>
|
|
||||||
</div>;
|
|
||||||
|
|
||||||
const summary = <div className='summary'>{item.summary}</div>;
|
|
||||||
|
|
||||||
const moreInfo = !!item.moreInfoLink && <a href={item.moreInfoLink} onClick={this.onClick}>More info</a>;
|
|
||||||
const installButton = item.installable && !item.installedVersion &&
|
|
||||||
<button className='install' onClick={install.bind(this, item)}>INSTALL</button>;
|
|
||||||
|
|
||||||
const versions = (() => {
|
|
||||||
const { availableVersions } = item;
|
|
||||||
if (!!item.installedVersion || availableVersions.length === 0) {
|
|
||||||
return undefined;
|
|
||||||
} else if (availableVersions.length === 1) {
|
|
||||||
return <label>{availableVersions[0]}</label>
|
|
||||||
} else {
|
|
||||||
return <select>{item.availableVersions.map(version => <option value={version} key={version}>{version}</option>)}</select>;
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
|
|
||||||
return <div className='component-list-item noselect'>
|
|
||||||
<div className='header'>
|
|
||||||
<span>{name} {author}</span>
|
|
||||||
{installedVersion}
|
|
||||||
</div>
|
|
||||||
<div className='content'>
|
|
||||||
{summary}
|
|
||||||
</div>
|
|
||||||
<div className='info'>
|
|
||||||
{moreInfo}
|
|
||||||
</div>
|
|
||||||
<div className='footer'>
|
|
||||||
{installButton}
|
|
||||||
{versions}
|
|
||||||
</div>
|
|
||||||
</div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,7 +1,7 @@
|
|||||||
import { inject, injectable } from 'inversify';
|
import { inject, injectable } from 'inversify';
|
||||||
import { Library, LibraryService } from '../../common/protocol/library-service';
|
import { Library, LibraryService } from '../../common/protocol/library-service';
|
||||||
import { ListWidget } from '../components/component-list/list-widget';
|
import { ListWidget } from '../components/component-list/list-widget';
|
||||||
import { LibraryItemRenderer } from './library-item-renderer';
|
import { ListItemRenderer } from '../components/component-list/list-item-renderer';
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class LibraryListWidget extends ListWidget<Library> {
|
export class LibraryListWidget extends ListWidget<Library> {
|
||||||
@ -11,7 +11,7 @@ export class LibraryListWidget extends ListWidget<Library> {
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@inject(LibraryService) protected service: LibraryService,
|
@inject(LibraryService) protected service: LibraryService,
|
||||||
@inject(LibraryItemRenderer) protected itemRenderer: LibraryItemRenderer) {
|
@inject(ListItemRenderer) protected itemRenderer: ListItemRenderer<Library>) {
|
||||||
|
|
||||||
super({
|
super({
|
||||||
id: LibraryListWidget.WIDGET_ID,
|
id: LibraryListWidget.WIDGET_ID,
|
||||||
|
@ -0,0 +1,24 @@
|
|||||||
|
import { injectable } from 'inversify';
|
||||||
|
import { FrontendApplication } from '@theia/core/lib/browser/frontend-application';
|
||||||
|
import { ShellLayoutRestorer } from '@theia/core/lib/browser/shell/shell-layout-restorer';
|
||||||
|
|
||||||
|
@injectable()
|
||||||
|
export class ArduinoShellLayoutRestorer extends ShellLayoutRestorer {
|
||||||
|
|
||||||
|
// Workaround for https://github.com/eclipse-theia/theia/issues/6579.
|
||||||
|
async storeLayoutAsync(app: FrontendApplication): Promise<void> {
|
||||||
|
if (this.shouldStoreLayout) {
|
||||||
|
try {
|
||||||
|
this.logger.info('>>> Storing the layout...');
|
||||||
|
const layoutData = app.shell.getLayoutData();
|
||||||
|
const serializedLayoutData = this.deflate(layoutData);
|
||||||
|
await this.storageService.setData(this.storageKey, serializedLayoutData);
|
||||||
|
this.logger.info('<<< The layout has been successfully stored.');
|
||||||
|
} catch (error) {
|
||||||
|
await this.storageService.setData(this.storageKey, undefined);
|
||||||
|
this.logger.error('Error during serialization of layout data', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -35,7 +35,7 @@ div#select-board-dialog .selectBoardContainer .body .list .item.selected i{
|
|||||||
#select-board-dialog .selectBoardContainer .search input,
|
#select-board-dialog .selectBoardContainer .search input,
|
||||||
#select-board-dialog .selectBoardContainer .list,
|
#select-board-dialog .selectBoardContainer .list,
|
||||||
#select-board-dialog .selectBoardContainer .list {
|
#select-board-dialog .selectBoardContainer .list {
|
||||||
background: white; /* TODO find a theia color instead! */
|
background: var(--theia-layout-color0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#select-board-dialog .selectBoardContainer .body .search input {
|
#select-board-dialog .selectBoardContainer .body .search input {
|
||||||
|
@ -61,6 +61,14 @@
|
|||||||
background-color: var(--theia-layout-color2);
|
background-color: var(--theia-layout-color2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Perfect scrollbar does not like if we explicitly set the `background-color` of the contained elements.
|
||||||
|
See above: `.filterable-list-container .items-container > div:nth-child(odd|event)`.
|
||||||
|
We have to increase `z-index` of the scroll-bar thumb. Otherwise, the thumb is not visible.
|
||||||
|
https://github.com/arduino/arduino-pro-ide/issues/82 */
|
||||||
|
.arduino-list-widget .ps__rail-y > .ps__thumb-y {
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
.component-list-item {
|
.component-list-item {
|
||||||
padding: 10px 10px 10px 15px;
|
padding: 10px 10px 10px 15px;
|
||||||
font-size: var(--theia-ui-font-size1);
|
font-size: var(--theia-ui-font-size1);
|
||||||
@ -108,8 +116,9 @@
|
|||||||
color: var(--theia-ui-font-color2);
|
color: var(--theia-ui-font-color2);
|
||||||
}
|
}
|
||||||
|
|
||||||
.component-list-item .header .installed {
|
.component-list-item .header .installed:before {
|
||||||
margin-left: 4px;
|
margin-left: 4px;
|
||||||
|
display: inline-block;
|
||||||
justify-self: end;
|
justify-self: end;
|
||||||
background-color: var(--theia-accent-color1);
|
background-color: var(--theia-accent-color1);
|
||||||
padding: 2px 4px 2px 4px;
|
padding: 2px 4px 2px 4px;
|
||||||
@ -117,6 +126,13 @@
|
|||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
max-height: calc(1em + 4px);
|
max-height: calc(1em + 4px);
|
||||||
color: var(--theia-inverse-ui-font-color0);
|
color: var(--theia-inverse-ui-font-color0);
|
||||||
|
content: 'INSTALLED';
|
||||||
|
}
|
||||||
|
|
||||||
|
.component-list-item .header .installed:hover:before {
|
||||||
|
background-color: var(--theia-inverse-ui-font-color0);
|
||||||
|
color: var(--theia-accent-color1);
|
||||||
|
content: 'UNINSTALL';
|
||||||
}
|
}
|
||||||
|
|
||||||
.component-list-item[min-width~="170px"] .footer {
|
.component-list-item[min-width~="170px"] .footer {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { Installable } from './installable';
|
||||||
|
|
||||||
export interface ArduinoComponent {
|
export interface ArduinoComponent {
|
||||||
readonly name: string;
|
readonly name: string;
|
||||||
@ -6,8 +7,8 @@ export interface ArduinoComponent {
|
|||||||
readonly description: string;
|
readonly description: string;
|
||||||
readonly moreInfoLink?: string;
|
readonly moreInfoLink?: string;
|
||||||
|
|
||||||
readonly availableVersions: string[];
|
readonly availableVersions: Installable.Version[];
|
||||||
readonly installable: boolean;
|
readonly installable: boolean;
|
||||||
|
|
||||||
readonly installedVersion?: string;
|
readonly installedVersion?: Installable.Version;
|
||||||
}
|
}
|
||||||
|
@ -46,10 +46,15 @@ export interface BoardInstalledEvent {
|
|||||||
readonly pkg: Readonly<BoardPackage>;
|
readonly pkg: Readonly<BoardPackage>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface BoardUninstalledEvent {
|
||||||
|
readonly pkg: Readonly<BoardPackage>;
|
||||||
|
}
|
||||||
|
|
||||||
export const BoardsServiceClient = Symbol('BoardsServiceClient');
|
export const BoardsServiceClient = Symbol('BoardsServiceClient');
|
||||||
export interface BoardsServiceClient {
|
export interface BoardsServiceClient {
|
||||||
notifyAttachedBoardsChanged(event: AttachedBoardsChangeEvent): void;
|
notifyAttachedBoardsChanged(event: AttachedBoardsChangeEvent): void;
|
||||||
notifyBoardInstalled(event: BoardInstalledEvent): void
|
notifyBoardInstalled(event: BoardInstalledEvent): void
|
||||||
|
notifyBoardUninstalled(event: BoardUninstalledEvent): void
|
||||||
}
|
}
|
||||||
|
|
||||||
export const BoardsServicePath = '/services/boards-service';
|
export const BoardsServicePath = '/services/boards-service';
|
||||||
@ -130,7 +135,7 @@ export namespace Port {
|
|||||||
}
|
}
|
||||||
if (isOSX) {
|
if (isOSX) {
|
||||||
// Example: `/dev/cu.usbmodem14401`
|
// Example: `/dev/cu.usbmodem14401`
|
||||||
if (/(tty|cu)\..*/.test(address.substring('/dev/'.length))) {
|
if (/(tty|cu)\..*/.test(address.substring('/dev/'.length))) {
|
||||||
return [
|
return [
|
||||||
'/dev/cu.MALS',
|
'/dev/cu.MALS',
|
||||||
'/dev/cu.SOC',
|
'/dev/cu.SOC',
|
||||||
|
@ -1,3 +1,23 @@
|
|||||||
export interface Installable<T> {
|
const naturalCompare: (left: string, right: string) => number = require('string-natural-compare').caseInsensitive;
|
||||||
install(item: T): Promise<void>;
|
import { ArduinoComponent } from './arduino-component';
|
||||||
}
|
|
||||||
|
export interface Installable<T extends ArduinoComponent> {
|
||||||
|
/**
|
||||||
|
* If `options.version` is specified, that will be installed. Otherwise, `item.availableVersions[0]`.
|
||||||
|
*/
|
||||||
|
install(options: { item: T, version?: Installable.Version }): Promise<void>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uninstalls the given component. It is a NOOP if not installed.
|
||||||
|
*/
|
||||||
|
uninstall(options: { item: T }): Promise<void>;
|
||||||
|
}
|
||||||
|
export namespace Installable {
|
||||||
|
export type Version = string;
|
||||||
|
export namespace Version {
|
||||||
|
/**
|
||||||
|
* Most recent version comes first, then the previous versions. (`1.8.1`, `1.6.3`, `1.6.2`, `1.6.1` and so on.)
|
||||||
|
*/
|
||||||
|
export const COMPARATOR = (left: Version, right: Version) => naturalCompare(right, left);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -5,14 +5,9 @@ import { ArduinoComponent } from './arduino-component';
|
|||||||
export const LibraryServicePath = '/services/library-service';
|
export const LibraryServicePath = '/services/library-service';
|
||||||
export const LibraryService = Symbol('LibraryService');
|
export const LibraryService = Symbol('LibraryService');
|
||||||
export interface LibraryService extends Installable<Library>, Searchable<Library> {
|
export interface LibraryService extends Installable<Library>, Searchable<Library> {
|
||||||
install(library: Library): Promise<void>;
|
install(options: { item: Library, version?: Installable.Version }): Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Library extends ArduinoComponent {
|
export interface Library extends ArduinoComponent {
|
||||||
readonly builtIn?: boolean;
|
readonly builtIn?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export namespace Library {
|
|
||||||
// TODO: figure out whether we need a dedicated `version` type.
|
|
||||||
export type Version = string;
|
|
||||||
}
|
|
@ -8,10 +8,11 @@ export interface SketchesService {
|
|||||||
getSketches(uri?: string): Promise<Sketch[]>
|
getSketches(uri?: string): Promise<Sketch[]>
|
||||||
getSketchFiles(uri: string): Promise<string[]>
|
getSketchFiles(uri: string): Promise<string[]>
|
||||||
/**
|
/**
|
||||||
* Creates a new sketch folder in the `parentUri` location. If `parentUri` is not specified,
|
* Creates a new sketch folder in the `parentUri` location.
|
||||||
* it falls back to the default `sketchDirUri` from the CLI.
|
* Normally, `parentUri` is the client's workspace root, or the default `sketchDirUri` from the CLI.
|
||||||
|
* Note, `parentUri` and `sketchDirUri` can be the same.
|
||||||
*/
|
*/
|
||||||
createNewSketch(parentUri?: string): Promise<Sketch>
|
createNewSketch(parentUri: string): Promise<Sketch>
|
||||||
isSketchFolder(uri: string): Promise<boolean>
|
isSketchFolder(uri: string): Promise<boolean>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,10 +38,10 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
|||||||
bind(ArduinoCliContribution).toSelf().inSingletonScope();
|
bind(ArduinoCliContribution).toSelf().inSingletonScope();
|
||||||
bind(CliContribution).toService(ArduinoCliContribution);
|
bind(CliContribution).toService(ArduinoCliContribution);
|
||||||
|
|
||||||
// Provides the path of the Ardunio CLI.
|
// Provides the path of the Arduino CLI.
|
||||||
bind(ArduinoCli).toSelf().inSingletonScope();
|
bind(ArduinoCli).toSelf().inSingletonScope();
|
||||||
|
|
||||||
// Shared daemonn
|
// Shared daemon
|
||||||
bind(ArduinoDaemon).toSelf().inSingletonScope();
|
bind(ArduinoDaemon).toSelf().inSingletonScope();
|
||||||
bind(BackendApplicationContribution).toService(ArduinoDaemon);
|
bind(BackendApplicationContribution).toService(ArduinoDaemon);
|
||||||
|
|
||||||
|
@ -26,30 +26,15 @@ export class ArduinoCli {
|
|||||||
const buildVersion = await this.spawn(`"${buildCli}"`, ['version']);
|
const buildVersion = await this.spawn(`"${buildCli}"`, ['version']);
|
||||||
const buildShortVersion = (buildVersion.match(version) || [])[0];
|
const buildShortVersion = (buildVersion.match(version) || [])[0];
|
||||||
this.execPath = buildCli;
|
this.execPath = buildCli;
|
||||||
try {
|
const pathCli = await new Promise<string | undefined>(resolve => which(cli, (error, path) => resolve(error ? undefined : path)));
|
||||||
const pathCli = await new Promise<string>((resolve, reject) => {
|
if (!pathCli) {
|
||||||
which(cli, (error, path) => {
|
return buildCli;
|
||||||
if (error) {
|
}
|
||||||
reject(error);
|
const pathVersion = await this.spawn(`"${pathCli}"`, ['version']);
|
||||||
return;
|
const pathShortVersion = (pathVersion.match(version) || [])[0];
|
||||||
}
|
if (semver.gt(pathShortVersion, buildShortVersion)) {
|
||||||
resolve(path);
|
this.execPath = pathCli;
|
||||||
});
|
return pathCli;
|
||||||
});
|
|
||||||
if (!pathCli) {
|
|
||||||
return buildCli;
|
|
||||||
}
|
|
||||||
const pathVersion = await this.spawn(`"${pathCli}"`, ['version']);
|
|
||||||
const pathShortVersion = (pathVersion.match(version) || [])[0];
|
|
||||||
if (semver.gt(pathShortVersion, buildShortVersion)) {
|
|
||||||
this.execPath = pathCli;
|
|
||||||
return pathCli;
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
this.logger.warn(`Could not check for Arduino CLI in $PATH, using embedded CLI instead:`, error);
|
|
||||||
// Any errors here should be safe to ignore, e.g.:
|
|
||||||
// - Could not search for CLI in $PATH
|
|
||||||
// - Could not get version of CLI in $PATH
|
|
||||||
}
|
}
|
||||||
return buildCli;
|
return buildCli;
|
||||||
}
|
}
|
||||||
|
@ -2,14 +2,28 @@ import * as PQueue from 'p-queue';
|
|||||||
import { injectable, inject, postConstruct, named } from 'inversify';
|
import { injectable, inject, postConstruct, named } from 'inversify';
|
||||||
import { ILogger } from '@theia/core/lib/common/logger';
|
import { ILogger } from '@theia/core/lib/common/logger';
|
||||||
import { BoardsService, AttachedSerialBoard, BoardPackage, Board, AttachedNetworkBoard, BoardsServiceClient, Port } from '../common/protocol/boards-service';
|
import { BoardsService, AttachedSerialBoard, BoardPackage, Board, AttachedNetworkBoard, BoardsServiceClient, Port } from '../common/protocol/boards-service';
|
||||||
import { PlatformSearchReq, PlatformSearchResp, PlatformInstallReq, PlatformInstallResp, PlatformListReq, PlatformListResp } from './cli-protocol/commands/core_pb';
|
import {
|
||||||
|
PlatformSearchReq,
|
||||||
|
PlatformSearchResp,
|
||||||
|
PlatformInstallReq,
|
||||||
|
PlatformInstallResp,
|
||||||
|
PlatformListReq,
|
||||||
|
PlatformListResp,
|
||||||
|
Platform,
|
||||||
|
PlatformUninstallReq,
|
||||||
|
PlatformUninstallResp
|
||||||
|
} from './cli-protocol/commands/core_pb';
|
||||||
import { CoreClientProvider } from './core-client-provider';
|
import { CoreClientProvider } from './core-client-provider';
|
||||||
import { BoardListReq, BoardListResp } from './cli-protocol/commands/board_pb';
|
import { BoardListReq, BoardListResp } from './cli-protocol/commands/board_pb';
|
||||||
import { ToolOutputServiceServer } from '../common/protocol/tool-output-service';
|
import { ToolOutputServiceServer } from '../common/protocol/tool-output-service';
|
||||||
|
import { Installable } from '../common/protocol/installable';
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class BoardsServiceImpl implements BoardsService {
|
export class BoardsServiceImpl implements BoardsService {
|
||||||
|
|
||||||
|
@inject(ILogger)
|
||||||
|
protected logger: ILogger;
|
||||||
|
|
||||||
@inject(ILogger)
|
@inject(ILogger)
|
||||||
@named('discovery')
|
@named('discovery')
|
||||||
protected discoveryLogger: ILogger;
|
protected discoveryLogger: ILogger;
|
||||||
@ -24,11 +38,11 @@ export class BoardsServiceImpl implements BoardsService {
|
|||||||
protected discoveryTimer: NodeJS.Timeout | undefined;
|
protected discoveryTimer: NodeJS.Timeout | undefined;
|
||||||
/**
|
/**
|
||||||
* Poor man's serial discovery:
|
* Poor man's serial discovery:
|
||||||
* Stores the state of the currently discovered, attached boards.
|
* Stores the state of the currently discovered and attached boards.
|
||||||
* This state is updated via periodical polls.
|
* This state is updated via periodical polls. If there diff, a change event will be sent out to the frontend.
|
||||||
*/
|
*/
|
||||||
protected _attachedBoards: { boards: Board[] } = { boards: [] };
|
protected attachedBoards: { boards: Board[] } = { boards: [] };
|
||||||
protected _availablePorts: { ports: Port[] } = { ports: [] };
|
protected availablePorts: { ports: Port[] } = { ports: [] };
|
||||||
protected client: BoardsServiceClient | undefined;
|
protected client: BoardsServiceClient | undefined;
|
||||||
protected readonly queue = new PQueue({ autoStart: true, concurrency: 1 });
|
protected readonly queue = new PQueue({ autoStart: true, concurrency: 1 });
|
||||||
|
|
||||||
@ -38,8 +52,8 @@ export class BoardsServiceImpl implements BoardsService {
|
|||||||
this.discoveryLogger.trace('Discovering attached boards and available ports...');
|
this.discoveryLogger.trace('Discovering attached boards and available ports...');
|
||||||
this.doGetAttachedBoardsAndAvailablePorts().then(({ boards, ports }) => {
|
this.doGetAttachedBoardsAndAvailablePorts().then(({ boards, ports }) => {
|
||||||
const update = (oldBoards: Board[], newBoards: Board[], oldPorts: Port[], newPorts: Port[], message: string) => {
|
const update = (oldBoards: Board[], newBoards: Board[], oldPorts: Port[], newPorts: Port[], message: string) => {
|
||||||
this._attachedBoards = { boards: newBoards };
|
this.attachedBoards = { boards: newBoards };
|
||||||
this._availablePorts = { ports: newPorts };
|
this.availablePorts = { ports: newPorts };
|
||||||
this.discoveryLogger.info(`${message} - Discovered boards: ${JSON.stringify(newBoards)} and available ports: ${JSON.stringify(newPorts)}`);
|
this.discoveryLogger.info(`${message} - Discovered boards: ${JSON.stringify(newBoards)} and available ports: ${JSON.stringify(newPorts)}`);
|
||||||
if (this.client) {
|
if (this.client) {
|
||||||
this.client.notifyAttachedBoardsChanged({
|
this.client.notifyAttachedBoardsChanged({
|
||||||
@ -95,17 +109,21 @@ export class BoardsServiceImpl implements BoardsService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dispose(): void {
|
dispose(): void {
|
||||||
|
this.logger.info('>>> Disposing boards service...')
|
||||||
|
this.queue.pause();
|
||||||
|
this.queue.clear();
|
||||||
if (this.discoveryTimer !== undefined) {
|
if (this.discoveryTimer !== undefined) {
|
||||||
clearInterval(this.discoveryTimer);
|
clearInterval(this.discoveryTimer);
|
||||||
}
|
}
|
||||||
|
this.logger.info('<<< Disposed boards service.')
|
||||||
}
|
}
|
||||||
|
|
||||||
async getAttachedBoards(): Promise<{ boards: Board[] }> {
|
async getAttachedBoards(): Promise<{ boards: Board[] }> {
|
||||||
return this._attachedBoards;
|
return this.attachedBoards;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getAvailablePorts(): Promise<{ ports: Port[] }> {
|
async getAvailablePorts(): Promise<{ ports: Port[] }> {
|
||||||
return this._availablePorts;
|
return this.availablePorts;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async doGetAttachedBoardsAndAvailablePorts(): Promise<{ boards: Board[], ports: Port[] }> {
|
private async doGetAttachedBoardsAndAvailablePorts(): Promise<{ boards: Board[], ports: Port[] }> {
|
||||||
@ -207,35 +225,76 @@ export class BoardsServiceImpl implements BoardsService {
|
|||||||
|
|
||||||
const req = new PlatformSearchReq();
|
const req = new PlatformSearchReq();
|
||||||
req.setSearchArgs(options.query || "");
|
req.setSearchArgs(options.query || "");
|
||||||
|
req.setAllVersions(true);
|
||||||
req.setInstance(instance);
|
req.setInstance(instance);
|
||||||
const resp = await new Promise<PlatformSearchResp>((resolve, reject) => client.platformSearch(req, (err, resp) => (!!err ? reject : resolve)(!!err ? err : resp)));
|
const resp = await new Promise<PlatformSearchResp>((resolve, reject) => client.platformSearch(req, (err, resp) => (!!err ? reject : resolve)(!!err ? err : resp)));
|
||||||
|
const packages = new Map<string, BoardPackage>();
|
||||||
let items = resp.getSearchOutputList().map(item => {
|
const toPackage = (platform: Platform) => {
|
||||||
let installedVersion: string | undefined;
|
let installedVersion: string | undefined;
|
||||||
const matchingPlatform = installedPlatforms.find(ip => ip.getId() === item.getId());
|
const matchingPlatform = installedPlatforms.find(ip => ip.getId() === platform.getId());
|
||||||
if (!!matchingPlatform) {
|
if (!!matchingPlatform) {
|
||||||
installedVersion = matchingPlatform.getInstalled();
|
installedVersion = matchingPlatform.getInstalled();
|
||||||
}
|
}
|
||||||
|
return {
|
||||||
const result: BoardPackage = {
|
id: platform.getId(),
|
||||||
id: item.getId(),
|
name: platform.getName(),
|
||||||
name: item.getName(),
|
author: platform.getMaintainer(),
|
||||||
author: item.getMaintainer(),
|
availableVersions: [platform.getLatest()],
|
||||||
availableVersions: [item.getLatest()],
|
description: platform.getBoardsList().map(b => b.getName()).join(", "),
|
||||||
description: item.getBoardsList().map(b => b.getName()).join(", "),
|
|
||||||
installable: true,
|
installable: true,
|
||||||
summary: "Boards included in this package:",
|
summary: "Boards included in this package:",
|
||||||
installedVersion,
|
installedVersion,
|
||||||
boards: item.getBoardsList().map(b => <Board>{ name: b.getName(), fqbn: b.getFqbn() }),
|
boards: platform.getBoardsList().map(b => <Board>{ name: b.getName(), fqbn: b.getFqbn() }),
|
||||||
moreInfoLink: item.getWebsite()
|
moreInfoLink: platform.getWebsite()
|
||||||
}
|
}
|
||||||
return result;
|
}
|
||||||
});
|
|
||||||
|
|
||||||
return { items };
|
// We must group the cores by ID, and sort platforms by, first the installed version, then version alphabetical order.
|
||||||
|
// Otherwise we lose the FQBN information.
|
||||||
|
const groupedById: Map<string, Platform[]> = new Map();
|
||||||
|
for (const platform of resp.getSearchOutputList()) {
|
||||||
|
const id = platform.getId();
|
||||||
|
if (groupedById.has(id)) {
|
||||||
|
groupedById.get(id)!.push(platform);
|
||||||
|
} else {
|
||||||
|
groupedById.set(id, [platform]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const installedAwareVersionComparator = (left: Platform, right: Platform) => {
|
||||||
|
// XXX: we cannot rely on `platform.getInstalled()`, it is always an empty string.
|
||||||
|
const leftInstalled = !!installedPlatforms.find(ip => ip.getId() === left.getId() && ip.getInstalled() === left.getLatest());
|
||||||
|
const rightInstalled = !!installedPlatforms.find(ip => ip.getId() === right.getId() && ip.getInstalled() === right.getLatest());
|
||||||
|
if (leftInstalled && !rightInstalled) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (!leftInstalled && rightInstalled) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return Installable.Version.COMPARATOR(right.getLatest(), left.getLatest()); // Higher version comes first.
|
||||||
|
}
|
||||||
|
for (const id of groupedById.keys()) {
|
||||||
|
groupedById.get(id)!.sort(installedAwareVersionComparator);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const id of groupedById.keys()) {
|
||||||
|
for (const platform of groupedById.get(id)!) {
|
||||||
|
const id = platform.getId();
|
||||||
|
const pkg = packages.get(id);
|
||||||
|
if (pkg) {
|
||||||
|
pkg.availableVersions.push(platform.getLatest());
|
||||||
|
pkg.availableVersions.sort(Installable.Version.COMPARATOR);
|
||||||
|
} else {
|
||||||
|
packages.set(id, toPackage(platform));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { items: [...packages.values()] };
|
||||||
}
|
}
|
||||||
|
|
||||||
async install(pkg: BoardPackage): Promise<void> {
|
async install(options: { item: BoardPackage, version?: Installable.Version }): Promise<void> {
|
||||||
|
const pkg = options.item;
|
||||||
|
const version = !!options.version ? options.version : pkg.availableVersions[0];
|
||||||
const coreClient = await this.coreClientProvider.getClient();
|
const coreClient = await this.coreClientProvider.getClient();
|
||||||
if (!coreClient) {
|
if (!coreClient) {
|
||||||
return;
|
return;
|
||||||
@ -248,7 +307,7 @@ export class BoardsServiceImpl implements BoardsService {
|
|||||||
req.setInstance(instance);
|
req.setInstance(instance);
|
||||||
req.setArchitecture(boardName);
|
req.setArchitecture(boardName);
|
||||||
req.setPlatformPackage(platform);
|
req.setPlatformPackage(platform);
|
||||||
req.setVersion(pkg.availableVersions[0]);
|
req.setVersion(version);
|
||||||
|
|
||||||
console.info("Starting board installation", pkg);
|
console.info("Starting board installation", pkg);
|
||||||
const resp = client.platformInstall(req);
|
const resp = client.platformInstall(req);
|
||||||
@ -268,4 +327,38 @@ export class BoardsServiceImpl implements BoardsService {
|
|||||||
console.info("Board installation done", pkg);
|
console.info("Board installation done", pkg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async uninstall(options: { item: BoardPackage }): Promise<void> {
|
||||||
|
const pkg = options.item;
|
||||||
|
const coreClient = await this.coreClientProvider.getClient();
|
||||||
|
if (!coreClient) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const { client, instance } = coreClient;
|
||||||
|
|
||||||
|
const [platform, boardName] = pkg.id.split(":");
|
||||||
|
|
||||||
|
const req = new PlatformUninstallReq();
|
||||||
|
req.setInstance(instance);
|
||||||
|
req.setArchitecture(boardName);
|
||||||
|
req.setPlatformPackage(platform);
|
||||||
|
|
||||||
|
console.info("Starting board uninstallation", pkg);
|
||||||
|
let logged = false;
|
||||||
|
const resp = client.platformUninstall(req);
|
||||||
|
resp.on('data', (_: PlatformUninstallResp) => {
|
||||||
|
if (!logged) {
|
||||||
|
this.toolOutputService.publishNewOutput("board uninstall", `uninstalling ${pkg.id}\n`)
|
||||||
|
logged = true;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
await new Promise<void>((resolve, reject) => {
|
||||||
|
resp.on('end', resolve);
|
||||||
|
resp.on('error', reject);
|
||||||
|
});
|
||||||
|
if (this.client) {
|
||||||
|
this.client.notifyBoardUninstalled({ pkg });
|
||||||
|
}
|
||||||
|
console.info("Board uninstallation done", pkg);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
// file: commands/board.proto
|
// file: commands/board.proto
|
||||||
|
|
||||||
/* tslint:disable */
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
|
||||||
import * as jspb from "google-protobuf";
|
import * as jspb from "google-protobuf";
|
||||||
import * as commands_common_pb from "../commands/common_pb";
|
import * as commands_common_pb from "../commands/common_pb";
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
// file: commands/commands.proto
|
// file: commands/commands.proto
|
||||||
|
|
||||||
/* tslint:disable */
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
|
||||||
import * as grpc from "@grpc/grpc-js";
|
import * as grpc from "@grpc/grpc-js";
|
||||||
import * as commands_commands_pb from "../commands/commands_pb";
|
import * as commands_commands_pb from "../commands/commands_pb";
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
// file: commands/commands.proto
|
// file: commands/commands.proto
|
||||||
|
|
||||||
/* tslint:disable */
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
|
||||||
import * as jspb from "google-protobuf";
|
import * as jspb from "google-protobuf";
|
||||||
import * as commands_common_pb from "../commands/common_pb";
|
import * as commands_common_pb from "../commands/common_pb";
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
// file: commands/common.proto
|
// file: commands/common.proto
|
||||||
|
|
||||||
/* tslint:disable */
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
|
||||||
import * as jspb from "google-protobuf";
|
import * as jspb from "google-protobuf";
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
// file: commands/compile.proto
|
// file: commands/compile.proto
|
||||||
|
|
||||||
/* tslint:disable */
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
|
||||||
import * as jspb from "google-protobuf";
|
import * as jspb from "google-protobuf";
|
||||||
import * as commands_common_pb from "../commands/common_pb";
|
import * as commands_common_pb from "../commands/common_pb";
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
// file: commands/core.proto
|
// file: commands/core.proto
|
||||||
|
|
||||||
/* tslint:disable */
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
|
||||||
import * as jspb from "google-protobuf";
|
import * as jspb from "google-protobuf";
|
||||||
import * as commands_common_pb from "../commands/common_pb";
|
import * as commands_common_pb from "../commands/common_pb";
|
||||||
@ -262,6 +263,9 @@ export class PlatformSearchReq extends jspb.Message {
|
|||||||
getSearchArgs(): string;
|
getSearchArgs(): string;
|
||||||
setSearchArgs(value: string): void;
|
setSearchArgs(value: string): void;
|
||||||
|
|
||||||
|
getAllVersions(): boolean;
|
||||||
|
setAllVersions(value: boolean): void;
|
||||||
|
|
||||||
|
|
||||||
serializeBinary(): Uint8Array;
|
serializeBinary(): Uint8Array;
|
||||||
toObject(includeInstance?: boolean): PlatformSearchReq.AsObject;
|
toObject(includeInstance?: boolean): PlatformSearchReq.AsObject;
|
||||||
@ -277,6 +281,7 @@ export namespace PlatformSearchReq {
|
|||||||
export type AsObject = {
|
export type AsObject = {
|
||||||
instance?: commands_common_pb.Instance.AsObject,
|
instance?: commands_common_pb.Instance.AsObject,
|
||||||
searchArgs: string,
|
searchArgs: string,
|
||||||
|
allVersions: boolean,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1705,7 +1705,8 @@ proto.cc.arduino.cli.commands.PlatformSearchReq.prototype.toObject = function(op
|
|||||||
proto.cc.arduino.cli.commands.PlatformSearchReq.toObject = function(includeInstance, msg) {
|
proto.cc.arduino.cli.commands.PlatformSearchReq.toObject = function(includeInstance, msg) {
|
||||||
var f, obj = {
|
var f, obj = {
|
||||||
instance: (f = msg.getInstance()) && commands_common_pb.Instance.toObject(includeInstance, f),
|
instance: (f = msg.getInstance()) && commands_common_pb.Instance.toObject(includeInstance, f),
|
||||||
searchArgs: jspb.Message.getFieldWithDefault(msg, 2, "")
|
searchArgs: jspb.Message.getFieldWithDefault(msg, 2, ""),
|
||||||
|
allVersions: jspb.Message.getFieldWithDefault(msg, 3, false)
|
||||||
};
|
};
|
||||||
|
|
||||||
if (includeInstance) {
|
if (includeInstance) {
|
||||||
@ -1751,6 +1752,10 @@ proto.cc.arduino.cli.commands.PlatformSearchReq.deserializeBinaryFromReader = fu
|
|||||||
var value = /** @type {string} */ (reader.readString());
|
var value = /** @type {string} */ (reader.readString());
|
||||||
msg.setSearchArgs(value);
|
msg.setSearchArgs(value);
|
||||||
break;
|
break;
|
||||||
|
case 3:
|
||||||
|
var value = /** @type {boolean} */ (reader.readBool());
|
||||||
|
msg.setAllVersions(value);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
reader.skipField();
|
reader.skipField();
|
||||||
break;
|
break;
|
||||||
@ -1795,6 +1800,13 @@ proto.cc.arduino.cli.commands.PlatformSearchReq.serializeBinaryToWriter = functi
|
|||||||
f
|
f
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
f = message.getAllVersions();
|
||||||
|
if (f) {
|
||||||
|
writer.writeBool(
|
||||||
|
3,
|
||||||
|
f
|
||||||
|
);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -1843,6 +1855,23 @@ proto.cc.arduino.cli.commands.PlatformSearchReq.prototype.setSearchArgs = functi
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* optional bool all_versions = 3;
|
||||||
|
* Note that Boolean fields may be set to 0/1 when serialized from a Java server.
|
||||||
|
* You should avoid comparisons like {@code val === true/false} in those cases.
|
||||||
|
* @return {boolean}
|
||||||
|
*/
|
||||||
|
proto.cc.arduino.cli.commands.PlatformSearchReq.prototype.getAllVersions = function() {
|
||||||
|
return /** @type {boolean} */ (jspb.Message.getFieldWithDefault(this, 3, false));
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/** @param {boolean} value */
|
||||||
|
proto.cc.arduino.cli.commands.PlatformSearchReq.prototype.setAllVersions = function(value) {
|
||||||
|
jspb.Message.setProto3BooleanField(this, 3, value);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generated by JsPbCodeGenerator.
|
* Generated by JsPbCodeGenerator.
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
// file: commands/lib.proto
|
// file: commands/lib.proto
|
||||||
|
|
||||||
/* tslint:disable */
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
|
||||||
import * as jspb from "google-protobuf";
|
import * as jspb from "google-protobuf";
|
||||||
import * as commands_common_pb from "../commands/common_pb";
|
import * as commands_common_pb from "../commands/common_pb";
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
// file: commands/upload.proto
|
// file: commands/upload.proto
|
||||||
|
|
||||||
/* tslint:disable */
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
|
||||||
import * as jspb from "google-protobuf";
|
import * as jspb from "google-protobuf";
|
||||||
import * as commands_common_pb from "../commands/common_pb";
|
import * as commands_common_pb from "../commands/common_pb";
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
// file: monitor/monitor.proto
|
// file: monitor/monitor.proto
|
||||||
|
|
||||||
/* tslint:disable */
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
|
||||||
import * as grpc from "@grpc/grpc-js";
|
import * as grpc from "@grpc/grpc-js";
|
||||||
import * as monitor_monitor_pb from "../monitor/monitor_pb";
|
import * as monitor_monitor_pb from "../monitor/monitor_pb";
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
// file: monitor/monitor.proto
|
// file: monitor/monitor.proto
|
||||||
|
|
||||||
/* tslint:disable */
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
|
||||||
import * as jspb from "google-protobuf";
|
import * as jspb from "google-protobuf";
|
||||||
import * as google_protobuf_struct_pb from "google-protobuf/google/protobuf/struct_pb";
|
import * as google_protobuf_struct_pb from "google-protobuf/google/protobuf/struct_pb";
|
||||||
|
@ -42,7 +42,24 @@ export class CoreClientProviderImpl implements CoreClientProvider {
|
|||||||
|
|
||||||
async getClient(workspaceRootOrResourceUri?: string): Promise<Client | undefined> {
|
async getClient(workspaceRootOrResourceUri?: string): Promise<Client | undefined> {
|
||||||
return this.clientRequestQueue.add(() => new Promise<Client | undefined>(async resolve => {
|
return this.clientRequestQueue.add(() => new Promise<Client | undefined>(async resolve => {
|
||||||
const roots = await this.workspaceServiceExt.roots();
|
let roots = undefined;
|
||||||
|
try {
|
||||||
|
roots = await this.workspaceServiceExt.roots();
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof Error && e.message === 'Connection got disposed.') {
|
||||||
|
console.info('The frontend has already disconnected.');
|
||||||
|
// Ignore it for now: https://github.com/eclipse-theia/theia/issues/6499
|
||||||
|
// Client has disconnected, and the server still runs the serial board poll.
|
||||||
|
// The poll requires the client's workspace roots, but the client has disconnected :/
|
||||||
|
} else {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!roots) {
|
||||||
|
resolve(undefined);
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if (!workspaceRootOrResourceUri) {
|
if (!workspaceRootOrResourceUri) {
|
||||||
resolve(this.getOrCreateClient(roots[0]));
|
resolve(this.getOrCreateClient(roots[0]));
|
||||||
return;
|
return;
|
||||||
|
@ -1,9 +1,20 @@
|
|||||||
import { injectable, inject } from 'inversify';
|
import { injectable, inject } from 'inversify';
|
||||||
import { Library, LibraryService } from '../common/protocol/library-service';
|
import { Library, LibraryService } from '../common/protocol/library-service';
|
||||||
import { CoreClientProvider } from './core-client-provider';
|
import { CoreClientProvider } from './core-client-provider';
|
||||||
import { LibrarySearchReq, LibrarySearchResp, LibraryListReq, LibraryListResp, LibraryRelease,
|
import {
|
||||||
InstalledLibrary, LibraryInstallReq, LibraryInstallResp } from './cli-protocol/commands/lib_pb';
|
LibrarySearchReq,
|
||||||
|
LibrarySearchResp,
|
||||||
|
LibraryListReq,
|
||||||
|
LibraryListResp,
|
||||||
|
LibraryRelease,
|
||||||
|
InstalledLibrary,
|
||||||
|
LibraryInstallReq,
|
||||||
|
LibraryInstallResp,
|
||||||
|
LibraryUninstallReq,
|
||||||
|
LibraryUninstallResp
|
||||||
|
} from './cli-protocol/commands/lib_pb';
|
||||||
import { ToolOutputServiceServer } from '../common/protocol/tool-output-service';
|
import { ToolOutputServiceServer } from '../common/protocol/tool-output-service';
|
||||||
|
import { Installable } from '../common/protocol/installable';
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class LibraryServiceImpl implements LibraryService {
|
export class LibraryServiceImpl implements LibraryService {
|
||||||
@ -43,6 +54,8 @@ export class LibraryServiceImpl implements LibraryService {
|
|||||||
.filter(item => !!item.getLatest())
|
.filter(item => !!item.getLatest())
|
||||||
.slice(0, 50)
|
.slice(0, 50)
|
||||||
.map(item => {
|
.map(item => {
|
||||||
|
// TODO: This seems to contain only the latest item instead of all of the items.
|
||||||
|
const availableVersions = item.getReleasesMap().getEntryList().map(([key, _]) => key).sort(Installable.Version.COMPARATOR);
|
||||||
let installedVersion: string | undefined;
|
let installedVersion: string | undefined;
|
||||||
const installed = installedLibsIdx.get(item.getName());
|
const installed = installedLibsIdx.get(item.getName());
|
||||||
if (installed) {
|
if (installed) {
|
||||||
@ -51,14 +64,16 @@ export class LibraryServiceImpl implements LibraryService {
|
|||||||
return toLibrary({
|
return toLibrary({
|
||||||
name: item.getName(),
|
name: item.getName(),
|
||||||
installable: true,
|
installable: true,
|
||||||
installedVersion
|
installedVersion,
|
||||||
}, item.getLatest()!)
|
}, item.getLatest()!, availableVersions)
|
||||||
})
|
})
|
||||||
|
|
||||||
return { items };
|
return { items };
|
||||||
}
|
}
|
||||||
|
|
||||||
async install(library: Library): Promise<void> {
|
async install(options: { item: Library, version?: Installable.Version }): Promise<void> {
|
||||||
|
const library = options.item;
|
||||||
|
const version = !!options.version ? options.version : library.availableVersions[0];
|
||||||
const coreClient = await this.coreClientProvider.getClient();
|
const coreClient = await this.coreClientProvider.getClient();
|
||||||
if (!coreClient) {
|
if (!coreClient) {
|
||||||
return;
|
return;
|
||||||
@ -68,7 +83,7 @@ export class LibraryServiceImpl implements LibraryService {
|
|||||||
const req = new LibraryInstallReq();
|
const req = new LibraryInstallReq();
|
||||||
req.setInstance(instance);
|
req.setInstance(instance);
|
||||||
req.setName(library.name);
|
req.setName(library.name);
|
||||||
req.setVersion(library.availableVersions[0]);
|
req.setVersion(version);
|
||||||
|
|
||||||
const resp = client.libraryInstall(req);
|
const resp = client.libraryInstall(req);
|
||||||
resp.on('data', (r: LibraryInstallResp) => {
|
resp.on('data', (r: LibraryInstallResp) => {
|
||||||
@ -83,16 +98,43 @@ export class LibraryServiceImpl implements LibraryService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async uninstall(options: { item: Library }): Promise<void> {
|
||||||
|
const library = options.item;
|
||||||
|
const coreClient = await this.coreClientProvider.getClient();
|
||||||
|
if (!coreClient) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const { client, instance } = coreClient;
|
||||||
|
|
||||||
|
const req = new LibraryUninstallReq();
|
||||||
|
req.setInstance(instance);
|
||||||
|
req.setName(library.name);
|
||||||
|
req.setVersion(library.installedVersion!);
|
||||||
|
|
||||||
|
let logged = false;
|
||||||
|
const resp = client.libraryUninstall(req);
|
||||||
|
resp.on('data', (_: LibraryUninstallResp) => {
|
||||||
|
if (!logged) {
|
||||||
|
this.toolOutputService.publishNewOutput("library uninstall", `uninstalling ${library.name}:${library.installedVersion}%\n`)
|
||||||
|
logged = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
await new Promise<void>((resolve, reject) => {
|
||||||
|
resp.on('end', resolve);
|
||||||
|
resp.on('error', reject);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function toLibrary(tpl: Partial<Library>, release: LibraryRelease): Library {
|
function toLibrary(tpl: Partial<Library>, release: LibraryRelease, availableVersions: string[]): Library {
|
||||||
return {
|
return {
|
||||||
name: "",
|
name: "",
|
||||||
installable: false,
|
installable: false,
|
||||||
...tpl,
|
...tpl,
|
||||||
|
|
||||||
author: release.getAuthor(),
|
author: release.getAuthor(),
|
||||||
availableVersions: [release.getVersion()],
|
availableVersions,
|
||||||
description: release.getSentence(),
|
description: release.getSentence(),
|
||||||
moreInfoLink: release.getWebsite(),
|
moreInfoLink: release.getWebsite(),
|
||||||
summary: release.getParagraph()
|
summary: release.getParagraph()
|
||||||
|
@ -52,14 +52,16 @@ export class SketchesServiceImpl implements SketchesService {
|
|||||||
const uris: string[] = [];
|
const uris: string[] = [];
|
||||||
const fsPath = FileUri.fsPath(uri);
|
const fsPath = FileUri.fsPath(uri);
|
||||||
const stats = fs.lstatSync(fsPath);
|
const stats = fs.lstatSync(fsPath);
|
||||||
if (stats.isDirectory && await this.isSketchFolder(uri)) {
|
if (stats.isDirectory) {
|
||||||
const fileNames = fs.readdirSync(fsPath);
|
if (await this.isSketchFolder(uri)) {
|
||||||
for (const fileName of fileNames) {
|
const fileNames = fs.readdirSync(fsPath);
|
||||||
const filePath = path.join(fsPath, fileName);
|
for (const fileName of fileNames) {
|
||||||
if (ALLOWED_FILE_EXTENSIONS.indexOf(path.extname(filePath)) !== -1
|
const filePath = path.join(fsPath, fileName);
|
||||||
&& fs.existsSync(filePath)
|
if (ALLOWED_FILE_EXTENSIONS.indexOf(path.extname(filePath)) !== -1
|
||||||
&& fs.lstatSync(filePath).isFile()) {
|
&& fs.existsSync(filePath)
|
||||||
uris.push(FileUri.create(filePath).toString())
|
&& fs.lstatSync(filePath).isFile()) {
|
||||||
|
uris.push(FileUri.create(filePath).toString())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return uris;
|
return uris;
|
||||||
|
@ -28,4 +28,4 @@
|
|||||||
"files": [
|
"files": [
|
||||||
"../node_modules/@theia/monaco/src/typings/monaco/index.d.ts"
|
"../node_modules/@theia/monaco/src/typings/monaco/index.d.ts"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"private": true,
|
"private": true,
|
||||||
"name": "browser-app",
|
"name": "browser-app",
|
||||||
"version": "0.0.2",
|
"version": "0.0.3",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@theia/core": "next",
|
"@theia/core": "next",
|
||||||
@ -18,7 +18,7 @@
|
|||||||
"@theia/terminal": "next",
|
"@theia/terminal": "next",
|
||||||
"@theia/workspace": "next",
|
"@theia/workspace": "next",
|
||||||
"@theia/textmate-grammars": "next",
|
"@theia/textmate-grammars": "next",
|
||||||
"arduino-ide-extension": "0.0.2"
|
"arduino-ide-extension": "0.0.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@theia/cli": "next"
|
"@theia/cli": "next"
|
||||||
|
@ -1,50 +1,49 @@
|
|||||||
{
|
{
|
||||||
"private": true,
|
"private": true,
|
||||||
"name": "electron-app",
|
"name": "electron-app",
|
||||||
"version": "0.0.2",
|
"version": "0.0.3",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@theia/core": "next",
|
"@theia/core": "next",
|
||||||
"@theia/cpp": "next",
|
"@theia/cpp": "next",
|
||||||
"@theia/editor": "next",
|
"@theia/editor": "next",
|
||||||
"@theia/electron": "next",
|
"@theia/electron": "next",
|
||||||
"@theia/file-search": "next",
|
"@theia/file-search": "next",
|
||||||
"@theia/filesystem": "next",
|
"@theia/filesystem": "next",
|
||||||
"@theia/languages": "next",
|
"@theia/languages": "next",
|
||||||
"@theia/messages": "next",
|
"@theia/messages": "next",
|
||||||
"@theia/monaco": "next",
|
"@theia/monaco": "next",
|
||||||
"@theia/navigator": "next",
|
"@theia/navigator": "next",
|
||||||
"@theia/preferences": "next",
|
"@theia/preferences": "next",
|
||||||
"@theia/process": "next",
|
"@theia/process": "next",
|
||||||
"@theia/terminal": "next",
|
"@theia/terminal": "next",
|
||||||
"@theia/workspace": "next",
|
"@theia/workspace": "next",
|
||||||
"@theia/textmate-grammars": "next",
|
"@theia/textmate-grammars": "next",
|
||||||
"arduino-ide-extension": "0.0.2"
|
"arduino-ide-extension": "0.0.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@theia/cli": "next",
|
"@theia/cli": "next"
|
||||||
"electron": "^4.2.0"
|
},
|
||||||
},
|
"scripts": {
|
||||||
"scripts": {
|
"prepare": "theia build --mode development",
|
||||||
"prepare": "theia build --mode development",
|
"start": "theia start",
|
||||||
"start": "theia start",
|
"watch": "theia build --watch --mode development"
|
||||||
"watch": "theia build --watch --mode development"
|
},
|
||||||
},
|
"theia": {
|
||||||
"theia": {
|
"target": "electron",
|
||||||
"target": "electron",
|
"frontend": {
|
||||||
"frontend": {
|
"config": {
|
||||||
"config": {
|
"applicationName": "Arduino Pro IDE",
|
||||||
"applicationName": "Arduino Pro IDE",
|
"defaultTheme": "arduino-theme",
|
||||||
"defaultTheme": "arduino-theme",
|
"preferences": {
|
||||||
"preferences": {
|
"editor.autoSave": "on"
|
||||||
"editor.autoSave": "on"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"generator": {
|
|
||||||
"config": {
|
|
||||||
"preloadTemplate": "<div class='theia-preload' style='background-color: rgb(237, 241, 242);'></div>"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"generator": {
|
||||||
|
"config": {
|
||||||
|
"preloadTemplate": "<div class='theia-preload' style='background-color: rgb(237, 241, 242);'></div>"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -2,7 +2,7 @@
|
|||||||
"name": "arduino.Pro.IDE",
|
"name": "arduino.Pro.IDE",
|
||||||
"description": "Arduino Pro IDE",
|
"description": "Arduino Pro IDE",
|
||||||
"main": "src-gen/frontend/electron-main.js",
|
"main": "src-gen/frontend/electron-main.js",
|
||||||
"author": "TypeFox",
|
"author": "Arduino SA",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"google-protobuf": "^3.5.0",
|
"google-protobuf": "^3.5.0",
|
||||||
"arduino-ide-extension": "file:../working-copy/arduino-ide-extension"
|
"arduino-ide-extension": "file:../working-copy/arduino-ide-extension"
|
||||||
@ -27,14 +27,12 @@
|
|||||||
"url": "git+https://github.com/arduino/arduino-pro-ide.git"
|
"url": "git+https://github.com/arduino/arduino-pro-ide.git"
|
||||||
},
|
},
|
||||||
"// Notes:": [
|
"// Notes:": [
|
||||||
"The `electronVersion` version was pinned for `@grpc/grpc-js` -> Node.js version constraints.",
|
|
||||||
"`google-protobuf` was declared as it is not picked up by the `electron-builder` as a runtime dependency.",
|
"`google-protobuf` was declared as it is not picked up by the `electron-builder` as a runtime dependency.",
|
||||||
"The resolution for `fs-extra` was required due to this: https://spectrum.chat/theia/general/our-theia-electron-builder-app-no-longer-starts~f5cf09a0-6d88-448b-8818-24ad0ec2ee7c"
|
"The resolution for `fs-extra` was required due to this: https://spectrum.chat/theia/general/our-theia-electron-builder-app-no-longer-starts~f5cf09a0-6d88-448b-8818-24ad0ec2ee7c"
|
||||||
],
|
],
|
||||||
"build": {
|
"build": {
|
||||||
"productName": "Arduino Pro IDE",
|
"productName": "Arduino Pro IDE",
|
||||||
"appId": "arduino.Pro.IDE",
|
"appId": "arduino.Pro.IDE",
|
||||||
"electronVersion": "4.2.0",
|
|
||||||
"asar": false,
|
"asar": false,
|
||||||
"directories": {
|
"directories": {
|
||||||
"buildResources": "resources"
|
"buildResources": "resources"
|
||||||
@ -46,7 +44,6 @@
|
|||||||
"!node_modules/**/*.spec.js",
|
"!node_modules/**/*.spec.js",
|
||||||
"!node_modules/@theia/**/test/*",
|
"!node_modules/@theia/**/test/*",
|
||||||
"!node_modules/@theia/**/src/*.ts",
|
"!node_modules/@theia/**/src/*.ts",
|
||||||
"!node_modules/@theia/java/download",
|
|
||||||
"!node_modules/@theia/**/lib/*browser/*",
|
"!node_modules/@theia/**/lib/*browser/*",
|
||||||
"!node_modules/@typefox/monaco-editor-core/*",
|
"!node_modules/@typefox/monaco-editor-core/*",
|
||||||
"!node_modules/oniguruma/*",
|
"!node_modules/oniguruma/*",
|
||||||
|
@ -9,8 +9,8 @@
|
|||||||
"cli": "./cli"
|
"cli": "./cli"
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
"author": "",
|
"author": "Arduino SA",
|
||||||
"license": "ISC",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"depcheck": "^0.7.1",
|
"depcheck": "^0.7.1",
|
||||||
"shelljs": "^0.8.3",
|
"shelljs": "^0.8.3",
|
||||||
|
20
package.json
20
package.json
@ -1,25 +1,25 @@
|
|||||||
{
|
{
|
||||||
"name": "arduino-editor",
|
"name": "arduino-editor",
|
||||||
"version": "0.0.1",
|
"version": "0.0.3",
|
||||||
"description": "Arduino IDE built using Eclipse Theia",
|
"description": "Arduino Pro IDE",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"repository": "https://github.com/bcmi-labs/arduino-editor.git",
|
"repository": "https://github.com/bcmi-labs/arduino-editor.git",
|
||||||
"author": "Christian Weichel <christian.weichel@typefox.io>",
|
"author": "Arduino SA",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"private": true,
|
"private": true,
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"lerna": "^3.13.3"
|
"lerna": "^3.13.3"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"prepare": "lerna run prepare",
|
"prepare": "lerna run prepare",
|
||||||
"rebuild:browser": "theia rebuild:browser",
|
"rebuild:browser": "theia rebuild:browser",
|
||||||
"rebuild:electron": "theia rebuild:electron",
|
"rebuild:electron": "theia rebuild:electron",
|
||||||
"start": "yarn --cwd ./browser-app start",
|
"start": "yarn --cwd ./browser-app start",
|
||||||
"watch": "lerna run watch --parallel"
|
"watch": "lerna run watch --parallel"
|
||||||
},
|
},
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
"arduino-ide-extension",
|
"arduino-ide-extension",
|
||||||
"electron-app",
|
"electron-app",
|
||||||
"browser-app"
|
"browser-app"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user