mirror of
https://github.com/arduino/arduino-ide.git
synced 2025-10-07 18:38:32 +00:00
Compare commits
80 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
6447191bf5 | ||
![]() |
e78ed85761 | ||
![]() |
9bc520ccf9 | ||
![]() |
cfdb00529c | ||
![]() |
8ccea24452 | ||
![]() |
ad563d26ba | ||
![]() |
f0a628534e | ||
![]() |
1b95242ad1 | ||
![]() |
729588770e | ||
![]() |
6b2046e090 | ||
![]() |
80673ad18f | ||
![]() |
2f33038695 | ||
![]() |
6154d1e8d5 | ||
![]() |
557ec2ae42 | ||
![]() |
8c49c04359 | ||
![]() |
29ebf055e6 | ||
![]() |
71842abfa3 | ||
![]() |
ed660ccd64 | ||
![]() |
6af22ec9b8 | ||
![]() |
79f588d067 | ||
![]() |
e3e4a96db3 | ||
![]() |
4129544738 | ||
![]() |
a866bde4d1 | ||
![]() |
92b6208a76 | ||
![]() |
12deceef19 | ||
![]() |
f635751a8c | ||
![]() |
85bf50213d | ||
![]() |
5aeb2d388e | ||
![]() |
b6b4c75718 | ||
![]() |
c4a8062df4 | ||
![]() |
6e89e89738 | ||
![]() |
c7242ca34f | ||
![]() |
a4e5e65286 | ||
![]() |
80549db289 | ||
![]() |
eb7b3ad683 | ||
![]() |
9efcbcf2ae | ||
![]() |
d22c0b9e55 | ||
![]() |
5d2f09354d | ||
![]() |
fcd6c792e3 | ||
![]() |
94233a1a19 | ||
![]() |
7fb32766ca | ||
![]() |
85cf8757c4 | ||
![]() |
41c56c1126 | ||
![]() |
4c503c0c5e | ||
![]() |
3f180b6059 | ||
![]() |
6a8a76f720 | ||
![]() |
9a27252d91 | ||
![]() |
4e683b237d | ||
![]() |
a2a9cbb02e | ||
![]() |
dd10436051 | ||
![]() |
e79d42d6bd | ||
![]() |
a9c9dcde7b | ||
![]() |
62b18ccbed | ||
![]() |
0a8b6bc41e | ||
![]() |
b1388be5f9 | ||
![]() |
b4848f62fa | ||
![]() |
f359843635 | ||
![]() |
6448b447b3 | ||
![]() |
c7bb3abf19 | ||
![]() |
c3e2aa4feb | ||
![]() |
63cd2701b4 | ||
![]() |
35ac73181b | ||
![]() |
840cde872c | ||
![]() |
c2008460b0 | ||
![]() |
435fdcdf7f | ||
![]() |
7e6343e60e | ||
![]() |
fdda4a72d0 | ||
![]() |
7077303a36 | ||
![]() |
acd9bf1354 | ||
![]() |
d92fc25769 | ||
![]() |
f9a98d708e | ||
![]() |
df33c5689f | ||
![]() |
2dc73eb3b5 | ||
![]() |
f6444b2570 | ||
![]() |
186180800f | ||
![]() |
20bc3c6f13 | ||
![]() |
125ee70fa3 | ||
![]() |
3cfb1450c0 | ||
![]() |
9643dd397f | ||
![]() |
7c1ebf273c |
@@ -2,8 +2,12 @@ image:
|
||||
file: Dockerfile
|
||||
|
||||
ports:
|
||||
- port: 3000
|
||||
onOpen: open-browser
|
||||
- port: 3000
|
||||
onOpen: open-preview
|
||||
- port: 5900
|
||||
onOpen: ignore
|
||||
- port: 6080
|
||||
onOpen: ignore
|
||||
|
||||
tasks:
|
||||
- init: >
|
||||
|
140
.vscode/launch.json
vendored
140
.vscode/launch.json
vendored
@@ -1,39 +1,105 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"name": "Launch Electron Packager",
|
||||
"program": "${workspaceRoot}/electron/packager/index.js",
|
||||
"cwd": "${workspaceFolder}/electron/packager"
|
||||
},
|
||||
{
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"name": "Launch Backend",
|
||||
"program": "${workspaceRoot}/browser-app/src-gen/backend/main.js",
|
||||
"args": [
|
||||
"--hostname=0.0.0.0",
|
||||
"--port=3000",
|
||||
"--no-cluster",
|
||||
"--no-app-auto-install"
|
||||
],
|
||||
"env": {
|
||||
"NODE_ENV": "development"
|
||||
},
|
||||
"sourceMaps": true,
|
||||
"outFiles": [
|
||||
"${workspaceRoot}/browser-app/src-gen/backend/*.js",
|
||||
"${workspaceRoot}/browser-app/lib/**/*.js",
|
||||
"${workspaceRoot}/arduino-ide-extension/*/lib/**/*.js"
|
||||
],
|
||||
"smartStep": true,
|
||||
"internalConsoleOptions": "openOnSessionStart",
|
||||
"outputCapture": "std"
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"name": "App (Electron)",
|
||||
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron",
|
||||
"windows": {
|
||||
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron.cmd",
|
||||
"env": {
|
||||
"NODE_ENV": "development",
|
||||
"NODE_PRESERVE_SYMLINKS": "1"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"program": "${workspaceRoot}/electron-app/src-gen/frontend/electron-main.js",
|
||||
"protocol": "inspector",
|
||||
"args": [
|
||||
"--log-level=debug",
|
||||
"--hostname=localhost",
|
||||
"--no-cluster",
|
||||
"--remote-debugging-port=9222",
|
||||
"--no-app-auto-install"
|
||||
],
|
||||
"env": {
|
||||
"NODE_ENV": "development"
|
||||
},
|
||||
"sourceMaps": true,
|
||||
"outFiles": [
|
||||
"${workspaceRoot}/electron-app/src-gen/backend/*.js",
|
||||
"${workspaceRoot}/electron-app/src-gen/frontend/*.js",
|
||||
"${workspaceRoot}/electron-app/lib/**/*.js",
|
||||
"${workspaceRoot}/arduino-ide-extension/*/lib/**/*.js"
|
||||
],
|
||||
"smartStep": true,
|
||||
"internalConsoleOptions": "openOnSessionStart",
|
||||
"outputCapture": "std"
|
||||
},
|
||||
{
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"name": "App (Browser)",
|
||||
"program": "${workspaceRoot}/browser-app/src-gen/backend/main.js",
|
||||
"args": [
|
||||
"--hostname=0.0.0.0",
|
||||
"--port=3000",
|
||||
"--no-cluster",
|
||||
"--no-app-auto-install"
|
||||
],
|
||||
"windows": {
|
||||
"env": {
|
||||
"NODE_ENV": "development",
|
||||
"NODE_PRESERVE_SYMLINKS": "1"
|
||||
}
|
||||
},
|
||||
"env": {
|
||||
"NODE_ENV": "development"
|
||||
},
|
||||
"sourceMaps": true,
|
||||
"outFiles": [
|
||||
"${workspaceRoot}/browser-app/src-gen/backend/*.js",
|
||||
"${workspaceRoot}/browser-app/lib/**/*.js",
|
||||
"${workspaceRoot}/arduino-ide-extension/*/lib/**/*.js"
|
||||
],
|
||||
"smartStep": true,
|
||||
"internalConsoleOptions": "openOnSessionStart",
|
||||
"outputCapture": "std"
|
||||
},
|
||||
{
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"name": "App (Browser - Debug CLI daemon)",
|
||||
"program": "${workspaceRoot}/browser-app/src-gen/backend/main.js",
|
||||
"args": [
|
||||
"--hostname=0.0.0.0",
|
||||
"--port=3000",
|
||||
"--no-cluster",
|
||||
"--no-app-auto-install",
|
||||
"--debug-cli=true"
|
||||
],
|
||||
"env": {
|
||||
"NODE_ENV": "development"
|
||||
},
|
||||
"sourceMaps": true,
|
||||
"outFiles": [
|
||||
"${workspaceRoot}/browser-app/src-gen/backend/*.js",
|
||||
"${workspaceRoot}/browser-app/lib/**/*.js",
|
||||
"${workspaceRoot}/arduino-ide-extension/*/lib/**/*.js"
|
||||
],
|
||||
"smartStep": true,
|
||||
"internalConsoleOptions": "openOnSessionStart",
|
||||
"outputCapture": "std"
|
||||
},
|
||||
{
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"name": "Packager",
|
||||
"program": "${workspaceRoot}/electron/packager/index.js",
|
||||
"cwd": "${workspaceFolder}/electron/packager"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
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,4 +1,4 @@
|
||||
FROM gitpod/workspace-full
|
||||
FROM gitpod/workspace-full-vnc
|
||||
|
||||
USER root
|
||||
RUN apt-get update -q --fix-missing && \
|
||||
@@ -7,7 +7,8 @@ RUN apt-get update -q --fix-missing && \
|
||||
build-essential \
|
||||
libssl-dev \
|
||||
golang-go \
|
||||
libxkbfile-dev
|
||||
libxkbfile-dev \
|
||||
libnss3-dev
|
||||
|
||||
RUN set -ex && \
|
||||
tmpdir=$(mktemp -d) && \
|
||||
|
@@ -1,4 +1,4 @@
|
||||
# Arduino IDE PoC
|
||||
# Arduino Pro IDE
|
||||
|
||||
[](https://dev.azure.com/typefox/Arduino/_build/latest?definitionId=4&branchName=master)
|
||||
|
||||
@@ -29,6 +29,9 @@ Then you can start the browser example again:
|
||||
yarn --cwd browser-app start
|
||||
```
|
||||
|
||||
Click [here](./arduino-ide-extension/README.md) for more details on various IDE services, and the Arduino Pro IDE implementation in general.
|
||||
|
||||
|
||||
## Arduino Pro IDE Electron Application
|
||||
The project is built on [Azure DevOps](https://dev.azure.com/typefox/Arduino).
|
||||
|
||||
@@ -41,7 +44,7 @@ You can download the latest release applications fom [here](https://github.com/b
|
||||
If you want to get a nightly build, go to the [Azure DevOps page](https://dev.azure.com/typefox/Arduino/_build?definitionId=4),
|
||||
and follow the steps from below.
|
||||
|
||||

|
||||

|
||||
<img width="500" src="static/download_01.gif">
|
||||
<img width="500" src="static/download_02.gif">
|
||||
|
||||
Click [here](./electron/README.md) for more details on the CI/CD, the GitHub release, and the build process in general.
|
||||
|
52
arduino-ide-extension/README.md
Normal file
52
arduino-ide-extension/README.md
Normal file
@@ -0,0 +1,52 @@
|
||||
## Arduino IDE Extension
|
||||
|
||||
Arduino Pro IDE is based on Theia, and most of its IDE features, UIs and customizations are implemented in this Theia extension.
|
||||
|
||||
### IDE Services
|
||||
|
||||
IDE services typically have a backend part in [src/node/](./src/node/) and a front-end part in [src/browser/](./src/browser/).
|
||||
|
||||
#### Boards Service
|
||||
|
||||
The Boards Service continuously checks the computer's ports, in order to detect when you connect or disconnect an Arduino board.
|
||||
|
||||
The Boards Manager lists all the known board types, and allows downloading new cores to get additional board types.
|
||||
|
||||
- [src/common/protocol/boards-service.ts](./src/common/protocol/boards-service.ts) implements the common classes and interfaces
|
||||
- [src/node/boards-service-impl.ts](./src/node/boards-service-impl.ts) implements the service backend:
|
||||
- discovering ports & boards
|
||||
- searching for compatible board types
|
||||
- installing new board types
|
||||
- [src/browser/boards/boards-list-widget.ts](./src/browser/boards/boards-service-client-impl.ts) implements the Boards Manager front-end:
|
||||
- browsing/searching available board types
|
||||
- installing new board types
|
||||
|
||||
#### Core Service
|
||||
|
||||
The Core Service is responsible for building your sketches and uploading them to a board.
|
||||
|
||||
- [src/common/protocol/core-service.ts](./src/common/protocol/core-service.ts) implements the common classes and interfaces
|
||||
- [src/node/core-service-impl.ts](./src/node/core-service-impl.ts) implements the service backend:
|
||||
- compiling a sketch for a selected board type
|
||||
- uploading a sketch to a connected board
|
||||
|
||||
#### Monitor Service
|
||||
|
||||
The Monitor Service allows getting information back from sketches running on your Arduino boards.
|
||||
|
||||
- [src/common/protocol/monitor-service.ts](./src/common/protocol/monitor-service.ts) implements the common classes and interfaces
|
||||
- [src/node/monitor-service-impl.ts](./src/node/monitor-service-impl.ts) implements the service backend:
|
||||
- connecting to / disconnecting from a board
|
||||
- receiving and sending data
|
||||
- [src/browser/monitor/monitor-widget.tsx](./src/browser/monitor/monitor-widget.tsx) implements the serial monitor front-end:
|
||||
- viewing the output from a connected board
|
||||
- entering data to send to the board
|
||||
|
||||
#### Config Service
|
||||
|
||||
The Config Service knows about your system, like for example the default sketch locations.
|
||||
|
||||
- [src/common/protocol/config-service.ts](./src/common/protocol/config-service.ts) implements the common classes and interfaces
|
||||
- [src/node/config-service-impl.ts](./src/node/config-service-impl.ts) implements the service backend:
|
||||
- getting the `arduino-cli` version and configuration
|
||||
- checking whether a file is in a data or sketch directory
|
@@ -1,35 +1,39 @@
|
||||
{
|
||||
"name": "arduino-ide-extension",
|
||||
"version": "0.0.2",
|
||||
"version": "0.0.3",
|
||||
"description": "An extension for Theia building the Arduino IDE",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10.10.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@grpc/grpc-js": "^0.4.0",
|
||||
"@grpc/grpc-js": "^0.6.12",
|
||||
"@theia/application-package": "next",
|
||||
"@theia/core": "next",
|
||||
"@theia/cpp": "next",
|
||||
"@theia/editor": "next",
|
||||
"@theia/filesystem": "next",
|
||||
"@theia/git": "next",
|
||||
"@theia/languages": "next",
|
||||
"@theia/markers": "next",
|
||||
"@theia/monaco": "next",
|
||||
"@theia/outline-view": "next",
|
||||
"@theia/workspace": "next",
|
||||
"@theia/navigator": "next",
|
||||
"@theia/terminal": "next",
|
||||
"@theia/outline-view": "next",
|
||||
"@theia/search-in-workspace": "next",
|
||||
"@theia/cpp": "next",
|
||||
"@types/ps-tree": "^1.1.0",
|
||||
"@types/which": "^1.3.1",
|
||||
"@types/react-select": "^3.0.0",
|
||||
"@theia/terminal": "next",
|
||||
"@theia/workspace": "next",
|
||||
"@types/google-protobuf": "^3.7.1",
|
||||
"@types/dateformat": "^3.0.1",
|
||||
"@types/ps-tree": "^1.1.0",
|
||||
"@types/react-select": "^3.0.0",
|
||||
"@types/which": "^1.3.1",
|
||||
"css-element-queries": "^1.2.0",
|
||||
"react-select": "^3.0.4",
|
||||
"dateformat": "^3.0.3",
|
||||
"google-protobuf": "^3.11.0",
|
||||
"p-queue": "^5.0.0",
|
||||
"ps-tree": "^1.2.0",
|
||||
"react-select": "^3.0.4",
|
||||
"semver": "^6.3.0",
|
||||
"string-natural-compare": "^2.0.3",
|
||||
"tree-kill": "^1.2.1",
|
||||
"upath": "^1.1.2",
|
||||
@@ -50,14 +54,14 @@
|
||||
"decompress-targz": "^4.1.1",
|
||||
"decompress-unzip": "^4.0.1",
|
||||
"download": "^7.1.0",
|
||||
"grpc-tools": "^1.7.3",
|
||||
"grpc_tools_node_protoc_ts": "^2.5.0",
|
||||
"grpc-tools": "^1.8.0",
|
||||
"grpc_tools_node_protoc_ts": "^2.5.8",
|
||||
"moment": "^2.24.0",
|
||||
"ncp": "^2.0.0",
|
||||
"rimraf": "^2.6.1",
|
||||
"shelljs": "^0.8.3",
|
||||
"tslint": "^5.5.0",
|
||||
"typescript": "2.9.1",
|
||||
"typescript": "3.5.3",
|
||||
"uuid": "^3.2.1",
|
||||
"yargs": "^11.1.0"
|
||||
},
|
||||
@@ -77,4 +81,4 @@
|
||||
"frontendElectron": "lib/electron-browser/electron-arduino-menu-module"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@@ -3,7 +3,7 @@ import { injectable, inject, postConstruct } from 'inversify';
|
||||
import URI from '@theia/core/lib/common/uri';
|
||||
import { EditorWidget } from '@theia/editor/lib/browser/editor-widget';
|
||||
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 { BoardsService } from '../common/protocol/boards-service';
|
||||
import { ArduinoCommands } from './arduino-commands';
|
||||
@@ -22,9 +22,11 @@ import {
|
||||
OpenerService,
|
||||
Widget,
|
||||
StatusBar,
|
||||
ShellLayoutRestorer,
|
||||
StatusBarAlignment,
|
||||
QuickOpenService
|
||||
QuickOpenService,
|
||||
ApplicationShell,
|
||||
FrontendApplicationContribution,
|
||||
FrontendApplication
|
||||
} from '@theia/core/lib/browser';
|
||||
import { OpenFileDialogProps, FileDialogService } from '@theia/filesystem/lib/browser/file-dialog';
|
||||
import { FileSystem, FileStat } from '@theia/filesystem/lib/common';
|
||||
@@ -39,11 +41,18 @@ import { MaybePromise } from '@theia/core/lib/common/types';
|
||||
import { BoardsConfigDialog } from './boards/boards-config-dialog';
|
||||
import { BoardsToolBarItem } from './boards/boards-toolbar-item';
|
||||
import { BoardsConfig } from './boards/boards-config';
|
||||
import { MonitorService } from '../common/protocol/monitor-service';
|
||||
import { ConfigService } from '../common/protocol/config-service';
|
||||
import { MonitorConnection } from './monitor/monitor-connection';
|
||||
import { MonitorViewContribution } from './monitor/monitor-view-contribution';
|
||||
import { ArduinoWorkspaceService } from './arduino-workspace-service';
|
||||
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 const SKETCH = [...MAIN_MENU_BAR, '3_sketch'];
|
||||
@@ -57,16 +66,8 @@ export namespace ArduinoToolbarContextMenu {
|
||||
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()
|
||||
export class ArduinoFrontendContribution implements TabBarToolbarContribution, CommandContribution, MenuContribution {
|
||||
export class ArduinoFrontendContribution implements FrontendApplicationContribution, TabBarToolbarContribution, CommandContribution, MenuContribution {
|
||||
|
||||
@inject(MessageService)
|
||||
protected readonly messageService: MessageService;
|
||||
@@ -77,9 +78,6 @@ export class ArduinoFrontendContribution implements TabBarToolbarContribution, C
|
||||
@inject(CoreService)
|
||||
protected readonly coreService: CoreService;
|
||||
|
||||
@inject(MonitorService)
|
||||
protected readonly monitorService: MonitorService;
|
||||
|
||||
@inject(WorkspaceServiceExt)
|
||||
protected readonly workspaceServiceExt: WorkspaceServiceExt;
|
||||
|
||||
@@ -126,13 +124,13 @@ export class ArduinoFrontendContribution implements TabBarToolbarContribution, C
|
||||
protected readonly menuRegistry: MenuModelRegistry;
|
||||
|
||||
@inject(CommandRegistry)
|
||||
protected readonly commands: CommandRegistry;
|
||||
protected readonly commandRegistry: CommandRegistry;
|
||||
|
||||
@inject(StatusBar)
|
||||
protected readonly statusBar: StatusBar;
|
||||
|
||||
@inject(ShellLayoutRestorer)
|
||||
protected readonly layoutRestorer: ShellLayoutRestorer;
|
||||
@inject(ArduinoShellLayoutRestorer)
|
||||
protected readonly layoutRestorer: ArduinoShellLayoutRestorer;
|
||||
|
||||
@inject(QuickOpenService)
|
||||
protected readonly quickOpenService: QuickOpenService;
|
||||
@@ -146,8 +144,29 @@ export class ArduinoFrontendContribution implements TabBarToolbarContribution, C
|
||||
@inject(MonitorConnection)
|
||||
protected readonly monitorConnection: MonitorConnection;
|
||||
|
||||
protected boardsToolbarItem: BoardsToolBarItem | null;
|
||||
protected wsSketchCount: number = 0;
|
||||
@inject(ApplicationShell)
|
||||
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()
|
||||
protected async init(): Promise<void> {
|
||||
@@ -171,6 +190,22 @@ export class ArduinoFrontendContribution implements TabBarToolbarContribution, C
|
||||
]).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 {
|
||||
registry.registerItem({
|
||||
id: ArduinoCommands.VERIFY.id,
|
||||
@@ -196,8 +231,7 @@ export class ArduinoFrontendContribution implements TabBarToolbarContribution, C
|
||||
id: BoardsToolBarItem.TOOLBAR_ID,
|
||||
render: () => <BoardsToolBarItem
|
||||
key='boardsToolbarItem'
|
||||
ref={ref => this.boardsToolbarItem = ref}
|
||||
commands={this.commands}
|
||||
commands={this.commandRegistry}
|
||||
boardsServiceClient={this.boardsServiceClient}
|
||||
boardService={this.boardsService} />,
|
||||
isVisible: widget => ArduinoToolbar.is(widget) && widget.side === 'left'
|
||||
@@ -213,12 +247,48 @@ export class ArduinoFrontendContribution implements TabBarToolbarContribution, C
|
||||
id: ArduinoCommands.TOGGLE_ADVANCED_MODE.id,
|
||||
command: ArduinoCommands.TOGGLE_ADVANCED_MODE.id,
|
||||
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'
|
||||
});
|
||||
}
|
||||
|
||||
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, {
|
||||
isVisible: widget => ArduinoToolbar.is(widget) && widget.side === 'left',
|
||||
isEnabled: widget => true,
|
||||
@@ -261,8 +331,10 @@ export class ArduinoFrontendContribution implements TabBarToolbarContribution, C
|
||||
return;
|
||||
}
|
||||
|
||||
const connectionConfig = this.monitorConnection.connectionConfig;
|
||||
await this.monitorConnection.disconnect();
|
||||
const monitorConfig = this.monitorConnection.monitorConfig;
|
||||
if (monitorConfig) {
|
||||
await this.monitorConnection.disconnect();
|
||||
}
|
||||
|
||||
try {
|
||||
const { boardsConfig } = this.boardsServiceClient;
|
||||
@@ -277,8 +349,8 @@ export class ArduinoFrontendContribution implements TabBarToolbarContribution, C
|
||||
} catch (e) {
|
||||
await this.messageService.error(e.toString());
|
||||
} finally {
|
||||
if (connectionConfig) {
|
||||
await this.monitorConnection.connect(connectionConfig);
|
||||
if (monitorConfig) {
|
||||
await this.monitorConnection.connect(monitorConfig);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -296,7 +368,7 @@ export class ArduinoFrontendContribution implements TabBarToolbarContribution, C
|
||||
});
|
||||
}
|
||||
} else {
|
||||
this.commands.executeCommand(ArduinoCommands.OPEN_FILE_NAVIGATOR.id);
|
||||
this.commandRegistry.executeCommand(ArduinoCommands.OPEN_FILE_NAVIGATOR.id);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -342,18 +414,14 @@ export class ArduinoFrontendContribution implements TabBarToolbarContribution, C
|
||||
}
|
||||
})
|
||||
registry.registerCommand(ArduinoCommands.TOGGLE_ADVANCED_MODE, {
|
||||
execute: () => {
|
||||
const oldModeState = ArduinoAdvancedMode.TOGGLED;
|
||||
window.localStorage.setItem(ArduinoAdvancedMode.LS_ID, oldModeState ? 'false' : 'true');
|
||||
registry.executeCommand('reset.layout');
|
||||
},
|
||||
execute: () => this.editorMode.toggle(),
|
||||
isVisible: widget => ArduinoToolbar.is(widget) && widget.side === 'right',
|
||||
isToggled: () => ArduinoAdvancedMode.TOGGLED
|
||||
isToggled: () => this.editorMode.proMode
|
||||
})
|
||||
}
|
||||
|
||||
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.
|
||||
// Such as `Close`, `Close All`, etc.
|
||||
for (const command of [
|
||||
@@ -362,7 +430,8 @@ export class ArduinoFrontendContribution implements TabBarToolbarContribution, C
|
||||
CommonCommands.CLOSE_RIGHT_TABS,
|
||||
CommonCommands.CLOSE_ALL_TABS,
|
||||
CommonCommands.COLLAPSE_PANEL,
|
||||
CommonCommands.TOGGLE_MAXIMIZED
|
||||
CommonCommands.TOGGLE_MAXIMIZED,
|
||||
FileNavigatorCommands.REVEAL_IN_NAVIGATOR
|
||||
]) {
|
||||
registry.unregisterMenuAction(command);
|
||||
}
|
||||
@@ -370,8 +439,6 @@ export class ArduinoFrontendContribution implements TabBarToolbarContribution, C
|
||||
registry.unregisterMenuAction(FileSystemCommands.UPLOAD);
|
||||
registry.unregisterMenuAction(FileDownloadCommands.DOWNLOAD);
|
||||
|
||||
registry.unregisterMenuAction(WorkspaceCommands.NEW_FOLDER);
|
||||
|
||||
registry.unregisterMenuAction(WorkspaceCommands.OPEN_FOLDER);
|
||||
registry.unregisterMenuAction(WorkspaceCommands.OPEN_WORKSPACE);
|
||||
registry.unregisterMenuAction(WorkspaceCommands.OPEN_RECENT_WORKSPACE);
|
||||
@@ -425,8 +492,8 @@ export class ArduinoFrontendContribution implements TabBarToolbarContribution, C
|
||||
const command: Command = {
|
||||
id: 'openSketch' + sketch.name
|
||||
}
|
||||
this.commands.registerCommand(command, {
|
||||
execute: () => this.commands.executeCommand(ArduinoCommands.OPEN_SKETCH.id, sketch)
|
||||
this.commandRegistry.registerCommand(command, {
|
||||
execute: () => this.commandRegistry.executeCommand(ArduinoCommands.OPEN_SKETCH.id, sketch)
|
||||
});
|
||||
|
||||
registry.registerMenuAction(ArduinoToolbarContextMenu.WS_SKETCHES_GROUP, {
|
||||
@@ -466,7 +533,7 @@ export class ArduinoFrontendContribution implements TabBarToolbarContribution, C
|
||||
if (destinationFile && !destinationFile.isDirectory) {
|
||||
const message = await this.validate(destinationFile);
|
||||
if (!message) {
|
||||
await this.workspaceService.open(destinationFileUri);
|
||||
this.workspaceService.open(destinationFileUri);
|
||||
return destinationFileUri;
|
||||
} else {
|
||||
this.messageService.warn(message);
|
||||
|
@@ -10,7 +10,7 @@ import { LanguageGrammarDefinitionContribution } from '@theia/monaco/lib/browser
|
||||
import { LanguageClientContribution } from '@theia/languages/lib/browser';
|
||||
import { ArduinoLanguageClientContribution } from './language/arduino-language-client-contribution';
|
||||
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 { LibraryService, LibraryServicePath } from '../common/protocol/library-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 { MenuContribution } from '@theia/core';
|
||||
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 { SilentProblemContribution } from './customization/silent-problem-contribution';
|
||||
import { SilentNavigatorContribution } from './customization/silent-navigator-contribution';
|
||||
import { ArduinoProblemContribution } from './customization/arduino-problem-contribution';
|
||||
import { ArduinoNavigatorContribution } from './customization/arduino-navigator-contribution';
|
||||
import { FileNavigatorContribution } from '@theia/navigator/lib/browser/navigator-contribution';
|
||||
import { ArduinoToolbarContribution } from './toolbar/arduino-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 { ArduinoEditorContribution } from './customization/arduino-editor-contribution';
|
||||
import { MonacoStatusBarContribution } from '@theia/monaco/lib/browser/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 { ArduinoFrontendApplication } from './customization/arduino-frontend-application';
|
||||
import { BoardsConfigDialog, BoardsConfigDialogProps } from './boards/boards-config-dialog';
|
||||
import { BoardsConfigDialogWidget } from './boards/boards-config-dialog-widget';
|
||||
import { ScmContribution } from '@theia/scm/lib/browser/scm-contribution';
|
||||
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 { 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 { LibraryItemRenderer } from './library/library-item-renderer';
|
||||
import { BoardItemRenderer } from './boards/boards-item-renderer';
|
||||
import { MonitorServiceClientImpl } from './monitor/monitor-service-client-impl';
|
||||
import { MonitorServicePath, MonitorService, MonitorServiceClient } from '../common/protocol/monitor-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 { AboutDialog } from '@theia/core/lib/browser/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');
|
||||
|
||||
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(LanguageClientContribution).to(ArduinoLanguageClientContribution).inSingletonScope();
|
||||
|
||||
// Renderer for both the library and the core widgets.
|
||||
bind(ListItemRenderer).toSelf().inSingletonScope();
|
||||
|
||||
// Library service
|
||||
bind(LibraryService).toDynamicValue(context => WebSocketConnectionProvider.createProxy(context.container, LibraryServicePath)).inSingletonScope();
|
||||
|
||||
// Library list widget
|
||||
bind(LibraryListWidget).toSelf();
|
||||
bindViewContribution(bind, LibraryListWidgetFrontendContribution);
|
||||
@@ -101,7 +104,6 @@ export default new ContainerModule((bind: interfaces.Bind, unbind: interfaces.Un
|
||||
createWidget: () => context.container.get(LibraryListWidget)
|
||||
}));
|
||||
bind(FrontendApplicationContribution).toService(LibraryListWidgetFrontendContribution);
|
||||
bind(LibraryItemRenderer).toSelf().inSingletonScope();
|
||||
|
||||
// Sketch list service
|
||||
bind(SketchesService).toDynamicValue(context => WebSocketConnectionProvider.createProxy(context.container, SketchesServicePath)).inSingletonScope();
|
||||
@@ -117,6 +119,7 @@ export default new ContainerModule((bind: interfaces.Bind, unbind: interfaces.Un
|
||||
}).inSingletonScope();
|
||||
// Boards service client to receive and delegate notifications from the backend.
|
||||
bind(BoardsServiceClientImpl).toSelf().inSingletonScope();
|
||||
bind(FrontendApplicationContribution).toService(BoardsServiceClientImpl);
|
||||
bind(BoardsServiceClient).toDynamicValue(context => {
|
||||
const client = context.container.get(BoardsServiceClientImpl);
|
||||
WebSocketConnectionProvider.createProxy(context.container, BoardsServicePath, client);
|
||||
@@ -135,7 +138,6 @@ export default new ContainerModule((bind: interfaces.Bind, unbind: interfaces.Un
|
||||
createWidget: () => context.container.get(BoardsListWidget)
|
||||
}));
|
||||
bind(FrontendApplicationContribution).toService(BoardsListWidgetFrontendContribution);
|
||||
bind(BoardItemRenderer).toSelf().inSingletonScope();
|
||||
|
||||
// Board select dialog
|
||||
bind(BoardsConfigDialogWidget).toSelf().inSingletonScope();
|
||||
@@ -158,7 +160,7 @@ export default new ContainerModule((bind: interfaces.Bind, unbind: interfaces.Un
|
||||
}).inSingletonScope();
|
||||
|
||||
// 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);
|
||||
// Eagerly active the core, library, and boards services.
|
||||
container.get(CoreService);
|
||||
@@ -170,6 +172,7 @@ export default new ContainerModule((bind: interfaces.Bind, unbind: interfaces.Un
|
||||
|
||||
// Serial Monitor
|
||||
bind(MonitorModel).toSelf().inSingletonScope();
|
||||
bind(FrontendApplicationContribution).toService(MonitorModel);
|
||||
bind(MonitorWidget).toSelf();
|
||||
bindViewContribution(bind, MonitorViewContribution);
|
||||
bind(TabBarToolbarContribution).toService(MonitorViewContribution);
|
||||
@@ -198,50 +201,37 @@ export default new ContainerModule((bind: interfaces.Bind, unbind: interfaces.Un
|
||||
const themeService = ThemeService.get();
|
||||
themeService.register(...ArduinoTheme.themes);
|
||||
|
||||
// Customizing default Theia layout
|
||||
if (!ArduinoAdvancedMode.TOGGLED) {
|
||||
unbind(OutlineViewContribution);
|
||||
bind(OutlineViewContribution).to(SilentOutlineViewContribution).inSingletonScope();
|
||||
unbind(ProblemContribution);
|
||||
bind(ProblemContribution).to(SilentProblemContribution).inSingletonScope();
|
||||
unbind(FileNavigatorContribution);
|
||||
bind(FileNavigatorContribution).to(SilentNavigatorContribution).inSingletonScope();
|
||||
unbind(OutputToolbarContribution);
|
||||
bind(OutputToolbarContribution).to(ArduinoOutputToolContribution).inSingletonScope();
|
||||
unbind(EditorContribution);
|
||||
bind(EditorContribution).to(ArduinoEditorContribution).inSingletonScope();
|
||||
unbind(MonacoStatusBarContribution);
|
||||
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();
|
||||
// Customizing default Theia layout based on the editor mode: `pro-mode` or `classic`.
|
||||
bind(EditorMode).toSelf().inSingletonScope();
|
||||
bind(FrontendApplicationContribution).toService(EditorMode);
|
||||
rebind(OutlineViewContribution).to(ArduinoOutlineViewContribution).inSingletonScope();
|
||||
rebind(ProblemContribution).to(ArduinoProblemContribution).inSingletonScope();
|
||||
rebind(FileNavigatorContribution).to(ArduinoNavigatorContribution).inSingletonScope();
|
||||
rebind(OutputToolbarContribution).to(ArduinoOutputToolContribution).inSingletonScope();
|
||||
rebind(EditorContribution).to(ArduinoEditorContribution).inSingletonScope();
|
||||
rebind(MonacoStatusBarContribution).to(ArduinoMonacoStatusBarContribution).inSingletonScope();
|
||||
rebind(ApplicationShell).to(ArduinoApplicationShell).inSingletonScope();
|
||||
rebind(ScmContribution).to(ArduinoScmContribution).inSingletonScope();
|
||||
rebind(SearchInWorkspaceFrontendContribution).to(ArduinoSearchInWorkspaceContribution).inSingletonScope();
|
||||
rebind(FrontendApplication).to(ArduinoFrontendApplication).inSingletonScope();
|
||||
|
||||
// Monaco customizations
|
||||
unbind(MonacoEditorProvider);
|
||||
bind(ArduinoMonacoEditorProvider).toSelf().inSingletonScope();
|
||||
bind(MonacoEditorProvider).toService(ArduinoMonacoEditorProvider);
|
||||
rebind(MonacoEditorProvider).toService(ArduinoMonacoEditorProvider);
|
||||
|
||||
// Decorator customizations
|
||||
unbind(TabBarDecoratorService);
|
||||
bind(ArduinoTabBarDecoratorService).toSelf().inSingletonScope();
|
||||
bind(TabBarDecoratorService).toService(ArduinoTabBarDecoratorService);
|
||||
rebind(TabBarDecoratorService).toService(ArduinoTabBarDecoratorService);
|
||||
|
||||
// Problem markers
|
||||
unbind(ProblemManager);
|
||||
bind(ArduinoProblemManager).toSelf().inSingletonScope();
|
||||
bind(ProblemManager).toService(ArduinoProblemManager);
|
||||
rebind(ProblemManager).toService(ArduinoProblemManager);
|
||||
|
||||
// About dialog to show the CLI version
|
||||
unbind(AboutDialog);
|
||||
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 {
|
||||
|
||||
static readonly arduino: Theme = {
|
||||
type: 'light',
|
||||
id: 'arduino-theme',
|
||||
label: '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 { SketchesService } from '../common/protocol/sketches-service';
|
||||
import { ArduinoWorkspaceRootResolver } from './arduino-workspace-resolver';
|
||||
import { ArduinoAdvancedMode } from './arduino-frontend-contribution';
|
||||
import { EditorMode } from './editor-mode';
|
||||
|
||||
@injectable()
|
||||
export class ArduinoWorkspaceService extends WorkspaceService {
|
||||
@@ -18,7 +18,10 @@ export class ArduinoWorkspaceService extends WorkspaceService {
|
||||
@inject(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([
|
||||
window.location.hash,
|
||||
this.sketchService.getSketches().then(sketches => sketches.map(({ uri }) => uri)),
|
||||
@@ -36,7 +39,8 @@ export class ArduinoWorkspaceService extends WorkspaceService {
|
||||
await this.server.setMostRecentlyUsedWorkspace(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> {
|
||||
@@ -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 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;
|
||||
}
|
||||
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 { BoardsServiceClientImpl } from './boards-service-client-impl';
|
||||
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';
|
||||
|
||||
|
||||
@@ -41,23 +41,23 @@ export class BoardsAutoInstaller implements FrontendApplicationContribution {
|
||||
.filter(({ installable, installedVersion }) => installable && !installedVersion);
|
||||
for (const candidate of candidates) {
|
||||
// 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?`, 'Install Manually', 'Yes').then(async answer => {
|
||||
if (answer === 'Yes') {
|
||||
const dialog = new InstallationProgressDialog(candidate.name);
|
||||
const dialog = new InstallationProgressDialog(candidate.name, candidate.availableVersions[0]);
|
||||
dialog.open();
|
||||
try {
|
||||
await this.boardsService.install(candidate);
|
||||
await this.boardsService.install({ item: candidate });
|
||||
} finally {
|
||||
dialog.close();
|
||||
}
|
||||
}
|
||||
if (answer) {
|
||||
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 { BoardPackage, BoardsService } from '../../common/protocol/boards-service';
|
||||
import { ListWidget } from '../components/component-list/list-widget';
|
||||
import { BoardItemRenderer } from './boards-item-renderer';
|
||||
import { ListItemRenderer } from '../components/component-list/list-item-renderer';
|
||||
|
||||
@injectable()
|
||||
export class BoardsListWidget extends ListWidget<BoardPackage> {
|
||||
@@ -11,7 +11,7 @@ export class BoardsListWidget extends ListWidget<BoardPackage> {
|
||||
|
||||
constructor(
|
||||
@inject(BoardsService) protected service: BoardsService,
|
||||
@inject(BoardItemRenderer) protected itemRenderer: BoardItemRenderer) {
|
||||
@inject(ListItemRenderer) protected itemRenderer: ListItemRenderer<BoardPackage>) {
|
||||
|
||||
super({
|
||||
id: BoardsListWidget.WIDGET_ID,
|
||||
|
@@ -1,21 +1,27 @@
|
||||
import { injectable, inject, postConstruct } from 'inversify';
|
||||
import { injectable, inject } from 'inversify';
|
||||
import { Emitter } from '@theia/core/lib/common/event';
|
||||
import { ILogger } from '@theia/core/lib/common/logger';
|
||||
import { MessageService } from '@theia/core/lib/common/message-service';
|
||||
import { LocalStorageService } from '@theia/core/lib/browser/storage-service';
|
||||
import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application';
|
||||
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';
|
||||
|
||||
@injectable()
|
||||
export class BoardsServiceClientImpl implements BoardsServiceClient {
|
||||
export class BoardsServiceClientImpl implements BoardsServiceClient, FrontendApplicationContribution {
|
||||
|
||||
@inject(ILogger)
|
||||
protected logger: ILogger;
|
||||
|
||||
@inject(MessageService)
|
||||
protected messageService: MessageService;
|
||||
|
||||
@inject(LocalStorageService)
|
||||
protected storageService: LocalStorageService;
|
||||
|
||||
protected readonly onBoardInstalledEmitter = new Emitter<BoardInstalledEvent>();
|
||||
protected readonly onBoardUninstalledEmitter = new Emitter<BoardUninstalledEvent>();
|
||||
protected readonly onAttachedBoardsChangedEmitter = new Emitter<AttachedBoardsChangeEvent>();
|
||||
protected readonly onSelectedBoardsConfigChangedEmitter = new Emitter<BoardsConfig.Config>();
|
||||
|
||||
@@ -31,11 +37,11 @@ export class BoardsServiceClientImpl implements BoardsServiceClient {
|
||||
|
||||
readonly onBoardsChanged = this.onAttachedBoardsChangedEmitter.event;
|
||||
readonly onBoardInstalled = this.onBoardInstalledEmitter.event;
|
||||
readonly onBoardUninstalled = this.onBoardUninstalledEmitter.event;
|
||||
readonly onBoardsConfigChanged = this.onSelectedBoardsConfigChangedEmitter.event;
|
||||
|
||||
@postConstruct()
|
||||
protected init(): void {
|
||||
this.loadState();
|
||||
async onStart(): Promise<void> {
|
||||
return this.loadState();
|
||||
}
|
||||
|
||||
notifyAttachedBoardsChanged(event: AttachedBoardsChangeEvent): void {
|
||||
@@ -87,6 +93,11 @@ export class BoardsServiceClientImpl implements BoardsServiceClient {
|
||||
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) {
|
||||
this.logger.info('Board config changed: ', JSON.stringify(config));
|
||||
this._boardsConfig = config;
|
||||
@@ -100,6 +111,56 @@ export class BoardsServiceClientImpl implements BoardsServiceClient {
|
||||
return this._boardsConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* `true` if the `config.selectedBoard` is defined; hence can compile against the board. Otherwise, `false`.
|
||||
*/
|
||||
canVerify(
|
||||
config: BoardsConfig.Config | undefined = this.boardsConfig,
|
||||
options: { silent: boolean } = { silent: true }): config is BoardsConfig.Config & { selectedBoard: Board } {
|
||||
|
||||
if (!config) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!config.selectedBoard) {
|
||||
if (!options.silent) {
|
||||
this.messageService.warn('No boards selected.', { timeout: 3000 });
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* `true` if the `canVerify` and the `config.selectedPort` is also set with FQBN, hence can upload to board. Otherwise, `false`.
|
||||
*/
|
||||
canUploadTo(
|
||||
config: BoardsConfig.Config | undefined = this.boardsConfig,
|
||||
options: { silent: boolean } = { silent: true }): config is RecursiveRequired<BoardsConfig.Config> {
|
||||
|
||||
if (!this.canVerify(config, options)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const { name } = config.selectedBoard;
|
||||
if (!config.selectedPort) {
|
||||
if (!options.silent) {
|
||||
this.messageService.warn(`No ports selected for board: '${name}'.`, { timeout: 3000 });
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!config.selectedBoard.fqbn) {
|
||||
if (!options.silent) {
|
||||
this.messageService.warn(`The FQBN is not available for the selected board ${name}. Do you have the corresponding core installed?`, { timeout: 3000 });
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected saveState(): Promise<void> {
|
||||
return this.storageService.setData('latest-valid-boards-config', this.latestValidBoardsConfig);
|
||||
}
|
||||
@@ -108,15 +169,10 @@ export class BoardsServiceClientImpl implements BoardsServiceClient {
|
||||
const storedValidBoardsConfig = await this.storageService.getData<RecursiveRequired<BoardsConfig.Config>>('latest-valid-boards-config');
|
||||
if (storedValidBoardsConfig) {
|
||||
this.latestValidBoardsConfig = storedValidBoardsConfig;
|
||||
if (this.canUploadTo(this.latestValidBoardsConfig)) {
|
||||
this.boardsConfig = this.latestValidBoardsConfig;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected canVerify(config: BoardsConfig.Config | undefined): config is BoardsConfig.Config & { selectedBoard: Board } {
|
||||
return !!config && !!config.selectedBoard;
|
||||
}
|
||||
|
||||
protected canUploadTo(config: BoardsConfig.Config | undefined): config is RecursiveRequired<BoardsConfig.Config> {
|
||||
return this.canVerify(config) && !!config.selectedPort && !!config.selectedBoard.fqbn;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -0,0 +1,62 @@
|
||||
import * as React from 'react';
|
||||
import Select from 'react-select';
|
||||
import { Styles } from 'react-select/src/styles';
|
||||
import { Props } from 'react-select/src/components';
|
||||
import { ThemeConfig } from 'react-select/src/theme';
|
||||
|
||||
export class ArduinoSelect<T> extends Select<T> {
|
||||
|
||||
constructor(props: Readonly<Props<T>>) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render(): React.ReactNode {
|
||||
const controlHeight = 27; // from `monitor.css` -> `.serial-monitor-container .head` (`height: 27px;`)
|
||||
const styles: Styles = {
|
||||
control: styles => ({
|
||||
...styles,
|
||||
minWidth: 120,
|
||||
color: 'var(--theia-ui-font-color1)'
|
||||
}),
|
||||
dropdownIndicator: styles => ({
|
||||
...styles,
|
||||
padding: 0
|
||||
}),
|
||||
indicatorSeparator: () => ({
|
||||
display: 'none'
|
||||
}),
|
||||
indicatorsContainer: () => ({
|
||||
padding: '0px 5px'
|
||||
}),
|
||||
menu: styles => ({
|
||||
...styles,
|
||||
marginTop: 0
|
||||
})
|
||||
};
|
||||
const theme: ThemeConfig = theme => ({
|
||||
...theme,
|
||||
borderRadius: 0,
|
||||
spacing: {
|
||||
controlHeight,
|
||||
baseUnit: 2,
|
||||
menuGutter: 4
|
||||
}, colors: {
|
||||
...theme.colors,
|
||||
// `primary50`??? it's crazy but apparently, without this, we would get a light-blueish
|
||||
// color when selecting an option in the select by clicking and then not releasing the button.
|
||||
// https://react-select.com/styles#overriding-the-theme
|
||||
primary50: 'var(--theia-accent-color4)',
|
||||
}
|
||||
});
|
||||
const DropdownIndicator = () => <span className='fa fa-caret-down caret' />;
|
||||
return <Select
|
||||
{...this.props}
|
||||
components={{ DropdownIndicator }}
|
||||
theme={theme}
|
||||
styles={styles}
|
||||
classNamePrefix='arduino-select'
|
||||
isSearchable={false}
|
||||
/>
|
||||
}
|
||||
|
||||
}
|
@@ -1,25 +1,66 @@
|
||||
import * as React from 'react';
|
||||
import { Installable } from '../../../common/protocol/installable';
|
||||
import { ArduinoComponent } from '../../../common/protocol/arduino-component';
|
||||
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> {
|
||||
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 {
|
||||
const { item, itemRenderer, install } = this.props;
|
||||
return itemRenderer.renderItem(item, install.bind(this));
|
||||
const { item, itemRenderer } = this.props;
|
||||
return itemRenderer.renderItem(
|
||||
Object.assign(this.state, { item }),
|
||||
this.install.bind(this),
|
||||
this.uninstall.bind(this),
|
||||
this.onVersionChange.bind(this)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export namespace ComponentListItem {
|
||||
|
||||
export interface Props<T> {
|
||||
export interface Props<T extends ArduinoComponent> {
|
||||
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>;
|
||||
}
|
||||
|
||||
export interface State {
|
||||
selectedVersion?: Installable.Version;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -1,8 +1,10 @@
|
||||
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 { 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;
|
||||
|
||||
@@ -29,18 +31,20 @@ export class ComponentList<T> extends React.Component<ComponentList.Props<T>> {
|
||||
key={this.props.itemLabel(item)}
|
||||
item={item}
|
||||
itemRenderer={this.props.itemRenderer}
|
||||
install={this.props.install} />
|
||||
install={this.props.install}
|
||||
uninstall={this.props.uninstall} />
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export namespace ComponentList {
|
||||
|
||||
export interface Props<T> {
|
||||
export interface Props<T extends ArduinoComponent> {
|
||||
readonly items: T[];
|
||||
readonly itemLabel: (item: T) => string;
|
||||
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;
|
||||
}
|
||||
|
||||
|
@@ -1,14 +1,17 @@
|
||||
import * as React from 'react';
|
||||
import debounce = require('lodash.debounce');
|
||||
import { Event } from '@theia/core/lib/common/event';
|
||||
import { ConfirmDialog } from '@theia/core/lib/browser/dialogs';
|
||||
import { Searchable } from '../../../common/protocol/searchable';
|
||||
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 { ListWidget } from './list-widget';
|
||||
import { ComponentList } from './component-list';
|
||||
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>>) {
|
||||
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.handleFilterTextChange('');
|
||||
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 {
|
||||
return <div className={'filterable-list-container'}>
|
||||
{this.renderSearchFilter()}
|
||||
@@ -51,6 +60,7 @@ export class FilterableListContainer<T> extends React.Component<FilterableListCo
|
||||
itemLabel={itemLabel}
|
||||
itemRenderer={itemRenderer}
|
||||
install={this.install.bind(this)}
|
||||
uninstall={this.uninstall.bind(this)}
|
||||
resolveContainer={resolveContainer}
|
||||
/>
|
||||
}
|
||||
@@ -75,12 +85,34 @@ export class FilterableListContainer<T> extends React.Component<FilterableListCo
|
||||
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 dialog = new InstallationProgressDialog(itemLabel(item));
|
||||
const dialog = new InstallationProgressDialog(itemLabel(item), version);
|
||||
dialog.open();
|
||||
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 });
|
||||
this.setState({ items: this.sort(items) });
|
||||
} finally {
|
||||
@@ -92,7 +124,8 @@ export class FilterableListContainer<T> extends React.Component<FilterableListCo
|
||||
|
||||
export namespace FilterableListContainer {
|
||||
|
||||
export interface Props<T> {
|
||||
export interface Props<T extends ArduinoComponent> {
|
||||
readonly container: ListWidget<T>;
|
||||
readonly installable: Installable<T>;
|
||||
readonly searchable: Searchable<T>;
|
||||
readonly itemLabel: (item: T) => string;
|
||||
|
@@ -1,14 +1,17 @@
|
||||
import * as React from 'react';
|
||||
import { inject, injectable } from 'inversify';
|
||||
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()
|
||||
export abstract class ListItemRenderer<T> {
|
||||
export class ListItemRenderer<T extends ArduinoComponent> {
|
||||
|
||||
@inject(WindowService)
|
||||
protected windowService: WindowService;
|
||||
|
||||
protected onClick = (event: React.SyntheticEvent<HTMLAnchorElement, Event>) => {
|
||||
protected onMoreInfoClick = (event: React.SyntheticEvent<HTMLAnchorElement, Event>) => {
|
||||
const { target } = event.nativeEvent;
|
||||
if (target instanceof HTMLAnchorElement) {
|
||||
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 { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application';
|
||||
import { AbstractViewContribution } from '@theia/core/lib/browser/shell/view-contribution';
|
||||
import { ArduinoComponent } from '../../../common/protocol/arduino-component';
|
||||
import { ListWidget } from './list-widget';
|
||||
|
||||
@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> {
|
||||
}
|
||||
|
@@ -7,11 +7,12 @@ import { MaybePromise } from '@theia/core/lib/common/types';
|
||||
import { ReactWidget } from '@theia/core/lib/browser/widgets/react-widget';
|
||||
import { Installable } from '../../../common/protocol/installable';
|
||||
import { Searchable } from '../../../common/protocol/searchable';
|
||||
import { ArduinoComponent } from '../../../common/protocol/arduino-component';
|
||||
import { FilterableListContainer } from './filterable-list-container';
|
||||
import { ListItemRenderer } from './list-item-renderer';
|
||||
|
||||
@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.
|
||||
@@ -61,6 +62,7 @@ export abstract class ListWidget<T> extends ReactWidget {
|
||||
|
||||
render(): React.ReactNode {
|
||||
return <FilterableListContainer<T>
|
||||
container={this}
|
||||
resolveContainer={this.deferredContainer.resolve}
|
||||
resolveFocus={this.onFocusResolved}
|
||||
searchable={this.options.searchable}
|
||||
@@ -74,10 +76,16 @@ export abstract class ListWidget<T> extends ReactWidget {
|
||||
this.deferredContainer.promise.then(() => this.filterTextChangeEmitter.fire(filterText));
|
||||
}
|
||||
|
||||
updateScrollBar(): void {
|
||||
if (this.scrollBar) {
|
||||
this.scrollBar.update();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export namespace ListWidget {
|
||||
export interface Options<T> {
|
||||
export interface Options<T extends ArduinoComponent> {
|
||||
readonly id: string;
|
||||
readonly label: 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 { EditorWidget } from '@theia/editor/lib/browser';
|
||||
import { injectable, inject } from 'inversify';
|
||||
import { EditorMode } from '../editor-mode';
|
||||
|
||||
@injectable()
|
||||
export class ArduinoApplicationShell extends ApplicationShell {
|
||||
|
||||
@inject(EditorMode)
|
||||
protected readonly editorMode: EditorMode;
|
||||
|
||||
protected refreshBottomPanelToggleButton() {
|
||||
if (this.editorMode.proMode) {
|
||||
super.refreshBottomPanelToggleButton();
|
||||
}
|
||||
}
|
||||
|
||||
protected async track(widget: Widget): Promise<void> {
|
||||
const tracker = (this as any).tracker as FocusTracker<Widget>;
|
||||
tracker.add(widget);
|
||||
this.disableClose(Saveable.apply(widget));
|
||||
if (ApplicationShell.TrackableWidgetProvider.is(widget)) {
|
||||
for (const toTrack of await widget.getTrackableWidgets()) {
|
||||
tracker.add(toTrack);
|
||||
this.disableClose(Saveable.apply(toTrack));
|
||||
}
|
||||
if (widget.onDidChangeTrackableWidgets) {
|
||||
widget.onDidChangeTrackableWidgets(widgets => widgets.forEach(w => this.track(w)));
|
||||
if (this.editorMode.proMode) {
|
||||
super.track(widget);
|
||||
} else {
|
||||
const tracker = (this as any).tracker as FocusTracker<Widget>;
|
||||
tracker.add(widget);
|
||||
this.disableClose(Saveable.apply(widget));
|
||||
if (ApplicationShell.TrackableWidgetProvider.is(widget)) {
|
||||
for (const toTrack of await widget.getTrackableWidgets()) {
|
||||
tracker.add(toTrack);
|
||||
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 { WorkspaceService } from '@theia/workspace/lib/browser/workspace-service';
|
||||
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()
|
||||
export class ArduinoFrontendApplication extends FrontendApplication {
|
||||
@@ -16,12 +17,15 @@ export class ArduinoFrontendApplication extends FrontendApplication {
|
||||
@inject(ArduinoFrontendContribution)
|
||||
protected readonly frontendContribution: ArduinoFrontendContribution;
|
||||
|
||||
@inject(EditorMode)
|
||||
protected readonly editorMode: EditorMode;
|
||||
|
||||
protected async initializeLayout(): Promise<void> {
|
||||
super.initializeLayout().then(() => {
|
||||
return super.initializeLayout().then(() => {
|
||||
// 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.
|
||||
// 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 => {
|
||||
for (const root of roots) {
|
||||
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 { injectable } from 'inversify';
|
||||
import { OutputToolbarContribution } from '@theia/output/lib/browser/output-toolbar-contribution';
|
||||
import { EditorMode } from '../editor-mode';
|
||||
|
||||
@injectable()
|
||||
export class ArduinoOutputToolContribution extends OutputToolbarContribution {
|
||||
|
||||
@inject(EditorMode)
|
||||
protected readonly editorMode: EditorMode;
|
||||
|
||||
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';
|
||||
}
|
Before Width: | Height: | Size: 962 B After Width: | Height: | Size: 962 B |
@@ -2,6 +2,7 @@ import { injectable, inject, postConstruct } from 'inversify';
|
||||
import { BaseLanguageClientContribution } from '@theia/languages/lib/browser';
|
||||
import { BoardsServiceClientImpl } from '../boards/boards-service-client-impl';
|
||||
import { BoardsConfig } from '../boards/boards-config';
|
||||
import { Board, BoardPackage } from '../../common/protocol/boards-service';
|
||||
|
||||
@injectable()
|
||||
export class ArduinoLanguageClientContribution extends BaseLanguageClientContribution {
|
||||
@@ -25,6 +26,18 @@ export class ArduinoLanguageClientContribution extends BaseLanguageClientContrib
|
||||
@postConstruct()
|
||||
protected init() {
|
||||
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 {
|
||||
|
@@ -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 { Library, LibraryService } from '../../common/protocol/library-service';
|
||||
import { ListWidget } from '../components/component-list/list-widget';
|
||||
import { LibraryItemRenderer } from './library-item-renderer';
|
||||
import { ListItemRenderer } from '../components/component-list/list-item-renderer';
|
||||
|
||||
@injectable()
|
||||
export class LibraryListWidget extends ListWidget<Library> {
|
||||
@@ -11,7 +11,7 @@ export class LibraryListWidget extends ListWidget<Library> {
|
||||
|
||||
constructor(
|
||||
@inject(LibraryService) protected service: LibraryService,
|
||||
@inject(LibraryItemRenderer) protected itemRenderer: LibraryItemRenderer) {
|
||||
@inject(ListItemRenderer) protected itemRenderer: ListItemRenderer<Library>) {
|
||||
|
||||
super({
|
||||
id: LibraryListWidget.WIDGET_ID,
|
||||
|
@@ -1,53 +1,265 @@
|
||||
import { injectable, inject } from "inversify";
|
||||
import { MonitorService, ConnectionConfig } from "../../common/protocol/monitor-service";
|
||||
import { Emitter, Event } from "@theia/core";
|
||||
import { injectable, inject, postConstruct } from 'inversify';
|
||||
import { Emitter, Event } from '@theia/core/lib/common/event';
|
||||
import { MessageService } from '@theia/core/lib/common/message-service';
|
||||
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
|
||||
import { MonitorService, MonitorConfig, MonitorError, Status, MonitorReadEvent } from '../../common/protocol/monitor-service';
|
||||
import { BoardsServiceClientImpl } from '../boards/boards-service-client-impl';
|
||||
import { Port, Board, BoardsService, AttachedSerialBoard, AttachedBoardsChangeEvent } from '../../common/protocol/boards-service';
|
||||
import { MonitorServiceClientImpl } from './monitor-service-client-impl';
|
||||
import { BoardsConfig } from '../boards/boards-config';
|
||||
import { MonitorModel } from './monitor-model';
|
||||
|
||||
@injectable()
|
||||
export class MonitorConnection {
|
||||
|
||||
@inject(MonitorModel)
|
||||
protected readonly monitorModel: MonitorModel;
|
||||
|
||||
@inject(MonitorService)
|
||||
protected readonly monitorService: MonitorService;
|
||||
|
||||
connectionId: string | undefined;
|
||||
@inject(MonitorServiceClientImpl)
|
||||
protected readonly monitorServiceClient: MonitorServiceClientImpl;
|
||||
|
||||
protected _connectionConfig: ConnectionConfig | undefined;
|
||||
@inject(BoardsService)
|
||||
protected readonly boardsService: BoardsService;
|
||||
|
||||
protected readonly onConnectionChangedEmitter = new Emitter<string | undefined>();
|
||||
readonly onConnectionChanged: Event<string | undefined> = this.onConnectionChangedEmitter.event;
|
||||
@inject(BoardsServiceClientImpl)
|
||||
protected boardsServiceClient: BoardsServiceClientImpl;
|
||||
|
||||
get connectionConfig(): ConnectionConfig | undefined {
|
||||
return this._connectionConfig;
|
||||
@inject(MessageService)
|
||||
protected messageService: MessageService;
|
||||
|
||||
@inject(FrontendApplicationStateService)
|
||||
protected readonly applicationState: FrontendApplicationStateService;
|
||||
|
||||
protected state: MonitorConnection.State | undefined;
|
||||
/**
|
||||
* Note: The idea is to toggle this property from the UI (`Monitor` view)
|
||||
* and the boards config and the boards attachment/detachment logic can be at on place, here.
|
||||
*/
|
||||
protected _autoConnect: boolean = false;
|
||||
protected readonly onConnectionChangedEmitter = new Emitter<MonitorConnection.State | undefined>();
|
||||
/**
|
||||
* This emitter forwards all read events **iff** the connection is established.
|
||||
*/
|
||||
protected readonly onReadEmitter = new Emitter<MonitorReadEvent>();
|
||||
|
||||
/**
|
||||
* Array for storing previous monitor errors received from the server, and based on the number of elements in this array,
|
||||
* we adjust the reconnection delay.
|
||||
* Super naive way: we wait `array.length * 1000` ms. Once we hit 10 errors, we do not try to reconnect and clean the array.
|
||||
*/
|
||||
protected monitorErrors: MonitorError[] = [];
|
||||
protected reconnectTimeout?: number;
|
||||
|
||||
@postConstruct()
|
||||
protected init(): void {
|
||||
// Forward the messages from the board **iff** connected.
|
||||
this.monitorServiceClient.onRead(event => {
|
||||
if (this.connected) {
|
||||
this.onReadEmitter.fire(event);
|
||||
}
|
||||
});
|
||||
this.monitorServiceClient.onError(async error => {
|
||||
let shouldReconnect = false;
|
||||
if (this.state) {
|
||||
const { code, config } = error;
|
||||
const { board, port } = config;
|
||||
const options = { timeout: 3000 };
|
||||
switch (code) {
|
||||
case MonitorError.ErrorCodes.CLIENT_CANCEL: {
|
||||
console.debug(`Connection was canceled by client: ${MonitorConnection.State.toString(this.state)}.`);
|
||||
break;
|
||||
}
|
||||
case MonitorError.ErrorCodes.DEVICE_BUSY: {
|
||||
this.messageService.warn(`Connection failed. Serial port is busy: ${Port.toString(port)}.`, options);
|
||||
shouldReconnect = this.autoConnect;
|
||||
this.monitorErrors.push(error);
|
||||
break;
|
||||
}
|
||||
case MonitorError.ErrorCodes.DEVICE_NOT_CONFIGURED: {
|
||||
this.messageService.info(`Disconnected ${Board.toString(board, { useFqbn: false })} from ${Port.toString(port)}.`, options);
|
||||
break;
|
||||
}
|
||||
case undefined: {
|
||||
this.messageService.error(`Unexpected error. Reconnecting ${Board.toString(board)} on port ${Port.toString(port)}.`, options);
|
||||
console.error(JSON.stringify(error));
|
||||
shouldReconnect = this.connected && this.autoConnect;
|
||||
break;
|
||||
}
|
||||
}
|
||||
const oldState = this.state;
|
||||
this.state = undefined;
|
||||
this.onConnectionChangedEmitter.fire(this.state);
|
||||
if (shouldReconnect) {
|
||||
if (this.monitorErrors.length >= 10) {
|
||||
this.messageService.warn(`Failed to reconnect ${Board.toString(board, { useFqbn: false })} to the the serial-monitor after 10 consecutive attempts. The ${Port.toString(port)} serial port is busy. after 10 consecutive attempts.`);
|
||||
this.monitorErrors.length = 0;
|
||||
} else {
|
||||
const attempts = (this.monitorErrors.length || 1);
|
||||
if (this.reconnectTimeout !== undefined) {
|
||||
// Clear the previous timer.
|
||||
window.clearTimeout(this.reconnectTimeout);
|
||||
}
|
||||
const timeout = attempts * 1000;
|
||||
this.messageService.warn(`Reconnecting ${Board.toString(board, { useFqbn: false })} to ${Port.toString(port)} in ${attempts} seconds...`, { timeout });
|
||||
this.reconnectTimeout = window.setTimeout(() => this.connect(oldState.config), timeout);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
this.boardsServiceClient.onBoardsConfigChanged(this.handleBoardConfigChange.bind(this));
|
||||
this.boardsServiceClient.onBoardsChanged(event => {
|
||||
if (this.autoConnect && this.connected) {
|
||||
const { boardsConfig } = this.boardsServiceClient;
|
||||
if (this.boardsServiceClient.canUploadTo(boardsConfig, { silent: false })) {
|
||||
const { attached } = AttachedBoardsChangeEvent.diff(event);
|
||||
if (attached.boards.some(board => AttachedSerialBoard.is(board) && BoardsConfig.Config.sameAs(boardsConfig, board))) {
|
||||
const { selectedBoard: board, selectedPort: port } = boardsConfig;
|
||||
const { baudRate } = this.monitorModel;
|
||||
this.disconnect()
|
||||
.then(() => this.connect({ board, port, baudRate }));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
// Handles the `baudRate` changes by reconnecting if required.
|
||||
this.monitorModel.onChange(({ property }) => {
|
||||
if (property === 'baudRate' && this.autoConnect && this.connected) {
|
||||
const { boardsConfig } = this.boardsServiceClient;
|
||||
this.handleBoardConfigChange(boardsConfig);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async connect(config: ConnectionConfig): Promise<string | undefined> {
|
||||
if (this.connectionId) {
|
||||
await this.disconnect();
|
||||
}
|
||||
const { connectionId } = await this.monitorService.connect(config);
|
||||
this.connectionId = connectionId;
|
||||
this._connectionConfig = config;
|
||||
|
||||
this.onConnectionChangedEmitter.fire(this.connectionId);
|
||||
|
||||
return connectionId;
|
||||
get connected(): boolean {
|
||||
return !!this.state;
|
||||
}
|
||||
|
||||
async disconnect(): Promise<boolean> {
|
||||
let result = true;
|
||||
const connections = await this.monitorService.getConnectionIds();
|
||||
if (this.connectionId && connections.findIndex(id => id === this.connectionId) >= 0) {
|
||||
console.log('>>> Disposing existing monitor connection before establishing a new one...');
|
||||
result = await this.monitorService.disconnect(this.connectionId);
|
||||
if (!result) {
|
||||
// TODO: better!!!
|
||||
console.error(`Could not close connection: ${this.connectionId}. Check the backend logs.`);
|
||||
} else {
|
||||
console.log(`<<< Disposed ${this.connectionId} connection.`);
|
||||
this.connectionId = undefined;
|
||||
this._connectionConfig = undefined;
|
||||
this.onConnectionChangedEmitter.fire(this.connectionId);
|
||||
get monitorConfig(): MonitorConfig | undefined {
|
||||
return this.state ? this.state.config : undefined;
|
||||
}
|
||||
|
||||
get autoConnect(): boolean {
|
||||
return this._autoConnect;
|
||||
}
|
||||
|
||||
set autoConnect(value: boolean) {
|
||||
const oldValue = this._autoConnect;
|
||||
this._autoConnect = value;
|
||||
// When we enable the auto-connect, we have to connect
|
||||
if (!oldValue && value) {
|
||||
// We have to make sure the previous boards config has been restored.
|
||||
// Otherwise, we might start the auto-connection without configured boards.
|
||||
this.applicationState.reachedState('started_contributions').then(() => {
|
||||
const { boardsConfig } = this.boardsServiceClient;
|
||||
this.handleBoardConfigChange(boardsConfig);
|
||||
});
|
||||
} else if (oldValue && !value) {
|
||||
if (this.reconnectTimeout !== undefined) {
|
||||
window.clearTimeout(this.reconnectTimeout);
|
||||
this.monitorErrors.length = 0;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
async connect(config: MonitorConfig): Promise<Status> {
|
||||
if (this.connected) {
|
||||
const disconnectStatus = await this.disconnect();
|
||||
if (!Status.isOK(disconnectStatus)) {
|
||||
return disconnectStatus;
|
||||
}
|
||||
}
|
||||
console.info(`>>> Creating serial monitor connection for ${Board.toString(config.board)} on port ${Port.toString(config.port)}...`);
|
||||
const connectStatus = await this.monitorService.connect(config);
|
||||
if (Status.isOK(connectStatus)) {
|
||||
this.state = { config };
|
||||
console.info(`<<< Serial monitor connection created for ${Board.toString(config.board, { useFqbn: false })} on port ${Port.toString(config.port)}.`);
|
||||
}
|
||||
this.onConnectionChangedEmitter.fire(this.state);
|
||||
return Status.isOK(connectStatus);
|
||||
}
|
||||
|
||||
async disconnect(): Promise<Status> {
|
||||
if (!this.state) { // XXX: we user `this.state` instead of `this.connected` to make the type checker happy.
|
||||
return Status.OK;
|
||||
}
|
||||
console.log('>>> Disposing existing monitor connection...');
|
||||
const status = await this.monitorService.disconnect();
|
||||
if (Status.isOK(status)) {
|
||||
console.log(`<<< Disposed connection. Was: ${MonitorConnection.State.toString(this.state)}`);
|
||||
} else {
|
||||
console.warn(`<<< Could not dispose connection. Activate connection: ${MonitorConnection.State.toString(this.state)}`);
|
||||
}
|
||||
this.state = undefined;
|
||||
this.onConnectionChangedEmitter.fire(this.state);
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the data to the connected serial monitor.
|
||||
* The desired EOL is appended to `data`, you do not have to add it.
|
||||
* It is a NOOP if connected.
|
||||
*/
|
||||
async send(data: string): Promise<Status> {
|
||||
if (!this.connected) {
|
||||
return Status.NOT_CONNECTED;
|
||||
}
|
||||
return new Promise<Status>(resolve => {
|
||||
this.monitorService.send(data + this.monitorModel.lineEnding)
|
||||
.then(() => resolve(Status.OK));
|
||||
});
|
||||
}
|
||||
|
||||
get onConnectionChanged(): Event<MonitorConnection.State | undefined> {
|
||||
return this.onConnectionChangedEmitter.event;
|
||||
}
|
||||
|
||||
get onRead(): Event<MonitorReadEvent> {
|
||||
return this.onReadEmitter.event;
|
||||
}
|
||||
|
||||
protected async handleBoardConfigChange(boardsConfig: BoardsConfig.Config): Promise<void> {
|
||||
if (this.autoConnect) {
|
||||
if (this.boardsServiceClient.canUploadTo(boardsConfig, { silent: false })) {
|
||||
// Instead of calling `getAttachedBoards` and filtering for `AttachedSerialBoard` we have to check the available ports.
|
||||
// The connected board might be unknown. See: https://github.com/arduino/arduino-pro-ide/issues/127#issuecomment-563251881
|
||||
this.boardsService.getAvailablePorts().then(({ ports }) => {
|
||||
if (ports.some(port => Port.equals(port, boardsConfig.selectedPort))) {
|
||||
new Promise<void>(resolve => {
|
||||
// First, disconnect if connected.
|
||||
if (this.connected) {
|
||||
this.disconnect().then(() => resolve());
|
||||
return;
|
||||
}
|
||||
resolve();
|
||||
}).then(() => {
|
||||
// Then (re-)connect.
|
||||
const { selectedBoard: board, selectedPort: port } = boardsConfig;
|
||||
const { baudRate } = this.monitorModel;
|
||||
this.connect({ board, port, baudRate });
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export namespace MonitorConnection {
|
||||
|
||||
export interface State {
|
||||
readonly config: MonitorConfig;
|
||||
}
|
||||
|
||||
export namespace State {
|
||||
export function toString(state: State): string {
|
||||
const { config } = state;
|
||||
const { board, port } = config;
|
||||
return `${Board.toString(board)} ${Port.toString(port)}`;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -1,58 +1,119 @@
|
||||
import { injectable } from "inversify";
|
||||
import { Emitter } from "@theia/core";
|
||||
|
||||
export namespace MonitorModel {
|
||||
export interface Data {
|
||||
autoscroll: boolean,
|
||||
timestamp: boolean,
|
||||
baudRate: number,
|
||||
lineEnding: string
|
||||
}
|
||||
}
|
||||
import { injectable, inject } from 'inversify';
|
||||
import { Emitter, Event } from '@theia/core/lib/common/event';
|
||||
import { MonitorConfig } from '../../common/protocol/monitor-service';
|
||||
import { FrontendApplicationContribution, LocalStorageService } from '@theia/core/lib/browser';
|
||||
import { BoardsServiceClientImpl } from '../boards/boards-service-client-impl';
|
||||
|
||||
@injectable()
|
||||
export class MonitorModel {
|
||||
export class MonitorModel implements FrontendApplicationContribution {
|
||||
|
||||
protected readonly onChangeEmitter = new Emitter<void>();
|
||||
protected static STORAGE_ID = 'arduino-monitor-model';
|
||||
|
||||
readonly onChange = this.onChangeEmitter.event;
|
||||
@inject(LocalStorageService)
|
||||
protected readonly localStorageService: LocalStorageService;
|
||||
|
||||
protected _autoscroll: boolean = true;
|
||||
protected _timestamp: boolean = false;
|
||||
baudRate: number;
|
||||
lineEnding: string = '\n';
|
||||
@inject(BoardsServiceClientImpl)
|
||||
protected readonly boardsServiceClient: BoardsServiceClientImpl;
|
||||
|
||||
protected readonly onChangeEmitter: Emitter<MonitorModel.State.Change<keyof MonitorModel.State>>;
|
||||
protected _autoscroll: boolean;
|
||||
protected _timestamp: boolean;
|
||||
protected _baudRate: MonitorConfig.BaudRate;
|
||||
protected _lineEnding: MonitorModel.EOL;
|
||||
|
||||
constructor() {
|
||||
this._autoscroll = true;
|
||||
this._timestamp = false;
|
||||
this._baudRate = MonitorConfig.BaudRate.DEFAULT;
|
||||
this._lineEnding = MonitorModel.EOL.DEFAULT;
|
||||
this.onChangeEmitter = new Emitter<MonitorModel.State.Change<keyof MonitorModel.State>>();
|
||||
}
|
||||
|
||||
onStart(): void {
|
||||
this.localStorageService.getData<MonitorModel.State>(MonitorModel.STORAGE_ID).then(state => {
|
||||
if (state) {
|
||||
this.restoreState(state);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
get onChange(): Event<MonitorModel.State.Change<keyof MonitorModel.State>> {
|
||||
return this.onChangeEmitter.event;
|
||||
}
|
||||
|
||||
get autoscroll(): boolean {
|
||||
return this._autoscroll;
|
||||
}
|
||||
|
||||
toggleAutoscroll(): void {
|
||||
this._autoscroll = !this._autoscroll;
|
||||
this.storeState();
|
||||
this.storeState().then(() => this.onChangeEmitter.fire({ property: 'autoscroll', value: this._autoscroll }));
|
||||
}
|
||||
|
||||
get timestamp(): boolean {
|
||||
return this._timestamp;
|
||||
}
|
||||
|
||||
toggleAutoscroll(): void {
|
||||
this._autoscroll = !this._autoscroll;
|
||||
this.onChangeEmitter.fire(undefined);
|
||||
}
|
||||
|
||||
toggleTimestamp(): void {
|
||||
this._timestamp = !this._timestamp;
|
||||
this.onChangeEmitter.fire(undefined);
|
||||
this.storeState().then(() => this.onChangeEmitter.fire({ property: 'timestamp', value: this._timestamp }));
|
||||
}
|
||||
|
||||
restore(model: MonitorModel.Data) {
|
||||
this._autoscroll = model.autoscroll;
|
||||
this._timestamp = model.timestamp;
|
||||
this.baudRate = model.baudRate;
|
||||
this.lineEnding = model.lineEnding;
|
||||
get baudRate(): MonitorConfig.BaudRate {
|
||||
return this._baudRate;
|
||||
}
|
||||
|
||||
store(): MonitorModel.Data {
|
||||
return {
|
||||
set baudRate(baudRate: MonitorConfig.BaudRate) {
|
||||
this._baudRate = baudRate;
|
||||
this.storeState().then(() => this.onChangeEmitter.fire({ property: 'baudRate', value: this._baudRate }));
|
||||
}
|
||||
|
||||
get lineEnding(): MonitorModel.EOL {
|
||||
return this._lineEnding;
|
||||
}
|
||||
|
||||
set lineEnding(lineEnding: MonitorModel.EOL) {
|
||||
this._lineEnding = lineEnding;
|
||||
this.storeState().then(() => this.onChangeEmitter.fire({ property: 'lineEnding', value: this._lineEnding }));
|
||||
}
|
||||
|
||||
protected restoreState(state: MonitorModel.State): void {
|
||||
this._autoscroll = state.autoscroll;
|
||||
this._timestamp = state.timestamp;
|
||||
this._baudRate = state.baudRate;
|
||||
this._lineEnding = state.lineEnding;
|
||||
}
|
||||
|
||||
protected async storeState(): Promise<void> {
|
||||
return this.localStorageService.setData(MonitorModel.STORAGE_ID, {
|
||||
autoscroll: this._autoscroll,
|
||||
timestamp: this._timestamp,
|
||||
baudRate: this.baudRate,
|
||||
lineEnding: this.lineEnding
|
||||
baudRate: this._baudRate,
|
||||
lineEnding: this._lineEnding
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export namespace MonitorModel {
|
||||
|
||||
export interface State {
|
||||
autoscroll: boolean;
|
||||
timestamp: boolean;
|
||||
baudRate: MonitorConfig.BaudRate;
|
||||
lineEnding: EOL;
|
||||
}
|
||||
export namespace State {
|
||||
export interface Change<K extends keyof State> {
|
||||
readonly property: K;
|
||||
readonly value: State[K];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export type EOL = '' | '\n' | '\r' | '\r\n';
|
||||
export namespace EOL {
|
||||
export const DEFAULT: EOL = '\n';
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -12,8 +12,8 @@ export class MonitorServiceClientImpl implements MonitorServiceClient {
|
||||
|
||||
notifyRead(event: MonitorReadEvent): void {
|
||||
this.onReadEmitter.fire(event);
|
||||
const { connectionId, data } = event;
|
||||
console.log(`Received data from ${connectionId}: ${data}`);
|
||||
const { data } = event;
|
||||
console.debug(`Received data: ${data}`);
|
||||
}
|
||||
|
||||
notifyError(error: MonitorError): void {
|
||||
|
@@ -54,18 +54,18 @@ export class MonitorViewContribution extends AbstractViewContribution<MonitorWid
|
||||
}
|
||||
}
|
||||
|
||||
async registerToolbarItems(registry: TabBarToolbarRegistry) {
|
||||
registerToolbarItems(registry: TabBarToolbarRegistry): void {
|
||||
registry.registerItem({
|
||||
id: 'monitor-autoscroll',
|
||||
render: () => this.renderAutoScrollButton(),
|
||||
isVisible: widget => widget instanceof MonitorWidget,
|
||||
onDidChange: this.model.onChange
|
||||
onDidChange: this.model.onChange as any // XXX: it's a hack. See: https://github.com/eclipse-theia/theia/pull/6696/
|
||||
});
|
||||
registry.registerItem({
|
||||
id: 'monitor-timestamp',
|
||||
render: () => this.renderTimestampButton(),
|
||||
isVisible: widget => widget instanceof MonitorWidget,
|
||||
onDidChange: this.model.onChange
|
||||
onDidChange: this.model.onChange as any // XXX: it's a hack. See: https://github.com/eclipse-theia/theia/pull/6696/
|
||||
});
|
||||
registry.registerItem({
|
||||
id: SerialMonitor.Commands.CLEAR_OUTPUT.id,
|
||||
@@ -80,7 +80,7 @@ export class MonitorViewContribution extends AbstractViewContribution<MonitorWid
|
||||
isVisible: widget => widget instanceof MonitorWidget,
|
||||
execute: widget => {
|
||||
if (widget instanceof MonitorWidget) {
|
||||
widget.clear();
|
||||
widget.clearConsole();
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -106,7 +106,7 @@ export class MonitorViewContribution extends AbstractViewContribution<MonitorWid
|
||||
}
|
||||
|
||||
protected readonly toggleAutoScroll = () => this.doToggleAutoScroll();
|
||||
protected async doToggleAutoScroll() {
|
||||
protected async doToggleAutoScroll(): Promise<void> {
|
||||
this.model.toggleAutoscroll();
|
||||
}
|
||||
|
||||
@@ -121,7 +121,8 @@ export class MonitorViewContribution extends AbstractViewContribution<MonitorWid
|
||||
}
|
||||
|
||||
protected readonly toggleTimestamp = () => this.doToggleTimestamp();
|
||||
protected async doToggleTimestamp() {
|
||||
protected async doToggleTimestamp(): Promise<void> {
|
||||
this.model.toggleTimestamp();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -1,285 +1,117 @@
|
||||
import { ReactWidget, Message, Widget, StatefulWidget } from "@theia/core/lib/browser";
|
||||
import { postConstruct, injectable, inject } from "inversify";
|
||||
import * as React from 'react';
|
||||
import Select, { components } from 'react-select';
|
||||
import { Styles } from "react-select/src/styles";
|
||||
import { ThemeConfig } from "react-select/src/theme";
|
||||
import { OptionsType } from "react-select/src/types";
|
||||
import { MonitorServiceClientImpl } from "./monitor-service-client-impl";
|
||||
import { MessageService } from "@theia/core";
|
||||
import { ConnectionConfig, MonitorService } from "../../common/protocol/monitor-service";
|
||||
import { MonitorConnection } from "./monitor-connection";
|
||||
import { BoardsServiceClientImpl } from "../boards/boards-service-client-impl";
|
||||
import { AttachedSerialBoard, BoardsService, Board } from "../../common/protocol/boards-service";
|
||||
import { BoardsConfig } from "../boards/boards-config";
|
||||
import { MonitorModel } from "./monitor-model";
|
||||
|
||||
export namespace SerialMonitorSendField {
|
||||
export interface Props {
|
||||
onSend: (text: string) => void
|
||||
}
|
||||
|
||||
export interface State {
|
||||
value: string;
|
||||
}
|
||||
}
|
||||
|
||||
export class SerialMonitorSendField extends React.Component<SerialMonitorSendField.Props, SerialMonitorSendField.State> {
|
||||
|
||||
protected inputField: HTMLInputElement | null;
|
||||
|
||||
constructor(props: SerialMonitorSendField.Props) {
|
||||
super(props);
|
||||
this.state = { value: '' };
|
||||
|
||||
this.handleChange = this.handleChange.bind(this);
|
||||
this.handleSubmit = this.handleSubmit.bind(this);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
if (this.inputField) {
|
||||
this.inputField.focus();
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return <React.Fragment>
|
||||
<form onSubmit={this.handleSubmit}>
|
||||
<input
|
||||
tabIndex={-1}
|
||||
ref={ref => this.inputField = ref}
|
||||
type='text' id='serial-monitor-send'
|
||||
autoComplete='off'
|
||||
value={this.state.value}
|
||||
onChange={this.handleChange} />
|
||||
<input className="btn" type="submit" value="Submit" />
|
||||
</form>
|
||||
</React.Fragment>
|
||||
}
|
||||
|
||||
protected handleChange(event: React.ChangeEvent<HTMLInputElement>) {
|
||||
this.setState({ value: event.target.value });
|
||||
}
|
||||
|
||||
protected handleSubmit(event: React.FormEvent<HTMLFormElement>) {
|
||||
this.props.onSend(this.state.value);
|
||||
this.setState({ value: '' });
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
export namespace SerialMonitorOutput {
|
||||
export interface Props {
|
||||
lines: string[];
|
||||
model: MonitorModel;
|
||||
}
|
||||
}
|
||||
|
||||
export class SerialMonitorOutput extends React.Component<SerialMonitorOutput.Props> {
|
||||
protected theEnd: HTMLDivElement | null;
|
||||
|
||||
render() {
|
||||
let result = '';
|
||||
|
||||
const style: React.CSSProperties = {
|
||||
whiteSpace: 'pre',
|
||||
fontFamily: 'monospace',
|
||||
};
|
||||
|
||||
for (const text of this.props.lines) {
|
||||
result += text;
|
||||
}
|
||||
return <React.Fragment>
|
||||
<div style={style}>{result}</div>
|
||||
<div style={{ float: "left", clear: "both" }}
|
||||
ref={(el) => { this.theEnd = el; }}>
|
||||
</div>
|
||||
</React.Fragment>;
|
||||
}
|
||||
|
||||
protected scrollToBottom() {
|
||||
if (this.theEnd) {
|
||||
this.theEnd.scrollIntoView();
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
if (this.props.model.autoscroll) {
|
||||
this.scrollToBottom();
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
if (this.props.model.autoscroll) {
|
||||
this.scrollToBottom();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export interface SelectOption {
|
||||
label: string;
|
||||
value: string | number;
|
||||
}
|
||||
import * as dateFormat from 'dateformat';
|
||||
import { postConstruct, injectable, inject } from 'inversify';
|
||||
import { OptionsType } from 'react-select/src/types';
|
||||
import { isOSX } from '@theia/core/lib/common/os';
|
||||
import { Event, Emitter } from '@theia/core/lib/common/event';
|
||||
import { Key, KeyCode } from '@theia/core/lib/browser/keys';
|
||||
import { DisposableCollection } from '@theia/core/lib/common/disposable'
|
||||
import { ReactWidget, Message, Widget, MessageLoop } from '@theia/core/lib/browser/widgets';
|
||||
import { Board, Port } from '../../common/protocol/boards-service';
|
||||
import { MonitorConfig } from '../../common/protocol/monitor-service';
|
||||
import { ArduinoSelect } from '../components/arduino-select';
|
||||
import { MonitorModel } from './monitor-model';
|
||||
import { MonitorConnection } from './monitor-connection';
|
||||
import { MonitorServiceClientImpl } from './monitor-service-client-impl';
|
||||
|
||||
@injectable()
|
||||
export class MonitorWidget extends ReactWidget implements StatefulWidget {
|
||||
export class MonitorWidget extends ReactWidget {
|
||||
|
||||
static readonly ID = 'serial-monitor';
|
||||
|
||||
protected lines: string[];
|
||||
protected tempData: string;
|
||||
@inject(MonitorModel)
|
||||
protected readonly monitorModel: MonitorModel;
|
||||
|
||||
@inject(MonitorConnection)
|
||||
protected readonly monitorConnection: MonitorConnection;
|
||||
|
||||
@inject(MonitorServiceClientImpl)
|
||||
protected readonly monitorServiceClient: MonitorServiceClientImpl;
|
||||
|
||||
protected widgetHeight: number;
|
||||
|
||||
protected continuePreviousConnection: boolean;
|
||||
/**
|
||||
* Do not touch or use it. It is for setting the focus on the `input` after the widget activation.
|
||||
*/
|
||||
protected focusNode: HTMLElement | undefined;
|
||||
/**
|
||||
* Guard against re-rendering the view after the close was requested.
|
||||
* See: https://github.com/eclipse-theia/theia/issues/6704
|
||||
*/
|
||||
protected closing = false;
|
||||
protected readonly clearOutputEmitter = new Emitter<void>();
|
||||
|
||||
constructor(
|
||||
@inject(MonitorServiceClientImpl) protected readonly serviceClient: MonitorServiceClientImpl,
|
||||
@inject(MonitorConnection) protected readonly connection: MonitorConnection,
|
||||
@inject(MonitorService) protected readonly monitorService: MonitorService,
|
||||
@inject(BoardsServiceClientImpl) protected readonly boardsServiceClient: BoardsServiceClientImpl,
|
||||
@inject(MessageService) protected readonly messageService: MessageService,
|
||||
@inject(BoardsService) protected readonly boardsService: BoardsService,
|
||||
@inject(MonitorModel) protected readonly model: MonitorModel
|
||||
) {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.id = MonitorWidget.ID;
|
||||
this.title.label = 'Serial Monitor';
|
||||
this.title.iconClass = 'arduino-serial-monitor-tab-icon';
|
||||
|
||||
this.lines = [];
|
||||
this.tempData = '';
|
||||
|
||||
this.title.closable = true;
|
||||
this.scrollOptions = undefined;
|
||||
|
||||
this.toDisposeOnDetach.push(serviceClient.onRead(({ data, connectionId }) => {
|
||||
this.tempData += data;
|
||||
if (this.tempData.endsWith('\n')) {
|
||||
if (this.model.timestamp) {
|
||||
const nu = new Date();
|
||||
const h = (100 + nu.getHours()).toString().substr(1)
|
||||
const min = (100 + nu.getMinutes()).toString().substr(1)
|
||||
const sec = (100 + nu.getSeconds()).toString().substr(1)
|
||||
const ms = (1000 + nu.getMilliseconds()).toString().substr(1);
|
||||
this.tempData = `${h}:${min}:${sec}.${ms} -> ` + this.tempData;
|
||||
}
|
||||
this.lines.push(this.tempData);
|
||||
this.tempData = '';
|
||||
this.update();
|
||||
}
|
||||
}));
|
||||
|
||||
// TODO onError
|
||||
this.toDispose.push(this.clearOutputEmitter);
|
||||
}
|
||||
|
||||
@postConstruct()
|
||||
protected init(): void {
|
||||
this.update();
|
||||
this.toDispose.push(this.monitorConnection.onConnectionChanged(() => this.clearConsole()));
|
||||
}
|
||||
|
||||
clear(): void {
|
||||
this.lines = [];
|
||||
clearConsole(): void {
|
||||
this.clearOutputEmitter.fire(undefined);
|
||||
this.update();
|
||||
}
|
||||
|
||||
storeState(): MonitorModel.Data {
|
||||
return this.model.store();
|
||||
dispose(): void {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
restoreState(oldState: MonitorModel.Data): void {
|
||||
this.model.restore(oldState);
|
||||
}
|
||||
|
||||
protected onAfterAttach(msg: Message) {
|
||||
protected onAfterAttach(msg: Message): void {
|
||||
super.onAfterAttach(msg);
|
||||
this.clear();
|
||||
this.connect();
|
||||
this.toDisposeOnDetach.push(
|
||||
this.boardsServiceClient.onBoardsChanged(async states => {
|
||||
const currentConnectionConfig = this.connection.connectionConfig;
|
||||
const connectedBoard = states.newState.boards
|
||||
.filter(AttachedSerialBoard.is)
|
||||
.find(board => {
|
||||
const potentiallyConnected = currentConnectionConfig && currentConnectionConfig.board;
|
||||
if (AttachedSerialBoard.is(potentiallyConnected)) {
|
||||
return Board.equals(board, potentiallyConnected) && board.port === potentiallyConnected.port;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
if (connectedBoard && currentConnectionConfig) {
|
||||
this.continuePreviousConnection = true;
|
||||
this.connection.connect(currentConnectionConfig);
|
||||
}
|
||||
})
|
||||
);
|
||||
this.toDisposeOnDetach.push(
|
||||
this.boardsServiceClient.onBoardsConfigChanged(async boardConfig => {
|
||||
this.connect();
|
||||
})
|
||||
)
|
||||
|
||||
this.toDisposeOnDetach.push(this.connection.onConnectionChanged(() => {
|
||||
if (!this.continuePreviousConnection) {
|
||||
this.clear();
|
||||
} else {
|
||||
this.continuePreviousConnection = false;
|
||||
}
|
||||
}));
|
||||
this.monitorConnection.autoConnect = true;
|
||||
}
|
||||
|
||||
protected onBeforeDetach(msg: Message) {
|
||||
super.onBeforeDetach(msg);
|
||||
this.connection.disconnect();
|
||||
onCloseRequest(msg: Message): void {
|
||||
this.closing = true;
|
||||
this.monitorConnection.autoConnect = false;
|
||||
if (this.monitorConnection.connected) {
|
||||
this.monitorConnection.disconnect();
|
||||
}
|
||||
super.onCloseRequest(msg);
|
||||
}
|
||||
|
||||
protected onResize(msg: Widget.ResizeMessage) {
|
||||
protected onUpdateRequest(msg: Message): void {
|
||||
// TODO: `this.isAttached`
|
||||
// See: https://github.com/eclipse-theia/theia/issues/6704#issuecomment-562574713
|
||||
if (!this.closing && this.isAttached) {
|
||||
super.onUpdateRequest(msg);
|
||||
}
|
||||
}
|
||||
|
||||
protected onResize(msg: Widget.ResizeMessage): void {
|
||||
super.onResize(msg);
|
||||
this.widgetHeight = msg.height;
|
||||
this.update();
|
||||
}
|
||||
|
||||
protected async connect() {
|
||||
const config = await this.getConnectionConfig();
|
||||
if (config) {
|
||||
this.connection.connect(config);
|
||||
}
|
||||
protected onActivateRequest(msg: Message): void {
|
||||
super.onActivateRequest(msg);
|
||||
(this.focusNode || this.node).focus();
|
||||
}
|
||||
|
||||
protected async getConnectionConfig(): Promise<ConnectionConfig | undefined> {
|
||||
const baudRate = this.model.baudRate;
|
||||
const { boardsConfig } = this.boardsServiceClient;
|
||||
const { selectedBoard, selectedPort } = boardsConfig;
|
||||
if (!selectedBoard) {
|
||||
this.messageService.warn('No boards selected.');
|
||||
return;
|
||||
}
|
||||
const { name } = selectedBoard;
|
||||
if (!selectedPort) {
|
||||
this.messageService.warn(`No ports selected for board: '${name}'.`);
|
||||
return;
|
||||
}
|
||||
const attachedBoards = await this.boardsService.getAttachedBoards();
|
||||
const connectedBoard = attachedBoards.boards.filter(AttachedSerialBoard.is).find(board => BoardsConfig.Config.sameAs(boardsConfig, board));
|
||||
if (!connectedBoard) {
|
||||
return;
|
||||
}
|
||||
|
||||
return {
|
||||
baudRate,
|
||||
board: selectedBoard,
|
||||
port: selectedPort.address
|
||||
}
|
||||
protected onFocusResolved = (element: HTMLElement | undefined) => {
|
||||
this.focusNode = element;
|
||||
requestAnimationFrame(() => MessageLoop.sendMessage(this, Widget.Msg.ActivateRequest));
|
||||
}
|
||||
|
||||
protected getLineEndings(): OptionsType<SelectOption> {
|
||||
protected get lineEndings(): OptionsType<SelectOption<MonitorModel.EOL>> {
|
||||
return [
|
||||
{
|
||||
label: 'No Line Ending',
|
||||
value: ''
|
||||
},
|
||||
{
|
||||
label: 'Newline',
|
||||
label: 'New Line',
|
||||
value: '\n'
|
||||
},
|
||||
{
|
||||
@@ -290,109 +122,212 @@ export class MonitorWidget extends ReactWidget implements StatefulWidget {
|
||||
label: 'Both NL & CR',
|
||||
value: '\r\n'
|
||||
}
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
protected getBaudRates(): OptionsType<SelectOption> {
|
||||
const baudRates = [300, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200];
|
||||
return baudRates.map<SelectOption>(baudRate => ({ label: baudRate + ' baud', value: baudRate }))
|
||||
protected get baudRates(): OptionsType<SelectOption<MonitorConfig.BaudRate>> {
|
||||
const baudRates: Array<MonitorConfig.BaudRate> = [300, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200];
|
||||
return baudRates.map(baudRate => ({ label: baudRate + ' baud', value: baudRate }));
|
||||
}
|
||||
|
||||
protected render(): React.ReactNode {
|
||||
const le = this.getLineEndings();
|
||||
const br = this.getBaudRates();
|
||||
const leVal = this.model.lineEnding && le.find(val => val.value === this.model.lineEnding);
|
||||
const brVal = this.model.baudRate && br.find(val => val.value === this.model.baudRate);
|
||||
return <React.Fragment>
|
||||
<div className='serial-monitor-container'>
|
||||
<div className='head'>
|
||||
<div className='send'>
|
||||
<SerialMonitorSendField onSend={this.onSend} />
|
||||
</div>
|
||||
<div className='config'>
|
||||
{this.renderSelectField('arduino-serial-monitor-line-endings', le, leVal || le[1], this.onChangeLineEnding)}
|
||||
{this.renderSelectField('arduino-serial-monitor-baud-rates', br, brVal || br[4], this.onChangeBaudRate)}
|
||||
</div>
|
||||
const { baudRates, lineEndings } = this;
|
||||
const lineEnding = lineEndings.find(item => item.value === this.monitorModel.lineEnding) || lineEndings[1]; // Defaults to `\n`.
|
||||
const baudRate = baudRates.find(item => item.value === this.monitorModel.baudRate) || baudRates[4]; // Defaults to `9600`.
|
||||
return <div className='serial-monitor'>
|
||||
<div className='head'>
|
||||
<div className='send'>
|
||||
<SerialMonitorSendInput
|
||||
monitorConfig={this.monitorConnection.monitorConfig}
|
||||
resolveFocus={this.onFocusResolved}
|
||||
onSend={this.onSend} />
|
||||
</div>
|
||||
<div id='serial-monitor-output-container'>
|
||||
<SerialMonitorOutput model={this.model} lines={this.lines} />
|
||||
<div className='config'>
|
||||
<div className='select'>
|
||||
<ArduinoSelect
|
||||
maxMenuHeight={this.widgetHeight - 40}
|
||||
options={lineEndings}
|
||||
defaultValue={lineEnding}
|
||||
onChange={this.onChangeLineEnding} />
|
||||
</div>
|
||||
<div className='select'>
|
||||
<ArduinoSelect
|
||||
className='select'
|
||||
maxMenuHeight={this.widgetHeight - 40}
|
||||
options={baudRates}
|
||||
defaultValue={baudRate}
|
||||
onChange={this.onChangeBaudRate} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</React.Fragment>;
|
||||
<div className='body'>
|
||||
<SerialMonitorOutput
|
||||
monitorModel={this.monitorModel}
|
||||
monitorConnection={this.monitorConnection}
|
||||
clearConsoleEvent={this.clearOutputEmitter.event} />
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
|
||||
protected readonly onSend = (value: string) => this.doSend(value);
|
||||
protected async doSend(value: string) {
|
||||
const { connectionId } = this.connection;
|
||||
if (connectionId) {
|
||||
this.monitorService.send(connectionId, value + this.model.lineEnding);
|
||||
protected async doSend(value: string): Promise<void> {
|
||||
this.monitorConnection.send(value);
|
||||
}
|
||||
|
||||
protected readonly onChangeLineEnding = (option: SelectOption<MonitorModel.EOL>) => {
|
||||
this.monitorModel.lineEnding = option.value;
|
||||
}
|
||||
|
||||
protected readonly onChangeBaudRate = async (option: SelectOption<MonitorConfig.BaudRate>) => {
|
||||
this.monitorModel.baudRate = option.value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export namespace SerialMonitorSendInput {
|
||||
export interface Props {
|
||||
readonly monitorConfig?: MonitorConfig;
|
||||
readonly onSend: (text: string) => void;
|
||||
readonly resolveFocus: (element: HTMLElement | undefined) => void;
|
||||
}
|
||||
export interface State {
|
||||
text: string;
|
||||
}
|
||||
}
|
||||
|
||||
export class SerialMonitorSendInput extends React.Component<SerialMonitorSendInput.Props, SerialMonitorSendInput.State> {
|
||||
|
||||
constructor(props: Readonly<SerialMonitorSendInput.Props>) {
|
||||
super(props);
|
||||
this.state = { text: '' };
|
||||
this.onChange = this.onChange.bind(this);
|
||||
this.onSend = this.onSend.bind(this);
|
||||
this.onKeyDown = this.onKeyDown.bind(this);
|
||||
}
|
||||
|
||||
render(): React.ReactNode {
|
||||
return <input
|
||||
ref={this.setRef}
|
||||
type='text'
|
||||
className={this.props.monitorConfig ? '' : 'warning'}
|
||||
placeholder={this.placeholder}
|
||||
value={this.state.text}
|
||||
onChange={this.onChange}
|
||||
onKeyDown={this.onKeyDown} />
|
||||
}
|
||||
|
||||
protected get placeholder(): string {
|
||||
const { monitorConfig } = this.props;
|
||||
if (!monitorConfig) {
|
||||
return 'Not connected. Select a board and a port to connect automatically.'
|
||||
}
|
||||
const { board, port } = monitorConfig;
|
||||
return `Message (${isOSX ? '⌘' : 'Ctrl'}+Enter to send message to '${Board.toString(board, { useFqbn: false })}' on '${Port.toString(port)}')`;
|
||||
}
|
||||
|
||||
protected setRef = (element: HTMLElement | null) => {
|
||||
if (this.props.resolveFocus) {
|
||||
this.props.resolveFocus(element || undefined);
|
||||
}
|
||||
}
|
||||
|
||||
protected readonly onChangeLineEnding = (le: SelectOption) => {
|
||||
this.model.lineEnding = typeof le.value === 'string' ? le.value : '\n';
|
||||
protected onChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||
this.setState({ text: event.target.value });
|
||||
}
|
||||
|
||||
protected readonly onChangeBaudRate = async (br: SelectOption) => {
|
||||
await this.connection.disconnect();
|
||||
this.model.baudRate = typeof br.value === 'number' ? br.value : 9600;
|
||||
this.clear();
|
||||
const config = await this.getConnectionConfig();
|
||||
if (config) {
|
||||
await this.connection.connect(config);
|
||||
}
|
||||
protected onSend(): void {
|
||||
this.props.onSend(this.state.text);
|
||||
this.setState({ text: '' });
|
||||
}
|
||||
|
||||
protected renderSelectField(id: string, options: OptionsType<SelectOption>, defaultVal: SelectOption, onChange: (v: SelectOption) => void): React.ReactNode {
|
||||
const height = 25;
|
||||
const selectStyles: Styles = {
|
||||
control: (provided, state) => ({
|
||||
...provided,
|
||||
width: 200,
|
||||
border: 'none'
|
||||
}),
|
||||
dropdownIndicator: (p, s) => ({
|
||||
...p,
|
||||
padding: 0
|
||||
}),
|
||||
indicatorSeparator: (p, s) => ({
|
||||
display: 'none'
|
||||
}),
|
||||
indicatorsContainer: (p, s) => ({
|
||||
padding: '0 5px'
|
||||
}),
|
||||
menu: (p, s) => ({
|
||||
...p,
|
||||
marginTop: 0
|
||||
})
|
||||
};
|
||||
const theme: ThemeConfig = theme => ({
|
||||
...theme,
|
||||
borderRadius: 0,
|
||||
spacing: {
|
||||
controlHeight: height,
|
||||
baseUnit: 2,
|
||||
menuGutter: 4
|
||||
protected onKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void {
|
||||
const keyCode = KeyCode.createKeyCode(event.nativeEvent);
|
||||
if (keyCode) {
|
||||
const { key, meta, ctrl } = keyCode;
|
||||
if (key === Key.ENTER && ((isOSX && meta) || (!isOSX && ctrl))) {
|
||||
this.onSend();
|
||||
}
|
||||
});
|
||||
const DropdownIndicator = (
|
||||
props: React.Props<typeof components.DropdownIndicator>
|
||||
) => {
|
||||
return (
|
||||
<span className='fa fa-caret-down caret'></span>
|
||||
);
|
||||
};
|
||||
return <Select
|
||||
options={options}
|
||||
defaultValue={defaultVal}
|
||||
onChange={onChange}
|
||||
components={{ DropdownIndicator }}
|
||||
theme={theme}
|
||||
styles={selectStyles}
|
||||
maxMenuHeight={this.widgetHeight - 40}
|
||||
classNamePrefix='sms'
|
||||
className='serial-monitor-select'
|
||||
id={id}
|
||||
/>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export namespace SerialMonitorOutput {
|
||||
export interface Props {
|
||||
readonly monitorModel: MonitorModel;
|
||||
readonly monitorConnection: MonitorConnection;
|
||||
readonly clearConsoleEvent: Event<void>;
|
||||
}
|
||||
export interface State {
|
||||
content: string;
|
||||
timestamp: boolean;
|
||||
}
|
||||
}
|
||||
|
||||
export class SerialMonitorOutput extends React.Component<SerialMonitorOutput.Props, SerialMonitorOutput.State> {
|
||||
|
||||
/**
|
||||
* Do not touch it. It is used to be able to "follow" the serial monitor log.
|
||||
*/
|
||||
protected anchor: HTMLElement | null;
|
||||
protected toDisposeBeforeUnmount = new DisposableCollection();
|
||||
|
||||
constructor(props: Readonly<SerialMonitorOutput.Props>) {
|
||||
super(props);
|
||||
this.state = { content: '', timestamp: this.props.monitorModel.timestamp };
|
||||
}
|
||||
|
||||
render(): React.ReactNode {
|
||||
return <React.Fragment>
|
||||
<div style={({ whiteSpace: 'pre', fontFamily: 'monospace' })}>
|
||||
{this.state.content}
|
||||
</div>
|
||||
<div style={{ float: 'left', clear: 'both' }} ref={element => { this.anchor = element; }} />
|
||||
</React.Fragment>;
|
||||
}
|
||||
|
||||
componentDidMount(): void {
|
||||
this.scrollToBottom();
|
||||
let chunk = '';
|
||||
this.toDisposeBeforeUnmount.pushAll([
|
||||
this.props.monitorConnection.onRead(({ data }) => {
|
||||
chunk += data;
|
||||
const eolIndex = chunk.indexOf('\n');
|
||||
if (eolIndex !== -1) {
|
||||
const line = chunk.substring(0, eolIndex + 1);
|
||||
chunk = chunk.slice(eolIndex + 1);
|
||||
const content = `${this.state.content}${this.state.timestamp ? `${dateFormat(new Date(), 'H:M:ss.l')} -> ` : ''}${line}`;
|
||||
this.setState({ content });
|
||||
}
|
||||
}),
|
||||
this.props.clearConsoleEvent(() => this.setState({ content: '' })),
|
||||
this.props.monitorModel.onChange(({ property }) => {
|
||||
if (property === 'timestamp') {
|
||||
const { timestamp } = this.props.monitorModel;
|
||||
this.setState({ timestamp });
|
||||
}
|
||||
})
|
||||
]);
|
||||
}
|
||||
|
||||
componentDidUpdate(): void {
|
||||
this.scrollToBottom();
|
||||
}
|
||||
|
||||
componentWillUnmount(): void {
|
||||
// TODO: "Your preferred browser's local storage is almost full." Discard `content` before saving layout?
|
||||
this.toDisposeBeforeUnmount.dispose();
|
||||
}
|
||||
|
||||
protected scrollToBottom(): void {
|
||||
if (this.props.monitorModel.autoscroll && this.anchor) {
|
||||
this.anchor.scrollIntoView();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export interface SelectOption<T> {
|
||||
readonly label: string;
|
||||
readonly value: T;
|
||||
}
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
45
arduino-ide-extension/src/browser/style/arduino-select.css
Normal file
45
arduino-ide-extension/src/browser/style/arduino-select.css
Normal file
@@ -0,0 +1,45 @@
|
||||
.arduino-select__control {
|
||||
border: var(--theia-layout-color2) var(--theia-border-width) solid !important;
|
||||
background: var(--theia-layout-color2) !important;
|
||||
}
|
||||
|
||||
.arduino-select__control:hover {
|
||||
border: var(--theia-layout-color2) var(--theia-border-width) solid !important;
|
||||
}
|
||||
|
||||
.arduino-select__control--is-focused {
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
.arduino-select__option--is-selected {
|
||||
background-color: var(--theia-accent-color3) !important;
|
||||
color: var(--theia-content-font-color0) !important;
|
||||
border-color: var(--theia-accent-color3) !important;
|
||||
}
|
||||
|
||||
.arduino-select__option--is-focused {
|
||||
background-color: var(--theia-accent-color4) !important;
|
||||
border-color: var(--theia-accent-color3) !important;
|
||||
}
|
||||
|
||||
.arduino-select__menu {
|
||||
background-color: var(--theia-layout-color2) !important;
|
||||
border: 1px solid var(--theia-accent-color3) !important;
|
||||
top: auto !important; /* to align the top of the menu with the bottom of the control */
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
.arduino-select__control.arduino-select__control--menu-is-open {
|
||||
border: 1px solid !important;
|
||||
border-color: var(--theia-accent-color3) !important;
|
||||
border-bottom-color: var(--theia-layout-color2) !important; /* if the bottom border color has the same color as the background of the control, we make the border "invisible" */
|
||||
}
|
||||
|
||||
.arduino-select__value-container .arduino-select__single-value {
|
||||
color: var(--theia-ui-font-color1) !important;
|
||||
}
|
||||
|
||||
.arduino-select__menu-list {
|
||||
padding-top: 0 !important;
|
||||
padding-bottom: 0 !important;
|
||||
}
|
@@ -35,7 +35,7 @@ div#select-board-dialog .selectBoardContainer .body .list .item.selected i{
|
||||
#select-board-dialog .selectBoardContainer .search input,
|
||||
#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 {
|
||||
|
@@ -2,4 +2,42 @@
|
||||
@import './board-select-dialog.css';
|
||||
@import './main.css';
|
||||
@import './editor.css';
|
||||
@import './monitor.css';
|
||||
@import './monitor.css';
|
||||
@import './arduino-select.css';
|
||||
|
||||
input:focus {
|
||||
outline-width: 1px;
|
||||
outline-style: solid;
|
||||
outline-offset: -1px;
|
||||
opacity: 1 !important;
|
||||
outline-color: var(--theia-accent-color3);
|
||||
}
|
||||
|
||||
input.warning:focus {
|
||||
outline-width: 1px;
|
||||
outline-style: solid;
|
||||
outline-offset: -1px;
|
||||
opacity: 1 !important;
|
||||
color: var(--theia-warn-font-color0);
|
||||
background-color: var(--theia-warn-color0);
|
||||
}
|
||||
|
||||
input.warning {
|
||||
background-color: var(--theia-warn-color0);
|
||||
}
|
||||
|
||||
input.warning::placeholder { /* Chrome, Firefox, Opera, Safari 10.1+ */
|
||||
color: var(--theia-warn-font-color0);
|
||||
background-color: var(--theia-warn-color0);
|
||||
opacity: 1; /* Firefox */
|
||||
}
|
||||
|
||||
input.warning:-ms-input-placeholder { /* Internet Explorer 10-11 */
|
||||
color: var(--theia-warn-font-color0);
|
||||
background-color: var(--theia-warn-color0);
|
||||
}
|
||||
|
||||
input.warning::-ms-input-placeholder { /* Microsoft Edge */
|
||||
color: var(--theia-warn-font-color0);
|
||||
background-color: var(--theia-warn-color0);
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
.library-tab-icon {
|
||||
-webkit-mask: url('library-tab-icon.svg');
|
||||
mask: url('library-tab-icon.svg');
|
||||
-webkit-mask: url('../icons/library-tab-icon.svg');
|
||||
mask: url('../icons/library-tab-icon.svg');
|
||||
}
|
||||
|
||||
.arduino-list-widget {
|
||||
@@ -9,7 +9,6 @@
|
||||
|
||||
.arduino-list-widget .search-bar {
|
||||
margin: 0px 10px 10px 15px;
|
||||
border-color: var(--theia-border-color3);
|
||||
}
|
||||
|
||||
.arduino-list-widget .search-filters {
|
||||
@@ -61,6 +60,14 @@
|
||||
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 {
|
||||
padding: 10px 10px 10px 15px;
|
||||
font-size: var(--theia-ui-font-size1);
|
||||
@@ -108,8 +115,9 @@
|
||||
color: var(--theia-ui-font-color2);
|
||||
}
|
||||
|
||||
.component-list-item .header .installed {
|
||||
.component-list-item .header .installed:before {
|
||||
margin-left: 4px;
|
||||
display: inline-block;
|
||||
justify-self: end;
|
||||
background-color: var(--theia-accent-color1);
|
||||
padding: 2px 4px 2px 4px;
|
||||
@@ -117,6 +125,13 @@
|
||||
font-weight: bold;
|
||||
max-height: calc(1em + 4px);
|
||||
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 {
|
||||
|
@@ -5,63 +5,46 @@
|
||||
background-position-x: 19px;
|
||||
}
|
||||
|
||||
.serial-monitor-container {
|
||||
.serial-monitor {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.serial-monitor-container .head {
|
||||
.serial-monitor .head {
|
||||
display: flex;
|
||||
padding: 5px;
|
||||
background: var(--theia-layout-color0);
|
||||
height: 27px;
|
||||
}
|
||||
|
||||
.serial-monitor-container .head .send {
|
||||
.serial-monitor .head .send {
|
||||
display: flex;
|
||||
flex:1;
|
||||
}
|
||||
|
||||
.serial-monitor-container .head .send .btn {
|
||||
display: flex;
|
||||
padding: 0 5px;
|
||||
align-items: center;
|
||||
color: var(--theia-ui-font-color1);
|
||||
opacity: 0.7;
|
||||
}
|
||||
.serial-monitor-container .head .send .btn:hover {
|
||||
opacity: 1;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.serial-monitor-container .head .send form {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
background: var(--theia-layout-color2);
|
||||
margin-right: 2px;
|
||||
}
|
||||
|
||||
.serial-monitor-container .head .send input#serial-monitor-send {
|
||||
color: var(--theia-ui-font-color1);
|
||||
flex: 1;
|
||||
.serial-monitor .head .send > input {
|
||||
line-height: var(--theia-content-line-height);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.serial-monitor-container .head .send input:focus {
|
||||
outline: none;
|
||||
.serial-monitor .head .send > input:focus {
|
||||
border-color: var(--theia-accent-color3);
|
||||
}
|
||||
|
||||
.serial-monitor-container .head .config {
|
||||
.serial-monitor .head .config {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.serial-monitor-container .head .config .serial-monitor-select {
|
||||
margin-left: 5px;
|
||||
.serial-monitor .head .config .select {
|
||||
margin-left: 3px;
|
||||
}
|
||||
|
||||
#serial-monitor-output-container {
|
||||
.serial-monitor .body {
|
||||
overflow: auto;
|
||||
flex: 1;
|
||||
padding: 6px;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.p-TabBar-toolbar .item.arduino-monitor {
|
||||
@@ -78,49 +61,3 @@
|
||||
.p-TabBar-toolbar .item .clear-all {
|
||||
background: var(--theia-icon-clear) no-repeat;
|
||||
}
|
||||
|
||||
/* React Select Styles */
|
||||
.serial-monitor-select .sms__control {
|
||||
border: var(--theia-border-color1) var(--theia-border-width) solid;
|
||||
background: var(--theia-layout-color2);
|
||||
}
|
||||
|
||||
.serial-monitor-select .sms__control--is-focused {
|
||||
border-color: var(--theia-border-color2) !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
.sms__control--is-focused:hover {
|
||||
border-color: var(--theia-border-color2) !important;
|
||||
}
|
||||
|
||||
.serial-monitor-select .sms__option--is-selected {
|
||||
background-color: var(--theia-ui-button-color-secondary-hover);
|
||||
color: var(--theia-content-font-color0);
|
||||
}
|
||||
|
||||
.serial-monitor-select .sms__option--is-focused {
|
||||
background-color: var(--theia-ui-button-color-secondary-hover);
|
||||
}
|
||||
|
||||
.serial-monitor-select .sms__menu {
|
||||
background-color: var(--theia-layout-color1);
|
||||
border: 1px solid var(--theia-border-color2);
|
||||
border-top: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.serial-monitor-select .sms__control.sms__control--menu-is-open {
|
||||
border: 1px solid;
|
||||
border-color: var(--theia-border-color2) !important;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.serial-monitor-select .sms__value-container .sms__single-value {
|
||||
color: var(--theia-ui-font-color1);
|
||||
}
|
||||
|
||||
.sms__menu-list {
|
||||
padding-top: 0 !important;
|
||||
padding-bottom: 0 !important;
|
||||
}
|
@@ -13,11 +13,11 @@ export class ToolOutputServiceClientImpl implements ToolOutputServiceClient {
|
||||
protected readonly outputContribution: OutputContribution;
|
||||
|
||||
onNewOutput(tool: string, chunk: string): void {
|
||||
this.outputContribution.openView({ reveal: true }).then(() => {
|
||||
this.outputContribution.openView({ activate: true }).then(() => {
|
||||
const channel = this.outputChannelManager.getChannel(`Arduino: ${tool}`);
|
||||
channel.setVisibility(true);
|
||||
channel.append(chunk);
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -1,3 +1,4 @@
|
||||
import { Installable } from './installable';
|
||||
|
||||
export interface ArduinoComponent {
|
||||
readonly name: string;
|
||||
@@ -6,8 +7,8 @@ export interface ArduinoComponent {
|
||||
readonly description: string;
|
||||
readonly moreInfoLink?: string;
|
||||
|
||||
readonly availableVersions: string[];
|
||||
readonly availableVersions: Installable.Version[];
|
||||
readonly installable: boolean;
|
||||
|
||||
readonly installedVersion?: string;
|
||||
readonly installedVersion?: Installable.Version;
|
||||
}
|
||||
|
@@ -46,10 +46,15 @@ export interface BoardInstalledEvent {
|
||||
readonly pkg: Readonly<BoardPackage>;
|
||||
}
|
||||
|
||||
export interface BoardUninstalledEvent {
|
||||
readonly pkg: Readonly<BoardPackage>;
|
||||
}
|
||||
|
||||
export const BoardsServiceClient = Symbol('BoardsServiceClient');
|
||||
export interface BoardsServiceClient {
|
||||
notifyAttachedBoardsChanged(event: AttachedBoardsChangeEvent): void;
|
||||
notifyBoardInstalled(event: BoardInstalledEvent): void
|
||||
notifyBoardUninstalled(event: BoardUninstalledEvent): void
|
||||
}
|
||||
|
||||
export const BoardsServicePath = '/services/boards-service';
|
||||
@@ -130,7 +135,7 @@ export namespace Port {
|
||||
}
|
||||
if (isOSX) {
|
||||
// Example: `/dev/cu.usbmodem14401`
|
||||
if (/(tty|cu)\..*/.test(address.substring('/dev/'.length))) {
|
||||
if (/(tty|cu)\..*/.test(address.substring('/dev/'.length))) {
|
||||
return [
|
||||
'/dev/cu.MALS',
|
||||
'/dev/cu.SOC',
|
||||
@@ -209,6 +214,11 @@ export namespace Board {
|
||||
return !!board.fqbn;
|
||||
}
|
||||
|
||||
export function toString(board: Board, options: { useFqbn: boolean } = { useFqbn: true }): string {
|
||||
const fqbn = options && options.useFqbn && board.fqbn ? ` [${board.fqbn}]` : '';
|
||||
return `${board.name}${fqbn}`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export interface AttachedSerialBoard extends Board {
|
||||
|
@@ -1,3 +1,23 @@
|
||||
export interface Installable<T> {
|
||||
install(item: T): Promise<void>;
|
||||
}
|
||||
const naturalCompare: (left: string, right: string) => number = require('string-natural-compare').caseInsensitive;
|
||||
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 LibraryService = Symbol('LibraryService');
|
||||
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 {
|
||||
readonly builtIn?: boolean;
|
||||
}
|
||||
|
||||
export namespace Library {
|
||||
// TODO: figure out whether we need a dedicated `version` type.
|
||||
export type Version = string;
|
||||
}
|
@@ -1,14 +1,52 @@
|
||||
import { JsonRpcServer } from '@theia/core';
|
||||
import { Board } from './boards-service';
|
||||
import { JsonRpcServer } from '@theia/core/lib/common/messaging/proxy-factory';
|
||||
import { Board, Port } from './boards-service';
|
||||
|
||||
export interface MonitorError {
|
||||
export interface Status { }
|
||||
export interface OK extends Status { }
|
||||
export interface ErrorStatus extends Status {
|
||||
readonly message: string;
|
||||
readonly code: number
|
||||
}
|
||||
export namespace Status {
|
||||
export function isOK(status: Status & { message?: string }): status is OK {
|
||||
return typeof status.message !== 'string';
|
||||
}
|
||||
export const OK: OK = {};
|
||||
export const NOT_CONNECTED: ErrorStatus = { message: 'Not connected.' };
|
||||
export const ALREADY_CONNECTED: ErrorStatus = { message: 'Already connected.' };
|
||||
}
|
||||
|
||||
export interface MonitorReadEvent {
|
||||
readonly connectionId: string;
|
||||
readonly data: string;
|
||||
export const MonitorServicePath = '/services/serial-monitor';
|
||||
export const MonitorService = Symbol('MonitorService');
|
||||
export interface MonitorService extends JsonRpcServer<MonitorServiceClient> {
|
||||
connect(config: MonitorConfig): Promise<Status>;
|
||||
disconnect(): Promise<Status>;
|
||||
send(data: string | Uint8Array): Promise<Status>;
|
||||
}
|
||||
|
||||
export interface MonitorConfig {
|
||||
readonly board: Board;
|
||||
readonly port: Port;
|
||||
/**
|
||||
* Defaults to [`SERIAL`](MonitorConfig#ConnectionType#SERIAL).
|
||||
*/
|
||||
readonly type?: MonitorConfig.ConnectionType;
|
||||
/**
|
||||
* Defaults to `9600`.
|
||||
*/
|
||||
readonly baudRate?: MonitorConfig.BaudRate;
|
||||
|
||||
}
|
||||
export namespace MonitorConfig {
|
||||
|
||||
export type BaudRate = 300 | 1200 | 2400 | 4800 | 9600 | 19200 | 38400 | 57600 | 115200;
|
||||
export namespace BaudRate {
|
||||
export const DEFAULT: BaudRate = 9600;
|
||||
}
|
||||
|
||||
export enum ConnectionType {
|
||||
SERIAL = 0
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export const MonitorServiceClient = Symbol('MonitorServiceClient');
|
||||
@@ -17,28 +55,31 @@ export interface MonitorServiceClient {
|
||||
notifyError(event: MonitorError): void;
|
||||
}
|
||||
|
||||
export const MonitorServicePath = '/services/serial-monitor';
|
||||
export const MonitorService = Symbol('MonitorService');
|
||||
export interface MonitorService extends JsonRpcServer<MonitorServiceClient> {
|
||||
connect(config: ConnectionConfig): Promise<{ connectionId: string }>;
|
||||
disconnect(connectionId: string): Promise<boolean>;
|
||||
send(connectionId: string, data: string | Uint8Array): Promise<void>;
|
||||
getConnectionIds(): Promise<string[]>;
|
||||
export interface MonitorReadEvent {
|
||||
readonly data: string;
|
||||
}
|
||||
|
||||
export interface ConnectionConfig {
|
||||
readonly board: Board;
|
||||
readonly port: string;
|
||||
export interface MonitorError {
|
||||
readonly message: string;
|
||||
/**
|
||||
* Defaults to [`SERIAL`](ConnectionType#SERIAL).
|
||||
* If no `code` is available, clients must reestablish the serial-monitor connection.
|
||||
*/
|
||||
readonly type?: ConnectionType;
|
||||
/**
|
||||
* Defaults to `9600`.
|
||||
*/
|
||||
readonly baudRate?: number;
|
||||
readonly code: number | undefined;
|
||||
readonly config: MonitorConfig;
|
||||
}
|
||||
|
||||
export enum ConnectionType {
|
||||
SERIAL = 0
|
||||
export namespace MonitorError {
|
||||
export namespace ErrorCodes {
|
||||
/**
|
||||
* The frontend has refreshed the browser, for instance.
|
||||
*/
|
||||
export const CLIENT_CANCEL = 1;
|
||||
/**
|
||||
* When detaching a physical device when the duplex channel is still opened.
|
||||
*/
|
||||
export const DEVICE_NOT_CONFIGURED = 2;
|
||||
/**
|
||||
* Another serial monitor was opened on this port. For another electron-instance, Java IDE.
|
||||
*/
|
||||
export const DEVICE_BUSY = 3;
|
||||
}
|
||||
}
|
||||
|
@@ -8,10 +8,11 @@ export interface SketchesService {
|
||||
getSketches(uri?: string): Promise<Sketch[]>
|
||||
getSketchFiles(uri: string): Promise<string[]>
|
||||
/**
|
||||
* Creates a new sketch folder in the `parentUri` location. If `parentUri` is not specified,
|
||||
* it falls back to the default `sketchDirUri` from the CLI.
|
||||
* Creates a new sketch folder in the `parentUri` location.
|
||||
* Normally, `parentUri` is the client's workspace root, or the default `sketchDirUri` from the CLI.
|
||||
* Note, `parentUri` and `sketchDirUri` can be the same.
|
||||
*/
|
||||
createNewSketch(parentUri?: string): Promise<Sketch>
|
||||
createNewSketch(parentUri: string): Promise<Sketch>
|
||||
isSketchFolder(uri: string): Promise<boolean>
|
||||
}
|
||||
|
||||
|
@@ -38,10 +38,10 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
bind(ArduinoCliContribution).toSelf().inSingletonScope();
|
||||
bind(CliContribution).toService(ArduinoCliContribution);
|
||||
|
||||
// Provides the path of the Ardunio CLI.
|
||||
// Provides the path of the Arduino CLI.
|
||||
bind(ArduinoCli).toSelf().inSingletonScope();
|
||||
|
||||
// Shared daemonn
|
||||
// Shared daemon
|
||||
bind(ArduinoDaemon).toSelf().inSingletonScope();
|
||||
bind(BackendApplicationContribution).toService(ArduinoDaemon);
|
||||
|
||||
|
@@ -1,7 +1,8 @@
|
||||
import * as os from 'os';
|
||||
import * as which from 'which';
|
||||
import * as semver from 'semver';
|
||||
import { spawn } from 'child_process';
|
||||
import { join, delimiter } from 'path';
|
||||
import { join } from 'path';
|
||||
import { injectable, inject } from 'inversify';
|
||||
import { ILogger } from '@theia/core';
|
||||
import { FileUri } from '@theia/core/lib/node/file-uri';
|
||||
@@ -13,43 +14,34 @@ export class ArduinoCli {
|
||||
@inject(ILogger)
|
||||
protected logger: ILogger;
|
||||
|
||||
private execPath: string | undefined;
|
||||
|
||||
async getExecPath(): Promise<string> {
|
||||
const build = join(__dirname, '..', '..', 'build');
|
||||
return new Promise<string>((resolve, reject) => {
|
||||
which(`arduino-cli${os.platform() === 'win32' ? '.exe' : ''}`, { path: `${process.env.PATH}${delimiter}${build}` }, (err, path) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
return;
|
||||
}
|
||||
resolve(path);
|
||||
});
|
||||
});
|
||||
if (this.execPath) {
|
||||
return this.execPath;
|
||||
}
|
||||
const version = /\d+\.\d+\.\d+/;
|
||||
const cli = `arduino-cli${os.platform() === 'win32' ? '.exe' : ''}`;
|
||||
const buildCli = join(__dirname, '..', '..', 'build', cli);
|
||||
const buildVersion = await this.spawn(`"${buildCli}"`, ['version']);
|
||||
const buildShortVersion = (buildVersion.match(version) || [])[0];
|
||||
this.execPath = buildCli;
|
||||
const pathCli = await new Promise<string | undefined>(resolve => which(cli, (error, path) => resolve(error ? undefined : path)));
|
||||
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;
|
||||
}
|
||||
return buildCli;
|
||||
}
|
||||
|
||||
async getVersion(): Promise<string> {
|
||||
const execPath = await this.getExecPath();
|
||||
return this.spawn(`"${execPath}"`, ['version']);
|
||||
return new Promise<string>((resolve, reject) => {
|
||||
const buffers: Buffer[] = [];
|
||||
const cp = spawn(`"${execPath}"`, ['version'], { windowsHide: true, shell: true });
|
||||
cp.stdout.on('data', (b: Buffer) => buffers.push(b));
|
||||
cp.on('error', error => reject(error));
|
||||
cp.on('exit', (code, signal) => {
|
||||
if (code === 0) {
|
||||
const result = Buffer.concat(buffers).toString('utf8').trim()
|
||||
resolve(result);
|
||||
return;
|
||||
}
|
||||
if (signal) {
|
||||
reject(new Error(`Process exited with signal: ${signal}`));
|
||||
return;
|
||||
}
|
||||
if (code) {
|
||||
reject(new Error(`Process exited with exit code: ${code}`));
|
||||
return;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async getDefaultConfig(): Promise<Config> {
|
||||
|
@@ -1,15 +1,30 @@
|
||||
import * as PQueue from 'p-queue';
|
||||
import { injectable, inject, postConstruct, named } from 'inversify';
|
||||
import { ILogger } from '@theia/core/lib/common/logger';
|
||||
import { Deferred } from '@theia/core/lib/common/promise-util';
|
||||
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 { BoardListReq, BoardListResp } from './cli-protocol/commands/board_pb';
|
||||
import { ToolOutputServiceServer } from '../common/protocol/tool-output-service';
|
||||
import { Installable } from '../common/protocol/installable';
|
||||
|
||||
@injectable()
|
||||
export class BoardsServiceImpl implements BoardsService {
|
||||
|
||||
@inject(ILogger)
|
||||
protected logger: ILogger;
|
||||
|
||||
@inject(ILogger)
|
||||
@named('discovery')
|
||||
protected discoveryLogger: ILogger;
|
||||
@@ -21,14 +36,15 @@ export class BoardsServiceImpl implements BoardsService {
|
||||
protected readonly toolOutputService: ToolOutputServiceServer;
|
||||
|
||||
protected discoveryInitialized = false;
|
||||
protected discoveryTimer: NodeJS.Timeout | undefined;
|
||||
protected discoveryTimer: NodeJS.Timer | undefined;
|
||||
/**
|
||||
* Poor man's serial discovery:
|
||||
* Stores the state of the currently discovered, attached boards.
|
||||
* This state is updated via periodical polls.
|
||||
* Stores the state of the currently discovered and attached boards.
|
||||
* 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 _availablePorts: { ports: Port[] } = { ports: [] };
|
||||
protected attachedBoards: { boards: Board[] } = { boards: [] };
|
||||
protected availablePorts: { ports: Port[] } = { ports: [] };
|
||||
protected started = new Deferred<void>();
|
||||
protected client: BoardsServiceClient | undefined;
|
||||
protected readonly queue = new PQueue({ autoStart: true, concurrency: 1 });
|
||||
|
||||
@@ -38,8 +54,8 @@ export class BoardsServiceImpl implements BoardsService {
|
||||
this.discoveryLogger.trace('Discovering attached boards and available ports...');
|
||||
this.doGetAttachedBoardsAndAvailablePorts().then(({ boards, ports }) => {
|
||||
const update = (oldBoards: Board[], newBoards: Board[], oldPorts: Port[], newPorts: Port[], message: string) => {
|
||||
this._attachedBoards = { boards: newBoards };
|
||||
this._availablePorts = { ports: newPorts };
|
||||
this.attachedBoards = { boards: newBoards };
|
||||
this.availablePorts = { ports: newPorts };
|
||||
this.discoveryLogger.info(`${message} - Discovered boards: ${JSON.stringify(newBoards)} and available ports: ${JSON.stringify(newPorts)}`);
|
||||
if (this.client) {
|
||||
this.client.notifyAttachedBoardsChanged({
|
||||
@@ -60,6 +76,7 @@ export class BoardsServiceImpl implements BoardsService {
|
||||
if (!this.discoveryInitialized) {
|
||||
update([], sortedBoards, [], sortedPorts, 'Initialized attached boards and available ports.');
|
||||
this.discoveryInitialized = true;
|
||||
this.started.resolve();
|
||||
} else {
|
||||
Promise.all([
|
||||
this.getAttachedBoards(),
|
||||
@@ -95,17 +112,24 @@ export class BoardsServiceImpl implements BoardsService {
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.logger.info('>>> Disposing boards service...');
|
||||
this.queue.pause();
|
||||
this.queue.clear();
|
||||
if (this.discoveryTimer !== undefined) {
|
||||
clearInterval(this.discoveryTimer);
|
||||
}
|
||||
this.logger.info('<<< Disposed boards service.');
|
||||
this.client = undefined;
|
||||
}
|
||||
|
||||
async getAttachedBoards(): Promise<{ boards: Board[] }> {
|
||||
return this._attachedBoards;
|
||||
await this.started.promise;
|
||||
return this.attachedBoards;
|
||||
}
|
||||
|
||||
async getAvailablePorts(): Promise<{ ports: Port[] }> {
|
||||
return this._availablePorts;
|
||||
await this.started.promise;
|
||||
return this.availablePorts;
|
||||
}
|
||||
|
||||
private async doGetAttachedBoardsAndAvailablePorts(): Promise<{ boards: Board[], ports: Port[] }> {
|
||||
@@ -207,35 +231,76 @@ export class BoardsServiceImpl implements BoardsService {
|
||||
|
||||
const req = new PlatformSearchReq();
|
||||
req.setSearchArgs(options.query || "");
|
||||
req.setAllVersions(true);
|
||||
req.setInstance(instance);
|
||||
const resp = await new Promise<PlatformSearchResp>((resolve, reject) => client.platformSearch(req, (err, resp) => (!!err ? reject : resolve)(!!err ? err : resp)));
|
||||
|
||||
let items = resp.getSearchOutputList().map(item => {
|
||||
const packages = new Map<string, BoardPackage>();
|
||||
const toPackage = (platform: Platform) => {
|
||||
let installedVersion: string | undefined;
|
||||
const matchingPlatform = installedPlatforms.find(ip => ip.getId() === item.getId());
|
||||
const matchingPlatform = installedPlatforms.find(ip => ip.getId() === platform.getId());
|
||||
if (!!matchingPlatform) {
|
||||
installedVersion = matchingPlatform.getInstalled();
|
||||
}
|
||||
|
||||
const result: BoardPackage = {
|
||||
id: item.getId(),
|
||||
name: item.getName(),
|
||||
author: item.getMaintainer(),
|
||||
availableVersions: [item.getLatest()],
|
||||
description: item.getBoardsList().map(b => b.getName()).join(", "),
|
||||
return {
|
||||
id: platform.getId(),
|
||||
name: platform.getName(),
|
||||
author: platform.getMaintainer(),
|
||||
availableVersions: [platform.getLatest()],
|
||||
description: platform.getBoardsList().map(b => b.getName()).join(", "),
|
||||
installable: true,
|
||||
summary: "Boards included in this package:",
|
||||
installedVersion,
|
||||
boards: item.getBoardsList().map(b => <Board>{ name: b.getName(), fqbn: b.getFqbn() }),
|
||||
moreInfoLink: item.getWebsite()
|
||||
boards: platform.getBoardsList().map(b => <Board>{ name: b.getName(), fqbn: b.getFqbn() }),
|
||||
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();
|
||||
if (!coreClient) {
|
||||
return;
|
||||
@@ -248,7 +313,7 @@ export class BoardsServiceImpl implements BoardsService {
|
||||
req.setInstance(instance);
|
||||
req.setArchitecture(boardName);
|
||||
req.setPlatformPackage(platform);
|
||||
req.setVersion(pkg.availableVersions[0]);
|
||||
req.setVersion(version);
|
||||
|
||||
console.info("Starting board installation", pkg);
|
||||
const resp = client.platformInstall(req);
|
||||
@@ -268,4 +333,38 @@ export class BoardsServiceImpl implements BoardsService {
|
||||
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
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import * as jspb from "google-protobuf";
|
||||
import * as commands_common_pb from "../commands/common_pb";
|
||||
|
@@ -2,6 +2,7 @@
|
||||
// file: commands/commands.proto
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import * as grpc from "@grpc/grpc-js";
|
||||
import * as commands_commands_pb from "../commands/commands_pb";
|
||||
@@ -35,6 +36,7 @@ interface IArduinoCoreService extends grpc.ServiceDefinition<grpc.UntypedService
|
||||
libraryInstall: IArduinoCoreService_ILibraryInstall;
|
||||
libraryUninstall: IArduinoCoreService_ILibraryUninstall;
|
||||
libraryUpgradeAll: IArduinoCoreService_ILibraryUpgradeAll;
|
||||
libraryResolveDependencies: IArduinoCoreService_ILibraryResolveDependencies;
|
||||
librarySearch: IArduinoCoreService_ILibrarySearch;
|
||||
libraryList: IArduinoCoreService_ILibraryList;
|
||||
}
|
||||
@@ -237,6 +239,15 @@ interface IArduinoCoreService_ILibraryUpgradeAll extends grpc.MethodDefinition<c
|
||||
responseSerialize: grpc.serialize<commands_lib_pb.LibraryUpgradeAllResp>;
|
||||
responseDeserialize: grpc.deserialize<commands_lib_pb.LibraryUpgradeAllResp>;
|
||||
}
|
||||
interface IArduinoCoreService_ILibraryResolveDependencies extends grpc.MethodDefinition<commands_lib_pb.LibraryResolveDependenciesReq, commands_lib_pb.LibraryResolveDependenciesResp> {
|
||||
path: string; // "/cc.arduino.cli.commands.ArduinoCore/LibraryResolveDependencies"
|
||||
requestStream: boolean; // false
|
||||
responseStream: boolean; // false
|
||||
requestSerialize: grpc.serialize<commands_lib_pb.LibraryResolveDependenciesReq>;
|
||||
requestDeserialize: grpc.deserialize<commands_lib_pb.LibraryResolveDependenciesReq>;
|
||||
responseSerialize: grpc.serialize<commands_lib_pb.LibraryResolveDependenciesResp>;
|
||||
responseDeserialize: grpc.deserialize<commands_lib_pb.LibraryResolveDependenciesResp>;
|
||||
}
|
||||
interface IArduinoCoreService_ILibrarySearch extends grpc.MethodDefinition<commands_lib_pb.LibrarySearchReq, commands_lib_pb.LibrarySearchResp> {
|
||||
path: string; // "/cc.arduino.cli.commands.ArduinoCore/LibrarySearch"
|
||||
requestStream: boolean; // false
|
||||
@@ -281,6 +292,7 @@ export interface IArduinoCoreServer {
|
||||
libraryInstall: grpc.handleServerStreamingCall<commands_lib_pb.LibraryInstallReq, commands_lib_pb.LibraryInstallResp>;
|
||||
libraryUninstall: grpc.handleServerStreamingCall<commands_lib_pb.LibraryUninstallReq, commands_lib_pb.LibraryUninstallResp>;
|
||||
libraryUpgradeAll: grpc.handleServerStreamingCall<commands_lib_pb.LibraryUpgradeAllReq, commands_lib_pb.LibraryUpgradeAllResp>;
|
||||
libraryResolveDependencies: grpc.handleUnaryCall<commands_lib_pb.LibraryResolveDependenciesReq, commands_lib_pb.LibraryResolveDependenciesResp>;
|
||||
librarySearch: grpc.handleUnaryCall<commands_lib_pb.LibrarySearchReq, commands_lib_pb.LibrarySearchResp>;
|
||||
libraryList: grpc.handleUnaryCall<commands_lib_pb.LibraryListReq, commands_lib_pb.LibraryListResp>;
|
||||
}
|
||||
@@ -338,6 +350,9 @@ export interface IArduinoCoreClient {
|
||||
libraryUninstall(request: commands_lib_pb.LibraryUninstallReq, metadata?: grpc.Metadata, options?: Partial<grpc.CallOptions>): grpc.ClientReadableStream<commands_lib_pb.LibraryUninstallResp>;
|
||||
libraryUpgradeAll(request: commands_lib_pb.LibraryUpgradeAllReq, options?: Partial<grpc.CallOptions>): grpc.ClientReadableStream<commands_lib_pb.LibraryUpgradeAllResp>;
|
||||
libraryUpgradeAll(request: commands_lib_pb.LibraryUpgradeAllReq, metadata?: grpc.Metadata, options?: Partial<grpc.CallOptions>): grpc.ClientReadableStream<commands_lib_pb.LibraryUpgradeAllResp>;
|
||||
libraryResolveDependencies(request: commands_lib_pb.LibraryResolveDependenciesReq, callback: (error: grpc.ServiceError | null, response: commands_lib_pb.LibraryResolveDependenciesResp) => void): grpc.ClientUnaryCall;
|
||||
libraryResolveDependencies(request: commands_lib_pb.LibraryResolveDependenciesReq, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: commands_lib_pb.LibraryResolveDependenciesResp) => void): grpc.ClientUnaryCall;
|
||||
libraryResolveDependencies(request: commands_lib_pb.LibraryResolveDependenciesReq, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: commands_lib_pb.LibraryResolveDependenciesResp) => void): grpc.ClientUnaryCall;
|
||||
librarySearch(request: commands_lib_pb.LibrarySearchReq, callback: (error: grpc.ServiceError | null, response: commands_lib_pb.LibrarySearchResp) => void): grpc.ClientUnaryCall;
|
||||
librarySearch(request: commands_lib_pb.LibrarySearchReq, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: commands_lib_pb.LibrarySearchResp) => void): grpc.ClientUnaryCall;
|
||||
librarySearch(request: commands_lib_pb.LibrarySearchReq, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: commands_lib_pb.LibrarySearchResp) => void): grpc.ClientUnaryCall;
|
||||
@@ -400,6 +415,9 @@ export class ArduinoCoreClient extends grpc.Client implements IArduinoCoreClient
|
||||
public libraryUninstall(request: commands_lib_pb.LibraryUninstallReq, metadata?: grpc.Metadata, options?: Partial<grpc.CallOptions>): grpc.ClientReadableStream<commands_lib_pb.LibraryUninstallResp>;
|
||||
public libraryUpgradeAll(request: commands_lib_pb.LibraryUpgradeAllReq, options?: Partial<grpc.CallOptions>): grpc.ClientReadableStream<commands_lib_pb.LibraryUpgradeAllResp>;
|
||||
public libraryUpgradeAll(request: commands_lib_pb.LibraryUpgradeAllReq, metadata?: grpc.Metadata, options?: Partial<grpc.CallOptions>): grpc.ClientReadableStream<commands_lib_pb.LibraryUpgradeAllResp>;
|
||||
public libraryResolveDependencies(request: commands_lib_pb.LibraryResolveDependenciesReq, callback: (error: grpc.ServiceError | null, response: commands_lib_pb.LibraryResolveDependenciesResp) => void): grpc.ClientUnaryCall;
|
||||
public libraryResolveDependencies(request: commands_lib_pb.LibraryResolveDependenciesReq, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: commands_lib_pb.LibraryResolveDependenciesResp) => void): grpc.ClientUnaryCall;
|
||||
public libraryResolveDependencies(request: commands_lib_pb.LibraryResolveDependenciesReq, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: commands_lib_pb.LibraryResolveDependenciesResp) => void): grpc.ClientUnaryCall;
|
||||
public librarySearch(request: commands_lib_pb.LibrarySearchReq, callback: (error: grpc.ServiceError | null, response: commands_lib_pb.LibrarySearchResp) => void): grpc.ClientUnaryCall;
|
||||
public librarySearch(request: commands_lib_pb.LibrarySearchReq, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: commands_lib_pb.LibrarySearchResp) => void): grpc.ClientUnaryCall;
|
||||
public librarySearch(request: commands_lib_pb.LibrarySearchReq, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: commands_lib_pb.LibrarySearchResp) => void): grpc.ClientUnaryCall;
|
||||
|
@@ -248,6 +248,28 @@ function deserialize_cc_arduino_cli_commands_LibraryListResp(buffer_arg) {
|
||||
return commands_lib_pb.LibraryListResp.deserializeBinary(new Uint8Array(buffer_arg));
|
||||
}
|
||||
|
||||
function serialize_cc_arduino_cli_commands_LibraryResolveDependenciesReq(arg) {
|
||||
if (!(arg instanceof commands_lib_pb.LibraryResolveDependenciesReq)) {
|
||||
throw new Error('Expected argument of type cc.arduino.cli.commands.LibraryResolveDependenciesReq');
|
||||
}
|
||||
return Buffer.from(arg.serializeBinary());
|
||||
}
|
||||
|
||||
function deserialize_cc_arduino_cli_commands_LibraryResolveDependenciesReq(buffer_arg) {
|
||||
return commands_lib_pb.LibraryResolveDependenciesReq.deserializeBinary(new Uint8Array(buffer_arg));
|
||||
}
|
||||
|
||||
function serialize_cc_arduino_cli_commands_LibraryResolveDependenciesResp(arg) {
|
||||
if (!(arg instanceof commands_lib_pb.LibraryResolveDependenciesResp)) {
|
||||
throw new Error('Expected argument of type cc.arduino.cli.commands.LibraryResolveDependenciesResp');
|
||||
}
|
||||
return Buffer.from(arg.serializeBinary());
|
||||
}
|
||||
|
||||
function deserialize_cc_arduino_cli_commands_LibraryResolveDependenciesResp(buffer_arg) {
|
||||
return commands_lib_pb.LibraryResolveDependenciesResp.deserializeBinary(new Uint8Array(buffer_arg));
|
||||
}
|
||||
|
||||
function serialize_cc_arduino_cli_commands_LibrarySearchReq(arg) {
|
||||
if (!(arg instanceof commands_lib_pb.LibrarySearchReq)) {
|
||||
throw new Error('Expected argument of type cc.arduino.cli.commands.LibrarySearchReq');
|
||||
@@ -810,6 +832,17 @@ var ArduinoCoreService = exports.ArduinoCoreService = {
|
||||
responseSerialize: serialize_cc_arduino_cli_commands_LibraryUpgradeAllResp,
|
||||
responseDeserialize: deserialize_cc_arduino_cli_commands_LibraryUpgradeAllResp,
|
||||
},
|
||||
libraryResolveDependencies: {
|
||||
path: '/cc.arduino.cli.commands.ArduinoCore/LibraryResolveDependencies',
|
||||
requestStream: false,
|
||||
responseStream: false,
|
||||
requestType: commands_lib_pb.LibraryResolveDependenciesReq,
|
||||
responseType: commands_lib_pb.LibraryResolveDependenciesResp,
|
||||
requestSerialize: serialize_cc_arduino_cli_commands_LibraryResolveDependenciesReq,
|
||||
requestDeserialize: deserialize_cc_arduino_cli_commands_LibraryResolveDependenciesReq,
|
||||
responseSerialize: serialize_cc_arduino_cli_commands_LibraryResolveDependenciesResp,
|
||||
responseDeserialize: deserialize_cc_arduino_cli_commands_LibraryResolveDependenciesResp,
|
||||
},
|
||||
librarySearch: {
|
||||
path: '/cc.arduino.cli.commands.ArduinoCore/LibrarySearch',
|
||||
requestStream: false,
|
||||
|
@@ -2,6 +2,7 @@
|
||||
// file: commands/commands.proto
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import * as jspb from "google-protobuf";
|
||||
import * as commands_common_pb from "../commands/common_pb";
|
||||
|
@@ -2,6 +2,7 @@
|
||||
// file: commands/common.proto
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import * as jspb from "google-protobuf";
|
||||
|
||||
|
@@ -2,6 +2,7 @@
|
||||
// file: commands/compile.proto
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import * as jspb from "google-protobuf";
|
||||
import * as commands_common_pb from "../commands/common_pb";
|
||||
|
@@ -2,6 +2,7 @@
|
||||
// file: commands/core.proto
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import * as jspb from "google-protobuf";
|
||||
import * as commands_common_pb from "../commands/common_pb";
|
||||
@@ -262,6 +263,9 @@ export class PlatformSearchReq extends jspb.Message {
|
||||
getSearchArgs(): string;
|
||||
setSearchArgs(value: string): void;
|
||||
|
||||
getAllVersions(): boolean;
|
||||
setAllVersions(value: boolean): void;
|
||||
|
||||
|
||||
serializeBinary(): Uint8Array;
|
||||
toObject(includeInstance?: boolean): PlatformSearchReq.AsObject;
|
||||
@@ -277,6 +281,7 @@ export namespace PlatformSearchReq {
|
||||
export type AsObject = {
|
||||
instance?: commands_common_pb.Instance.AsObject,
|
||||
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) {
|
||||
var f, obj = {
|
||||
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) {
|
||||
@@ -1751,6 +1752,10 @@ proto.cc.arduino.cli.commands.PlatformSearchReq.deserializeBinaryFromReader = fu
|
||||
var value = /** @type {string} */ (reader.readString());
|
||||
msg.setSearchArgs(value);
|
||||
break;
|
||||
case 3:
|
||||
var value = /** @type {boolean} */ (reader.readBool());
|
||||
msg.setAllVersions(value);
|
||||
break;
|
||||
default:
|
||||
reader.skipField();
|
||||
break;
|
||||
@@ -1795,6 +1800,13 @@ proto.cc.arduino.cli.commands.PlatformSearchReq.serializeBinaryToWriter = functi
|
||||
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.
|
||||
|
@@ -2,6 +2,7 @@
|
||||
// file: commands/lib.proto
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import * as jspb from "google-protobuf";
|
||||
import * as commands_common_pb from "../commands/common_pb";
|
||||
@@ -236,6 +237,90 @@ export namespace LibraryUpgradeAllResp {
|
||||
}
|
||||
}
|
||||
|
||||
export class LibraryResolveDependenciesReq extends jspb.Message {
|
||||
|
||||
hasInstance(): boolean;
|
||||
clearInstance(): void;
|
||||
getInstance(): commands_common_pb.Instance | undefined;
|
||||
setInstance(value?: commands_common_pb.Instance): void;
|
||||
|
||||
getName(): string;
|
||||
setName(value: string): void;
|
||||
|
||||
getVersion(): string;
|
||||
setVersion(value: string): void;
|
||||
|
||||
|
||||
serializeBinary(): Uint8Array;
|
||||
toObject(includeInstance?: boolean): LibraryResolveDependenciesReq.AsObject;
|
||||
static toObject(includeInstance: boolean, msg: LibraryResolveDependenciesReq): LibraryResolveDependenciesReq.AsObject;
|
||||
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
|
||||
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
|
||||
static serializeBinaryToWriter(message: LibraryResolveDependenciesReq, writer: jspb.BinaryWriter): void;
|
||||
static deserializeBinary(bytes: Uint8Array): LibraryResolveDependenciesReq;
|
||||
static deserializeBinaryFromReader(message: LibraryResolveDependenciesReq, reader: jspb.BinaryReader): LibraryResolveDependenciesReq;
|
||||
}
|
||||
|
||||
export namespace LibraryResolveDependenciesReq {
|
||||
export type AsObject = {
|
||||
instance?: commands_common_pb.Instance.AsObject,
|
||||
name: string,
|
||||
version: string,
|
||||
}
|
||||
}
|
||||
|
||||
export class LibraryResolveDependenciesResp extends jspb.Message {
|
||||
clearDependenciesList(): void;
|
||||
getDependenciesList(): Array<LibraryDependencyStatus>;
|
||||
setDependenciesList(value: Array<LibraryDependencyStatus>): void;
|
||||
addDependencies(value?: LibraryDependencyStatus, index?: number): LibraryDependencyStatus;
|
||||
|
||||
|
||||
serializeBinary(): Uint8Array;
|
||||
toObject(includeInstance?: boolean): LibraryResolveDependenciesResp.AsObject;
|
||||
static toObject(includeInstance: boolean, msg: LibraryResolveDependenciesResp): LibraryResolveDependenciesResp.AsObject;
|
||||
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
|
||||
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
|
||||
static serializeBinaryToWriter(message: LibraryResolveDependenciesResp, writer: jspb.BinaryWriter): void;
|
||||
static deserializeBinary(bytes: Uint8Array): LibraryResolveDependenciesResp;
|
||||
static deserializeBinaryFromReader(message: LibraryResolveDependenciesResp, reader: jspb.BinaryReader): LibraryResolveDependenciesResp;
|
||||
}
|
||||
|
||||
export namespace LibraryResolveDependenciesResp {
|
||||
export type AsObject = {
|
||||
dependenciesList: Array<LibraryDependencyStatus.AsObject>,
|
||||
}
|
||||
}
|
||||
|
||||
export class LibraryDependencyStatus extends jspb.Message {
|
||||
getName(): string;
|
||||
setName(value: string): void;
|
||||
|
||||
getVersionrequired(): string;
|
||||
setVersionrequired(value: string): void;
|
||||
|
||||
getVersioninstalled(): string;
|
||||
setVersioninstalled(value: string): void;
|
||||
|
||||
|
||||
serializeBinary(): Uint8Array;
|
||||
toObject(includeInstance?: boolean): LibraryDependencyStatus.AsObject;
|
||||
static toObject(includeInstance: boolean, msg: LibraryDependencyStatus): LibraryDependencyStatus.AsObject;
|
||||
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
|
||||
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
|
||||
static serializeBinaryToWriter(message: LibraryDependencyStatus, writer: jspb.BinaryWriter): void;
|
||||
static deserializeBinary(bytes: Uint8Array): LibraryDependencyStatus;
|
||||
static deserializeBinaryFromReader(message: LibraryDependencyStatus, reader: jspb.BinaryReader): LibraryDependencyStatus;
|
||||
}
|
||||
|
||||
export namespace LibraryDependencyStatus {
|
||||
export type AsObject = {
|
||||
name: string,
|
||||
versionrequired: string,
|
||||
versioninstalled: string,
|
||||
}
|
||||
}
|
||||
|
||||
export class LibrarySearchReq extends jspb.Message {
|
||||
|
||||
hasInstance(): boolean;
|
||||
|
@@ -16,6 +16,7 @@ goog.object.extend(proto, commands_common_pb);
|
||||
goog.exportSymbol('proto.cc.arduino.cli.commands.DownloadResource', null, global);
|
||||
goog.exportSymbol('proto.cc.arduino.cli.commands.InstalledLibrary', null, global);
|
||||
goog.exportSymbol('proto.cc.arduino.cli.commands.Library', null, global);
|
||||
goog.exportSymbol('proto.cc.arduino.cli.commands.LibraryDependencyStatus', null, global);
|
||||
goog.exportSymbol('proto.cc.arduino.cli.commands.LibraryDownloadReq', null, global);
|
||||
goog.exportSymbol('proto.cc.arduino.cli.commands.LibraryDownloadResp', null, global);
|
||||
goog.exportSymbol('proto.cc.arduino.cli.commands.LibraryInstallReq', null, global);
|
||||
@@ -25,6 +26,8 @@ goog.exportSymbol('proto.cc.arduino.cli.commands.LibraryListReq', null, global);
|
||||
goog.exportSymbol('proto.cc.arduino.cli.commands.LibraryListResp', null, global);
|
||||
goog.exportSymbol('proto.cc.arduino.cli.commands.LibraryLocation', null, global);
|
||||
goog.exportSymbol('proto.cc.arduino.cli.commands.LibraryRelease', null, global);
|
||||
goog.exportSymbol('proto.cc.arduino.cli.commands.LibraryResolveDependenciesReq', null, global);
|
||||
goog.exportSymbol('proto.cc.arduino.cli.commands.LibraryResolveDependenciesResp', null, global);
|
||||
goog.exportSymbol('proto.cc.arduino.cli.commands.LibrarySearchReq', null, global);
|
||||
goog.exportSymbol('proto.cc.arduino.cli.commands.LibrarySearchResp', null, global);
|
||||
goog.exportSymbol('proto.cc.arduino.cli.commands.LibraryUninstallReq', null, global);
|
||||
@@ -1555,6 +1558,583 @@ proto.cc.arduino.cli.commands.LibraryUpgradeAllResp.prototype.hasTaskProgress =
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Generated by JsPbCodeGenerator.
|
||||
* @param {Array=} opt_data Optional initial data array, typically from a
|
||||
* server response, or constructed directly in Javascript. The array is used
|
||||
* in place and becomes part of the constructed object. It is not cloned.
|
||||
* If no data is provided, the constructed object will be empty, but still
|
||||
* valid.
|
||||
* @extends {jspb.Message}
|
||||
* @constructor
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.LibraryResolveDependenciesReq = function(opt_data) {
|
||||
jspb.Message.initialize(this, opt_data, 0, -1, null, null);
|
||||
};
|
||||
goog.inherits(proto.cc.arduino.cli.commands.LibraryResolveDependenciesReq, jspb.Message);
|
||||
if (goog.DEBUG && !COMPILED) {
|
||||
proto.cc.arduino.cli.commands.LibraryResolveDependenciesReq.displayName = 'proto.cc.arduino.cli.commands.LibraryResolveDependenciesReq';
|
||||
}
|
||||
|
||||
|
||||
if (jspb.Message.GENERATE_TO_OBJECT) {
|
||||
/**
|
||||
* Creates an object representation of this proto suitable for use in Soy templates.
|
||||
* Field names that are reserved in JavaScript and will be renamed to pb_name.
|
||||
* To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.
|
||||
* For the list of reserved names please see:
|
||||
* com.google.apps.jspb.JsClassTemplate.JS_RESERVED_WORDS.
|
||||
* @param {boolean=} opt_includeInstance Whether to include the JSPB instance
|
||||
* for transitional soy proto support: http://goto/soy-param-migration
|
||||
* @return {!Object}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.LibraryResolveDependenciesReq.prototype.toObject = function(opt_includeInstance) {
|
||||
return proto.cc.arduino.cli.commands.LibraryResolveDependenciesReq.toObject(opt_includeInstance, this);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Static version of the {@see toObject} method.
|
||||
* @param {boolean|undefined} includeInstance Whether to include the JSPB
|
||||
* instance for transitional soy proto support:
|
||||
* http://goto/soy-param-migration
|
||||
* @param {!proto.cc.arduino.cli.commands.LibraryResolveDependenciesReq} msg The msg instance to transform.
|
||||
* @return {!Object}
|
||||
* @suppress {unusedLocalVariables} f is only used for nested messages
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.LibraryResolveDependenciesReq.toObject = function(includeInstance, msg) {
|
||||
var f, obj = {
|
||||
instance: (f = msg.getInstance()) && commands_common_pb.Instance.toObject(includeInstance, f),
|
||||
name: jspb.Message.getFieldWithDefault(msg, 2, ""),
|
||||
version: jspb.Message.getFieldWithDefault(msg, 3, "")
|
||||
};
|
||||
|
||||
if (includeInstance) {
|
||||
obj.$jspbMessageInstance = msg;
|
||||
}
|
||||
return obj;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Deserializes binary data (in protobuf wire format).
|
||||
* @param {jspb.ByteSource} bytes The bytes to deserialize.
|
||||
* @return {!proto.cc.arduino.cli.commands.LibraryResolveDependenciesReq}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.LibraryResolveDependenciesReq.deserializeBinary = function(bytes) {
|
||||
var reader = new jspb.BinaryReader(bytes);
|
||||
var msg = new proto.cc.arduino.cli.commands.LibraryResolveDependenciesReq;
|
||||
return proto.cc.arduino.cli.commands.LibraryResolveDependenciesReq.deserializeBinaryFromReader(msg, reader);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Deserializes binary data (in protobuf wire format) from the
|
||||
* given reader into the given message object.
|
||||
* @param {!proto.cc.arduino.cli.commands.LibraryResolveDependenciesReq} msg The message object to deserialize into.
|
||||
* @param {!jspb.BinaryReader} reader The BinaryReader to use.
|
||||
* @return {!proto.cc.arduino.cli.commands.LibraryResolveDependenciesReq}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.LibraryResolveDependenciesReq.deserializeBinaryFromReader = function(msg, reader) {
|
||||
while (reader.nextField()) {
|
||||
if (reader.isEndGroup()) {
|
||||
break;
|
||||
}
|
||||
var field = reader.getFieldNumber();
|
||||
switch (field) {
|
||||
case 1:
|
||||
var value = new commands_common_pb.Instance;
|
||||
reader.readMessage(value,commands_common_pb.Instance.deserializeBinaryFromReader);
|
||||
msg.setInstance(value);
|
||||
break;
|
||||
case 2:
|
||||
var value = /** @type {string} */ (reader.readString());
|
||||
msg.setName(value);
|
||||
break;
|
||||
case 3:
|
||||
var value = /** @type {string} */ (reader.readString());
|
||||
msg.setVersion(value);
|
||||
break;
|
||||
default:
|
||||
reader.skipField();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return msg;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Serializes the message to binary data (in protobuf wire format).
|
||||
* @return {!Uint8Array}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.LibraryResolveDependenciesReq.prototype.serializeBinary = function() {
|
||||
var writer = new jspb.BinaryWriter();
|
||||
proto.cc.arduino.cli.commands.LibraryResolveDependenciesReq.serializeBinaryToWriter(this, writer);
|
||||
return writer.getResultBuffer();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Serializes the given message to binary data (in protobuf wire
|
||||
* format), writing to the given BinaryWriter.
|
||||
* @param {!proto.cc.arduino.cli.commands.LibraryResolveDependenciesReq} message
|
||||
* @param {!jspb.BinaryWriter} writer
|
||||
* @suppress {unusedLocalVariables} f is only used for nested messages
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.LibraryResolveDependenciesReq.serializeBinaryToWriter = function(message, writer) {
|
||||
var f = undefined;
|
||||
f = message.getInstance();
|
||||
if (f != null) {
|
||||
writer.writeMessage(
|
||||
1,
|
||||
f,
|
||||
commands_common_pb.Instance.serializeBinaryToWriter
|
||||
);
|
||||
}
|
||||
f = message.getName();
|
||||
if (f.length > 0) {
|
||||
writer.writeString(
|
||||
2,
|
||||
f
|
||||
);
|
||||
}
|
||||
f = message.getVersion();
|
||||
if (f.length > 0) {
|
||||
writer.writeString(
|
||||
3,
|
||||
f
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional Instance instance = 1;
|
||||
* @return {?proto.cc.arduino.cli.commands.Instance}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.LibraryResolveDependenciesReq.prototype.getInstance = function() {
|
||||
return /** @type{?proto.cc.arduino.cli.commands.Instance} */ (
|
||||
jspb.Message.getWrapperField(this, commands_common_pb.Instance, 1));
|
||||
};
|
||||
|
||||
|
||||
/** @param {?proto.cc.arduino.cli.commands.Instance|undefined} value */
|
||||
proto.cc.arduino.cli.commands.LibraryResolveDependenciesReq.prototype.setInstance = function(value) {
|
||||
jspb.Message.setWrapperField(this, 1, value);
|
||||
};
|
||||
|
||||
|
||||
proto.cc.arduino.cli.commands.LibraryResolveDependenciesReq.prototype.clearInstance = function() {
|
||||
this.setInstance(undefined);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether this field is set.
|
||||
* @return {boolean}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.LibraryResolveDependenciesReq.prototype.hasInstance = function() {
|
||||
return jspb.Message.getField(this, 1) != null;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional string name = 2;
|
||||
* @return {string}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.LibraryResolveDependenciesReq.prototype.getName = function() {
|
||||
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, ""));
|
||||
};
|
||||
|
||||
|
||||
/** @param {string} value */
|
||||
proto.cc.arduino.cli.commands.LibraryResolveDependenciesReq.prototype.setName = function(value) {
|
||||
jspb.Message.setProto3StringField(this, 2, value);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional string version = 3;
|
||||
* @return {string}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.LibraryResolveDependenciesReq.prototype.getVersion = function() {
|
||||
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 3, ""));
|
||||
};
|
||||
|
||||
|
||||
/** @param {string} value */
|
||||
proto.cc.arduino.cli.commands.LibraryResolveDependenciesReq.prototype.setVersion = function(value) {
|
||||
jspb.Message.setProto3StringField(this, 3, value);
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Generated by JsPbCodeGenerator.
|
||||
* @param {Array=} opt_data Optional initial data array, typically from a
|
||||
* server response, or constructed directly in Javascript. The array is used
|
||||
* in place and becomes part of the constructed object. It is not cloned.
|
||||
* If no data is provided, the constructed object will be empty, but still
|
||||
* valid.
|
||||
* @extends {jspb.Message}
|
||||
* @constructor
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.LibraryResolveDependenciesResp = function(opt_data) {
|
||||
jspb.Message.initialize(this, opt_data, 0, -1, proto.cc.arduino.cli.commands.LibraryResolveDependenciesResp.repeatedFields_, null);
|
||||
};
|
||||
goog.inherits(proto.cc.arduino.cli.commands.LibraryResolveDependenciesResp, jspb.Message);
|
||||
if (goog.DEBUG && !COMPILED) {
|
||||
proto.cc.arduino.cli.commands.LibraryResolveDependenciesResp.displayName = 'proto.cc.arduino.cli.commands.LibraryResolveDependenciesResp';
|
||||
}
|
||||
/**
|
||||
* List of repeated fields within this message type.
|
||||
* @private {!Array<number>}
|
||||
* @const
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.LibraryResolveDependenciesResp.repeatedFields_ = [1];
|
||||
|
||||
|
||||
|
||||
if (jspb.Message.GENERATE_TO_OBJECT) {
|
||||
/**
|
||||
* Creates an object representation of this proto suitable for use in Soy templates.
|
||||
* Field names that are reserved in JavaScript and will be renamed to pb_name.
|
||||
* To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.
|
||||
* For the list of reserved names please see:
|
||||
* com.google.apps.jspb.JsClassTemplate.JS_RESERVED_WORDS.
|
||||
* @param {boolean=} opt_includeInstance Whether to include the JSPB instance
|
||||
* for transitional soy proto support: http://goto/soy-param-migration
|
||||
* @return {!Object}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.LibraryResolveDependenciesResp.prototype.toObject = function(opt_includeInstance) {
|
||||
return proto.cc.arduino.cli.commands.LibraryResolveDependenciesResp.toObject(opt_includeInstance, this);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Static version of the {@see toObject} method.
|
||||
* @param {boolean|undefined} includeInstance Whether to include the JSPB
|
||||
* instance for transitional soy proto support:
|
||||
* http://goto/soy-param-migration
|
||||
* @param {!proto.cc.arduino.cli.commands.LibraryResolveDependenciesResp} msg The msg instance to transform.
|
||||
* @return {!Object}
|
||||
* @suppress {unusedLocalVariables} f is only used for nested messages
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.LibraryResolveDependenciesResp.toObject = function(includeInstance, msg) {
|
||||
var f, obj = {
|
||||
dependenciesList: jspb.Message.toObjectList(msg.getDependenciesList(),
|
||||
proto.cc.arduino.cli.commands.LibraryDependencyStatus.toObject, includeInstance)
|
||||
};
|
||||
|
||||
if (includeInstance) {
|
||||
obj.$jspbMessageInstance = msg;
|
||||
}
|
||||
return obj;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Deserializes binary data (in protobuf wire format).
|
||||
* @param {jspb.ByteSource} bytes The bytes to deserialize.
|
||||
* @return {!proto.cc.arduino.cli.commands.LibraryResolveDependenciesResp}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.LibraryResolveDependenciesResp.deserializeBinary = function(bytes) {
|
||||
var reader = new jspb.BinaryReader(bytes);
|
||||
var msg = new proto.cc.arduino.cli.commands.LibraryResolveDependenciesResp;
|
||||
return proto.cc.arduino.cli.commands.LibraryResolveDependenciesResp.deserializeBinaryFromReader(msg, reader);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Deserializes binary data (in protobuf wire format) from the
|
||||
* given reader into the given message object.
|
||||
* @param {!proto.cc.arduino.cli.commands.LibraryResolveDependenciesResp} msg The message object to deserialize into.
|
||||
* @param {!jspb.BinaryReader} reader The BinaryReader to use.
|
||||
* @return {!proto.cc.arduino.cli.commands.LibraryResolveDependenciesResp}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.LibraryResolveDependenciesResp.deserializeBinaryFromReader = function(msg, reader) {
|
||||
while (reader.nextField()) {
|
||||
if (reader.isEndGroup()) {
|
||||
break;
|
||||
}
|
||||
var field = reader.getFieldNumber();
|
||||
switch (field) {
|
||||
case 1:
|
||||
var value = new proto.cc.arduino.cli.commands.LibraryDependencyStatus;
|
||||
reader.readMessage(value,proto.cc.arduino.cli.commands.LibraryDependencyStatus.deserializeBinaryFromReader);
|
||||
msg.addDependencies(value);
|
||||
break;
|
||||
default:
|
||||
reader.skipField();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return msg;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Serializes the message to binary data (in protobuf wire format).
|
||||
* @return {!Uint8Array}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.LibraryResolveDependenciesResp.prototype.serializeBinary = function() {
|
||||
var writer = new jspb.BinaryWriter();
|
||||
proto.cc.arduino.cli.commands.LibraryResolveDependenciesResp.serializeBinaryToWriter(this, writer);
|
||||
return writer.getResultBuffer();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Serializes the given message to binary data (in protobuf wire
|
||||
* format), writing to the given BinaryWriter.
|
||||
* @param {!proto.cc.arduino.cli.commands.LibraryResolveDependenciesResp} message
|
||||
* @param {!jspb.BinaryWriter} writer
|
||||
* @suppress {unusedLocalVariables} f is only used for nested messages
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.LibraryResolveDependenciesResp.serializeBinaryToWriter = function(message, writer) {
|
||||
var f = undefined;
|
||||
f = message.getDependenciesList();
|
||||
if (f.length > 0) {
|
||||
writer.writeRepeatedMessage(
|
||||
1,
|
||||
f,
|
||||
proto.cc.arduino.cli.commands.LibraryDependencyStatus.serializeBinaryToWriter
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* repeated LibraryDependencyStatus dependencies = 1;
|
||||
* @return {!Array<!proto.cc.arduino.cli.commands.LibraryDependencyStatus>}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.LibraryResolveDependenciesResp.prototype.getDependenciesList = function() {
|
||||
return /** @type{!Array<!proto.cc.arduino.cli.commands.LibraryDependencyStatus>} */ (
|
||||
jspb.Message.getRepeatedWrapperField(this, proto.cc.arduino.cli.commands.LibraryDependencyStatus, 1));
|
||||
};
|
||||
|
||||
|
||||
/** @param {!Array<!proto.cc.arduino.cli.commands.LibraryDependencyStatus>} value */
|
||||
proto.cc.arduino.cli.commands.LibraryResolveDependenciesResp.prototype.setDependenciesList = function(value) {
|
||||
jspb.Message.setRepeatedWrapperField(this, 1, value);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {!proto.cc.arduino.cli.commands.LibraryDependencyStatus=} opt_value
|
||||
* @param {number=} opt_index
|
||||
* @return {!proto.cc.arduino.cli.commands.LibraryDependencyStatus}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.LibraryResolveDependenciesResp.prototype.addDependencies = function(opt_value, opt_index) {
|
||||
return jspb.Message.addToRepeatedWrapperField(this, 1, opt_value, proto.cc.arduino.cli.commands.LibraryDependencyStatus, opt_index);
|
||||
};
|
||||
|
||||
|
||||
proto.cc.arduino.cli.commands.LibraryResolveDependenciesResp.prototype.clearDependenciesList = function() {
|
||||
this.setDependenciesList([]);
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Generated by JsPbCodeGenerator.
|
||||
* @param {Array=} opt_data Optional initial data array, typically from a
|
||||
* server response, or constructed directly in Javascript. The array is used
|
||||
* in place and becomes part of the constructed object. It is not cloned.
|
||||
* If no data is provided, the constructed object will be empty, but still
|
||||
* valid.
|
||||
* @extends {jspb.Message}
|
||||
* @constructor
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.LibraryDependencyStatus = function(opt_data) {
|
||||
jspb.Message.initialize(this, opt_data, 0, -1, null, null);
|
||||
};
|
||||
goog.inherits(proto.cc.arduino.cli.commands.LibraryDependencyStatus, jspb.Message);
|
||||
if (goog.DEBUG && !COMPILED) {
|
||||
proto.cc.arduino.cli.commands.LibraryDependencyStatus.displayName = 'proto.cc.arduino.cli.commands.LibraryDependencyStatus';
|
||||
}
|
||||
|
||||
|
||||
if (jspb.Message.GENERATE_TO_OBJECT) {
|
||||
/**
|
||||
* Creates an object representation of this proto suitable for use in Soy templates.
|
||||
* Field names that are reserved in JavaScript and will be renamed to pb_name.
|
||||
* To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.
|
||||
* For the list of reserved names please see:
|
||||
* com.google.apps.jspb.JsClassTemplate.JS_RESERVED_WORDS.
|
||||
* @param {boolean=} opt_includeInstance Whether to include the JSPB instance
|
||||
* for transitional soy proto support: http://goto/soy-param-migration
|
||||
* @return {!Object}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.LibraryDependencyStatus.prototype.toObject = function(opt_includeInstance) {
|
||||
return proto.cc.arduino.cli.commands.LibraryDependencyStatus.toObject(opt_includeInstance, this);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Static version of the {@see toObject} method.
|
||||
* @param {boolean|undefined} includeInstance Whether to include the JSPB
|
||||
* instance for transitional soy proto support:
|
||||
* http://goto/soy-param-migration
|
||||
* @param {!proto.cc.arduino.cli.commands.LibraryDependencyStatus} msg The msg instance to transform.
|
||||
* @return {!Object}
|
||||
* @suppress {unusedLocalVariables} f is only used for nested messages
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.LibraryDependencyStatus.toObject = function(includeInstance, msg) {
|
||||
var f, obj = {
|
||||
name: jspb.Message.getFieldWithDefault(msg, 1, ""),
|
||||
versionrequired: jspb.Message.getFieldWithDefault(msg, 2, ""),
|
||||
versioninstalled: jspb.Message.getFieldWithDefault(msg, 3, "")
|
||||
};
|
||||
|
||||
if (includeInstance) {
|
||||
obj.$jspbMessageInstance = msg;
|
||||
}
|
||||
return obj;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Deserializes binary data (in protobuf wire format).
|
||||
* @param {jspb.ByteSource} bytes The bytes to deserialize.
|
||||
* @return {!proto.cc.arduino.cli.commands.LibraryDependencyStatus}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.LibraryDependencyStatus.deserializeBinary = function(bytes) {
|
||||
var reader = new jspb.BinaryReader(bytes);
|
||||
var msg = new proto.cc.arduino.cli.commands.LibraryDependencyStatus;
|
||||
return proto.cc.arduino.cli.commands.LibraryDependencyStatus.deserializeBinaryFromReader(msg, reader);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Deserializes binary data (in protobuf wire format) from the
|
||||
* given reader into the given message object.
|
||||
* @param {!proto.cc.arduino.cli.commands.LibraryDependencyStatus} msg The message object to deserialize into.
|
||||
* @param {!jspb.BinaryReader} reader The BinaryReader to use.
|
||||
* @return {!proto.cc.arduino.cli.commands.LibraryDependencyStatus}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.LibraryDependencyStatus.deserializeBinaryFromReader = function(msg, reader) {
|
||||
while (reader.nextField()) {
|
||||
if (reader.isEndGroup()) {
|
||||
break;
|
||||
}
|
||||
var field = reader.getFieldNumber();
|
||||
switch (field) {
|
||||
case 1:
|
||||
var value = /** @type {string} */ (reader.readString());
|
||||
msg.setName(value);
|
||||
break;
|
||||
case 2:
|
||||
var value = /** @type {string} */ (reader.readString());
|
||||
msg.setVersionrequired(value);
|
||||
break;
|
||||
case 3:
|
||||
var value = /** @type {string} */ (reader.readString());
|
||||
msg.setVersioninstalled(value);
|
||||
break;
|
||||
default:
|
||||
reader.skipField();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return msg;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Serializes the message to binary data (in protobuf wire format).
|
||||
* @return {!Uint8Array}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.LibraryDependencyStatus.prototype.serializeBinary = function() {
|
||||
var writer = new jspb.BinaryWriter();
|
||||
proto.cc.arduino.cli.commands.LibraryDependencyStatus.serializeBinaryToWriter(this, writer);
|
||||
return writer.getResultBuffer();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Serializes the given message to binary data (in protobuf wire
|
||||
* format), writing to the given BinaryWriter.
|
||||
* @param {!proto.cc.arduino.cli.commands.LibraryDependencyStatus} message
|
||||
* @param {!jspb.BinaryWriter} writer
|
||||
* @suppress {unusedLocalVariables} f is only used for nested messages
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.LibraryDependencyStatus.serializeBinaryToWriter = function(message, writer) {
|
||||
var f = undefined;
|
||||
f = message.getName();
|
||||
if (f.length > 0) {
|
||||
writer.writeString(
|
||||
1,
|
||||
f
|
||||
);
|
||||
}
|
||||
f = message.getVersionrequired();
|
||||
if (f.length > 0) {
|
||||
writer.writeString(
|
||||
2,
|
||||
f
|
||||
);
|
||||
}
|
||||
f = message.getVersioninstalled();
|
||||
if (f.length > 0) {
|
||||
writer.writeString(
|
||||
3,
|
||||
f
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional string name = 1;
|
||||
* @return {string}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.LibraryDependencyStatus.prototype.getName = function() {
|
||||
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, ""));
|
||||
};
|
||||
|
||||
|
||||
/** @param {string} value */
|
||||
proto.cc.arduino.cli.commands.LibraryDependencyStatus.prototype.setName = function(value) {
|
||||
jspb.Message.setProto3StringField(this, 1, value);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional string versionRequired = 2;
|
||||
* @return {string}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.LibraryDependencyStatus.prototype.getVersionrequired = function() {
|
||||
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, ""));
|
||||
};
|
||||
|
||||
|
||||
/** @param {string} value */
|
||||
proto.cc.arduino.cli.commands.LibraryDependencyStatus.prototype.setVersionrequired = function(value) {
|
||||
jspb.Message.setProto3StringField(this, 2, value);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional string versionInstalled = 3;
|
||||
* @return {string}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.LibraryDependencyStatus.prototype.getVersioninstalled = function() {
|
||||
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 3, ""));
|
||||
};
|
||||
|
||||
|
||||
/** @param {string} value */
|
||||
proto.cc.arduino.cli.commands.LibraryDependencyStatus.prototype.setVersioninstalled = function(value) {
|
||||
jspb.Message.setProto3StringField(this, 3, value);
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Generated by JsPbCodeGenerator.
|
||||
* @param {Array=} opt_data Optional initial data array, typically from a
|
||||
|
@@ -2,6 +2,7 @@
|
||||
// file: commands/upload.proto
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import * as jspb from "google-protobuf";
|
||||
import * as commands_common_pb from "../commands/common_pb";
|
||||
|
@@ -2,6 +2,7 @@
|
||||
// file: monitor/monitor.proto
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import * as grpc from "@grpc/grpc-js";
|
||||
import * as monitor_monitor_pb from "../monitor/monitor_pb";
|
||||
|
@@ -2,6 +2,7 @@
|
||||
// file: monitor/monitor.proto
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import * as jspb from "google-protobuf";
|
||||
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> {
|
||||
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) {
|
||||
resolve(this.getOrCreateClient(roots[0]));
|
||||
return;
|
||||
|
@@ -1,9 +1,20 @@
|
||||
import { injectable, inject } from 'inversify';
|
||||
import { Library, LibraryService } from '../common/protocol/library-service';
|
||||
import { CoreClientProvider } from './core-client-provider';
|
||||
import { LibrarySearchReq, LibrarySearchResp, LibraryListReq, LibraryListResp, LibraryRelease,
|
||||
InstalledLibrary, LibraryInstallReq, LibraryInstallResp } from './cli-protocol/commands/lib_pb';
|
||||
import {
|
||||
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 { Installable } from '../common/protocol/installable';
|
||||
|
||||
@injectable()
|
||||
export class LibraryServiceImpl implements LibraryService {
|
||||
@@ -43,6 +54,8 @@ export class LibraryServiceImpl implements LibraryService {
|
||||
.filter(item => !!item.getLatest())
|
||||
.slice(0, 50)
|
||||
.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;
|
||||
const installed = installedLibsIdx.get(item.getName());
|
||||
if (installed) {
|
||||
@@ -51,14 +64,16 @@ export class LibraryServiceImpl implements LibraryService {
|
||||
return toLibrary({
|
||||
name: item.getName(),
|
||||
installable: true,
|
||||
installedVersion
|
||||
}, item.getLatest()!)
|
||||
installedVersion,
|
||||
}, item.getLatest()!, availableVersions)
|
||||
})
|
||||
|
||||
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();
|
||||
if (!coreClient) {
|
||||
return;
|
||||
@@ -68,7 +83,7 @@ export class LibraryServiceImpl implements LibraryService {
|
||||
const req = new LibraryInstallReq();
|
||||
req.setInstance(instance);
|
||||
req.setName(library.name);
|
||||
req.setVersion(library.availableVersions[0]);
|
||||
req.setVersion(version);
|
||||
|
||||
const resp = client.libraryInstall(req);
|
||||
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 {
|
||||
name: "",
|
||||
installable: false,
|
||||
...tpl,
|
||||
|
||||
author: release.getAuthor(),
|
||||
availableVersions: [release.getVersion()],
|
||||
availableVersions,
|
||||
description: release.getSentence(),
|
||||
moreInfoLink: release.getWebsite(),
|
||||
summary: release.getParagraph()
|
||||
|
@@ -1,40 +1,37 @@
|
||||
import { v4 } from 'uuid';
|
||||
import * as grpc from '@grpc/grpc-js';
|
||||
import { ClientDuplexStream } from '@grpc/grpc-js';
|
||||
import { TextDecoder, TextEncoder } from 'util';
|
||||
import { injectable, inject, named } from 'inversify';
|
||||
import { ILogger, Disposable, DisposableCollection } from '@theia/core';
|
||||
import { MonitorService, MonitorServiceClient, ConnectionConfig, ConnectionType } from '../../common/protocol/monitor-service';
|
||||
import { StreamingOpenReq, StreamingOpenResp, MonitorConfig } from '../cli-protocol/monitor/monitor_pb';
|
||||
import { Struct } from 'google-protobuf/google/protobuf/struct_pb';
|
||||
import { ILogger } from '@theia/core/lib/common/logger';
|
||||
import { MonitorService, MonitorServiceClient, MonitorConfig, MonitorError, Status } from '../../common/protocol/monitor-service';
|
||||
import { StreamingOpenReq, StreamingOpenResp, MonitorConfig as GrpcMonitorConfig } from '../cli-protocol/monitor/monitor_pb';
|
||||
import { MonitorClientProvider } from './monitor-client-provider';
|
||||
import * as google_protobuf_struct_pb from "google-protobuf/google/protobuf/struct_pb";
|
||||
import { Board, Port } from '../../common/protocol/boards-service';
|
||||
|
||||
export interface MonitorDuplex {
|
||||
readonly toDispose: Disposable;
|
||||
readonly duplex: grpc.ClientDuplexStream<StreamingOpenReq, StreamingOpenResp>;
|
||||
interface ErrorWithCode extends Error {
|
||||
readonly code: number;
|
||||
}
|
||||
|
||||
type ErrorCode = { code: number };
|
||||
type MonitorError = Error & ErrorCode;
|
||||
namespace MonitorError {
|
||||
|
||||
export function is(error: Error & Partial<ErrorCode>): error is MonitorError {
|
||||
namespace ErrorWithCode {
|
||||
export function toMonitorError(error: Error, config: MonitorConfig): MonitorError {
|
||||
const { message } = error;
|
||||
let code = undefined;
|
||||
if (is(error)) {
|
||||
// TODO: const `mapping`. Use regex for the `message`.
|
||||
const mapping = new Map<string, number>();
|
||||
mapping.set('1 CANCELLED: Cancelled on client', MonitorError.ErrorCodes.CLIENT_CANCEL);
|
||||
mapping.set('2 UNKNOWN: device not configured', MonitorError.ErrorCodes.DEVICE_NOT_CONFIGURED);
|
||||
mapping.set('2 UNKNOWN: error opening serial monitor: Serial port busy', MonitorError.ErrorCodes.DEVICE_BUSY);
|
||||
code = mapping.get(message);
|
||||
}
|
||||
return {
|
||||
message,
|
||||
code,
|
||||
config
|
||||
};
|
||||
}
|
||||
function is(error: Error & { code?: number }): error is ErrorWithCode {
|
||||
return typeof error.code === 'number';
|
||||
}
|
||||
|
||||
/**
|
||||
* The frontend has refreshed the browser, for instance.
|
||||
*/
|
||||
export function isClientCancelledError(error: MonitorError): boolean {
|
||||
return error.code === 1 && error.message === 'Cancelled on client';
|
||||
}
|
||||
|
||||
/**
|
||||
* When detaching a physical device when the duplex channel is still opened.
|
||||
*/
|
||||
export function isDeviceNotConfiguredError(error: MonitorError): boolean {
|
||||
return error.code === 2 && error.message === 'device not configured';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@injectable()
|
||||
@@ -48,124 +45,111 @@ export class MonitorServiceImpl implements MonitorService {
|
||||
protected readonly monitorClientProvider: MonitorClientProvider;
|
||||
|
||||
protected client?: MonitorServiceClient;
|
||||
protected readonly connections = new Map<string, MonitorDuplex>();
|
||||
protected connection?: { duplex: ClientDuplexStream<StreamingOpenReq, StreamingOpenResp>, config: MonitorConfig };
|
||||
|
||||
setClient(client: MonitorServiceClient | undefined): void {
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
for (const [connectionId, duplex] of this.connections.entries()) {
|
||||
this.doDisconnect(connectionId, duplex);
|
||||
this.logger.info('>>> Disposing monitor service...');
|
||||
if (this.connection) {
|
||||
this.disconnect();
|
||||
}
|
||||
this.logger.info('<<< Disposed monitor service.');
|
||||
this.client = undefined;
|
||||
}
|
||||
|
||||
async getConnectionIds(): Promise<string[]> {
|
||||
return Array.from(this.connections.keys());
|
||||
}
|
||||
|
||||
async connect(config: ConnectionConfig): Promise<{ connectionId: string }> {
|
||||
async connect(config: MonitorConfig): Promise<Status> {
|
||||
this.logger.info(`>>> Creating serial monitor connection for ${Board.toString(config.board)} on port ${Port.toString(config.port)}...`);
|
||||
if (this.connection) {
|
||||
return Status.ALREADY_CONNECTED;
|
||||
}
|
||||
const client = await this.monitorClientProvider.client;
|
||||
const duplex = client.streamingOpen();
|
||||
const connectionId = v4();
|
||||
const toDispose = new DisposableCollection(
|
||||
Disposable.create(() => this.disconnect(connectionId))
|
||||
);
|
||||
this.connection = { duplex, config };
|
||||
|
||||
duplex.on('error', ((error: Error) => {
|
||||
if (MonitorError.is(error) && (
|
||||
MonitorError.isClientCancelledError(error)
|
||||
|| MonitorError.isDeviceNotConfiguredError(error)
|
||||
)) {
|
||||
const monitorError = ErrorWithCode.toMonitorError(error, config);
|
||||
this.disconnect(monitorError).then(() => {
|
||||
if (this.client) {
|
||||
this.client.notifyError(error);
|
||||
this.client.notifyError(monitorError);
|
||||
}
|
||||
}
|
||||
this.logger.error(`Error occurred for connection ${connectionId}.`, error);
|
||||
toDispose.dispose();
|
||||
if (monitorError.code === undefined) {
|
||||
// Log the original, unexpected error.
|
||||
this.logger.error(error);
|
||||
}
|
||||
});
|
||||
}).bind(this));
|
||||
|
||||
duplex.on('data', ((resp: StreamingOpenResp) => {
|
||||
if (this.client) {
|
||||
const raw = resp.getData();
|
||||
const data = typeof raw === 'string' ? raw : new TextDecoder('utf8').decode(raw);
|
||||
this.client.notifyRead({ connectionId, data });
|
||||
this.client.notifyRead({ data });
|
||||
}
|
||||
}).bind(this));
|
||||
|
||||
const { type, port } = config;
|
||||
const req = new StreamingOpenReq();
|
||||
const monitorConfig = new MonitorConfig();
|
||||
const monitorConfig = new GrpcMonitorConfig();
|
||||
monitorConfig.setType(this.mapType(type));
|
||||
monitorConfig.setTarget(port);
|
||||
monitorConfig.setTarget(port.address);
|
||||
if (config.baudRate !== undefined) {
|
||||
const obj = google_protobuf_struct_pb.Struct.fromJavaScript({ 'BaudRate': config.baudRate });
|
||||
monitorConfig.setAdditionalconfig(obj);
|
||||
monitorConfig.setAdditionalconfig(Struct.fromJavaScript({ 'BaudRate': config.baudRate }));
|
||||
}
|
||||
req.setMonitorconfig(monitorConfig);
|
||||
|
||||
return new Promise<{ connectionId: string }>(resolve => {
|
||||
duplex.write(req, () => {
|
||||
this.connections.set(connectionId, { toDispose, duplex });
|
||||
resolve({ connectionId });
|
||||
});
|
||||
return new Promise<Status>(resolve => {
|
||||
if (this.connection) {
|
||||
this.connection.duplex.write(req, () => {
|
||||
this.logger.info(`<<< Serial monitor connection created for ${Board.toString(config.board, { useFqbn: false })} on port ${Port.toString(config.port)}.`);
|
||||
resolve(Status.OK);
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.disconnect().then(() => resolve(Status.NOT_CONNECTED));
|
||||
});
|
||||
}
|
||||
|
||||
async disconnect(connectionId: string): Promise<boolean> {
|
||||
this.logger.info(`>>> Received disconnect request for connection: ${connectionId}`);
|
||||
const disposable = this.connections.get(connectionId);
|
||||
if (!disposable) {
|
||||
this.logger.warn(`<<< No connection was found for ID: ${connectionId}`);
|
||||
return false;
|
||||
async disconnect(reason?: MonitorError): Promise<Status> {
|
||||
if (!this.connection && reason && reason.code === MonitorError.ErrorCodes.CLIENT_CANCEL) {
|
||||
return Status.OK;
|
||||
}
|
||||
const result = await this.doDisconnect(connectionId, disposable);
|
||||
if (result) {
|
||||
this.logger.info(`<<< Successfully disconnected from ${connectionId}.`);
|
||||
} else {
|
||||
this.logger.info(`<<< Could not disconnected from ${connectionId}.`);
|
||||
this.logger.info(`>>> Disposing monitor connection...`);
|
||||
if (!this.connection) {
|
||||
this.logger.warn(`<<< Not connected. Nothing to dispose.`);
|
||||
return Status.NOT_CONNECTED;
|
||||
}
|
||||
return result;
|
||||
const { duplex, config } = this.connection;
|
||||
duplex.cancel();
|
||||
this.logger.info(`<<< Disposed monitor connection for ${Board.toString(config.board, { useFqbn: false })} on port ${Port.toString(config.port)}.`);
|
||||
this.connection = undefined;
|
||||
return Status.OK;
|
||||
}
|
||||
|
||||
protected async doDisconnect(connectionId: string, monitorDuplex: MonitorDuplex): Promise<boolean> {
|
||||
const { duplex } = monitorDuplex;
|
||||
this.logger.info(`>>> Disposing monitor connection: ${connectionId}...`);
|
||||
try {
|
||||
duplex.cancel();
|
||||
this.connections.delete(connectionId);
|
||||
this.logger.info(`<<< Connection disposed: ${connectionId}.`);
|
||||
return true;
|
||||
} catch (e) {
|
||||
this.logger.error(`<<< Error occurred when disposing monitor connection: ${connectionId}. ${e}`);
|
||||
return false;
|
||||
async send(data: string): Promise<Status> {
|
||||
if (!this.connection) {
|
||||
return Status.NOT_CONNECTED;
|
||||
}
|
||||
const req = new StreamingOpenReq();
|
||||
req.setData(new TextEncoder().encode(data));
|
||||
return new Promise<Status>(resolve => {
|
||||
if (this.connection) {
|
||||
this.connection.duplex.write(req, () => {
|
||||
resolve(Status.OK);
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.disconnect().then(() => resolve(Status.NOT_CONNECTED));
|
||||
});
|
||||
}
|
||||
|
||||
async send(connectionId: string, data: string): Promise<void> {
|
||||
const duplex = this.duplex(connectionId);
|
||||
if (duplex) {
|
||||
const req = new StreamingOpenReq();
|
||||
req.setData(new TextEncoder().encode(data));
|
||||
return new Promise<void>(resolve => duplex.duplex.write(req, resolve));
|
||||
} else {
|
||||
throw new Error(`No connection with ID: ${connectionId}.`);
|
||||
}
|
||||
}
|
||||
|
||||
protected mapType(type?: ConnectionType): MonitorConfig.TargetType {
|
||||
protected mapType(type?: MonitorConfig.ConnectionType): GrpcMonitorConfig.TargetType {
|
||||
switch (type) {
|
||||
case ConnectionType.SERIAL: return MonitorConfig.TargetType.SERIAL;
|
||||
default: return MonitorConfig.TargetType.SERIAL;
|
||||
case MonitorConfig.ConnectionType.SERIAL: return GrpcMonitorConfig.TargetType.SERIAL;
|
||||
default: return GrpcMonitorConfig.TargetType.SERIAL;
|
||||
}
|
||||
}
|
||||
|
||||
protected duplex(connectionId: string): MonitorDuplex | undefined {
|
||||
const monitorClient = this.connections.get(connectionId);
|
||||
if (!monitorClient) {
|
||||
this.logger.warn(`Could not find monitor client for connection ID: ${connectionId}`);
|
||||
}
|
||||
return monitorClient;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -52,14 +52,16 @@ export class SketchesServiceImpl implements SketchesService {
|
||||
const uris: string[] = [];
|
||||
const fsPath = FileUri.fsPath(uri);
|
||||
const stats = fs.lstatSync(fsPath);
|
||||
if (stats.isDirectory && await this.isSketchFolder(uri)) {
|
||||
const fileNames = fs.readdirSync(fsPath);
|
||||
for (const fileName of fileNames) {
|
||||
const filePath = path.join(fsPath, fileName);
|
||||
if (ALLOWED_FILE_EXTENSIONS.indexOf(path.extname(filePath)) !== -1
|
||||
&& fs.existsSync(filePath)
|
||||
&& fs.lstatSync(filePath).isFile()) {
|
||||
uris.push(FileUri.create(filePath).toString())
|
||||
if (stats.isDirectory) {
|
||||
if (await this.isSketchFolder(uri)) {
|
||||
const fileNames = fs.readdirSync(fsPath);
|
||||
for (const fileName of fileNames) {
|
||||
const filePath = path.join(fsPath, fileName);
|
||||
if (ALLOWED_FILE_EXTENSIONS.indexOf(path.extname(filePath)) !== -1
|
||||
&& fs.existsSync(filePath)
|
||||
&& fs.lstatSync(filePath).isFile()) {
|
||||
uris.push(FileUri.create(filePath).toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
return uris;
|
||||
|
@@ -28,4 +28,4 @@
|
||||
"files": [
|
||||
"../node_modules/@theia/monaco/src/typings/monaco/index.d.ts"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "browser-app",
|
||||
"version": "0.0.2",
|
||||
"version": "0.0.3",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@theia/core": "next",
|
||||
@@ -18,7 +18,7 @@
|
||||
"@theia/terminal": "next",
|
||||
"@theia/workspace": "next",
|
||||
"@theia/textmate-grammars": "next",
|
||||
"arduino-ide-extension": "0.0.2"
|
||||
"arduino-ide-extension": "0.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@theia/cli": "next"
|
||||
|
@@ -1,50 +1,49 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "electron-app",
|
||||
"version": "0.0.2",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@theia/core": "next",
|
||||
"@theia/cpp": "next",
|
||||
"@theia/editor": "next",
|
||||
"@theia/electron": "next",
|
||||
"@theia/file-search": "next",
|
||||
"@theia/filesystem": "next",
|
||||
"@theia/languages": "next",
|
||||
"@theia/messages": "next",
|
||||
"@theia/monaco": "next",
|
||||
"@theia/navigator": "next",
|
||||
"@theia/preferences": "next",
|
||||
"@theia/process": "next",
|
||||
"@theia/terminal": "next",
|
||||
"@theia/workspace": "next",
|
||||
"@theia/textmate-grammars": "next",
|
||||
"arduino-ide-extension": "0.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@theia/cli": "next",
|
||||
"electron": "^4.2.0"
|
||||
},
|
||||
"scripts": {
|
||||
"prepare": "theia build --mode development",
|
||||
"start": "theia start",
|
||||
"watch": "theia build --watch --mode development"
|
||||
},
|
||||
"theia": {
|
||||
"target": "electron",
|
||||
"frontend": {
|
||||
"config": {
|
||||
"applicationName": "Arduino Pro IDE",
|
||||
"defaultTheme": "arduino-theme",
|
||||
"preferences": {
|
||||
"editor.autoSave": "on"
|
||||
}
|
||||
}
|
||||
},
|
||||
"generator": {
|
||||
"config": {
|
||||
"preloadTemplate": "<div class='theia-preload' style='background-color: rgb(237, 241, 242);'></div>"
|
||||
}
|
||||
"private": true,
|
||||
"name": "electron-app",
|
||||
"version": "0.0.3",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@theia/core": "next",
|
||||
"@theia/cpp": "next",
|
||||
"@theia/editor": "next",
|
||||
"@theia/electron": "next",
|
||||
"@theia/file-search": "next",
|
||||
"@theia/filesystem": "next",
|
||||
"@theia/languages": "next",
|
||||
"@theia/messages": "next",
|
||||
"@theia/monaco": "next",
|
||||
"@theia/navigator": "next",
|
||||
"@theia/preferences": "next",
|
||||
"@theia/process": "next",
|
||||
"@theia/terminal": "next",
|
||||
"@theia/workspace": "next",
|
||||
"@theia/textmate-grammars": "next",
|
||||
"arduino-ide-extension": "0.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@theia/cli": "next"
|
||||
},
|
||||
"scripts": {
|
||||
"prepare": "theia build --mode development",
|
||||
"start": "theia start",
|
||||
"watch": "theia build --watch --mode development"
|
||||
},
|
||||
"theia": {
|
||||
"target": "electron",
|
||||
"frontend": {
|
||||
"config": {
|
||||
"applicationName": "Arduino Pro IDE",
|
||||
"defaultTheme": "arduino-theme",
|
||||
"preferences": {
|
||||
"editor.autoSave": "on"
|
||||
}
|
||||
}
|
||||
},
|
||||
"generator": {
|
||||
"config": {
|
||||
"preloadTemplate": "<div class='theia-preload' style='background-color: rgb(237, 241, 242);'></div>"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -6,7 +6,7 @@ All-in-one packager producing the `Arduino Pro IDE` Electron-based application.
|
||||
|
||||
The prerequisites are defined [here](https://github.com/theia-ide/theia/blob/master/doc/Developing.md#prerequisites).
|
||||
|
||||
### Build:
|
||||
## Build
|
||||
To build the Arduino Pro IDE application you have to do the followings:
|
||||
```bash
|
||||
yarn --cwd ./electron/packager/ && yarn --cwd ./electron/packager/ package
|
||||
@@ -14,12 +14,14 @@ yarn --cwd ./electron/packager/ && yarn --cwd ./electron/packager/ package
|
||||
|
||||
The packaged application will be under the `./electron/build/dist` folder.
|
||||
|
||||
### CI:
|
||||
## CI
|
||||
We always build an electron-based application for Windows. Create a PR, and the CI will automatically create the app for Windows. Do you need the builds for macOS and Linux? Start a build manually.
|
||||
|
||||
The electron packager runs when:
|
||||
- the build is manually triggered by the user, or
|
||||
- on scheduled (CRON) jobs.
|
||||
|
||||
### Creating a Release Draft:
|
||||
## Creating a Release Draft
|
||||
One can create a GitHub release draft, tag the source, and upload the artifacts to GitHub with Azure.
|
||||
- Go to the Azure [build](https://dev.azure.com/typefox/Arduino/_build) page.
|
||||
- Click on `Queue` in the top right corner.
|
||||
@@ -32,7 +34,7 @@ One can create a GitHub release draft, tag the source, and upload the artifacts
|
||||
- 🎈🎉
|
||||
|
||||
|
||||
### Publishing the Release Draft:
|
||||
## Publishing the Release Draft
|
||||
One has to manually publish the GitHub release.
|
||||
- Go to the [release page](https://github.com/bcmi-labs/arduino-editor/releases) of the `arduino-editor` repository.
|
||||
- Select your release draft.
|
||||
|
@@ -1,10 +1,9 @@
|
||||
{
|
||||
"name": "arduino.Pro.IDE",
|
||||
"name": "arduino-pro-ide",
|
||||
"description": "Arduino Pro IDE",
|
||||
"main": "src-gen/frontend/electron-main.js",
|
||||
"author": "TypeFox",
|
||||
"author": "Arduino SA",
|
||||
"dependencies": {
|
||||
"google-protobuf": "^3.5.0",
|
||||
"arduino-ide-extension": "file:../working-copy/arduino-ide-extension"
|
||||
},
|
||||
"resolutions": {
|
||||
@@ -27,14 +26,11 @@
|
||||
"url": "git+https://github.com/arduino/arduino-pro-ide.git"
|
||||
},
|
||||
"// 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.",
|
||||
"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": {
|
||||
"productName": "Arduino Pro IDE",
|
||||
"appId": "arduino.Pro.IDE",
|
||||
"electronVersion": "4.2.0",
|
||||
"appId": "arduino.ProIDE",
|
||||
"asar": false,
|
||||
"directories": {
|
||||
"buildResources": "resources"
|
||||
@@ -46,7 +42,6 @@
|
||||
"!node_modules/**/*.spec.js",
|
||||
"!node_modules/@theia/**/test/*",
|
||||
"!node_modules/@theia/**/src/*.ts",
|
||||
"!node_modules/@theia/java/download",
|
||||
"!node_modules/@theia/**/lib/*browser/*",
|
||||
"!node_modules/@typefox/monaco-editor-core/*",
|
||||
"!node_modules/oniguruma/*",
|
||||
|
@@ -9,8 +9,8 @@
|
||||
"cli": "./cli"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"author": "Arduino SA",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"depcheck": "^0.7.1",
|
||||
"shelljs": "^0.8.3",
|
||||
|
20
package.json
20
package.json
@@ -1,25 +1,25 @@
|
||||
{
|
||||
"name": "arduino-editor",
|
||||
"version": "0.0.1",
|
||||
"description": "Arduino IDE built using Eclipse Theia",
|
||||
"version": "0.0.3",
|
||||
"description": "Arduino Pro IDE",
|
||||
"main": "index.js",
|
||||
"repository": "https://github.com/bcmi-labs/arduino-editor.git",
|
||||
"author": "Christian Weichel <christian.weichel@typefox.io>",
|
||||
"author": "Arduino SA",
|
||||
"license": "MIT",
|
||||
"private": true,
|
||||
"devDependencies": {
|
||||
"lerna": "^3.13.3"
|
||||
},
|
||||
"scripts": {
|
||||
"prepare": "lerna run prepare",
|
||||
"rebuild:browser": "theia rebuild:browser",
|
||||
"rebuild:electron": "theia rebuild:electron",
|
||||
"start": "yarn --cwd ./browser-app start",
|
||||
"watch": "lerna run watch --parallel"
|
||||
"prepare": "lerna run prepare",
|
||||
"rebuild:browser": "theia rebuild:browser",
|
||||
"rebuild:electron": "theia rebuild:electron",
|
||||
"start": "yarn --cwd ./browser-app start",
|
||||
"watch": "lerna run watch --parallel"
|
||||
},
|
||||
"workspaces": [
|
||||
"arduino-ide-extension",
|
||||
"electron-app",
|
||||
"browser-app"
|
||||
"electron-app",
|
||||
"browser-app"
|
||||
]
|
||||
}
|
||||
|
Reference in New Issue
Block a user