Compare commits

..

35 Commits

Author SHA1 Message Date
David Simpson
9a16cf9e02 change MonitorService providers to injectable deps 2022-05-20 17:53:34 +02:00
Francesco Stasi
a4ff05a82b monitor service provider singleton 2022-05-19 16:56:37 +02:00
Alberto Iannaccone
0427759fdb refactor monitor settings interfaces 2022-05-19 11:19:13 +02:00
Francesco Stasi
80ade4c37e updated pseudo code 2022-05-19 10:59:20 +02:00
Francesco Stasi
355dec8aaa monitor settings provider stub 2022-05-19 10:40:01 +02:00
Alberto Iannaccone
7bf4ea0637 add MonitorSettingsProvider interface 2022-05-17 17:45:47 +02:00
Alberto Iannaccone
1982609c87 fix upload when monitor is open 2022-05-12 15:28:13 +02:00
Alberto Iannaccone
62eaeb1c74 update arduino-cli to 0.22.0 2022-05-10 15:54:04 +02:00
Alberto Iannaccone
9b58c9d0c8 delete duplex when connection is closed 2022-05-10 15:53:48 +02:00
Alberto Iannaccone
eff960bb7f fix monitor connection 2022-05-09 14:56:52 +02:00
Mark Sujew
fbe8fb421a Fix MonitorManagerProxy DI issue 2022-05-09 14:56:52 +02:00
Silvano Cerza
a8d803e7c3 Add missing binding 2022-05-09 14:56:52 +02:00
Silvano Cerza
397ca5665f coreClientProvider is now set when constructing MonitorService 2022-05-09 14:56:52 +02:00
Silvano Cerza
f9da9fc24b Delete unnecessary Symbol 2022-05-09 14:56:52 +02:00
Silvano Cerza
b97af32bb8 Fix backend logger bindings 2022-05-09 14:56:52 +02:00
Silvano Cerza
ce2f1c227a Updated MonitorWidget to use new monitor proxy 2022-05-09 14:56:52 +02:00
Silvano Cerza
7889f40834 Changed MonitorWidget and children to use new monitor proxy 2022-05-09 14:56:52 +02:00
Silvano Cerza
6b7b33356d Changed plotter contribution to use new manager proxy 2022-05-09 14:56:52 +02:00
Silvano Cerza
ad781f0bfc Remove unused file 2022-05-09 14:56:52 +02:00
Silvano Cerza
6cf61c498a More serial classes removal 2022-05-09 14:56:52 +02:00
Silvano Cerza
50239c5756 Add generic monitor settings storaging 2022-05-09 14:56:52 +02:00
Silvano Cerza
cbd5b4de1b WebSocketProvider is not injectable anymore 2022-05-09 14:56:52 +02:00
Silvano Cerza
bf958fd8cf Proxied more monitor methods to frontend 2022-05-09 14:56:52 +02:00
Silvano Cerza
ee265aec90 Changed how connection is handled on upload 2022-05-09 14:56:52 +02:00
Silvano Cerza
9058abb015 Remove several unnecessary serial monitor classes 2022-05-09 14:56:52 +02:00
Silvano Cerza
31b704cdb9 Moved settings to MonitorService 2022-05-09 14:56:52 +02:00
Silvano Cerza
61b8bdeec9 Add monitor proxy functions for the frontend 2022-05-09 14:56:52 +02:00
Silvano Cerza
c5695d3a76 Fixed WebSocketChange event signature 2022-05-09 14:56:52 +02:00
Silvano Cerza
480492a7c8 Enhance MonitorManager APIs 2022-05-09 14:56:52 +02:00
Silvano Cerza
2c95e7f033 Changed upload settings 2022-05-09 14:56:52 +02:00
Silvano Cerza
116b3d5984 Moved some interfaces 2022-05-09 14:56:52 +02:00
Silvano Cerza
750796d3a0 Rename WebSocketService to WebSocketProvider and uninjected it 2022-05-09 14:56:52 +02:00
Silvano Cerza
3133b01c4a Implement MonitorService to handle pluggable monitor lifetime 2022-05-09 14:56:52 +02:00
Silvano Cerza
ebab0b226f Scaffold interfaces and classes for pluggable monitors 2022-05-09 14:56:52 +02:00
Francesco Stasi
2b2ea72643 backend structure WIP 2022-05-09 14:56:52 +02:00
300 changed files with 15926 additions and 22937 deletions

View File

@@ -78,7 +78,6 @@ jobs:
fi fi
fi fi
npx node-gyp install
yarn --cwd ./electron/packager/ yarn --cwd ./electron/packager/
yarn --cwd ./electron/packager/ package yarn --cwd ./electron/packager/ package

View File

@@ -1,49 +0,0 @@
name: themes-weekly-pull
on:
schedule:
# run every friday at 5AM
- cron: '0 5 * * 5'
workflow_dispatch:
env:
NODE_VERSION: 14.x
jobs:
pull-from-jsonbin:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Install Node.js
uses: actions/setup-node@v3
with:
node-version: ${{ env.NODE_VERSION }}
registry-url: 'https://registry.npmjs.org'
- name: Install dependencies
run: yarn
- name: Run themes:pull script
run: yarn run themes:pull
env:
JSONBIN_MASTER_KEY: ${{ secrets.JSONBIN_MASTER_KEY }}
JSONBIN_ID: ${{ secrets.JSONBIN_ID }}
- name: Generate dark tokens
run: npx token-transformer scripts/themes/tokens/arduino-tokens.json scripts/themes/tokens/dark.json core,ide-default,ide-dark,theia core,ide-default,ide-dark
- name: Generate default tokens
run: npx token-transformer scripts/themes/tokens/arduino-tokens.json scripts/themes/tokens/default.json core,ide-default,theia core,ide-default
- name: Run themes:generate script
run: yarn run themes:generate
- name: Create Pull Request
uses: peter-evans/create-pull-request@v4
with:
commit-message: Updated themes
title: Update themes
branch: themes/themes-update
author: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

6
.gitignore vendored
View File

@@ -17,9 +17,3 @@ yarn*.log
plugins plugins
# the config files for the CLI # the config files for the CLI
arduino-ide-extension/data/cli/config arduino-ide-extension/data/cli/config
# the tokens folder for the themes
scripts/themes/tokens
# environment variables
.env
# content trace files for electron
electron-app/traces

View File

@@ -2,6 +2,5 @@
"singleQuote": true, "singleQuote": true,
"tabWidth": 2, "tabWidth": 2,
"useTabs": false, "useTabs": false,
"printWidth": 80, "printWidth": 80
"endOfLine": "auto"
} }

40
.vscode/launch.json vendored
View File

@@ -1,44 +1,6 @@
{ {
"version": "0.2.0", "version": "0.2.0",
"configurations": [ "configurations": [
{
"type": "node",
"request": "launch",
"name": "App (Electron) [Dev]",
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron",
"windows": {
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron.cmd",
},
"cwd": "${workspaceFolder}/electron-app",
"args": [
".",
"--log-level=debug",
"--hostname=localhost",
"--no-cluster",
"--app-project-path=${workspaceRoot}/electron-app",
"--remote-debugging-port=9222",
"--no-app-auto-install",
"--plugins=local-dir:../plugins",
"--hosted-plugin-inspect=9339",
"--nosplash",
"--content-trace",
"--open-devtools"
],
"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",
"${workspaceRoot}/node_modules/@theia/**/*.js"
],
"smartStep": true,
"internalConsoleOptions": "openOnSessionStart",
"outputCapture": "std"
},
{ {
"type": "node", "type": "node",
"request": "launch", "request": "launch",
@@ -48,6 +10,7 @@
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron.cmd", "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron.cmd",
}, },
"cwd": "${workspaceFolder}/electron-app", "cwd": "${workspaceFolder}/electron-app",
"protocol": "inspector",
"args": [ "args": [
".", ".",
"--log-level=debug", "--log-level=debug",
@@ -115,6 +78,7 @@
{ {
"type": "node", "type": "node",
"request": "launch", "request": "launch",
"protocol": "inspector",
"name": "Run Test [current]", "name": "Run Test [current]",
"program": "${workspaceRoot}/node_modules/mocha/bin/_mocha", "program": "${workspaceRoot}/node_modules/mocha/bin/_mocha",
"args": [ "args": [

View File

@@ -1,6 +1,6 @@
{ {
"name": "arduino-ide-extension", "name": "arduino-ide-extension",
"version": "2.0.0-rc8", "version": "2.0.0-rc6",
"description": "An extension for Theia building the Arduino IDE", "description": "An extension for Theia building the Arduino IDE",
"license": "AGPL-3.0-or-later", "license": "AGPL-3.0-or-later",
"scripts": { "scripts": {
@@ -21,30 +21,31 @@
"test:watch": "mocha --watch --watch-files lib \"./lib/test/**/*.test.js\"" "test:watch": "mocha --watch --watch-files lib \"./lib/test/**/*.test.js\""
}, },
"dependencies": { "dependencies": {
"@grpc/grpc-js": "^1.6.7", "@grpc/grpc-js": "^1.3.7",
"@theia/application-package": "1.25.0", "@theia/application-package": "1.22.1",
"@theia/core": "1.25.0", "@theia/core": "1.22.1",
"@theia/editor": "1.25.0", "@theia/editor": "1.22.1",
"@theia/electron": "1.25.0", "@theia/editor-preview": "1.22.1",
"@theia/filesystem": "1.25.0", "@theia/electron": "1.22.1",
"@theia/keymaps": "1.25.0", "@theia/filesystem": "1.22.1",
"@theia/markers": "1.25.0", "@theia/git": "1.22.1",
"@theia/monaco": "1.25.0", "@theia/keymaps": "1.22.1",
"@theia/navigator": "1.25.0", "@theia/markers": "1.22.1",
"@theia/outline-view": "1.25.0", "@theia/monaco": "1.22.1",
"@theia/output": "1.25.0", "@theia/navigator": "1.22.1",
"@theia/preferences": "1.25.0", "@theia/outline-view": "1.22.1",
"@theia/search-in-workspace": "1.25.0", "@theia/output": "1.22.1",
"@theia/terminal": "1.25.0", "@theia/preferences": "1.22.1",
"@theia/workspace": "1.25.0", "@theia/search-in-workspace": "1.22.1",
"@theia/terminal": "1.22.1",
"@theia/workspace": "1.22.1",
"@tippyjs/react": "^4.2.5", "@tippyjs/react": "^4.2.5",
"@types/atob": "^2.1.2", "@types/atob": "^2.1.2",
"@types/auth0-js": "^9.14.0", "@types/auth0-js": "^9.14.0",
"@types/btoa": "^1.2.3", "@types/btoa": "^1.2.3",
"@types/dateformat": "^3.0.1", "@types/dateformat": "^3.0.1",
"@types/deep-equal": "^1.0.1",
"@types/deepmerge": "^2.2.0", "@types/deepmerge": "^2.2.0",
"@types/glob": "^7.2.0", "@types/glob": "^5.0.35",
"@types/google-protobuf": "^3.7.2", "@types/google-protobuf": "^3.7.2",
"@types/js-yaml": "^3.12.2", "@types/js-yaml": "^3.12.2",
"@types/keytar": "^4.4.0", "@types/keytar": "^4.4.0",
@@ -57,18 +58,19 @@
"@types/temp": "^0.8.34", "@types/temp": "^0.8.34",
"@types/which": "^1.3.1", "@types/which": "^1.3.1",
"ajv": "^6.5.3", "ajv": "^6.5.3",
"arduino-serial-plotter-webapp": "0.1.0", "arduino-serial-plotter-webapp": "0.0.17",
"async-mutex": "^0.3.0", "async-mutex": "^0.3.0",
"atob": "^2.1.2", "atob": "^2.1.2",
"auth0-js": "^9.14.0", "auth0-js": "^9.14.0",
"btoa": "^1.2.1", "btoa": "^1.2.1",
"css-element-queries": "^1.2.0",
"dateformat": "^3.0.3", "dateformat": "^3.0.3",
"deep-equal": "^2.0.5",
"deepmerge": "2.0.1", "deepmerge": "2.0.1",
"electron-updater": "^4.6.5", "electron-updater": "^4.6.5",
"fast-safe-stringify": "^2.1.1", "fuzzy": "^0.1.3",
"glob": "^7.1.6", "glob": "^7.1.6",
"google-protobuf": "^3.20.1", "google-protobuf": "^3.11.4",
"grpc": "^1.24.11",
"hash.js": "^1.1.7", "hash.js": "^1.1.7",
"is-valid-path": "^0.1.1", "is-valid-path": "^0.1.1",
"js-yaml": "^3.13.1", "js-yaml": "^3.13.1",
@@ -89,7 +91,6 @@
"semver": "^7.3.2", "semver": "^7.3.2",
"string-natural-compare": "^2.0.3", "string-natural-compare": "^2.0.3",
"temp": "^0.9.1", "temp": "^0.9.1",
"temp-dir": "^2.0.0",
"tree-kill": "^1.2.1", "tree-kill": "^1.2.1",
"upath": "^1.1.2", "upath": "^1.1.2",
"url": "^0.11.0", "url": "^0.11.0",
@@ -156,13 +157,13 @@
], ],
"arduino": { "arduino": {
"cli": { "cli": {
"version": "0.24.0" "version": "0.22.0"
}, },
"fwuploader": { "fwuploader": {
"version": "2.2.0" "version": "2.0.0"
}, },
"clangd": { "clangd": {
"version": "14.0.0" "version": "13.0.0"
}, },
"languageServer": { "languageServer": {
"version": "0.6.0" "version": "0.6.0"

View File

@@ -4,93 +4,30 @@
const version = '1.9.1'; const version = '1.9.1';
(async () => { (async () => {
const os = require('os');
const { promises: fs } = require('fs');
const path = require('path');
const shell = require('shelljs');
const { v4 } = require('uuid');
const repository = path.join(os.tmpdir(), `${v4()}-arduino-examples`); const os = require('os');
if (shell.mkdir('-p', repository).code !== 0) { const path = require('path');
shell.exit(1); const shell = require('shelljs');
} const { v4 } = require('uuid');
if ( const repository = path.join(os.tmpdir(), `${v4()}-arduino-examples`);
shell.exec( if (shell.mkdir('-p', repository).code !== 0) {
`git clone https://github.com/arduino/arduino-examples.git ${repository}` shell.exit(1);
).code !== 0 process.exit(1);
) {
shell.exit(1);
}
if (
shell.exec(`git -C ${repository} checkout tags/${version} -b ${version}`)
.code !== 0
) {
shell.exit(1);
}
const destination = path.join(__dirname, '..', 'Examples');
shell.mkdir('-p', destination);
shell.cp('-fR', path.join(repository, 'examples', '*'), destination);
const isSketch = async (pathLike) => {
try {
const names = await fs.readdir(pathLike);
const dirName = path.basename(pathLike);
return names.indexOf(`${dirName}.ino`) !== -1;
} catch (e) {
if (e.code === 'ENOTDIR') {
return false;
}
throw e;
} }
};
const examples = []; if (shell.exec(`git clone https://github.com/arduino/arduino-examples.git ${repository}`).code !== 0) {
const categories = await fs.readdir(destination); shell.exit(1);
const visit = async (pathLike, container) => { process.exit(1);
const stat = await fs.lstat(pathLike);
if (stat.isDirectory()) {
if (await isSketch(pathLike)) {
container.sketches.push({
name: path.basename(pathLike),
relativePath: path.relative(destination, pathLike),
});
} else {
const names = await fs.readdir(pathLike);
for (const name of names) {
const childPath = path.join(pathLike, name);
if (await isSketch(childPath)) {
container.sketches.push({
name,
relativePath: path.relative(destination, childPath),
});
} else {
const child = {
label: name,
children: [],
sketches: [],
};
container.children.push(child);
await visit(childPath, child);
}
}
}
} }
};
for (const category of categories) { if (shell.exec(`git -C ${repository} checkout tags/${version} -b ${version}`).code !== 0) {
const example = { shell.exit(1);
label: category, process.exit(1);
children: [], }
sketches: [],
}; const destination = path.join(__dirname, '..', 'Examples');
await visit(path.join(destination, category), example); shell.mkdir('-p', destination);
examples.push(example); shell.cp('-fR', path.join(repository, 'examples', '*'), destination);
}
await fs.writeFile(
path.join(destination, 'examples.json'),
JSON.stringify(examples, null, 2),
{ encoding: 'utf8' }
);
shell.echo(`Generated output to ${path.join(destination, 'examples.json')}`);
})(); })();

View File

@@ -66,24 +66,21 @@
build, build,
`arduino-language-server${platform === 'win32' ? '.exe' : ''}` `arduino-language-server${platform === 'win32' ? '.exe' : ''}`
); );
let clangdExecutablePath, clangFormatExecutablePath, lsSuffix, clangdSuffix; let clangdExecutablePath, lsSuffix, clangdSuffix;
switch (platformArch) { switch (platformArch) {
case 'darwin-x64': case 'darwin-x64':
clangdExecutablePath = path.join(build, 'clangd'); clangdExecutablePath = path.join(build, 'clangd');
clangFormatExecutablePath = path.join(build, 'clang-format');
lsSuffix = 'macOS_64bit.tar.gz'; lsSuffix = 'macOS_64bit.tar.gz';
clangdSuffix = 'macOS_64bit'; clangdSuffix = 'macOS_64bit';
break; break;
case 'linux-x64': case 'linux-x64':
clangdExecutablePath = path.join(build, 'clangd'); clangdExecutablePath = path.join(build, 'clangd');
clangFormatExecutablePath = path.join(build, 'clang-format');
lsSuffix = 'Linux_64bit.tar.gz'; lsSuffix = 'Linux_64bit.tar.gz';
clangdSuffix = 'Linux_64bit'; clangdSuffix = 'Linux_64bit';
break; break;
case 'win32-x64': case 'win32-x64':
clangdExecutablePath = path.join(build, 'clangd.exe'); clangdExecutablePath = path.join(build, 'clangd.exe');
clangFormatExecutablePath = path.join(build, 'clang-format.exe');
lsSuffix = 'Windows_64bit.zip'; lsSuffix = 'Windows_64bit.zip';
clangdSuffix = 'Windows_64bit'; clangdSuffix = 'Windows_64bit';
break; break;
@@ -106,15 +103,4 @@
downloader.downloadUnzipAll(clangdUrl, build, clangdExecutablePath, force, { downloader.downloadUnzipAll(clangdUrl, build, clangdExecutablePath, force, {
strip: 1, strip: 1,
}); // `strip`: the new clangd (12.x) is zipped into a folder, so we have to strip the outmost folder. }); // `strip`: the new clangd (12.x) is zipped into a folder, so we have to strip the outmost folder.
const clangdFormatUrl = `https://downloads.arduino.cc/tools/clang-format_${clangdVersion}_${clangdSuffix}.tar.bz2`;
downloader.downloadUnzipAll(
clangdFormatUrl,
build,
clangFormatExecutablePath,
force,
{
strip: 1,
}
);
})(); })();

View File

@@ -1,9 +1,5 @@
import { import { inject, injectable, postConstruct } from 'inversify';
inject, import * as React from 'react';
injectable,
postConstruct,
} from '@theia/core/shared/inversify';
import * as React from '@theia/core/shared/react';
import * as remote from '@theia/core/electron-shared/@electron/remote'; import * as remote from '@theia/core/electron-shared/@electron/remote';
import { import {
BoardsService, BoardsService,
@@ -11,7 +7,6 @@ import {
ExecutableService, ExecutableService,
Sketch, Sketch,
LibraryService, LibraryService,
ArduinoDaemon,
} from '../common/protocol'; } from '../common/protocol';
import { Mutex } from 'async-mutex'; import { Mutex } from 'async-mutex';
import { import {
@@ -22,11 +17,9 @@ import {
DisposableCollection, DisposableCollection,
} from '@theia/core'; } from '@theia/core';
import { import {
Dialog,
FrontendApplication, FrontendApplication,
FrontendApplicationContribution, FrontendApplicationContribution,
LocalStorageService, LocalStorageService,
OnWillStopAction,
SaveableWidget, SaveableWidget,
StatusBar, StatusBar,
StatusBarAlignment, StatusBarAlignment,
@@ -51,12 +44,19 @@ import {
EditorManager, EditorManager,
EditorOpenerOptions, EditorOpenerOptions,
} from '@theia/editor/lib/browser'; } from '@theia/editor/lib/browser';
import { ProblemContribution } from '@theia/markers/lib/browser/problem/problem-contribution';
import { MonacoMenus } from '@theia/monaco/lib/browser/monaco-menu'; import { MonacoMenus } from '@theia/monaco/lib/browser/monaco-menu';
import { FileNavigatorCommands } from '@theia/navigator/lib/browser/navigator-contribution'; import { FileNavigatorCommands, FileNavigatorContribution } from '@theia/navigator/lib/browser/navigator-contribution';
import { OutlineViewContribution } from '@theia/outline-view/lib/browser/outline-view-contribution';
import { OutputContribution } from '@theia/output/lib/browser/output-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 { TerminalMenus } from '@theia/terminal/lib/browser/terminal-frontend-contribution'; import { TerminalMenus } from '@theia/terminal/lib/browser/terminal-frontend-contribution';
import { HostedPluginSupport } from '@theia/plugin-ext/lib/hosted/browser/hosted-plugin';
import { FileService } from '@theia/filesystem/lib/browser/file-service'; import { FileService } from '@theia/filesystem/lib/browser/file-service';
import { FileChangeType } from '@theia/filesystem/lib/browser'; import { FileChangeType } from '@theia/filesystem/lib/browser';
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state'; import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
import { ConfigService } from '../common/protocol/config-service';
import { ArduinoCommands } from './arduino-commands'; import { ArduinoCommands } from './arduino-commands';
import { BoardsConfig } from './boards/boards-config'; import { BoardsConfig } from './boards/boards-config';
import { BoardsConfigDialog } from './boards/boards-config-dialog'; import { BoardsConfigDialog } from './boards/boards-config-dialog';
@@ -67,15 +67,12 @@ import { ArduinoMenus } from './menu/arduino-menus';
import { MonitorViewContribution } from './serial/monitor/monitor-view-contribution'; import { MonitorViewContribution } from './serial/monitor/monitor-view-contribution';
import { ArduinoToolbar } from './toolbar/arduino-toolbar'; import { ArduinoToolbar } from './toolbar/arduino-toolbar';
import { ArduinoPreferences } from './arduino-preferences'; import { ArduinoPreferences } from './arduino-preferences';
import { import { SketchesServiceClientImpl } from '../common/protocol/sketches-service-client-impl';
CurrentSketch,
SketchesServiceClientImpl,
} from '../common/protocol/sketches-service-client-impl';
import { SaveAsSketch } from './contributions/save-as-sketch'; import { SaveAsSketch } from './contributions/save-as-sketch';
import { SketchbookWidgetContribution } from './widgets/sketchbook/sketchbook-widget-contribution';
import { IDEUpdaterDialog } from './dialogs/ide-updater/ide-updater-dialog'; import { IDEUpdaterDialog } from './dialogs/ide-updater/ide-updater-dialog';
import { IDEUpdater } from '../common/protocol/ide-updater'; import { IDEUpdater } from '../common/protocol/ide-updater';
import { FileSystemFrontendContribution } from '@theia/filesystem/lib/browser/filesystem-frontend-contribution'; import { FileSystemFrontendContribution } from '@theia/filesystem/lib/browser/filesystem-frontend-contribution';
import { HostedPluginEvents } from './hosted-plugin-events';
const INIT_LIBS_AND_PACKAGES = 'initializedLibsAndPackages'; const INIT_LIBS_AND_PACKAGES = 'initializedLibsAndPackages';
export const SKIP_IDE_VERSION = 'skipIDEVersion'; export const SKIP_IDE_VERSION = 'skipIDEVersion';
@@ -87,73 +84,92 @@ export class ArduinoFrontendContribution
TabBarToolbarContribution, TabBarToolbarContribution,
CommandContribution, CommandContribution,
MenuContribution, MenuContribution,
ColorContribution ColorContribution {
{
@inject(ILogger) @inject(ILogger)
private readonly logger: ILogger; protected logger: ILogger;
@inject(MessageService) @inject(MessageService)
private readonly messageService: MessageService; protected readonly messageService: MessageService;
@inject(BoardsService) @inject(BoardsService)
private readonly boardsService: BoardsService; protected readonly boardsService: BoardsService;
@inject(LibraryService) @inject(LibraryService)
private readonly libraryService: LibraryService; protected readonly libraryService: LibraryService;
@inject(BoardsServiceProvider) @inject(BoardsServiceProvider)
private readonly boardsServiceClientImpl: BoardsServiceProvider; protected readonly boardsServiceClientImpl: BoardsServiceProvider;
@inject(EditorManager) @inject(EditorManager)
private readonly editorManager: EditorManager; protected readonly editorManager: EditorManager;
@inject(FileService) @inject(FileService)
private readonly fileService: FileService; protected readonly fileService: FileService;
@inject(SketchesService) @inject(SketchesService)
private readonly sketchService: SketchesService; protected readonly sketchService: SketchesService;
@inject(BoardsConfigDialog) @inject(BoardsConfigDialog)
private readonly boardsConfigDialog: BoardsConfigDialog; protected readonly boardsConfigDialog: BoardsConfigDialog;
@inject(CommandRegistry) @inject(CommandRegistry)
private readonly commandRegistry: CommandRegistry; protected readonly commandRegistry: CommandRegistry;
@inject(StatusBar) @inject(StatusBar)
private readonly statusBar: StatusBar; protected readonly statusBar: StatusBar;
@inject(FileNavigatorContribution)
protected readonly fileNavigatorContributions: FileNavigatorContribution;
@inject(OutputContribution)
protected readonly outputContribution: OutputContribution;
@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(SketchbookWidgetContribution)
protected readonly sketchbookWidgetContribution: SketchbookWidgetContribution;
@inject(EditorMode) @inject(EditorMode)
private readonly editorMode: EditorMode; protected readonly editorMode: EditorMode;
@inject(HostedPluginEvents) @inject(ConfigService)
private readonly hostedPluginEvents: HostedPluginEvents; protected readonly configService: ConfigService;
@inject(HostedPluginSupport)
protected hostedPluginSupport: HostedPluginSupport;
@inject(ExecutableService) @inject(ExecutableService)
private readonly executableService: ExecutableService; protected executableService: ExecutableService;
@inject(ArduinoPreferences) @inject(ArduinoPreferences)
private readonly arduinoPreferences: ArduinoPreferences; protected readonly arduinoPreferences: ArduinoPreferences;
@inject(SketchesServiceClientImpl) @inject(SketchesServiceClientImpl)
private readonly sketchServiceClient: SketchesServiceClientImpl; protected readonly sketchServiceClient: SketchesServiceClientImpl;
@inject(FrontendApplicationStateService) protected readonly appStateService: FrontendApplicationStateService;
private readonly appStateService: FrontendApplicationStateService;
@inject(LocalStorageService) @inject(LocalStorageService)
private readonly localStorageService: LocalStorageService; protected readonly localStorageService: LocalStorageService;
@inject(FileSystemFrontendContribution) @inject(FileSystemFrontendContribution)
private readonly fileSystemFrontendContribution: FileSystemFrontendContribution; protected readonly fileSystemFrontendContribution: FileSystemFrontendContribution;
@inject(IDEUpdater) @inject(IDEUpdater)
private readonly updater: IDEUpdater; protected readonly updater: IDEUpdater;
@inject(IDEUpdaterDialog) @inject(IDEUpdaterDialog)
private readonly updaterDialog: IDEUpdaterDialog; protected readonly updaterDialog: IDEUpdaterDialog;
@inject(ArduinoDaemon)
private readonly daemon: ArduinoDaemon;
protected invalidConfigPopup: protected invalidConfigPopup:
| Promise<void | 'No' | 'Yes' | undefined> | Promise<void | 'No' | 'Yes' | undefined>
@@ -224,10 +240,7 @@ export class ArduinoFrontendContribution
updateStatusBar(this.boardsServiceClientImpl.boardsConfig); updateStatusBar(this.boardsServiceClientImpl.boardsConfig);
this.appStateService.reachedState('ready').then(async () => { this.appStateService.reachedState('ready').then(async () => {
const sketch = await this.sketchServiceClient.currentSketch(); const sketch = await this.sketchServiceClient.currentSketch();
if ( if (sketch && !(await this.sketchService.isTemp(sketch))) {
CurrentSketch.isValid(sketch) &&
!(await this.sketchService.isTemp(sketch))
) {
this.toDisposeOnStop.push(this.fileService.watch(new URI(sketch.uri))); this.toDisposeOnStop.push(this.fileService.watch(new URI(sketch.uri)));
this.toDisposeOnStop.push( this.toDisposeOnStop.push(
this.fileService.onDidFilesChange(async (event) => { this.fileService.onDidFilesChange(async (event) => {
@@ -253,6 +266,21 @@ export class ArduinoFrontendContribution
} }
async onStart(app: FrontendApplication): Promise<void> { async onStart(app: FrontendApplication): Promise<void> {
// Initialize all `pro-mode` widgets. This is a NOOP if in normal mode.
for (const viewContribution of [
this.fileNavigatorContributions,
this.outputContribution,
this.outlineContribution,
this.problemContribution,
this.scmContribution,
this.siwContribution,
this.sketchbookWidgetContribution,
] as Array<FrontendApplicationContribution>) {
if (viewContribution.initializeLayout) {
viewContribution.initializeLayout(app);
}
}
this.updater this.updater
.init( .init(
this.arduinoPreferences.get('arduino.ide.updateChannel'), this.arduinoPreferences.get('arduino.ide.updateChannel'),
@@ -286,12 +314,6 @@ export class ArduinoFrontendContribution
} }
}; };
this.boardsServiceClientImpl.onBoardsConfigChanged(start); this.boardsServiceClientImpl.onBoardsConfigChanged(start);
this.hostedPluginEvents.onPluginsDidStart(() =>
start(this.boardsServiceClientImpl.boardsConfig)
);
this.hostedPluginEvents.onPluginsWillUnload(
() => (this.languageServerFqbn = undefined)
);
this.arduinoPreferences.onPreferenceChanged((event) => { this.arduinoPreferences.onPreferenceChanged((event) => {
if (event.newValue !== event.oldValue) { if (event.newValue !== event.oldValue) {
switch (event.preferenceName) { switch (event.preferenceName) {
@@ -322,18 +344,16 @@ export class ArduinoFrontendContribution
app.shell.leftPanelHandler.removeBottomMenu('settings-menu'); app.shell.leftPanelHandler.removeBottomMenu('settings-menu');
this.fileSystemFrontendContribution.onDidChangeEditorFile( this.fileSystemFrontendContribution.onDidChangeEditorFile(e => {
({ type, editor }) => { if (e.type === FileChangeType.DELETED) {
if (type === FileChangeType.DELETED) { const editorWidget = e.editor;
const editorWidget = editor; if (SaveableWidget.is(editorWidget)) {
if (SaveableWidget.is(editorWidget)) { editorWidget.closeWithoutSaving();
editorWidget.closeWithoutSaving(); } else {
} else { editorWidget.close();
editorWidget.close();
}
} }
} }
); });
} }
onStop(): void { onStop(): void {
@@ -346,13 +366,9 @@ export class ArduinoFrontendContribution
fqbn: string, fqbn: string,
name: string | undefined name: string | undefined
): Promise<void> { ): Promise<void> {
const port = await this.daemon.tryGetPort();
if (!port) {
return;
}
const release = await this.languageServerStartMutex.acquire(); const release = await this.languageServerStartMutex.acquire();
try { try {
await this.hostedPluginEvents.didStart; await this.hostedPluginSupport.didStart;
const details = await this.boardsService.getBoardDetails({ fqbn }); const details = await this.boardsService.getBoardDetails({ fqbn });
if (!details) { if (!details) {
// Core is not installed for the selected board. // Core is not installed for the selected board.
@@ -387,7 +403,7 @@ export class ArduinoFrontendContribution
let currentSketchPath: string | undefined = undefined; let currentSketchPath: string | undefined = undefined;
if (log) { if (log) {
const currentSketch = await this.sketchServiceClient.currentSketch(); const currentSketch = await this.sketchServiceClient.currentSketch();
if (CurrentSketch.isValid(currentSketch)) { if (currentSketch) {
currentSketchPath = await this.fileService.fsPath( currentSketchPath = await this.fileService.fsPath(
new URI(currentSketch.uri) new URI(currentSketch.uri)
); );
@@ -399,6 +415,8 @@ export class ArduinoFrontendContribution
this.fileService.fsPath(new URI(lsUri)), this.fileService.fsPath(new URI(lsUri)),
]); ]);
const config = await this.configService.getConfiguration();
this.languageServerFqbn = await Promise.race([ this.languageServerFqbn = await Promise.race([
new Promise<undefined>((_, reject) => new Promise<undefined>((_, reject) =>
setTimeout( setTimeout(
@@ -410,7 +428,7 @@ export class ArduinoFrontendContribution
'arduino.languageserver.start', 'arduino.languageserver.start',
{ {
lsPath, lsPath,
cliDaemonAddr: `localhost:${port}`, cliDaemonAddr: `localhost:${config.daemon.port}`, // TODO: verify if this port is coming from the BE
clangdPath, clangdPath,
log: currentSketchPath ? currentSketchPath : log, log: currentSketchPath ? currentSketchPath : log,
cliDaemonInstance: '1', cliDaemonInstance: '1',
@@ -476,13 +494,13 @@ export class ArduinoFrontendContribution
EditorCommands.SPLIT_EDITOR_UP, EditorCommands.SPLIT_EDITOR_UP,
EditorCommands.SPLIT_EDITOR_VERTICAL, EditorCommands.SPLIT_EDITOR_VERTICAL,
EditorCommands.SPLIT_EDITOR_HORIZONTAL, EditorCommands.SPLIT_EDITOR_HORIZONTAL,
FileNavigatorCommands.REVEAL_IN_NAVIGATOR, FileNavigatorCommands.REVEAL_IN_NAVIGATOR
]) { ]) {
registry.unregisterCommand(command); registry.unregisterCommand(command);
} }
} }
registerMenus(registry: MenuModelRegistry): void { registerMenus(registry: MenuModelRegistry) {
const menuId = (menuPath: string[]): string => { const menuId = (menuPath: string[]): string => {
const index = menuPath.length - 1; const index = menuPath.length - 1;
const menuId = menuPath[index]; const menuId = menuPath[index];
@@ -551,19 +569,12 @@ export class ArduinoFrontendContribution
uri: string, uri: string,
forceOpen = false, forceOpen = false,
options?: EditorOpenerOptions | undefined options?: EditorOpenerOptions | undefined
): Promise<unknown> { ): Promise<any> {
const widget = this.editorManager.all.find( const widget = this.editorManager.all.find(
(widget) => widget.editor.uri.toString() === uri (widget) => widget.editor.uri.toString() === uri
); );
if (!widget || forceOpen) { if (!widget || forceOpen) {
return this.editorManager.open( return this.editorManager.open(new URI(uri), options);
new URI(uri),
options ?? {
mode: 'reveal',
preview: false,
counter: 0,
}
);
} }
} }
@@ -647,57 +658,4 @@ export class ArduinoFrontendContribution
} }
); );
} }
onWillStop(): OnWillStopAction {
return {
reason: 'temp-sketch',
action: () => {
return this.showTempSketchDialog();
},
};
}
private async showTempSketchDialog(): Promise<boolean> {
const sketch = await this.sketchServiceClient.currentSketch();
if (!CurrentSketch.isValid(sketch)) {
return true;
}
const isTemp = await this.sketchService.isTemp(sketch);
if (!isTemp) {
return true;
}
const messageBoxResult = await remote.dialog.showMessageBox(
remote.getCurrentWindow(),
{
message: nls.localize(
'arduino/sketch/saveTempSketch',
'Save your sketch to open it again later.'
),
title: nls.localize(
'theia/core/quitTitle',
'Are you sure you want to quit?'
),
type: 'question',
buttons: [
Dialog.CANCEL,
nls.localizeByDefault('Save As...'),
nls.localizeByDefault("Don't Save"),
],
}
);
const result = messageBoxResult.response;
if (result === 2) {
return true;
} else if (result === 1) {
return !!(await this.commandRegistry.executeCommand(
SaveAsSketch.Commands.SAVE_AS_SKETCH.id,
{
execOnlyIfTemp: false,
openAfterMove: false,
wipeOriginal: true,
}
));
}
return false;
}
} }

View File

@@ -1,5 +1,5 @@
import '../../src/browser/style/index.css'; import '../../src/browser/style/index.css';
import { ContainerModule } from '@theia/core/shared/inversify'; import { ContainerModule } from 'inversify';
import { WidgetFactory } from '@theia/core/lib/browser/widget-manager'; import { WidgetFactory } from '@theia/core/lib/browser/widget-manager';
import { CommandContribution } from '@theia/core/lib/common/command'; import { CommandContribution } from '@theia/core/lib/common/command';
import { bindViewContribution } from '@theia/core/lib/browser/shell/view-contribution'; import { bindViewContribution } from '@theia/core/lib/browser/shell/view-contribution';
@@ -42,14 +42,15 @@ import { FileNavigatorContribution as TheiaFileNavigatorContribution } from '@th
import { KeymapsFrontendContribution } from './theia/keymaps/keymaps-frontend-contribution'; import { KeymapsFrontendContribution } from './theia/keymaps/keymaps-frontend-contribution';
import { KeymapsFrontendContribution as TheiaKeymapsFrontendContribution } from '@theia/keymaps/lib/browser/keymaps-frontend-contribution'; import { KeymapsFrontendContribution as TheiaKeymapsFrontendContribution } from '@theia/keymaps/lib/browser/keymaps-frontend-contribution';
import { ArduinoToolbarContribution } from './toolbar/arduino-toolbar-contribution'; import { ArduinoToolbarContribution } from './toolbar/arduino-toolbar-contribution';
import { EditorContribution as TheiaEditorContribution } from '@theia/editor/lib/browser/editor-contribution'; import { EditorPreviewContribution as TheiaEditorPreviewContribution } from '@theia/editor-preview/lib/browser/editor-preview-contribution';
import { EditorContribution } from './theia/editor/editor-contribution'; import { EditorPreviewContribution } from './theia/editor/editor-contribution';
import { MonacoStatusBarContribution as TheiaMonacoStatusBarContribution } from '@theia/monaco/lib/browser/monaco-status-bar-contribution'; import { MonacoStatusBarContribution as TheiaMonacoStatusBarContribution } from '@theia/monaco/lib/browser/monaco-status-bar-contribution';
import { MonacoStatusBarContribution } from './theia/monaco/monaco-status-bar-contribution'; import { MonacoStatusBarContribution } from './theia/monaco/monaco-status-bar-contribution';
import { import {
ApplicationShell as TheiaApplicationShell, ApplicationShell as TheiaApplicationShell,
ShellLayoutRestorer as TheiaShellLayoutRestorer, ShellLayoutRestorer as TheiaShellLayoutRestorer,
CommonFrontendContribution as TheiaCommonFrontendContribution, CommonFrontendContribution as TheiaCommonFrontendContribution,
KeybindingRegistry as TheiaKeybindingRegistry,
TabBarRendererFactory, TabBarRendererFactory,
ContextMenuRenderer, ContextMenuRenderer,
createTreeContainer, createTreeContainer,
@@ -121,7 +122,6 @@ import { SaveAsSketch } from './contributions/save-as-sketch';
import { SaveSketch } from './contributions/save-sketch'; import { SaveSketch } from './contributions/save-sketch';
import { VerifySketch } from './contributions/verify-sketch'; import { VerifySketch } from './contributions/verify-sketch';
import { UploadSketch } from './contributions/upload-sketch'; import { UploadSketch } from './contributions/upload-sketch';
import { SurveyNotification } from './contributions/survey-notification';
import { CommonFrontendContribution } from './theia/core/common-frontend-contribution'; import { CommonFrontendContribution } from './theia/core/common-frontend-contribution';
import { EditContributions } from './contributions/edit-contributions'; import { EditContributions } from './contributions/edit-contributions';
import { OpenSketchExternal } from './contributions/open-sketch-external'; import { OpenSketchExternal } from './contributions/open-sketch-external';
@@ -130,6 +130,7 @@ import { PreferencesContribution } from './theia/preferences/preferences-contrib
import { QuitApp } from './contributions/quit-app'; import { QuitApp } from './contributions/quit-app';
import { SketchControl } from './contributions/sketch-control'; import { SketchControl } from './contributions/sketch-control';
import { Settings } from './contributions/settings'; import { Settings } from './contributions/settings';
import { KeybindingRegistry } from './theia/core/keybindings';
import { WorkspaceCommandContribution } from './theia/workspace/workspace-commands'; import { WorkspaceCommandContribution } from './theia/workspace/workspace-commands';
import { WorkspaceDeleteHandler as TheiaWorkspaceDeleteHandler } from '@theia/workspace/lib/browser/workspace-delete-handler'; import { WorkspaceDeleteHandler as TheiaWorkspaceDeleteHandler } from '@theia/workspace/lib/browser/workspace-delete-handler';
import { WorkspaceDeleteHandler } from './theia/workspace/workspace-delete-handler'; import { WorkspaceDeleteHandler } from './theia/workspace/workspace-delete-handler';
@@ -151,14 +152,7 @@ import {
OutputChannelRegistryMainImpl as TheiaOutputChannelRegistryMainImpl, OutputChannelRegistryMainImpl as TheiaOutputChannelRegistryMainImpl,
OutputChannelRegistryMainImpl, OutputChannelRegistryMainImpl,
} from './theia/plugin-ext/output-channel-registry-main'; } from './theia/plugin-ext/output-channel-registry-main';
import { import { ExecutableService, ExecutableServicePath, MonitorManagerProxy, MonitorManagerProxyClient, MonitorManagerProxyFactory, MonitorManagerProxyPath } from '../common/protocol';
ExecutableService,
ExecutableServicePath,
MonitorManagerProxy,
MonitorManagerProxyClient,
MonitorManagerProxyFactory,
MonitorManagerProxyPath,
} from '../common/protocol';
import { MonacoTextModelService as TheiaMonacoTextModelService } from '@theia/monaco/lib/browser/monaco-text-model-service'; import { MonacoTextModelService as TheiaMonacoTextModelService } from '@theia/monaco/lib/browser/monaco-text-model-service';
import { MonacoTextModelService } from './theia/monaco/monaco-text-model-service'; import { MonacoTextModelService } from './theia/monaco/monaco-text-model-service';
import { ResponseServiceImpl } from './response-service-impl'; import { ResponseServiceImpl } from './response-service-impl';
@@ -275,49 +269,20 @@ import {
import { ElectronIpcConnectionProvider } from '@theia/core/lib/electron-browser/messaging/electron-ipc-connection-provider'; import { ElectronIpcConnectionProvider } from '@theia/core/lib/electron-browser/messaging/electron-ipc-connection-provider';
import { MonitorModel } from './monitor-model'; import { MonitorModel } from './monitor-model';
import { MonitorManagerProxyClientImpl } from './monitor-manager-proxy-client-impl'; import { MonitorManagerProxyClientImpl } from './monitor-manager-proxy-client-impl';
import { EditorManager as TheiaEditorManager } from '@theia/editor/lib/browser/editor-manager';
import { EditorManager } from './theia/editor/editor-manager'; const ElementQueries = require('css-element-queries/src/ElementQueries');
import { HostedPluginEvents } from './hosted-plugin-events';
import { HostedPluginSupport } from './theia/plugin-ext/hosted-plugin';
import { HostedPluginSupport as TheiaHostedPluginSupport } from '@theia/plugin-ext/lib/hosted/browser/hosted-plugin';
import { Formatter, FormatterPath } from '../common/protocol/formatter';
import { Format } from './contributions/format';
import { MonacoFormattingConflictsContribution } from './theia/monaco/monaco-formatting-conflicts';
import { MonacoFormattingConflictsContribution as TheiaMonacoFormattingConflictsContribution } from '@theia/monaco/lib/browser/monaco-formatting-conflicts';
import { DefaultJsonSchemaContribution } from './theia/core/json-schema-store';
import { DefaultJsonSchemaContribution as TheiaDefaultJsonSchemaContribution } from '@theia/core/lib/browser/json-schema-store';
import { EditorNavigationContribution } from './theia/editor/editor-navigation-contribution';
import { EditorNavigationContribution as TheiaEditorNavigationContribution } from '@theia/editor/lib/browser/editor-navigation-contribution';
import { PreferenceTreeGenerator } from './theia/preferences/preference-tree-generator';
import { PreferenceTreeGenerator as TheiaPreferenceTreeGenerator } from '@theia/preferences/lib/browser/util/preference-tree-generator';
import { AboutDialog } from './theia/core/about-dialog';
import { AboutDialog as TheiaAboutDialog } from '@theia/core/lib/browser/about-dialog';
import {
SurveyNotificationService,
SurveyNotificationServicePath,
} from '../common/protocol/survey-service';
import { WindowContribution } from './theia/core/window-contribution';
import { WindowContribution as TheiaWindowContribution } from '@theia/core/lib/browser/window-contribution';
import { CoreErrorHandler } from './contributions/core-error-handler';
import { CompilerErrors } from './contributions/compiler-errors';
import { WidgetManager } from './theia/core/widget-manager';
import { WidgetManager as TheiaWidgetManager } from '@theia/core/lib/browser/widget-manager';
MonacoThemingService.register({ MonacoThemingService.register({
id: 'arduino-theme', id: 'arduino-theme',
label: 'Light (Arduino)', label: 'Light (Arduino)',
uiTheme: 'vs', uiTheme: 'vs',
json: require('../../src/browser/data/default.color-theme.json'), json: require('../../src/browser/data/arduino.color-theme.json'),
});
MonacoThemingService.register({
id: 'arduino-theme-dark',
label: 'Dark (Arduino)',
uiTheme: 'vs-dark',
json: require('../../src/browser/data/dark.color-theme.json'),
}); });
export default new ContainerModule((bind, unbind, isBound, rebind) => { export default new ContainerModule((bind, unbind, isBound, rebind) => {
ElementQueries.listen();
ElementQueries.init();
// Commands and toolbar items // Commands and toolbar items
bind(ArduinoFrontendContribution).toSelf().inSingletonScope(); bind(ArduinoFrontendContribution).toSelf().inSingletonScope();
bind(CommandContribution).toService(ArduinoFrontendContribution); bind(CommandContribution).toService(ArduinoFrontendContribution);
@@ -434,7 +399,6 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
) )
) )
.inSingletonScope(); .inSingletonScope();
bind(CoreErrorHandler).toSelf().inSingletonScope();
// Serial monitor // Serial monitor
bind(MonitorWidget).toSelf(); bind(MonitorWidget).toSelf();
@@ -447,34 +411,21 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
createWidget: () => { createWidget: () => {
return new MonitorWidget( return new MonitorWidget(
context.container.get<MonitorModel>(MonitorModel), context.container.get<MonitorModel>(MonitorModel),
context.container.get<MonitorManagerProxyClient>( context.container.get<MonitorManagerProxyClient>(MonitorManagerProxyClient),
MonitorManagerProxyClient context.container.get<BoardsServiceProvider>(BoardsServiceProvider),
),
context.container.get<BoardsServiceProvider>(BoardsServiceProvider)
); );
}, }
})); }));
bind(MonitorManagerProxyFactory).toFactory( bind(MonitorManagerProxyFactory).toFactory((context) => () => context.container.get<MonitorManagerProxy>(MonitorManagerProxy))
(context) => () =>
context.container.get<MonitorManagerProxy>(MonitorManagerProxy)
);
bind(MonitorManagerProxy) bind(MonitorManagerProxy).toDynamicValue((context) =>
.toDynamicValue((context) => WebSocketConnectionProvider.createProxy(context.container, MonitorManagerProxyPath, context.container.get(MonitorManagerProxyClient))
WebSocketConnectionProvider.createProxy( ).inSingletonScope();
context.container,
MonitorManagerProxyPath,
context.container.get(MonitorManagerProxyClient)
)
)
.inSingletonScope();
// Monitor manager proxy client to receive and delegate pluggable monitors // Monitor manager proxy client to receive and delegate pluggable monitors
// notifications from the backend // notifications from the backend
bind(MonitorManagerProxyClient) bind(MonitorManagerProxyClient).to(MonitorManagerProxyClientImpl).inSingletonScope();
.to(MonitorManagerProxyClientImpl)
.inSingletonScope();
bind(WorkspaceService).toSelf().inSingletonScope(); bind(WorkspaceService).toSelf().inSingletonScope();
rebind(TheiaWorkspaceService).toService(WorkspaceService); rebind(TheiaWorkspaceService).toService(WorkspaceService);
@@ -487,19 +438,6 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
bind(EditorMode).toSelf().inSingletonScope(); bind(EditorMode).toSelf().inSingletonScope();
bind(FrontendApplicationContribution).toService(EditorMode); bind(FrontendApplicationContribution).toService(EditorMode);
// Survey notification
bind(SurveyNotification).toSelf().inSingletonScope();
bind(FrontendApplicationContribution).toService(SurveyNotification);
bind(SurveyNotificationService)
.toDynamicValue((context) => {
return ElectronIpcConnectionProvider.createProxy(
context.container,
SurveyNotificationServicePath
);
})
.inSingletonScope();
// Layout and shell customizations. // Layout and shell customizations.
rebind(TheiaOutlineViewContribution) rebind(TheiaOutlineViewContribution)
.to(OutlineViewContribution) .to(OutlineViewContribution)
@@ -511,7 +449,9 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
rebind(TheiaKeymapsFrontendContribution) rebind(TheiaKeymapsFrontendContribution)
.to(KeymapsFrontendContribution) .to(KeymapsFrontendContribution)
.inSingletonScope(); .inSingletonScope();
rebind(TheiaEditorContribution).to(EditorContribution).inSingletonScope(); rebind(TheiaEditorPreviewContribution)
.to(EditorPreviewContribution)
.inSingletonScope();
rebind(TheiaMonacoStatusBarContribution) rebind(TheiaMonacoStatusBarContribution)
.to(MonacoStatusBarContribution) .to(MonacoStatusBarContribution)
.inSingletonScope(); .inSingletonScope();
@@ -533,6 +473,7 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
rebind(TheiaPreferencesContribution) rebind(TheiaPreferencesContribution)
.to(PreferencesContribution) .to(PreferencesContribution)
.inSingletonScope(); .inSingletonScope();
rebind(TheiaKeybindingRegistry).to(KeybindingRegistry).inSingletonScope();
rebind(TheiaWorkspaceCommandContribution) rebind(TheiaWorkspaceCommandContribution)
.to(WorkspaceCommandContribution) .to(WorkspaceCommandContribution)
.inSingletonScope(); .inSingletonScope();
@@ -541,12 +482,11 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
.inSingletonScope(); .inSingletonScope();
rebind(TheiaEditorWidgetFactory).to(EditorWidgetFactory).inSingletonScope(); rebind(TheiaEditorWidgetFactory).to(EditorWidgetFactory).inSingletonScope();
rebind(TabBarToolbarFactory).toFactory( rebind(TabBarToolbarFactory).toFactory(
({ container: parentContainer }) => ({ container: parentContainer }) => () => {
() => { const container = parentContainer.createChild();
const container = parentContainer.createChild(); container.bind(TabBarToolbar).toSelf().inSingletonScope();
container.bind(TabBarToolbar).toSelf().inSingletonScope(); return container.get(TabBarToolbar);
return container.get(TabBarToolbar); }
}
); );
bind(OutputWidget).toSelf().inSingletonScope(); bind(OutputWidget).toSelf().inSingletonScope();
rebind(TheiaOutputWidget).toService(OutputWidget); rebind(TheiaOutputWidget).toService(OutputWidget);
@@ -564,10 +504,6 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
bind(SearchInWorkspaceWidget).toSelf(); bind(SearchInWorkspaceWidget).toSelf();
rebind(TheiaSearchInWorkspaceWidget).toService(SearchInWorkspaceWidget); rebind(TheiaSearchInWorkspaceWidget).toService(SearchInWorkspaceWidget);
// Disabled reference counter in the editor manager to avoid opening the same editor (with different opener options) multiple times.
bind(EditorManager).toSelf().inSingletonScope();
rebind(TheiaEditorManager).to(EditorManager);
// replace search icon // replace search icon
rebind(TheiaSearchInWorkspaceFactory) rebind(TheiaSearchInWorkspaceFactory)
.to(SearchInWorkspaceFactory) .to(SearchInWorkspaceFactory)
@@ -610,10 +546,6 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
bind(OutputToolbarContribution).toSelf().inSingletonScope(); bind(OutputToolbarContribution).toSelf().inSingletonScope();
rebind(TheiaOutputToolbarContribution).toService(OutputToolbarContribution); rebind(TheiaOutputToolbarContribution).toService(OutputToolbarContribution);
// To remove `New Window` from the `File` menu
bind(WindowContribution).toSelf().inSingletonScope();
rebind(TheiaWindowContribution).toService(WindowContribution);
bind(ArduinoDaemon) bind(ArduinoDaemon)
.toDynamicValue((context) => .toDynamicValue((context) =>
WebSocketConnectionProvider.createProxy( WebSocketConnectionProvider.createProxy(
@@ -623,12 +555,6 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
) )
.inSingletonScope(); .inSingletonScope();
bind(Formatter)
.toDynamicValue(({ container }) =>
WebSocketConnectionProvider.createProxy(container, FormatterPath)
)
.inSingletonScope();
bind(ArduinoFirmwareUploader) bind(ArduinoFirmwareUploader)
.toDynamicValue((context) => .toDynamicValue((context) =>
WebSocketConnectionProvider.createProxy( WebSocketConnectionProvider.createProxy(
@@ -696,15 +622,6 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
Contribution.configure(bind, ArchiveSketch); Contribution.configure(bind, ArchiveSketch);
Contribution.configure(bind, AddZipLibrary); Contribution.configure(bind, AddZipLibrary);
Contribution.configure(bind, PlotterFrontendContribution); Contribution.configure(bind, PlotterFrontendContribution);
Contribution.configure(bind, Format);
Contribution.configure(bind, CompilerErrors);
// Disabled the quick-pick customization from Theia when multiple formatters are available.
// Use the default VS Code behavior, and pick the first one. In the IDE2, clang-format has `exclusive` selectors.
bind(MonacoFormattingConflictsContribution).toSelf().inSingletonScope();
rebind(TheiaMonacoFormattingConflictsContribution).toService(
MonacoFormattingConflictsContribution
);
bind(ResponseServiceImpl) bind(ResponseServiceImpl)
.toSelf() .toSelf()
@@ -734,13 +651,15 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
// Enable the dirty indicator on uncloseable widgets. // Enable the dirty indicator on uncloseable widgets.
rebind(TabBarRendererFactory).toFactory((context) => () => { rebind(TabBarRendererFactory).toFactory((context) => () => {
const contextMenuRenderer = const contextMenuRenderer = context.container.get<ContextMenuRenderer>(
context.container.get<ContextMenuRenderer>(ContextMenuRenderer); ContextMenuRenderer
);
const decoratorService = context.container.get<TabBarDecoratorService>( const decoratorService = context.container.get<TabBarDecoratorService>(
TabBarDecoratorService TabBarDecoratorService
); );
const iconThemeService = const iconThemeService = context.container.get<IconThemeService>(
context.container.get<IconThemeService>(IconThemeService); IconThemeService
);
return new TabBarRenderer( return new TabBarRenderer(
contextMenuRenderer, contextMenuRenderer,
decoratorService, decoratorService,
@@ -750,8 +669,6 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
// Workaround for https://github.com/eclipse-theia/theia/issues/8722 // Workaround for https://github.com/eclipse-theia/theia/issues/8722
// Do not trigger a save on IDE startup if `"editor.autoSave": "on"` was set as a preference. // Do not trigger a save on IDE startup if `"editor.autoSave": "on"` was set as a preference.
// Note: `"editor.autoSave" was renamed to `"files.autoSave" and `"on"` was replaced with three
// different cases, but we treat `!== 'off'` as auto save enabled. (https://github.com/eclipse-theia/theia/issues/10812)
bind(EditorCommandContribution).toSelf().inSingletonScope(); bind(EditorCommandContribution).toSelf().inSingletonScope();
rebind(TheiaEditorCommandContribution).toService(EditorCommandContribution); rebind(TheiaEditorCommandContribution).toService(EditorCommandContribution);
@@ -759,26 +676,6 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
bind(NavigatorTabBarDecorator).toSelf().inSingletonScope(); bind(NavigatorTabBarDecorator).toSelf().inSingletonScope();
rebind(TheiaNavigatorTabBarDecorator).toService(NavigatorTabBarDecorator); rebind(TheiaNavigatorTabBarDecorator).toService(NavigatorTabBarDecorator);
// Do not fetch the `catalog.json` from Azure on FE load.
bind(DefaultJsonSchemaContribution).toSelf().inSingletonScope();
rebind(TheiaDefaultJsonSchemaContribution).toService(
DefaultJsonSchemaContribution
);
// Do not block the app startup when initializing the editor navigation history.
bind(EditorNavigationContribution).toSelf().inSingletonScope();
rebind(TheiaEditorNavigationContribution).toService(
EditorNavigationContribution
);
// IDE2 does not use the Theia preferences widget, no need to create and sync the underlying tree model.
bind(PreferenceTreeGenerator).toSelf().inSingletonScope();
rebind(TheiaPreferenceTreeGenerator).toService(PreferenceTreeGenerator);
// IDE2 has a custom about dialog, so there is no need to load the Theia extensions on FE load
bind(AboutDialog).toSelf().inSingletonScope();
rebind(TheiaAboutDialog).toService(AboutDialog);
// To avoid running `Save All` when there are no dirty editors before starting the debug session. // To avoid running `Save All` when there are no dirty editors before starting the debug session.
bind(DebugSessionManager).toSelf().inSingletonScope(); bind(DebugSessionManager).toSelf().inSingletonScope();
rebind(TheiaDebugSessionManager).toService(DebugSessionManager); rebind(TheiaDebugSessionManager).toService(DebugSessionManager);
@@ -791,10 +688,6 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
bind(DebugConfigurationManager).toSelf().inSingletonScope(); bind(DebugConfigurationManager).toSelf().inSingletonScope();
rebind(TheiaDebugConfigurationManager).toService(DebugConfigurationManager); rebind(TheiaDebugConfigurationManager).toService(DebugConfigurationManager);
// To avoid duplicate tabs use deepEqual instead of string equal: https://github.com/eclipse-theia/theia/issues/11309
bind(WidgetManager).toSelf().inSingletonScope();
rebind(TheiaWidgetManager).toService(WidgetManager);
// Preferences // Preferences
bindArduinoPreferences(bind); bindArduinoPreferences(bind);
@@ -902,9 +795,4 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
); );
}) })
.inSingletonScope(); .inSingletonScope();
bind(HostedPluginSupport).toSelf().inSingletonScope();
rebind(TheiaHostedPluginSupport).toService(HostedPluginSupport);
bind(HostedPluginEvents).toSelf().inSingletonScope();
bind(FrontendApplicationContribution).toService(HostedPluginEvents);
}); });

View File

@@ -1,4 +1,4 @@
import { interfaces } from '@theia/core/shared/inversify'; import { interfaces } from 'inversify';
import { import {
createPreferenceProxy, createPreferenceProxy,
PreferenceProxy, PreferenceProxy,
@@ -13,32 +13,6 @@ export enum UpdateChannel {
Stable = 'stable', Stable = 'stable',
Nightly = 'nightly', Nightly = 'nightly',
} }
export const ErrorRevealStrategyLiterals = [
/**
* Scroll vertically as necessary and reveal a line.
*/
'auto',
/**
* Scroll vertically as necessary and reveal a line centered vertically.
*/
'center',
/**
* Scroll vertically as necessary and reveal a line close to the top of the viewport, optimized for viewing a code definition.
*/
'top',
/**
* Scroll vertically as necessary and reveal a line centered vertically only if it lies outside the viewport.
*/
'centerIfOutsideViewport',
] as const;
export type ErrorRevealStrategy = typeof ErrorRevealStrategyLiterals[number];
export namespace ErrorRevealStrategy {
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any
export function is(arg: any): arg is ErrorRevealStrategy {
return !!arg && ErrorRevealStrategyLiterals.includes(arg);
}
export const Default: ErrorRevealStrategy = 'centerIfOutsideViewport';
}
export const ArduinoConfigSchema: PreferenceSchema = { export const ArduinoConfigSchema: PreferenceSchema = {
type: 'object', type: 'object',
@@ -59,23 +33,6 @@ export const ArduinoConfigSchema: PreferenceSchema = {
), ),
default: false, default: false,
}, },
'arduino.compile.experimental': {
type: 'boolean',
description: nls.localize(
'arduino/preferences/compile.experimental',
'True if the IDE should handle multiple compiler errors. False by default'
),
default: false,
},
'arduino.compile.revealRange': {
enum: [...ErrorRevealStrategyLiterals],
description: nls.localize(
'arduino/preferences/compile.revealRange',
"Adjusts how compiler errors are revealed in the editor after a failed verify/upload. Possible values: 'auto': Scroll vertically as necessary and reveal a line. 'center': Scroll vertically as necessary and reveal a line centered vertically. 'top': Scroll vertically as necessary and reveal a line close to the top of the viewport, optimized for viewing a code definition. 'centerIfOutsideViewport': Scroll vertically as necessary and reveal a line centered vertically only if it lies outside the viewport. The default value is '{0}'.",
ErrorRevealStrategy.Default
),
default: ErrorRevealStrategy.Default,
},
'arduino.compile.warnings': { 'arduino.compile.warnings': {
enum: [...CompilerWarningLiterals], enum: [...CompilerWarningLiterals],
description: nls.localize( description: nls.localize(
@@ -126,7 +83,7 @@ export const ArduinoConfigSchema: PreferenceSchema = {
default: 'https://downloads.arduino.cc/arduino-ide', default: 'https://downloads.arduino.cc/arduino-ide',
description: nls.localize( description: nls.localize(
'arduino/preferences/ide.updateBaseUrl', 'arduino/preferences/ide.updateBaseUrl',
"The base URL where to download updates from. Defaults to 'https://downloads.arduino.cc/arduino-ide'" `The base URL where to download updates from. Defaults to 'https://downloads.arduino.cc/arduino-ide'`
), ),
}, },
'arduino.board.certificates': { 'arduino.board.certificates': {
@@ -217,30 +174,12 @@ export const ArduinoConfigSchema: PreferenceSchema = {
), ),
default: 'https://auth.arduino.cc/login#/register', default: 'https://auth.arduino.cc/login#/register',
}, },
'arduino.survey.notification': {
type: 'boolean',
description: nls.localize(
'arduino/preferences/survey.notification',
'True if users should be notified if a survey is available. True by default.'
),
default: true,
},
'arduino.cli.daemon.debug': {
type: 'boolean',
description: nls.localize(
'arduino/preferences/cli.daemonDebug',
"Enable debug logging of the gRPC calls to the Arduino CLI. A restart of the IDE is needed for this setting to take effect. It's false by default."
),
default: false,
},
}, },
}; };
export interface ArduinoConfiguration { export interface ArduinoConfiguration {
'arduino.language.log': boolean; 'arduino.language.log': boolean;
'arduino.compile.verbose': boolean; 'arduino.compile.verbose': boolean;
'arduino.compile.experimental': boolean;
'arduino.compile.revealRange': ErrorRevealStrategy;
'arduino.compile.warnings': CompilerWarnings; 'arduino.compile.warnings': CompilerWarnings;
'arduino.upload.verbose': boolean; 'arduino.upload.verbose': boolean;
'arduino.upload.verify': boolean; 'arduino.upload.verify': boolean;
@@ -259,8 +198,6 @@ export interface ArduinoConfiguration {
'arduino.auth.domain': string; 'arduino.auth.domain': string;
'arduino.auth.audience': string; 'arduino.auth.audience': string;
'arduino.auth.registerUri': string; 'arduino.auth.registerUri': string;
'arduino.survey.notification': boolean;
'arduino.cli.daemon.debug': boolean;
} }
export const ArduinoPreferences = Symbol('ArduinoPreferences'); export const ArduinoPreferences = Symbol('ArduinoPreferences');

View File

@@ -1,4 +1,4 @@
import { inject, injectable } from '@theia/core/shared/inversify'; import { inject, injectable } from 'inversify';
import { Emitter } from '@theia/core/lib/common/event'; import { Emitter } from '@theia/core/lib/common/event';
import { JsonRpcProxy } from '@theia/core/lib/common/messaging/proxy-factory'; import { JsonRpcProxy } from '@theia/core/lib/common/messaging/proxy-factory';
import { WindowService } from '@theia/core/lib/browser/window/window-service'; import { WindowService } from '@theia/core/lib/browser/window/window-service';
@@ -43,15 +43,13 @@ export class AuthenticationClientService
readonly onSessionDidChange = this.onSessionDidChangeEmitter.event; readonly onSessionDidChange = this.onSessionDidChangeEmitter.event;
async onStart(): Promise<void> { onStart(): void {
this.toDispose.push(this.onSessionDidChangeEmitter); this.toDispose.push(this.onSessionDidChangeEmitter);
this.service.setClient(this); this.service.setClient(this);
this.service this.service
.session() .session()
.then((session) => this.notifySessionDidChange(session)); .then((session) => this.notifySessionDidChange(session));
this.setOptions();
this.setOptions().then(() => this.service.initAuthSession());
this.arduinoPreferences.onPreferenceChanged((event) => { this.arduinoPreferences.onPreferenceChanged((event) => {
if (event.preferenceName.startsWith('arduino.auth.')) { if (event.preferenceName.startsWith('arduino.auth.')) {
this.setOptions(); this.setOptions();
@@ -59,8 +57,8 @@ export class AuthenticationClientService
}); });
} }
setOptions(): Promise<void> { setOptions(): void {
return this.service.setOptions({ this.service.setOptions({
redirectUri: `http://localhost:${serverPort}/callback`, redirectUri: `http://localhost:${serverPort}/callback`,
responseType: 'code', responseType: 'code',
clientID: this.arduinoPreferences['arduino.auth.clientID'], clientID: this.arduinoPreferences['arduino.auth.clientID'],

View File

@@ -1,4 +1,4 @@
import { injectable, inject } from '@theia/core/shared/inversify'; import { injectable, inject } from 'inversify';
import { MessageService } from '@theia/core/lib/common/message-service'; import { MessageService } from '@theia/core/lib/common/message-service';
import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application'; import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application';
import { import {

View File

@@ -1,5 +1,5 @@
import * as React from '@theia/core/shared/react'; import * as React from 'react';
import { injectable, inject } from '@theia/core/shared/inversify'; import { injectable, inject } from 'inversify';
import { Emitter } from '@theia/core/lib/common/event'; import { Emitter } from '@theia/core/lib/common/event';
import { ReactWidget, Message } from '@theia/core/lib/browser'; import { ReactWidget, Message } from '@theia/core/lib/browser';
import { BoardsService } from '../../common/protocol/boards-service'; import { BoardsService } from '../../common/protocol/boards-service';
@@ -55,13 +55,12 @@ export class BoardsConfigDialogWidget extends ReactWidget {
onConfigChange={this.fireConfigChanged} onConfigChange={this.fireConfigChanged}
onFocusNodeSet={this.setFocusNode} onFocusNodeSet={this.setFocusNode}
onFilteredTextDidChangeEvent={this.onFilterTextDidChangeEmitter.event} onFilteredTextDidChangeEvent={this.onFilterTextDidChangeEmitter.event}
onAppStateDidChange={this.notificationCenter.onAppStateDidChange}
/> />
</div> </div>
); );
} }
protected override onActivateRequest(msg: Message): void { protected onActivateRequest(msg: Message): void {
super.onActivateRequest(msg); super.onActivateRequest(msg);
if (this.focusNode instanceof HTMLInputElement) { if (this.focusNode instanceof HTMLInputElement) {
this.focusNode.select(); this.focusNode.select();

View File

@@ -1,5 +1,5 @@
import { injectable, inject, postConstruct } from '@theia/core/shared/inversify'; import { injectable, inject, postConstruct } from 'inversify';
import { Message } from '@theia/core/shared/@phosphor/messaging'; import { Message } from '@phosphor/messaging';
import { DialogProps, Widget, DialogError } from '@theia/core/lib/browser'; import { DialogProps, Widget, DialogError } from '@theia/core/lib/browser';
import { AbstractDialog } from '../theia/dialogs/dialogs'; import { AbstractDialog } from '../theia/dialogs/dialogs';
import { BoardsConfig } from './boards-config'; import { BoardsConfig } from './boards-config';
@@ -26,7 +26,7 @@ export class BoardsConfigDialog extends AbstractDialog<BoardsConfig.Config> {
constructor( constructor(
@inject(BoardsConfigDialogProps) @inject(BoardsConfigDialogProps)
protected override readonly props: BoardsConfigDialogProps protected readonly props: BoardsConfigDialogProps
) { ) {
super(props); super(props);
@@ -52,7 +52,7 @@ export class BoardsConfigDialog extends AbstractDialog<BoardsConfig.Config> {
/** /**
* Pass in an empty string if you want to reset the search term. Using `undefined` has no effect. * Pass in an empty string if you want to reset the search term. Using `undefined` has no effect.
*/ */
override async open( async open(
query: string | undefined = undefined query: string | undefined = undefined
): Promise<BoardsConfig.Config | undefined> { ): Promise<BoardsConfig.Config | undefined> {
if (typeof query === 'string') { if (typeof query === 'string') {
@@ -95,7 +95,7 @@ export class BoardsConfigDialog extends AbstractDialog<BoardsConfig.Config> {
return head; return head;
} }
protected override onAfterAttach(msg: Message): void { protected onAfterAttach(msg: Message): void {
if (this.widget.isAttached) { if (this.widget.isAttached) {
Widget.detach(this.widget); Widget.detach(this.widget);
} }
@@ -110,23 +110,23 @@ export class BoardsConfigDialog extends AbstractDialog<BoardsConfig.Config> {
this.update(); this.update();
} }
protected override onUpdateRequest(msg: Message): void { protected onUpdateRequest(msg: Message) {
super.onUpdateRequest(msg); super.onUpdateRequest(msg);
this.widget.update(); this.widget.update();
} }
protected override onActivateRequest(msg: Message): void { protected onActivateRequest(msg: Message): void {
super.onActivateRequest(msg); super.onActivateRequest(msg);
this.widget.activate(); this.widget.activate();
} }
protected override handleEnter(event: KeyboardEvent): boolean | void { protected handleEnter(event: KeyboardEvent): boolean | void {
if (event.target instanceof HTMLTextAreaElement) { if (event.target instanceof HTMLTextAreaElement) {
return false; return false;
} }
} }
protected override isValid(value: BoardsConfig.Config): DialogError { protected isValid(value: BoardsConfig.Config): DialogError {
if (!value.selectedBoard) { if (!value.selectedBoard) {
if (value.selectedPort) { if (value.selectedPort) {
return nls.localize( return nls.localize(

View File

@@ -1,4 +1,4 @@
import * as React from '@theia/core/shared/react'; import * as React from 'react';
import { Event } from '@theia/core/lib/common/event'; import { Event } from '@theia/core/lib/common/event';
import { notEmpty } from '@theia/core/lib/common/objects'; import { notEmpty } from '@theia/core/lib/common/objects';
import { MaybePromise } from '@theia/core/lib/common/types'; import { MaybePromise } from '@theia/core/lib/common/types';
@@ -16,7 +16,6 @@ import {
} from './boards-service-provider'; } from './boards-service-provider';
import { naturalCompare } from '../../common/utils'; import { naturalCompare } from '../../common/utils';
import { nls } from '@theia/core/lib/common'; import { nls } from '@theia/core/lib/common';
import { FrontendApplicationState } from '@theia/core/lib/common/frontend-application-state';
export namespace BoardsConfig { export namespace BoardsConfig {
export interface Config { export interface Config {
@@ -30,7 +29,6 @@ export namespace BoardsConfig {
readonly onConfigChange: (config: Config) => void; readonly onConfigChange: (config: Config) => void;
readonly onFocusNodeSet: (element: HTMLElement | undefined) => void; readonly onFocusNodeSet: (element: HTMLElement | undefined) => void;
readonly onFilteredTextDidChangeEvent: Event<string>; readonly onFilteredTextDidChangeEvent: Event<string>;
readonly onAppStateDidChange: Event<FrontendApplicationState>;
} }
export interface State extends Config { export interface State extends Config {
@@ -49,7 +47,7 @@ export abstract class Item<T> extends React.Component<{
missing?: boolean; missing?: boolean;
details?: string; details?: string;
}> { }> {
override render(): React.ReactNode { render(): React.ReactNode {
const { selected, label, missing, details } = this.props; const { selected, label, missing, details } = this.props;
const classNames = ['item']; const classNames = ['item'];
if (selected) { if (selected) {
@@ -101,18 +99,14 @@ export class BoardsConfig extends React.Component<
}; };
} }
override componentDidMount(): void { componentDidMount() {
this.updateBoards();
this.updatePorts(
this.props.boardsServiceProvider.availableBoards
.map(({ port }) => port)
.filter(notEmpty)
);
this.toDispose.pushAll([ this.toDispose.pushAll([
this.props.onAppStateDidChange((state) => {
if (state === 'ready') {
this.updateBoards();
this.updatePorts(
this.props.boardsServiceProvider.availableBoards
.map(({ port }) => port)
.filter(notEmpty)
);
}
}),
this.props.notificationCenter.onAttachedBoardsChanged((event) => this.props.notificationCenter.onAttachedBoardsChanged((event) =>
this.updatePorts( this.updatePorts(
event.newState.ports, event.newState.ports,
@@ -147,11 +141,11 @@ export class BoardsConfig extends React.Component<
]); ]);
} }
override componentWillUnmount(): void { componentWillUnmount(): void {
this.toDispose.dispose(); this.toDispose.dispose();
} }
protected fireConfigChanged(): void { protected fireConfigChanged() {
const { selectedBoard, selectedPort } = this.state; const { selectedBoard, selectedPort } = this.state;
this.props.onConfigChange({ selectedBoard, selectedPort }); this.props.onConfigChange({ selectedBoard, selectedPort });
} }
@@ -256,7 +250,7 @@ export class BoardsConfig extends React.Component<
this.props.onFocusNodeSet(element || undefined); this.props.onFocusNodeSet(element || undefined);
}; };
override render(): React.ReactNode { render(): React.ReactNode {
return ( return (
<div className="body"> <div className="body">
{this.renderContainer('boards', this.renderBoards.bind(this))} {this.renderContainer('boards', this.renderBoards.bind(this))}

View File

@@ -1,5 +1,5 @@
import * as PQueue from 'p-queue'; import * as PQueue from 'p-queue';
import { inject, injectable } from '@theia/core/shared/inversify'; import { inject, injectable } from 'inversify';
import { CommandRegistry } from '@theia/core/lib/common/command'; import { CommandRegistry } from '@theia/core/lib/common/command';
import { MenuModelRegistry } from '@theia/core/lib/common/menu'; import { MenuModelRegistry } from '@theia/core/lib/common/menu';
import { import {
@@ -13,7 +13,6 @@ import { BoardsDataStore } from './boards-data-store';
import { MainMenuManager } from '../../common/main-menu-manager'; import { MainMenuManager } from '../../common/main-menu-manager';
import { ArduinoMenus, unregisterSubmenu } from '../menu/arduino-menus'; import { ArduinoMenus, unregisterSubmenu } from '../menu/arduino-menus';
import { nls } from '@theia/core/lib/common'; import { nls } from '@theia/core/lib/common';
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
@injectable() @injectable()
export class BoardsDataMenuUpdater implements FrontendApplicationContribution { export class BoardsDataMenuUpdater implements FrontendApplicationContribution {
@@ -32,20 +31,11 @@ export class BoardsDataMenuUpdater implements FrontendApplicationContribution {
@inject(BoardsServiceProvider) @inject(BoardsServiceProvider)
protected readonly boardsServiceClient: BoardsServiceProvider; protected readonly boardsServiceClient: BoardsServiceProvider;
@inject(FrontendApplicationStateService)
private readonly appStateService: FrontendApplicationStateService;
protected readonly queue = new PQueue({ autoStart: true, concurrency: 1 }); protected readonly queue = new PQueue({ autoStart: true, concurrency: 1 });
protected readonly toDisposeOnBoardChange = new DisposableCollection(); protected readonly toDisposeOnBoardChange = new DisposableCollection();
async onStart(): Promise<void> { async onStart(): Promise<void> {
this.appStateService this.updateMenuActions(this.boardsServiceClient.boardsConfig.selectedBoard);
.reachedState('ready')
.then(() =>
this.updateMenuActions(
this.boardsServiceClient.boardsConfig.selectedBoard
)
);
this.boardsDataStore.onChanged(() => this.boardsDataStore.onChanged(() =>
this.updateMenuActions( this.updateMenuActions(
this.boardsServiceClient.boardsConfig.selectedBoard this.boardsServiceClient.boardsConfig.selectedBoard

View File

@@ -1,4 +1,4 @@
import { injectable, inject, named } from '@theia/core/shared/inversify'; import { injectable, inject, named } from 'inversify';
import { ILogger } from '@theia/core/lib/common/logger'; import { ILogger } from '@theia/core/lib/common/logger';
import { deepClone } from '@theia/core/lib/common/objects'; import { deepClone } from '@theia/core/lib/common/objects';
import { Event, Emitter } from '@theia/core/lib/common/event'; import { Event, Emitter } from '@theia/core/lib/common/event';

View File

@@ -1,4 +1,4 @@
import { inject, injectable, postConstruct } from '@theia/core/shared/inversify'; import { inject, injectable, postConstruct } from 'inversify';
import { import {
BoardsPackage, BoardsPackage,
BoardsService, BoardsService,
@@ -30,7 +30,7 @@ export class BoardsListWidget extends ListWidget<BoardsPackage> {
} }
@postConstruct() @postConstruct()
protected override init(): void { protected init(): void {
super.init(); super.init();
this.toDispose.pushAll([ this.toDispose.pushAll([
this.notificationCenter.onPlatformInstalled(() => this.notificationCenter.onPlatformInstalled(() =>
@@ -42,7 +42,7 @@ export class BoardsListWidget extends ListWidget<BoardsPackage> {
]); ]);
} }
protected override async install({ protected async install({
item, item,
progressId, progressId,
version, version,
@@ -63,7 +63,7 @@ export class BoardsListWidget extends ListWidget<BoardsPackage> {
); );
} }
protected override async uninstall({ protected async uninstall({
item, item,
progressId, progressId,
}: { }: {

View File

@@ -1,4 +1,4 @@
import { injectable, inject } from '@theia/core/shared/inversify'; import { injectable, inject } from 'inversify';
import { Emitter } from '@theia/core/lib/common/event'; import { Emitter } from '@theia/core/lib/common/event';
import { ILogger } from '@theia/core/lib/common/logger'; import { ILogger } from '@theia/core/lib/common/logger';
import { CommandService } from '@theia/core/lib/common/command'; import { CommandService } from '@theia/core/lib/common/command';

View File

@@ -1,5 +1,5 @@
import * as React from '@theia/core/shared/react'; import * as React from 'react';
import * as ReactDOM from '@theia/core/shared/react-dom'; import * as ReactDOM from 'react-dom';
import { CommandRegistry } from '@theia/core/lib/common/command'; import { CommandRegistry } from '@theia/core/lib/common/command';
import { DisposableCollection } from '@theia/core/lib/common/disposable'; import { DisposableCollection } from '@theia/core/lib/common/disposable';
import { Port } from '../../common/protocol'; import { Port } from '../../common/protocol';
@@ -41,7 +41,7 @@ export class BoardsDropDown extends React.Component<BoardsDropDown.Props> {
} }
} }
override render(): React.ReactNode { render(): React.ReactNode {
return ReactDOM.createPortal(this.renderNode(), this.dropdownElement); return ReactDOM.createPortal(this.renderNode(), this.dropdownElement);
} }
@@ -130,13 +130,13 @@ export class BoardsToolBarItem extends React.Component<
}); });
} }
override componentDidMount(): void { componentDidMount() {
this.props.boardsServiceClient.onAvailableBoardsChanged((availableBoards) => this.props.boardsServiceClient.onAvailableBoardsChanged((availableBoards) =>
this.setState({ availableBoards }) this.setState({ availableBoards })
); );
} }
override componentWillUnmount(): void { componentWillUnmount(): void {
this.toDispose.dispose(); this.toDispose.dispose();
} }
@@ -161,7 +161,7 @@ export class BoardsToolBarItem extends React.Component<
event.nativeEvent.stopImmediatePropagation(); event.nativeEvent.stopImmediatePropagation();
}; };
override render(): React.ReactNode { render(): React.ReactNode {
const { coords, availableBoards } = this.state; const { coords, availableBoards } = this.state;
const boardsConfig = this.props.boardsServiceClient.boardsConfig; const boardsConfig = this.props.boardsServiceClient.boardsConfig;
const title = BoardsConfig.Config.toString(boardsConfig, { const title = BoardsConfig.Config.toString(boardsConfig, {

View File

@@ -1,4 +1,4 @@
import { injectable } from '@theia/core/shared/inversify'; import { injectable } from 'inversify';
import { BoardsListWidget } from './boards-list-widget'; import { BoardsListWidget } from './boards-list-widget';
import { BoardsPackage } from '../../common/protocol/boards-service'; import { BoardsPackage } from '../../common/protocol/boards-service';
import { ListWidgetFrontendContribution } from '../widgets/component-list/list-widget-frontend-contribution'; import { ListWidgetFrontendContribution } from '../widgets/component-list/list-widget-frontend-contribution';
@@ -18,7 +18,7 @@ export class BoardsListWidgetFrontendContribution extends ListWidgetFrontendCont
}); });
} }
override async initializeLayout(): Promise<void> { async initializeLayout(): Promise<void> {
this.openView(); this.openView();
} }
} }

View File

@@ -1,4 +1,4 @@
import * as React from '@theia/core/shared/react'; import * as React from 'react';
export type ProgressBarProps = { export type ProgressBarProps = {
percent?: number; percent?: number;

View File

@@ -1,4 +1,4 @@
import { inject, injectable } from '@theia/core/shared/inversify'; import { inject, injectable } from 'inversify';
import * as moment from 'moment'; import * as moment from 'moment';
import * as remote from '@theia/core/electron-shared/@electron/remote'; import * as remote from '@theia/core/electron-shared/@electron/remote';
import { isOSX, isWindows } from '@theia/core/lib/common/os'; import { isOSX, isWindows } from '@theia/core/lib/common/os';
@@ -22,13 +22,13 @@ export class About extends Contribution {
@inject(ConfigService) @inject(ConfigService)
protected readonly configService: ConfigService; protected readonly configService: ConfigService;
override registerCommands(registry: CommandRegistry): void { registerCommands(registry: CommandRegistry): void {
registry.registerCommand(About.Commands.ABOUT_APP, { registry.registerCommand(About.Commands.ABOUT_APP, {
execute: () => this.showAbout(), execute: () => this.showAbout(),
}); });
} }
override registerMenus(registry: MenuModelRegistry): void { registerMenus(registry: MenuModelRegistry): void {
registry.registerMenuAction(ArduinoMenus.HELP__ABOUT_GROUP, { registry.registerMenuAction(ArduinoMenus.HELP__ABOUT_GROUP, {
commandId: About.Commands.ABOUT_APP.id, commandId: About.Commands.ABOUT_APP.id,
label: nls.localize( label: nls.localize(

View File

@@ -1,4 +1,4 @@
import { inject, injectable } from '@theia/core/shared/inversify'; import { inject, injectable } from 'inversify';
import * as remote from '@theia/core/electron-shared/@electron/remote'; import * as remote from '@theia/core/electron-shared/@electron/remote';
import { ArduinoMenus } from '../menu/arduino-menus'; import { ArduinoMenus } from '../menu/arduino-menus';
import { import {
@@ -10,20 +10,19 @@ import {
} from './contribution'; } from './contribution';
import { FileDialogService } from '@theia/filesystem/lib/browser'; import { FileDialogService } from '@theia/filesystem/lib/browser';
import { nls } from '@theia/core/lib/common'; import { nls } from '@theia/core/lib/common';
import { CurrentSketch } from '../../common/protocol/sketches-service-client-impl';
@injectable() @injectable()
export class AddFile extends SketchContribution { export class AddFile extends SketchContribution {
@inject(FileDialogService) @inject(FileDialogService)
protected readonly fileDialogService: FileDialogService; protected readonly fileDialogService: FileDialogService;
override registerCommands(registry: CommandRegistry): void { registerCommands(registry: CommandRegistry): void {
registry.registerCommand(AddFile.Commands.ADD_FILE, { registry.registerCommand(AddFile.Commands.ADD_FILE, {
execute: () => this.addFile(), execute: () => this.addFile(),
}); });
} }
override registerMenus(registry: MenuModelRegistry): void { registerMenus(registry: MenuModelRegistry): void {
registry.registerMenuAction(ArduinoMenus.SKETCH__UTILS_GROUP, { registry.registerMenuAction(ArduinoMenus.SKETCH__UTILS_GROUP, {
commandId: AddFile.Commands.ADD_FILE.id, commandId: AddFile.Commands.ADD_FILE.id,
label: nls.localize('arduino/contributions/addFile', 'Add File') + '...', label: nls.localize('arduino/contributions/addFile', 'Add File') + '...',
@@ -33,7 +32,7 @@ export class AddFile extends SketchContribution {
protected async addFile(): Promise<void> { protected async addFile(): Promise<void> {
const sketch = await this.sketchServiceClient.currentSketch(); const sketch = await this.sketchServiceClient.currentSketch();
if (!CurrentSketch.isValid(sketch)) { if (!sketch) {
return; return;
} }
const toAddUri = await this.fileDialogService.showOpenDialog({ const toAddUri = await this.fileDialogService.showOpenDialog({

View File

@@ -1,4 +1,4 @@
import { inject, injectable } from '@theia/core/shared/inversify'; import { inject, injectable } from 'inversify';
import * as remote from '@theia/core/electron-shared/@electron/remote'; import * as remote from '@theia/core/electron-shared/@electron/remote';
import URI from '@theia/core/lib/common/uri'; import URI from '@theia/core/lib/common/uri';
import { ConfirmDialog } from '@theia/core/lib/browser/dialogs'; import { ConfirmDialog } from '@theia/core/lib/browser/dialogs';
@@ -28,13 +28,13 @@ export class AddZipLibrary extends SketchContribution {
@inject(LibraryService) @inject(LibraryService)
protected readonly libraryService: LibraryService; protected readonly libraryService: LibraryService;
override registerCommands(registry: CommandRegistry): void { registerCommands(registry: CommandRegistry): void {
registry.registerCommand(AddZipLibrary.Commands.ADD_ZIP_LIBRARY, { registry.registerCommand(AddZipLibrary.Commands.ADD_ZIP_LIBRARY, {
execute: () => this.addZipLibrary(), execute: () => this.addZipLibrary(),
}); });
} }
override registerMenus(registry: MenuModelRegistry): void { registerMenus(registry: MenuModelRegistry): void {
const includeLibMenuPath = [ const includeLibMenuPath = [
...ArduinoMenus.SKETCH__UTILS_GROUP, ...ArduinoMenus.SKETCH__UTILS_GROUP,
'0_include', '0_include',

View File

@@ -1,4 +1,4 @@
import { injectable } from '@theia/core/shared/inversify'; import { injectable } from 'inversify';
import * as remote from '@theia/core/electron-shared/@electron/remote'; import * as remote from '@theia/core/electron-shared/@electron/remote';
import * as dateFormat from 'dateformat'; import * as dateFormat from 'dateformat';
import URI from '@theia/core/lib/common/uri'; import URI from '@theia/core/lib/common/uri';
@@ -10,17 +10,16 @@ import {
MenuModelRegistry, MenuModelRegistry,
} from './contribution'; } from './contribution';
import { nls } from '@theia/core/lib/common'; import { nls } from '@theia/core/lib/common';
import { CurrentSketch } from '../../common/protocol/sketches-service-client-impl';
@injectable() @injectable()
export class ArchiveSketch extends SketchContribution { export class ArchiveSketch extends SketchContribution {
override registerCommands(registry: CommandRegistry): void { registerCommands(registry: CommandRegistry): void {
registry.registerCommand(ArchiveSketch.Commands.ARCHIVE_SKETCH, { registry.registerCommand(ArchiveSketch.Commands.ARCHIVE_SKETCH, {
execute: () => this.archiveSketch(), execute: () => this.archiveSketch(),
}); });
} }
override registerMenus(registry: MenuModelRegistry): void { registerMenus(registry: MenuModelRegistry): void {
registry.registerMenuAction(ArduinoMenus.TOOLS__MAIN_GROUP, { registry.registerMenuAction(ArduinoMenus.TOOLS__MAIN_GROUP, {
commandId: ArchiveSketch.Commands.ARCHIVE_SKETCH.id, commandId: ArchiveSketch.Commands.ARCHIVE_SKETCH.id,
label: nls.localize('arduino/sketch/archiveSketch', 'Archive Sketch'), label: nls.localize('arduino/sketch/archiveSketch', 'Archive Sketch'),
@@ -33,7 +32,7 @@ export class ArchiveSketch extends SketchContribution {
this.sketchServiceClient.currentSketch(), this.sketchServiceClient.currentSketch(),
this.configService.getConfiguration(), this.configService.getConfiguration(),
]); ]);
if (!CurrentSketch.isValid(sketch)) { if (!sketch) {
return; return;
} }
const archiveBasename = `${sketch.name}-${dateFormat( const archiveBasename = `${sketch.name}-${dateFormat(

View File

@@ -1,4 +1,4 @@
import { inject, injectable } from '@theia/core/shared/inversify'; import { inject, injectable } from 'inversify';
import * as remote from '@theia/core/electron-shared/@electron/remote'; import * as remote from '@theia/core/electron-shared/@electron/remote';
import { MenuModelRegistry } from '@theia/core/lib/common/menu'; import { MenuModelRegistry } from '@theia/core/lib/common/menu';
import { import {
@@ -47,7 +47,7 @@ export class BoardSelection extends SketchContribution {
protected readonly toDisposeBeforeMenuRebuild = new DisposableCollection(); protected readonly toDisposeBeforeMenuRebuild = new DisposableCollection();
override registerCommands(registry: CommandRegistry): void { registerCommands(registry: CommandRegistry): void {
registry.registerCommand(BoardSelection.Commands.GET_BOARD_INFO, { registry.registerCommand(BoardSelection.Commands.GET_BOARD_INFO, {
execute: async () => { execute: async () => {
const { selectedBoard, selectedPort } = const { selectedBoard, selectedPort } =
@@ -100,20 +100,19 @@ PID: ${PID}`;
}); });
} }
override onStart(): void { onStart(): void {
this.notificationCenter.onPlatformInstalled(() => this.updateMenus());
this.notificationCenter.onPlatformUninstalled(() => this.updateMenus());
this.boardsServiceProvider.onBoardsConfigChanged(() => this.updateMenus());
this.boardsServiceProvider.onAvailableBoardsChanged(() =>
this.updateMenus()
);
this.boardsServiceProvider.onAvailablePortsChanged(() =>
this.updateMenus()
);
}
override async onReady(): Promise<void> {
this.updateMenus(); this.updateMenus();
this.notificationCenter.onPlatformInstalled(this.updateMenus.bind(this));
this.notificationCenter.onPlatformUninstalled(this.updateMenus.bind(this));
this.boardsServiceProvider.onBoardsConfigChanged(
this.updateMenus.bind(this)
);
this.boardsServiceProvider.onAvailableBoardsChanged(
this.updateMenus.bind(this)
);
this.boardsServiceProvider.onAvailablePortsChanged(
this.updateMenus.bind(this)
);
} }
protected async updateMenus(): Promise<void> { protected async updateMenus(): Promise<void> {

View File

@@ -1,9 +1,11 @@
import { inject, injectable } from '@theia/core/shared/inversify'; import { inject, injectable } from 'inversify';
import { OutputChannelManager } from '@theia/output/lib/browser/output-channel';
import { CoreService } from '../../common/protocol';
import { ArduinoMenus } from '../menu/arduino-menus'; import { ArduinoMenus } from '../menu/arduino-menus';
import { BoardsDataStore } from '../boards/boards-data-store'; import { BoardsDataStore } from '../boards/boards-data-store';
import { BoardsServiceProvider } from '../boards/boards-service-provider'; import { BoardsServiceProvider } from '../boards/boards-service-provider';
import { import {
CoreServiceContribution, SketchContribution,
Command, Command,
CommandRegistry, CommandRegistry,
MenuModelRegistry, MenuModelRegistry,
@@ -11,20 +13,27 @@ import {
import { nls } from '@theia/core/lib/common'; import { nls } from '@theia/core/lib/common';
@injectable() @injectable()
export class BurnBootloader extends CoreServiceContribution { export class BurnBootloader extends SketchContribution {
@inject(CoreService)
protected readonly coreService: CoreService;
@inject(BoardsDataStore) @inject(BoardsDataStore)
protected readonly boardsDataStore: BoardsDataStore; protected readonly boardsDataStore: BoardsDataStore;
@inject(BoardsServiceProvider) @inject(BoardsServiceProvider)
protected readonly boardsServiceClientImpl: BoardsServiceProvider; protected readonly boardsServiceClientImpl: BoardsServiceProvider;
override registerCommands(registry: CommandRegistry): void { @inject(OutputChannelManager)
protected readonly outputChannelManager: OutputChannelManager;
registerCommands(registry: CommandRegistry): void {
registry.registerCommand(BurnBootloader.Commands.BURN_BOOTLOADER, { registry.registerCommand(BurnBootloader.Commands.BURN_BOOTLOADER, {
execute: () => this.burnBootloader(), execute: () => this.burnBootloader(),
}); });
} }
override registerMenus(registry: MenuModelRegistry): void { registerMenus(registry: MenuModelRegistry): void {
registry.registerMenuAction(ArduinoMenus.TOOLS__BOARD_SETTINGS_GROUP, { registry.registerMenuAction(ArduinoMenus.TOOLS__BOARD_SETTINGS_GROUP, {
commandId: BurnBootloader.Commands.BURN_BOOTLOADER.id, commandId: BurnBootloader.Commands.BURN_BOOTLOADER.id,
label: nls.localize( label: nls.localize(
@@ -53,7 +62,7 @@ export class BurnBootloader extends CoreServiceContribution {
...boardsConfig.selectedBoard, ...boardsConfig.selectedBoard,
name: boardsConfig.selectedBoard?.name || '', name: boardsConfig.selectedBoard?.name || '',
fqbn, fqbn,
}; }
this.outputChannelManager.getChannel('Arduino').clear(); this.outputChannelManager.getChannel('Arduino').clear();
await this.coreService.burnBootloader({ await this.coreService.burnBootloader({
board, board,
@@ -72,7 +81,13 @@ export class BurnBootloader extends CoreServiceContribution {
} }
); );
} catch (e) { } catch (e) {
this.handleError(e); let errorMessage = "";
if (typeof e === "string") {
errorMessage = e;
} else {
errorMessage = e.toString();
}
this.messageService.error(errorMessage);
} }
} }
} }

View File

@@ -1,10 +1,12 @@
import { inject, injectable } from '@theia/core/shared/inversify'; import { inject, injectable } from 'inversify';
import { toArray } from '@phosphor/algorithm';
import * as remote from '@theia/core/electron-shared/@electron/remote'; import * as remote from '@theia/core/electron-shared/@electron/remote';
import { MonacoEditor } from '@theia/monaco/lib/browser/monaco-editor'; import { MonacoEditor } from '@theia/monaco/lib/browser/monaco-editor';
import { EditorManager } from '@theia/editor/lib/browser/editor-manager'; import { EditorManager } from '@theia/editor/lib/browser/editor-manager';
import { ApplicationShell } from '@theia/core/lib/browser/shell/application-shell'; import { ApplicationShell } from '@theia/core/lib/browser/shell/application-shell';
import { FrontendApplication } from '@theia/core/lib/browser/frontend-application'; import { FrontendApplication } from '@theia/core/lib/browser/frontend-application';
import { ArduinoMenus } from '../menu/arduino-menus'; import { ArduinoMenus } from '../menu/arduino-menus';
import { SaveAsSketch } from './save-as-sketch';
import { import {
SketchContribution, SketchContribution,
Command, Command,
@@ -21,21 +23,90 @@ import { nls } from '@theia/core/lib/common';
@injectable() @injectable()
export class Close extends SketchContribution { export class Close extends SketchContribution {
@inject(EditorManager) @inject(EditorManager)
protected override readonly editorManager: EditorManager; protected readonly editorManager: EditorManager;
protected shell: ApplicationShell; protected shell: ApplicationShell;
override onStart(app: FrontendApplication): void { onStart(app: FrontendApplication): void {
this.shell = app.shell; this.shell = app.shell;
} }
override registerCommands(registry: CommandRegistry): void { registerCommands(registry: CommandRegistry): void {
registry.registerCommand(Close.Commands.CLOSE, { registry.registerCommand(Close.Commands.CLOSE, {
execute: () => remote.getCurrentWindow().close() execute: async () => {
// Close current editor if closeable.
const { currentEditor } = this.editorManager;
if (currentEditor && currentEditor.title.closable) {
currentEditor.close();
return;
}
// Close current widget from the main area if possible.
const { currentWidget } = this.shell;
if (currentWidget) {
const currentWidgetInMain = toArray(
this.shell.mainPanel.widgets()
).find((widget) => widget === currentWidget);
if (currentWidgetInMain && currentWidgetInMain.title.closable) {
return currentWidgetInMain.close();
}
}
// Close the sketch (window).
const sketch = await this.sketchServiceClient.currentSketch();
if (!sketch) {
return;
}
const isTemp = await this.sketchService.isTemp(sketch);
const uri = await this.sketchServiceClient.currentSketchFile();
if (!uri) {
return;
}
if (isTemp && (await this.wasTouched(uri))) {
const { response } = await remote.dialog.showMessageBox({
type: 'question',
buttons: [
nls.localize(
'vscode/abstractTaskService/saveBeforeRun.dontSave',
"Don't Save"
),
nls.localize('vscode/issueMainService/cancel', 'Cancel'),
nls.localize(
'vscode/abstractTaskService/saveBeforeRun.save',
'Save'
),
],
message: nls.localize(
'arduino/common/saveChangesToSketch',
'Do you want to save changes to this sketch before closing?'
),
detail: nls.localize(
'arduino/common/loseChanges',
"If you don't save, your changes will be lost."
),
});
if (response === 1) {
// Cancel
return;
}
if (response === 2) {
// Save
const saved = await this.commandService.executeCommand(
SaveAsSketch.Commands.SAVE_AS_SKETCH.id,
{ openAfterMove: false, execOnlyIfTemp: true }
);
if (!saved) {
// If it was not saved, do bail the close.
return;
}
}
}
window.close();
},
}); });
} }
override registerMenus(registry: MenuModelRegistry): void { registerMenus(registry: MenuModelRegistry): void {
registry.registerMenuAction(ArduinoMenus.FILE__SKETCH_GROUP, { registry.registerMenuAction(ArduinoMenus.FILE__SKETCH_GROUP, {
commandId: Close.Commands.CLOSE.id, commandId: Close.Commands.CLOSE.id,
label: nls.localize('vscode/editor.contribution/close', 'Close'), label: nls.localize('vscode/editor.contribution/close', 'Close'),
@@ -43,7 +114,7 @@ export class Close extends SketchContribution {
}); });
} }
override registerKeybindings(registry: KeybindingRegistry): void { registerKeybindings(registry: KeybindingRegistry): void {
registry.registerKeybinding({ registry.registerKeybinding({
command: Close.Commands.CLOSE.id, command: Close.Commands.CLOSE.id,
keybinding: 'CtrlCmd+W', keybinding: 'CtrlCmd+W',

View File

@@ -1,656 +0,0 @@
import {
Command,
CommandRegistry,
Disposable,
DisposableCollection,
Emitter,
MaybePromise,
nls,
notEmpty,
} from '@theia/core';
import { ApplicationShell, FrontendApplication } from '@theia/core/lib/browser';
import URI from '@theia/core/lib/common/uri';
import { inject, injectable } from '@theia/core/shared/inversify';
import {
Location,
Range,
} from '@theia/core/shared/vscode-languageserver-protocol';
import {
EditorWidget,
TextDocumentChangeEvent,
} from '@theia/editor/lib/browser';
import {
EditorDecoration,
TrackedRangeStickiness,
} from '@theia/editor/lib/browser/decorations/editor-decoration';
import { EditorManager } from '@theia/editor/lib/browser/editor-manager';
import * as monaco from '@theia/monaco-editor-core';
import { MonacoEditor } from '@theia/monaco/lib/browser/monaco-editor';
import { MonacoToProtocolConverter } from '@theia/monaco/lib/browser/monaco-to-protocol-converter';
import { ProtocolToMonacoConverter } from '@theia/monaco/lib/browser/protocol-to-monaco-converter';
import { CoreError } from '../../common/protocol/core-service';
import {
ArduinoPreferences,
ErrorRevealStrategy,
} from '../arduino-preferences';
import { InoSelector } from '../ino-selectors';
import { fullRange } from '../utils/monaco';
import { Contribution } from './contribution';
import { CoreErrorHandler } from './core-error-handler';
interface ErrorDecoration {
/**
* This is the unique ID of the decoration given by `monaco`.
*/
readonly id: string;
/**
* The resource this decoration belongs to.
*/
readonly uri: string;
}
namespace ErrorDecoration {
export function rangeOf(
{ id, uri }: ErrorDecoration,
editorProvider: (uri: string) => Promise<MonacoEditor | undefined>
): Promise<monaco.Range | undefined>;
export function rangeOf(
{ id, uri }: ErrorDecoration,
editorProvider: MonacoEditor
): monaco.Range | undefined;
export function rangeOf(
{ id, uri }: ErrorDecoration,
editorProvider:
| ((uri: string) => Promise<MonacoEditor | undefined>)
| MonacoEditor
): MaybePromise<monaco.Range | undefined> {
if (editorProvider instanceof MonacoEditor) {
const control = editorProvider.getControl();
const model = control.getModel();
if (model) {
return control
.getDecorationsInRange(fullRange(model))
?.find(({ id: candidateId }) => id === candidateId)?.range;
}
return undefined;
}
return editorProvider(uri).then((editor) => {
if (editor) {
return rangeOf({ id, uri }, editor);
}
return undefined;
});
}
// export async function rangeOf(
// { id, uri }: ErrorDecoration,
// editorProvider:
// | ((uri: string) => Promise<MonacoEditor | undefined>)
// | MonacoEditor
// ): Promise<monaco.Range | undefined> {
// const editor =
// editorProvider instanceof MonacoEditor
// ? editorProvider
// : await editorProvider(uri);
// if (editor) {
// const control = editor.getControl();
// const model = control.getModel();
// if (model) {
// return control
// .getDecorationsInRange(fullRange(model))
// ?.find(({ id: candidateId }) => id === candidateId)?.range;
// }
// }
// return undefined;
// }
export function sameAs(
left: ErrorDecoration,
right: ErrorDecoration
): boolean {
return left.id === right.id && left.uri === right.uri;
}
}
@injectable()
export class CompilerErrors
extends Contribution
implements monaco.languages.CodeLensProvider
{
@inject(EditorManager)
private readonly editorManager: EditorManager;
@inject(ProtocolToMonacoConverter)
private readonly p2m: ProtocolToMonacoConverter;
@inject(MonacoToProtocolConverter)
private readonly mp2: MonacoToProtocolConverter;
@inject(CoreErrorHandler)
private readonly coreErrorHandler: CoreErrorHandler;
@inject(ArduinoPreferences)
private readonly preferences: ArduinoPreferences;
private readonly errors: ErrorDecoration[] = [];
private readonly onDidChangeEmitter = new monaco.Emitter<this>();
private readonly currentErrorDidChangEmitter = new Emitter<ErrorDecoration>();
private readonly onCurrentErrorDidChange =
this.currentErrorDidChangEmitter.event;
private readonly toDisposeOnCompilerErrorDidChange =
new DisposableCollection();
private shell: ApplicationShell | undefined;
private revealStrategy = ErrorRevealStrategy.Default;
private currentError: ErrorDecoration | undefined;
private get currentErrorIndex(): number {
const current = this.currentError;
if (!current) {
return -1;
}
return this.errors.findIndex((error) =>
ErrorDecoration.sameAs(error, current)
);
}
override onStart(app: FrontendApplication): void {
this.shell = app.shell;
monaco.languages.registerCodeLensProvider(InoSelector, this);
this.coreErrorHandler.onCompilerErrorsDidChange((errors) =>
this.filter(errors).then(this.handleCompilerErrorsDidChange.bind(this))
);
this.onCurrentErrorDidChange(async (error) => {
const range = await ErrorDecoration.rangeOf(error, (uri) =>
this.monacoEditor(uri)
);
if (!range) {
console.warn(
'compiler-errors',
`Could not find range of decoration: ${error.id}`
);
return;
}
const editor = await this.revealLocationInEditor({
uri: error.uri,
range: this.mp2.asRange(range),
});
if (!editor) {
console.warn(
'compiler-errors',
`Failed to mark error ${error.id} as the current one.`
);
}
});
this.preferences.ready.then(() => {
this.preferences.onPreferenceChanged(({ preferenceName, newValue }) => {
if (preferenceName === 'arduino.compile.revealRange') {
this.revealStrategy = ErrorRevealStrategy.is(newValue)
? newValue
: ErrorRevealStrategy.Default;
}
});
});
}
override registerCommands(registry: CommandRegistry): void {
registry.registerCommand(CompilerErrors.Commands.NEXT_ERROR, {
execute: () => {
const index = this.currentErrorIndex;
if (index < 0) {
console.warn(
'compiler-errors',
`Could not advance to next error. Unknown current error.`
);
return;
}
const nextError =
this.errors[index === this.errors.length - 1 ? 0 : index + 1];
this.markAsCurrentError(nextError);
},
isEnabled: () => !!this.currentError && this.errors.length > 1,
});
registry.registerCommand(CompilerErrors.Commands.PREVIOUS_ERROR, {
execute: () => {
const index = this.currentErrorIndex;
if (index < 0) {
console.warn(
'compiler-errors',
`Could not advance to previous error. Unknown current error.`
);
return;
}
const previousError =
this.errors[index === 0 ? this.errors.length - 1 : index - 1];
this.markAsCurrentError(previousError);
},
isEnabled: () => !!this.currentError && this.errors.length > 1,
});
}
get onDidChange(): monaco.IEvent<this> {
return this.onDidChangeEmitter.event;
}
async provideCodeLenses(
model: monaco.editor.ITextModel,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
_token: monaco.CancellationToken
): Promise<monaco.languages.CodeLensList> {
const lenses: monaco.languages.CodeLens[] = [];
if (
this.currentError &&
this.currentError.uri === model.uri.toString() &&
this.errors.length > 1
) {
const range = await ErrorDecoration.rangeOf(this.currentError, (uri) =>
this.monacoEditor(uri)
);
if (range) {
lenses.push(
{
range,
command: {
id: CompilerErrors.Commands.PREVIOUS_ERROR.id,
title: nls.localize(
'arduino/editor/previousError',
'Previous Error'
),
arguments: [this.currentError],
},
},
{
range,
command: {
id: CompilerErrors.Commands.NEXT_ERROR.id,
title: nls.localize('arduino/editor/nextError', 'Next Error'),
arguments: [this.currentError],
},
}
);
}
}
return {
lenses,
dispose: () => {
/* NOOP */
},
};
}
private async handleCompilerErrorsDidChange(
errors: CoreError.Compiler[]
): Promise<void> {
this.toDisposeOnCompilerErrorDidChange.dispose();
const compilerErrorsPerResource = this.groupByResource(
await this.filter(errors)
);
const decorations = await this.decorateEditors(compilerErrorsPerResource);
this.errors.push(...decorations.errors);
this.toDisposeOnCompilerErrorDidChange.pushAll([
Disposable.create(() => (this.errors.length = 0)),
Disposable.create(() => this.onDidChangeEmitter.fire(this)),
...(await Promise.all([
decorations.dispose,
this.trackEditors(
compilerErrorsPerResource,
(editor) =>
editor.editor.onSelectionChanged((selection) =>
this.handleSelectionChange(editor, selection)
),
(editor) =>
editor.onDidDispose(() =>
this.handleEditorDidDispose(editor.editor.uri.toString())
),
(editor) =>
editor.editor.onDocumentContentChanged((event) =>
this.handleDocumentContentChange(editor, event)
)
),
])),
]);
const currentError = this.errors[0];
if (currentError) {
await this.markAsCurrentError(currentError);
}
}
private async filter(
errors: CoreError.Compiler[]
): Promise<CoreError.Compiler[]> {
if (!errors.length) {
return [];
}
await this.preferences.ready;
if (this.preferences['arduino.compile.experimental']) {
return errors;
}
// Always shows maximum one error; hence the code lens navigation is unavailable.
return [errors[0]];
}
private async decorateEditors(
errors: Map<string, CoreError.Compiler[]>
): Promise<{ dispose: Disposable; errors: ErrorDecoration[] }> {
const composite = await Promise.all(
[...errors.entries()].map(([uri, errors]) =>
this.decorateEditor(uri, errors)
)
);
return {
dispose: new DisposableCollection(
...composite.map(({ dispose }) => dispose)
),
errors: composite.reduce(
(acc, { errors }) => acc.concat(errors),
[] as ErrorDecoration[]
),
};
}
private async decorateEditor(
uri: string,
errors: CoreError.Compiler[]
): Promise<{ dispose: Disposable; errors: ErrorDecoration[] }> {
const editor = await this.editorManager.getByUri(new URI(uri));
if (!editor) {
return { dispose: Disposable.NULL, errors: [] };
}
const oldDecorations = editor.editor.deltaDecorations({
oldDecorations: [],
newDecorations: errors.map((error) =>
this.compilerErrorDecoration(error.location.range)
),
});
return {
dispose: Disposable.create(() => {
if (editor) {
editor.editor.deltaDecorations({
oldDecorations,
newDecorations: [],
});
}
}),
errors: oldDecorations.map((id) => ({ id, uri })),
};
}
private compilerErrorDecoration(range: Range): EditorDecoration {
return {
range,
options: {
isWholeLine: true,
className: 'compiler-error',
stickiness: TrackedRangeStickiness.AlwaysGrowsWhenTypingAtEdges,
},
};
}
/**
* Tracks the selection in all editors that have an error. If the editor selection overlaps one of the compiler error's range, mark as current error.
*/
private handleSelectionChange(editor: EditorWidget, selection: Range): void {
const monacoEditor = this.monacoEditor(editor);
if (!monacoEditor) {
return;
}
const uri = monacoEditor.uri.toString();
const monacoSelection = this.p2m.asRange(selection);
console.log(
'compiler-errors',
`Handling selection change in editor ${uri}. New (monaco) selection: ${monacoSelection.toJSON()}`
);
const calculatePriority = (
candidateErrorRange: monaco.Range,
currentSelection: monaco.Range
) => {
console.trace(
'compiler-errors',
`Candidate error range: ${candidateErrorRange.toJSON()}`
);
console.trace(
'compiler-errors',
`Current selection range: ${currentSelection.toJSON()}`
);
if (candidateErrorRange.intersectRanges(currentSelection)) {
console.trace('Intersects.');
return { score: 2 };
}
if (
candidateErrorRange.startLineNumber <=
currentSelection.startLineNumber &&
candidateErrorRange.endLineNumber >= currentSelection.endLineNumber
) {
console.trace('Same line.');
return { score: 1 };
}
console.trace('No match');
return undefined;
};
const error = this.errors
.filter((error) => error.uri === uri)
.map((error) => ({
error,
range: ErrorDecoration.rangeOf(error, monacoEditor),
}))
.map(({ error, range }) => {
if (range) {
const priority = calculatePriority(range, monacoSelection);
if (priority) {
return { ...priority, error };
}
}
return undefined;
})
.filter(notEmpty)
.sort((left, right) => right.score - left.score) // highest first
.map(({ error }) => error)
.shift();
if (error) {
this.markAsCurrentError(error);
} else {
console.info(
'compiler-errors',
`New (monaco) selection ${monacoSelection.toJSON()} does not intersect any error locations. Skipping.`
);
}
}
/**
* This code does not deal with resource deletion, but tracks editor dispose events. It does not matter what was the cause of the editor disposal.
* If editor closes, delete the decorators.
*/
private handleEditorDidDispose(uri: string): void {
let i = this.errors.length;
// `splice` re-indexes the array. It's better to "iterate and modify" from the last element.
while (i--) {
const error = this.errors[i];
if (error.uri === uri) {
this.errors.splice(i, 1);
}
}
this.onDidChangeEmitter.fire(this);
}
/**
* If a document change "destroys" the range of the decoration, the decoration must be removed.
*/
private handleDocumentContentChange(
editor: EditorWidget,
event: TextDocumentChangeEvent
): void {
const monacoEditor = this.monacoEditor(editor);
if (!monacoEditor) {
return;
}
// A decoration location can be "destroyed", hence should be deleted when:
// - deleting range (start != end AND text is empty)
// - inserting text into range (start != end AND text is not empty)
// Filter unrelated delta changes to spare the CPU.
const relevantChanges = event.contentChanges.filter(
({ range: { start, end } }) =>
start.line !== end.line || start.character !== end.character
);
if (!relevantChanges.length) {
return;
}
const resolvedMarkers = this.errors
.filter((error) => error.uri === event.document.uri)
.map((error, index) => {
const range = ErrorDecoration.rangeOf(error, monacoEditor);
if (range) {
return { error, range, index };
}
return undefined;
})
.filter(notEmpty);
const decorationIdsToRemove = relevantChanges
.map(({ range }) => this.p2m.asRange(range))
.map((changeRange) =>
resolvedMarkers.filter(({ range: decorationRange }) =>
changeRange.containsRange(decorationRange)
)
)
.reduce((acc, curr) => acc.concat(curr), [])
.map(({ error, index }) => {
this.errors.splice(index, 1);
return error.id;
});
if (!decorationIdsToRemove.length) {
return;
}
monacoEditor.getControl().deltaDecorations(decorationIdsToRemove, []);
this.onDidChangeEmitter.fire(this);
}
private async trackEditors(
errors: Map<string, CoreError.Compiler[]>,
...track: ((editor: EditorWidget) => Disposable)[]
): Promise<Disposable> {
return new DisposableCollection(
...(await Promise.all(
Array.from(errors.keys()).map(async (uri) => {
const editor = await this.editorManager.getByUri(new URI(uri));
if (!editor) {
return Disposable.NULL;
}
return new DisposableCollection(...track.map((t) => t(editor)));
})
))
);
}
private async markAsCurrentError(error: ErrorDecoration): Promise<void> {
const index = this.errors.findIndex((candidate) =>
ErrorDecoration.sameAs(candidate, error)
);
if (index < 0) {
console.warn(
'compiler-errors',
`Failed to mark error ${
error.id
} as the current one. Error is unknown. Known errors are: ${this.errors.map(
({ id }) => id
)}`
);
return;
}
const newError = this.errors[index];
if (
!this.currentError ||
!ErrorDecoration.sameAs(this.currentError, newError)
) {
this.currentError = this.errors[index];
console.log(
'compiler-errors',
`Current error changed to ${this.currentError.id}`
);
this.currentErrorDidChangEmitter.fire(this.currentError);
this.onDidChangeEmitter.fire(this);
}
}
// The double editor activation logic is required: https://github.com/eclipse-theia/theia/issues/11284
private async revealLocationInEditor(
location: Location
): Promise<EditorWidget | undefined> {
const { uri, range } = location;
const editor = await this.editorManager.getByUri(new URI(uri), {
mode: 'activate',
});
if (editor && this.shell) {
// to avoid flickering, reveal the range here and not with `getByUri`, because it uses `at: 'center'` for the reveal option.
// TODO: check the community reaction whether it is better to set the focus at the error marker. it might cause flickering even if errors are close to each other
editor.editor.revealRange(range, { at: this.revealStrategy });
const activeWidget = await this.shell.activateWidget(editor.id);
if (!activeWidget) {
console.warn(
'compiler-errors',
`editor widget activation has failed. editor widget ${editor.id} expected to be the active one.`
);
return editor;
}
if (editor !== activeWidget) {
console.warn(
'compiler-errors',
`active widget was not the same as previously activated editor. editor widget ID ${editor.id}, active widget ID: ${activeWidget.id}`
);
}
return editor;
}
console.warn(
'compiler-errors',
`could not found editor widget for URI: ${uri}`
);
return undefined;
}
private groupByResource(
errors: CoreError.Compiler[]
): Map<string, CoreError.Compiler[]> {
return errors.reduce((acc, curr) => {
const {
location: { uri },
} = curr;
let errors = acc.get(uri);
if (!errors) {
errors = [];
acc.set(uri, errors);
}
errors.push(curr);
return acc;
}, new Map<string, CoreError.Compiler[]>());
}
private monacoEditor(widget: EditorWidget): MonacoEditor | undefined;
private monacoEditor(uri: string): Promise<MonacoEditor | undefined>;
private monacoEditor(
uriOrWidget: string | EditorWidget
): MaybePromise<MonacoEditor | undefined> {
if (uriOrWidget instanceof EditorWidget) {
const editor = uriOrWidget.editor;
if (editor instanceof MonacoEditor) {
return editor;
}
return undefined;
} else {
return this.editorManager
.getByUri(new URI(uriOrWidget))
.then((editor) => {
if (editor) {
return this.monacoEditor(editor);
}
return undefined;
});
}
}
}
export namespace CompilerErrors {
export namespace Commands {
export const NEXT_ERROR: Command = {
id: 'arduino-editor-next-error',
};
export const PREVIOUS_ERROR: Command = {
id: 'arduino-editor-previous-error',
};
}
}

View File

@@ -1,9 +1,4 @@
import { import { inject, injectable, interfaces } from 'inversify';
inject,
injectable,
interfaces,
postConstruct,
} from '@theia/core/shared/inversify';
import URI from '@theia/core/lib/common/uri'; import URI from '@theia/core/lib/common/uri';
import { ILogger } from '@theia/core/lib/common/logger'; import { ILogger } from '@theia/core/lib/common/logger';
import { Saveable } from '@theia/core/lib/browser/saveable'; import { Saveable } from '@theia/core/lib/browser/saveable';
@@ -14,7 +9,7 @@ import { EditorManager } from '@theia/editor/lib/browser/editor-manager';
import { MessageService } from '@theia/core/lib/common/message-service'; import { MessageService } from '@theia/core/lib/common/message-service';
import { WorkspaceService } from '@theia/workspace/lib/browser/workspace-service'; import { WorkspaceService } from '@theia/workspace/lib/browser/workspace-service';
import { open, OpenerService } from '@theia/core/lib/browser/opener-service'; import { open, OpenerService } from '@theia/core/lib/browser/opener-service';
import { OutputChannelManager } from '@theia/output/lib/browser/output-channel';
import { import {
MenuModelRegistry, MenuModelRegistry,
MenuContribution, MenuContribution,
@@ -39,24 +34,14 @@ import {
} from '@theia/core/lib/common/command'; } from '@theia/core/lib/common/command';
import { EditorMode } from '../editor-mode'; import { EditorMode } from '../editor-mode';
import { SettingsService } from '../dialogs/settings/settings'; import { SettingsService } from '../dialogs/settings/settings';
import { import { SketchesServiceClientImpl } from '../../common/protocol/sketches-service-client-impl';
CurrentSketch,
SketchesServiceClientImpl,
} from '../../common/protocol/sketches-service-client-impl';
import { import {
SketchesService, SketchesService,
ConfigService, ConfigService,
FileSystemExt, FileSystemExt,
Sketch, Sketch,
CoreService,
CoreError,
} from '../../common/protocol'; } from '../../common/protocol';
import { ArduinoPreferences } from '../arduino-preferences'; import { ArduinoPreferences } from '../arduino-preferences';
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
import { CoreErrorHandler } from './core-error-handler';
import { nls } from '@theia/core';
import { OutputChannelManager } from '../theia/output/output-channel';
import { ClipboardService } from '@theia/core/lib/browser/clipboard-service';
export { export {
Command, Command,
@@ -99,31 +84,15 @@ export abstract class Contribution
@inject(SettingsService) @inject(SettingsService)
protected readonly settingsService: SettingsService; protected readonly settingsService: SettingsService;
@inject(FrontendApplicationStateService)
protected readonly appStateService: FrontendApplicationStateService;
@postConstruct()
protected init(): void {
this.appStateService.reachedState('ready').then(() => this.onReady());
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function, unused-imports/no-unused-vars
onStart(app: FrontendApplication): MaybePromise<void> {} onStart(app: FrontendApplication): MaybePromise<void> {}
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function, unused-imports/no-unused-vars
registerCommands(registry: CommandRegistry): void {} registerCommands(registry: CommandRegistry): void {}
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function, unused-imports/no-unused-vars
registerMenus(registry: MenuModelRegistry): void {} registerMenus(registry: MenuModelRegistry): void {}
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function, unused-imports/no-unused-vars
registerKeybindings(registry: KeybindingRegistry): void {} registerKeybindings(registry: KeybindingRegistry): void {}
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function, unused-imports/no-unused-vars
registerToolbarItems(registry: TabBarToolbarRegistry): void {} registerToolbarItems(registry: TabBarToolbarRegistry): void {}
// eslint-disable-next-line @typescript-eslint/no-empty-function
onReady(): MaybePromise<void> {}
} }
@injectable() @injectable()
@@ -158,7 +127,7 @@ export abstract class SketchContribution extends Contribution {
protected async sourceOverride(): Promise<Record<string, string>> { protected async sourceOverride(): Promise<Record<string, string>> {
const override: Record<string, string> = {}; const override: Record<string, string> = {};
const sketch = await this.sketchServiceClient.currentSketch(); const sketch = await this.sketchServiceClient.currentSketch();
if (CurrentSketch.isValid(sketch)) { if (sketch) {
for (const editor of this.editorManager.all) { for (const editor of this.editorManager.all) {
const uri = editor.editor.uri; const uri = editor.editor.uri;
if (Saveable.isDirty(editor) && Sketch.isInSketch(uri, sketch)) { if (Saveable.isDirty(editor) && Sketch.isInSketch(uri, sketch)) {
@@ -170,58 +139,8 @@ export abstract class SketchContribution extends Contribution {
} }
} }
@injectable()
export class CoreServiceContribution extends SketchContribution {
@inject(CoreService)
protected readonly coreService: CoreService;
@inject(CoreErrorHandler)
protected readonly coreErrorHandler: CoreErrorHandler;
@inject(ClipboardService)
private readonly clipboardService: ClipboardService;
protected handleError(error: unknown): void {
this.coreErrorHandler.tryHandle(error);
this.tryToastErrorMessage(error);
}
private tryToastErrorMessage(error: unknown): void {
let message: undefined | string = undefined;
if (CoreError.is(error)) {
message = error.message;
} else if (error instanceof Error) {
message = error.message;
} else if (typeof error === 'string') {
message = error;
} else {
try {
message = JSON.stringify(error);
} catch {}
}
if (message) {
const copyAction = nls.localize(
'arduino/coreContribution/copyError',
'Copy error messages'
);
this.messageService.error(message, copyAction).then(async (action) => {
if (action === copyAction) {
const content = await this.outputChannelManager.contentOfChannel(
'Arduino'
);
if (content) {
this.clipboardService.writeText(content);
}
}
});
} else {
throw error;
}
}
}
export namespace Contribution { export namespace Contribution {
export function configure( export function configure<T>(
bind: interfaces.Bind, bind: interfaces.Bind,
serviceIdentifier: typeof Contribution serviceIdentifier: typeof Contribution
): void { ): void {

View File

@@ -1,32 +0,0 @@
import { Emitter, Event } from '@theia/core';
import { injectable } from '@theia/core/shared/inversify';
import { CoreError } from '../../common/protocol/core-service';
@injectable()
export class CoreErrorHandler {
private readonly compilerErrors: CoreError.Compiler[] = [];
private readonly compilerErrorsDidChangeEmitter = new Emitter<
CoreError.Compiler[]
>();
tryHandle(error: unknown): void {
if (CoreError.is(error)) {
this.compilerErrors.length = 0;
this.compilerErrors.push(...error.data.filter(CoreError.Compiler.is));
this.fireCompilerErrorsDidChange();
}
}
reset(): void {
this.compilerErrors.length = 0;
this.fireCompilerErrorsDidChange();
}
get onCompilerErrorsDidChange(): Event<CoreError.Compiler[]> {
return this.compilerErrorsDidChangeEmitter.event;
}
private fireCompilerErrorsDidChange(): void {
this.compilerErrorsDidChangeEmitter.fire(this.compilerErrors.slice());
}
}

View File

@@ -1,4 +1,4 @@
import { inject, injectable } from '@theia/core/shared/inversify'; import { inject, injectable } from 'inversify';
import { Event, Emitter } from '@theia/core/lib/common/event'; import { Event, Emitter } from '@theia/core/lib/common/event';
import { HostedPluginSupport } from '@theia/plugin-ext/lib/hosted/browser/hosted-plugin'; import { HostedPluginSupport } from '@theia/plugin-ext/lib/hosted/browser/hosted-plugin';
import { ArduinoToolbar } from '../toolbar/arduino-toolbar'; import { ArduinoToolbar } from '../toolbar/arduino-toolbar';
@@ -12,8 +12,7 @@ import {
SketchContribution, SketchContribution,
TabBarToolbarRegistry, TabBarToolbarRegistry,
} from './contribution'; } from './contribution';
import { MaybePromise, nls } from '@theia/core/lib/common'; import { nls } from '@theia/core/lib/common';
import { CurrentSketch } from '../../common/protocol/sketches-service-client-impl';
@injectable() @injectable()
export class Debug extends SketchContribution { export class Debug extends SketchContribution {
@@ -67,7 +66,7 @@ export class Debug extends SketchContribution {
onDidChange: this.onDisabledMessageDidChange as Event<void>, onDidChange: this.onDisabledMessageDidChange as Event<void>,
}; };
override onStart(): void { onStart(): void {
this.onDisabledMessageDidChange( this.onDisabledMessageDidChange(
() => () =>
(this.debugToolbarItem.tooltip = `${ (this.debugToolbarItem.tooltip = `${
@@ -80,18 +79,55 @@ export class Debug extends SketchContribution {
: Debug.Commands.START_DEBUGGING.label : Debug.Commands.START_DEBUGGING.label
}`) }`)
); );
const refreshState = async (
board: Board | undefined = this.boardsServiceProvider.boardsConfig
.selectedBoard
) => {
if (!board) {
this.disabledMessage = nls.localize(
'arduino/common/noBoardSelected',
'No board selected'
);
return;
}
const fqbn = board.fqbn;
if (!fqbn) {
this.disabledMessage = nls.localize(
'arduino/debug/noPlatformInstalledFor',
"Platform is not installed for '{0}'",
board.name
);
return;
}
const details = await this.boardService.getBoardDetails({ fqbn });
if (!details) {
this.disabledMessage = nls.localize(
'arduino/debug/noPlatformInstalledFor',
"Platform is not installed for '{0}'",
board.name
);
return;
}
const { debuggingSupported } = details;
if (!debuggingSupported) {
this.disabledMessage = nls.localize(
'arduino/debug/debuggingNotSupported',
"Debugging is not supported by '{0}'",
board.name
);
} else {
this.disabledMessage = undefined;
}
};
this.boardsServiceProvider.onBoardsConfigChanged(({ selectedBoard }) => this.boardsServiceProvider.onBoardsConfigChanged(({ selectedBoard }) =>
this.refreshState(selectedBoard) refreshState(selectedBoard)
); );
this.notificationCenter.onPlatformInstalled(() => this.refreshState()); this.notificationCenter.onPlatformInstalled(() => refreshState());
this.notificationCenter.onPlatformUninstalled(() => this.refreshState()); this.notificationCenter.onPlatformUninstalled(() => refreshState());
refreshState();
} }
override onReady(): MaybePromise<void> { registerCommands(registry: CommandRegistry): void {
this.refreshState();
}
override registerCommands(registry: CommandRegistry): void {
registry.registerCommand(Debug.Commands.START_DEBUGGING, { registry.registerCommand(Debug.Commands.START_DEBUGGING, {
execute: () => this.startDebug(), execute: () => this.startDebug(),
isVisible: (widget) => isVisible: (widget) =>
@@ -100,51 +136,10 @@ export class Debug extends SketchContribution {
}); });
} }
override registerToolbarItems(registry: TabBarToolbarRegistry): void { registerToolbarItems(registry: TabBarToolbarRegistry): void {
registry.registerItem(this.debugToolbarItem); registry.registerItem(this.debugToolbarItem);
} }
private async refreshState(
board: Board | undefined = this.boardsServiceProvider.boardsConfig
.selectedBoard
): Promise<void> {
if (!board) {
this.disabledMessage = nls.localize(
'arduino/common/noBoardSelected',
'No board selected'
);
return;
}
const fqbn = board.fqbn;
if (!fqbn) {
this.disabledMessage = nls.localize(
'arduino/debug/noPlatformInstalledFor',
"Platform is not installed for '{0}'",
board.name
);
return;
}
const details = await this.boardService.getBoardDetails({ fqbn });
if (!details) {
this.disabledMessage = nls.localize(
'arduino/debug/noPlatformInstalledFor',
"Platform is not installed for '{0}'",
board.name
);
return;
}
const { debuggingSupported } = details;
if (!debuggingSupported) {
this.disabledMessage = nls.localize(
'arduino/debug/debuggingNotSupported',
"Debugging is not supported by '{0}'",
board.name
);
} else {
this.disabledMessage = undefined;
}
}
protected async startDebug( protected async startDebug(
board: Board | undefined = this.boardsServiceProvider.boardsConfig board: Board | undefined = this.boardsServiceProvider.boardsConfig
.selectedBoard .selectedBoard
@@ -161,7 +156,7 @@ export class Debug extends SketchContribution {
this.sketchServiceClient.currentSketch(), this.sketchServiceClient.currentSketch(),
this.executableService.list(), this.executableService.list(),
]); ]);
if (!CurrentSketch.isValid(sketch)) { if (!sketch) {
return; return;
} }
const ideTempFolderUri = await this.sketchService.getIdeTempFolderUri( const ideTempFolderUri = await this.sketchService.getIdeTempFolderUri(

View File

@@ -1,4 +1,4 @@
import { inject, injectable } from '@theia/core/shared/inversify'; import { inject, injectable } from 'inversify';
import { CommonCommands } from '@theia/core/lib/browser/common-frontend-contribution'; import { CommonCommands } from '@theia/core/lib/browser/common-frontend-contribution';
import { ClipboardService } from '@theia/core/lib/browser/clipboard-service'; import { ClipboardService } from '@theia/core/lib/browser/clipboard-service';
import { PreferenceService } from '@theia/core/lib/browser/preferences/preference-service'; import { PreferenceService } from '@theia/core/lib/browser/preferences/preference-service';
@@ -12,8 +12,6 @@ import {
} from './contribution'; } from './contribution';
import { ArduinoMenus } from '../menu/arduino-menus'; import { ArduinoMenus } from '../menu/arduino-menus';
import { nls } from '@theia/core/lib/common'; import { nls } from '@theia/core/lib/common';
import type { ICodeEditor } from '@theia/monaco-editor-core/esm/vs/editor/browser/editorBrowser';
import type { StandaloneCodeEditor } from '@theia/monaco-editor-core/esm/vs/editor/standalone/browser/standaloneCodeEditor';
// TODO: [macOS]: to remove `Start Dictation...` and `Emoji & Symbol` see this thread: https://github.com/electron/electron/issues/8283#issuecomment-269522072 // TODO: [macOS]: to remove `Start Dictation...` and `Emoji & Symbol` see this thread: https://github.com/electron/electron/issues/8283#issuecomment-269522072
// Depends on https://github.com/eclipse-theia/theia/pull/7964 // Depends on https://github.com/eclipse-theia/theia/pull/7964
@@ -28,7 +26,7 @@ export class EditContributions extends Contribution {
@inject(PreferenceService) @inject(PreferenceService)
protected readonly preferences: PreferenceService; protected readonly preferences: PreferenceService;
override registerCommands(registry: CommandRegistry): void { registerCommands(registry: CommandRegistry): void {
registry.registerCommand(EditContributions.Commands.GO_TO_LINE, { registry.registerCommand(EditContributions.Commands.GO_TO_LINE, {
execute: () => this.run('editor.action.gotoLine'), execute: () => this.run('editor.action.gotoLine'),
}); });
@@ -93,7 +91,7 @@ ${value}
}); });
} }
override registerMenus(registry: MenuModelRegistry): void { registerMenus(registry: MenuModelRegistry): void {
registry.registerMenuAction(ArduinoMenus.EDIT__TEXT_CONTROL_GROUP, { registry.registerMenuAction(ArduinoMenus.EDIT__TEXT_CONTROL_GROUP, {
commandId: CommonCommands.CUT.id, commandId: CommonCommands.CUT.id,
order: '0', order: '0',
@@ -201,7 +199,7 @@ ${value}
}); });
} }
override registerKeybindings(registry: KeybindingRegistry): void { registerKeybindings(registry: KeybindingRegistry): void {
registry.registerKeybinding({ registry.registerKeybinding({
command: EditContributions.Commands.COPY_FOR_FORUM.id, command: EditContributions.Commands.COPY_FOR_FORUM.id,
keybinding: 'CtrlCmd+Shift+C', keybinding: 'CtrlCmd+Shift+C',
@@ -252,10 +250,10 @@ ${value}
}); });
} }
protected async current(): Promise<ICodeEditor | StandaloneCodeEditor | undefined> { protected async current(): Promise<monaco.editor.ICodeEditor | undefined> {
return ( return (
this.codeEditorService.getFocusedCodeEditor() || this.codeEditorService.getFocusedCodeEditor() ||
this.codeEditorService.getActiveCodeEditor() || undefined this.codeEditorService.getActiveCodeEditor()
); );
} }

View File

@@ -1,5 +1,5 @@
import * as PQueue from 'p-queue'; import * as PQueue from 'p-queue';
import { inject, injectable } from '@theia/core/shared/inversify'; import { inject, injectable, postConstruct } from 'inversify';
import { CommandHandler } from '@theia/core/lib/common/command'; import { CommandHandler } from '@theia/core/lib/common/command';
import { import {
MenuPath, MenuPath,
@@ -21,7 +21,7 @@ import {
MenuModelRegistry, MenuModelRegistry,
} from './contribution'; } from './contribution';
import { NotificationCenter } from '../notification-center'; import { NotificationCenter } from '../notification-center';
import { Board, SketchRef, SketchContainer } from '../../common/protocol'; import { Board, Sketch, SketchContainer } from '../../common/protocol';
import { nls } from '@theia/core/lib/common'; import { nls } from '@theia/core/lib/common';
@injectable() @injectable()
@@ -43,8 +43,8 @@ export abstract class Examples extends SketchContribution {
protected readonly toDispose = new DisposableCollection(); protected readonly toDispose = new DisposableCollection();
protected override init(): void { @postConstruct()
super.init(); init(): void {
this.boardsServiceClient.onBoardsConfigChanged(({ selectedBoard }) => this.boardsServiceClient.onBoardsConfigChanged(({ selectedBoard }) =>
this.handleBoardChanged(selectedBoard) this.handleBoardChanged(selectedBoard)
); );
@@ -54,7 +54,7 @@ export abstract class Examples extends SketchContribution {
// NOOP // NOOP
} }
override registerMenus(registry: MenuModelRegistry): void { registerMenus(registry: MenuModelRegistry): void {
try { try {
// This is a hack the ensures the desired menu ordering! We cannot use https://github.com/eclipse-theia/theia/pull/8377 due to ATL-222. // This is a hack the ensures the desired menu ordering! We cannot use https://github.com/eclipse-theia/theia/pull/8377 due to ATL-222.
const index = ArduinoMenus.FILE__EXAMPLES_SUBMENU.length - 1; const index = ArduinoMenus.FILE__EXAMPLES_SUBMENU.length - 1;
@@ -82,7 +82,7 @@ export abstract class Examples extends SketchContribution {
registerRecursively( registerRecursively(
sketchContainerOrPlaceholder: sketchContainerOrPlaceholder:
| SketchContainer | SketchContainer
| (SketchRef | SketchContainer)[] | (Sketch | SketchContainer)[]
| string, | string,
menuPath: MenuPath, menuPath: MenuPath,
pushToDispose: DisposableCollection = new DisposableCollection(), pushToDispose: DisposableCollection = new DisposableCollection(),
@@ -100,7 +100,7 @@ export abstract class Examples extends SketchContribution {
) )
); );
} else { } else {
const sketches: SketchRef[] = []; const sketches: Sketch[] = [];
const children: SketchContainer[] = []; const children: SketchContainer[] = [];
let submenuPath = menuPath; let submenuPath = menuPath;
@@ -161,7 +161,7 @@ export abstract class Examples extends SketchContribution {
@injectable() @injectable()
export class BuiltInExamples extends Examples { export class BuiltInExamples extends Examples {
override async onReady(): Promise<void> { onStart(): void {
this.register(); // no `await` this.register(); // no `await`
} }
@@ -201,16 +201,13 @@ export class LibraryExamples extends Examples {
protected readonly queue = new PQueue({ autoStart: true, concurrency: 1 }); protected readonly queue = new PQueue({ autoStart: true, concurrency: 1 });
override onStart(): void { onStart(): void {
this.register(); // no `await`
this.notificationCenter.onLibraryInstalled(() => this.register()); this.notificationCenter.onLibraryInstalled(() => this.register());
this.notificationCenter.onLibraryUninstalled(() => this.register()); this.notificationCenter.onLibraryUninstalled(() => this.register());
} }
override async onReady(): Promise<void> { protected handleBoardChanged(board: Board | undefined): void {
this.register(); // no `await`
}
protected override handleBoardChanged(board: Board | undefined): void {
this.register(board); this.register(board);
} }

View File

@@ -1,79 +0,0 @@
import { MaybePromise } from '@theia/core';
import { inject, injectable } from '@theia/core/shared/inversify';
import * as monaco from '@theia/monaco-editor-core';
import { Formatter } from '../../common/protocol/formatter';
import { InoSelector } from '../ino-selectors';
import { fullRange } from '../utils/monaco';
import { Contribution, URI } from './contribution';
@injectable()
export class Format
extends Contribution
implements
monaco.languages.DocumentRangeFormattingEditProvider,
monaco.languages.DocumentFormattingEditProvider
{
@inject(Formatter)
private readonly formatter: Formatter;
override onStart(): MaybePromise<void> {
monaco.languages.registerDocumentRangeFormattingEditProvider(
InoSelector,
this
);
monaco.languages.registerDocumentFormattingEditProvider(InoSelector, this);
}
async provideDocumentRangeFormattingEdits(
model: monaco.editor.ITextModel,
range: monaco.Range,
options: monaco.languages.FormattingOptions,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
_token: monaco.CancellationToken
): Promise<monaco.languages.TextEdit[]> {
const text = await this.format(model, range, options);
return [{ range, text }];
}
async provideDocumentFormattingEdits(
model: monaco.editor.ITextModel,
options: monaco.languages.FormattingOptions,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
_token: monaco.CancellationToken
): Promise<monaco.languages.TextEdit[]> {
const range = fullRange(model);
const text = await this.format(model, range, options);
return [{ range, text }];
}
/**
* From the currently opened workspaces (IDE2 has always one), it calculates all possible
* folder locations where the `.clang-format` file could be.
*/
private formatterConfigFolderUris(model: monaco.editor.ITextModel): string[] {
const editorUri = new URI(model.uri.toString());
return this.workspaceService
.tryGetRoots()
.map(({ resource }) => resource)
.filter((workspaceUri) => workspaceUri.isEqualOrParent(editorUri))
.map((uri) => uri.toString());
}
private format(
model: monaco.editor.ITextModel,
range: monaco.Range,
options: monaco.languages.FormattingOptions
): Promise<string> {
console.info(
`Formatting ${model.uri.toString()} [Range: ${JSON.stringify(
range.toJSON()
)}]`
);
const content = model.getValueInRange(range);
const formatterConfigFolderUris = this.formatterConfigFolderUris(model);
return this.formatter.format({
content,
formatterConfigFolderUris,
options,
});
}
}

View File

@@ -1,4 +1,4 @@
import { inject, injectable } from '@theia/core/shared/inversify'; import { inject, injectable } from 'inversify';
import { MonacoEditor } from '@theia/monaco/lib/browser/monaco-editor'; import { MonacoEditor } from '@theia/monaco/lib/browser/monaco-editor';
import { EditorManager } from '@theia/editor/lib/browser/editor-manager'; import { EditorManager } from '@theia/editor/lib/browser/editor-manager';
import { WindowService } from '@theia/core/lib/browser/window/window-service'; import { WindowService } from '@theia/core/lib/browser/window/window-service';
@@ -15,7 +15,6 @@ import {
import { nls } from '@theia/core/lib/common'; import { nls } from '@theia/core/lib/common';
import { IDEUpdaterCommands } from '../ide-updater/ide-updater-commands'; import { IDEUpdaterCommands } from '../ide-updater/ide-updater-commands';
import { ElectronCommands } from '@theia/core/lib/electron-browser/menu/electron-menu-contribution'; import { ElectronCommands } from '@theia/core/lib/electron-browser/menu/electron-menu-contribution';
import * as monaco from '@theia/monaco-editor-core';
@injectable() @injectable()
export class Help extends Contribution { export class Help extends Contribution {
@@ -28,7 +27,7 @@ export class Help extends Contribution {
@inject(QuickInputService) @inject(QuickInputService)
protected readonly quickInputService: QuickInputService; protected readonly quickInputService: QuickInputService;
override registerCommands(registry: CommandRegistry): void { registerCommands(registry: CommandRegistry): void {
const open = (url: string) => const open = (url: string) =>
this.windowService.openNewWindow(url, { external: true }); this.windowService.openNewWindow(url, { external: true });
const createOpenHandler = (url: string) => const createOpenHandler = (url: string) =>
@@ -92,7 +91,7 @@ export class Help extends Contribution {
); );
} }
override registerMenus(registry: MenuModelRegistry): void { registerMenus(registry: MenuModelRegistry): void {
registry.unregisterMenuAction({ registry.unregisterMenuAction({
commandId: ElectronCommands.TOGGLE_DEVELOPER_TOOLS.id, commandId: ElectronCommands.TOGGLE_DEVELOPER_TOOLS.id,
}); });
@@ -136,7 +135,7 @@ export class Help extends Contribution {
}); });
} }
override registerKeybindings(registry: KeybindingRegistry): void { registerKeybindings(registry: KeybindingRegistry): void {
registry.registerKeybinding({ registry.registerKeybinding({
command: Help.Commands.FIND_IN_REFERENCE.id, command: Help.Commands.FIND_IN_REFERENCE.id,
keybinding: 'CtrlCmd+Shift+F', keybinding: 'CtrlCmd+Shift+F',

View File

@@ -1,5 +1,5 @@
import * as PQueue from 'p-queue'; import * as PQueue from 'p-queue';
import { inject, injectable } from '@theia/core/shared/inversify'; import { inject, injectable } from 'inversify';
import URI from '@theia/core/lib/common/uri'; import URI from '@theia/core/lib/common/uri';
import { MonacoEditor } from '@theia/monaco/lib/browser/monaco-editor'; import { MonacoEditor } from '@theia/monaco/lib/browser/monaco-editor';
import { EditorManager } from '@theia/editor/lib/browser'; import { EditorManager } from '@theia/editor/lib/browser';
@@ -16,8 +16,6 @@ import { BoardsServiceProvider } from '../boards/boards-service-provider';
import { SketchContribution, Command, CommandRegistry } from './contribution'; import { SketchContribution, Command, CommandRegistry } from './contribution';
import { NotificationCenter } from '../notification-center'; import { NotificationCenter } from '../notification-center';
import { nls } from '@theia/core/lib/common'; import { nls } from '@theia/core/lib/common';
import * as monaco from '@theia/monaco-editor-core';
import { CurrentSketch } from '../../common/protocol/sketches-service-client-impl';
@injectable() @injectable()
export class IncludeLibrary extends SketchContribution { export class IncludeLibrary extends SketchContribution {
@@ -31,7 +29,7 @@ export class IncludeLibrary extends SketchContribution {
protected readonly mainMenuManager: MainMenuManager; protected readonly mainMenuManager: MainMenuManager;
@inject(EditorManager) @inject(EditorManager)
protected override readonly editorManager: EditorManager; protected readonly editorManager: EditorManager;
@inject(NotificationCenter) @inject(NotificationCenter)
protected readonly notificationCenter: NotificationCenter; protected readonly notificationCenter: NotificationCenter;
@@ -45,7 +43,8 @@ export class IncludeLibrary extends SketchContribution {
protected readonly queue = new PQueue({ autoStart: true, concurrency: 1 }); protected readonly queue = new PQueue({ autoStart: true, concurrency: 1 });
protected readonly toDispose = new DisposableCollection(); protected readonly toDispose = new DisposableCollection();
override onStart(): void { onStart(): void {
this.updateMenuActions();
this.boardsServiceClient.onBoardsConfigChanged(() => this.boardsServiceClient.onBoardsConfigChanged(() =>
this.updateMenuActions() this.updateMenuActions()
); );
@@ -55,11 +54,7 @@ export class IncludeLibrary extends SketchContribution {
); );
} }
override async onReady(): Promise<void> { registerMenus(registry: MenuModelRegistry): void {
this.updateMenuActions();
}
override registerMenus(registry: MenuModelRegistry): void {
// `Include Library` submenu // `Include Library` submenu
const includeLibMenuPath = [ const includeLibMenuPath = [
...ArduinoMenus.SKETCH__UTILS_GROUP, ...ArduinoMenus.SKETCH__UTILS_GROUP,
@@ -82,7 +77,7 @@ export class IncludeLibrary extends SketchContribution {
}); });
} }
override registerCommands(registry: CommandRegistry): void { registerCommands(registry: CommandRegistry): void {
registry.registerCommand(IncludeLibrary.Commands.INCLUDE_LIBRARY, { registry.registerCommand(IncludeLibrary.Commands.INCLUDE_LIBRARY, {
execute: async (arg) => { execute: async (arg) => {
if (LibraryPackage.is(arg)) { if (LibraryPackage.is(arg)) {
@@ -173,7 +168,7 @@ export class IncludeLibrary extends SketchContribution {
protected async includeLibrary(library: LibraryPackage): Promise<void> { protected async includeLibrary(library: LibraryPackage): Promise<void> {
const sketch = await this.sketchServiceClient.currentSketch(); const sketch = await this.sketchServiceClient.currentSketch();
if (!CurrentSketch.isValid(sketch)) { if (!sketch) {
return; return;
} }
// If the current editor is one of the additional files from the sketch, we use that. // If the current editor is one of the additional files from the sketch, we use that.

View File

@@ -1,5 +1,5 @@
import { nls } from '@theia/core/lib/common'; import { nls } from '@theia/core/lib/common';
import { injectable } from '@theia/core/shared/inversify'; import { injectable } from 'inversify';
import { ArduinoMenus } from '../menu/arduino-menus'; import { ArduinoMenus } from '../menu/arduino-menus';
import { ArduinoToolbar } from '../toolbar/arduino-toolbar'; import { ArduinoToolbar } from '../toolbar/arduino-toolbar';
import { import {
@@ -14,7 +14,7 @@ import {
@injectable() @injectable()
export class NewSketch extends SketchContribution { export class NewSketch extends SketchContribution {
override registerCommands(registry: CommandRegistry): void { registerCommands(registry: CommandRegistry): void {
registry.registerCommand(NewSketch.Commands.NEW_SKETCH, { registry.registerCommand(NewSketch.Commands.NEW_SKETCH, {
execute: () => this.newSketch(), execute: () => this.newSketch(),
}); });
@@ -25,7 +25,7 @@ export class NewSketch extends SketchContribution {
}); });
} }
override registerMenus(registry: MenuModelRegistry): void { registerMenus(registry: MenuModelRegistry): void {
registry.registerMenuAction(ArduinoMenus.FILE__SKETCH_GROUP, { registry.registerMenuAction(ArduinoMenus.FILE__SKETCH_GROUP, {
commandId: NewSketch.Commands.NEW_SKETCH.id, commandId: NewSketch.Commands.NEW_SKETCH.id,
label: nls.localize('arduino/sketch/new', 'New'), label: nls.localize('arduino/sketch/new', 'New'),
@@ -33,14 +33,14 @@ export class NewSketch extends SketchContribution {
}); });
} }
override registerKeybindings(registry: KeybindingRegistry): void { registerKeybindings(registry: KeybindingRegistry): void {
registry.registerKeybinding({ registry.registerKeybinding({
command: NewSketch.Commands.NEW_SKETCH.id, command: NewSketch.Commands.NEW_SKETCH.id,
keybinding: 'CtrlCmd+N', keybinding: 'CtrlCmd+N',
}); });
} }
override registerToolbarItems(registry: TabBarToolbarRegistry): void { registerToolbarItems(registry: TabBarToolbarRegistry): void {
registry.registerItem({ registry.registerItem({
id: NewSketch.Commands.NEW_SKETCH__TOOLBAR.id, id: NewSketch.Commands.NEW_SKETCH__TOOLBAR.id,
command: NewSketch.Commands.NEW_SKETCH__TOOLBAR.id, command: NewSketch.Commands.NEW_SKETCH__TOOLBAR.id,

View File

@@ -1,4 +1,4 @@
import { inject, injectable } from '@theia/core/shared/inversify'; import { inject, injectable } from 'inversify';
import { WorkspaceServer } from '@theia/workspace/lib/common/workspace-protocol'; import { WorkspaceServer } from '@theia/workspace/lib/common/workspace-protocol';
import { import {
Disposable, Disposable,
@@ -35,19 +35,18 @@ export class OpenRecentSketch extends SketchContribution {
protected toDisposeBeforeRegister = new Map<string, DisposableCollection>(); protected toDisposeBeforeRegister = new Map<string, DisposableCollection>();
override onStart(): void { onStart(): void {
const refreshMenu = (sketches: Sketch[]) => {
this.register(sketches);
this.mainMenuManager.update();
};
this.notificationCenter.onRecentSketchesChanged(({ sketches }) => this.notificationCenter.onRecentSketchesChanged(({ sketches }) =>
this.refreshMenu(sketches) refreshMenu(sketches)
); );
this.sketchService.recentlyOpenedSketches().then(refreshMenu);
} }
override async onReady(): Promise<void> { registerMenus(registry: MenuModelRegistry): void {
this.sketchService
.recentlyOpenedSketches()
.then((sketches) => this.refreshMenu(sketches));
}
override registerMenus(registry: MenuModelRegistry): void {
registry.registerSubmenu( registry.registerSubmenu(
ArduinoMenus.FILE__OPEN_RECENT_SUBMENU, ArduinoMenus.FILE__OPEN_RECENT_SUBMENU,
nls.localize('arduino/sketch/openRecent', 'Open Recent'), nls.localize('arduino/sketch/openRecent', 'Open Recent'),
@@ -55,11 +54,6 @@ export class OpenRecentSketch extends SketchContribution {
); );
} }
private refreshMenu(sketches: Sketch[]): void {
this.register(sketches);
this.mainMenuManager.update();
}
protected register(sketches: Sketch[]): void { protected register(sketches: Sketch[]): void {
const order = 0; const order = 0;
for (const sketch of sketches) { for (const sketch of sketches) {

View File

@@ -1,4 +1,4 @@
import { injectable } from '@theia/core/shared/inversify'; import { injectable } from 'inversify';
import * as remote from '@theia/core/electron-shared/@electron/remote'; import * as remote from '@theia/core/electron-shared/@electron/remote';
import URI from '@theia/core/lib/common/uri'; import URI from '@theia/core/lib/common/uri';
import { ArduinoMenus } from '../menu/arduino-menus'; import { ArduinoMenus } from '../menu/arduino-menus';
@@ -13,13 +13,13 @@ import { nls } from '@theia/core/lib/common';
@injectable() @injectable()
export class OpenSketchExternal extends SketchContribution { export class OpenSketchExternal extends SketchContribution {
override registerCommands(registry: CommandRegistry): void { registerCommands(registry: CommandRegistry): void {
registry.registerCommand(OpenSketchExternal.Commands.OPEN_EXTERNAL, { registry.registerCommand(OpenSketchExternal.Commands.OPEN_EXTERNAL, {
execute: () => this.openExternal(), execute: () => this.openExternal(),
}); });
} }
override registerMenus(registry: MenuModelRegistry): void { registerMenus(registry: MenuModelRegistry): void {
registry.registerMenuAction(ArduinoMenus.SKETCH__UTILS_GROUP, { registry.registerMenuAction(ArduinoMenus.SKETCH__UTILS_GROUP, {
commandId: OpenSketchExternal.Commands.OPEN_EXTERNAL.id, commandId: OpenSketchExternal.Commands.OPEN_EXTERNAL.id,
label: nls.localize('arduino/sketch/showFolder', 'Show Sketch Folder'), label: nls.localize('arduino/sketch/showFolder', 'Show Sketch Folder'),
@@ -27,7 +27,7 @@ export class OpenSketchExternal extends SketchContribution {
}); });
} }
override registerKeybindings(registry: KeybindingRegistry): void { registerKeybindings(registry: KeybindingRegistry): void {
registry.registerKeybinding({ registry.registerKeybinding({
command: OpenSketchExternal.Commands.OPEN_EXTERNAL.id, command: OpenSketchExternal.Commands.OPEN_EXTERNAL.id,
keybinding: 'CtrlCmd+Alt+K', keybinding: 'CtrlCmd+Alt+K',

View File

@@ -1,4 +1,4 @@
import { inject, injectable } from '@theia/core/shared/inversify'; import { inject, injectable } from 'inversify';
import * as remote from '@theia/core/electron-shared/@electron/remote'; import * as remote from '@theia/core/electron-shared/@electron/remote';
import { MaybePromise } from '@theia/core/lib/common/types'; import { MaybePromise } from '@theia/core/lib/common/types';
import { Widget, ContextMenuRenderer } from '@theia/core/lib/browser'; import { Widget, ContextMenuRenderer } from '@theia/core/lib/browser';
@@ -43,7 +43,7 @@ export class OpenSketch extends SketchContribution {
protected readonly toDispose = new DisposableCollection(); protected readonly toDispose = new DisposableCollection();
override registerCommands(registry: CommandRegistry): void { registerCommands(registry: CommandRegistry): void {
registry.registerCommand(OpenSketch.Commands.OPEN_SKETCH, { registry.registerCommand(OpenSketch.Commands.OPEN_SKETCH, {
execute: (arg) => execute: (arg) =>
Sketch.is(arg) ? this.openSketch(arg) : this.openSketch(), Sketch.is(arg) ? this.openSketch(arg) : this.openSketch(),
@@ -116,7 +116,7 @@ export class OpenSketch extends SketchContribution {
}); });
} }
override registerMenus(registry: MenuModelRegistry): void { registerMenus(registry: MenuModelRegistry): void {
registry.registerMenuAction(ArduinoMenus.FILE__SKETCH_GROUP, { registry.registerMenuAction(ArduinoMenus.FILE__SKETCH_GROUP, {
commandId: OpenSketch.Commands.OPEN_SKETCH.id, commandId: OpenSketch.Commands.OPEN_SKETCH.id,
label: nls.localize('vscode/workspaceActions/openFileFolder', 'Open...'), label: nls.localize('vscode/workspaceActions/openFileFolder', 'Open...'),
@@ -124,14 +124,14 @@ export class OpenSketch extends SketchContribution {
}); });
} }
override registerKeybindings(registry: KeybindingRegistry): void { registerKeybindings(registry: KeybindingRegistry): void {
registry.registerKeybinding({ registry.registerKeybinding({
command: OpenSketch.Commands.OPEN_SKETCH.id, command: OpenSketch.Commands.OPEN_SKETCH.id,
keybinding: 'CtrlCmd+O', keybinding: 'CtrlCmd+O',
}); });
} }
override registerToolbarItems(registry: TabBarToolbarRegistry): void { registerToolbarItems(registry: TabBarToolbarRegistry): void {
registry.registerItem({ registry.registerItem({
id: OpenSketch.Commands.OPEN_SKETCH__TOOLBAR.id, id: OpenSketch.Commands.OPEN_SKETCH__TOOLBAR.id,
command: OpenSketch.Commands.OPEN_SKETCH__TOOLBAR.id, command: OpenSketch.Commands.OPEN_SKETCH__TOOLBAR.id,

View File

@@ -1,4 +1,4 @@
import { injectable } from '@theia/core/shared/inversify'; import { injectable } from 'inversify';
import * as remote from '@theia/core/electron-shared/@electron/remote'; import * as remote from '@theia/core/electron-shared/@electron/remote';
import { isOSX } from '@theia/core/lib/common/os'; import { isOSX } from '@theia/core/lib/common/os';
import { import {
@@ -13,7 +13,7 @@ import { nls } from '@theia/core/lib/common';
@injectable() @injectable()
export class QuitApp extends Contribution { export class QuitApp extends Contribution {
override registerCommands(registry: CommandRegistry): void { registerCommands(registry: CommandRegistry): void {
if (!isOSX) { if (!isOSX) {
registry.registerCommand(QuitApp.Commands.QUIT_APP, { registry.registerCommand(QuitApp.Commands.QUIT_APP, {
execute: () => remote.app.quit(), execute: () => remote.app.quit(),
@@ -21,7 +21,7 @@ export class QuitApp extends Contribution {
} }
} }
override registerMenus(registry: MenuModelRegistry): void { registerMenus(registry: MenuModelRegistry): void {
// On macOS we will get the `Quit ${YOUR_APP_NAME}` menu item natively, no need to duplicate it. // On macOS we will get the `Quit ${YOUR_APP_NAME}` menu item natively, no need to duplicate it.
if (!isOSX) { if (!isOSX) {
registry.registerMenuAction(ArduinoMenus.FILE__QUIT_GROUP, { registry.registerMenuAction(ArduinoMenus.FILE__QUIT_GROUP, {
@@ -32,7 +32,7 @@ export class QuitApp extends Contribution {
} }
} }
override registerKeybindings(registry: KeybindingRegistry): void { registerKeybindings(registry: KeybindingRegistry): void {
if (!isOSX) { if (!isOSX) {
registry.registerKeybinding({ registry.registerKeybinding({
command: QuitApp.Commands.QUIT_APP.id, command: QuitApp.Commands.QUIT_APP.id,

View File

@@ -1,4 +1,4 @@
import { inject, injectable } from '@theia/core/shared/inversify'; import { inject, injectable } from 'inversify';
import * as remote from '@theia/core/electron-shared/@electron/remote'; import * as remote from '@theia/core/electron-shared/@electron/remote';
import * as dateFormat from 'dateformat'; import * as dateFormat from 'dateformat';
import { ArduinoMenus } from '../menu/arduino-menus'; import { ArduinoMenus } from '../menu/arduino-menus';
@@ -14,7 +14,6 @@ import { nls } from '@theia/core/lib/common';
import { ApplicationShell, NavigatableWidget, Saveable } from '@theia/core/lib/browser'; import { ApplicationShell, NavigatableWidget, Saveable } from '@theia/core/lib/browser';
import { EditorManager } from '@theia/editor/lib/browser'; import { EditorManager } from '@theia/editor/lib/browser';
import { WindowService } from '@theia/core/lib/browser/window/window-service'; import { WindowService } from '@theia/core/lib/browser/window/window-service';
import { CurrentSketch } from '../../common/protocol/sketches-service-client-impl';
@injectable() @injectable()
export class SaveAsSketch extends SketchContribution { export class SaveAsSketch extends SketchContribution {
@@ -23,18 +22,18 @@ export class SaveAsSketch extends SketchContribution {
protected readonly applicationShell: ApplicationShell; protected readonly applicationShell: ApplicationShell;
@inject(EditorManager) @inject(EditorManager)
protected override readonly editorManager: EditorManager; protected readonly editorManager: EditorManager;
@inject(WindowService) @inject(WindowService)
protected readonly windowService: WindowService; protected readonly windowService: WindowService;
override registerCommands(registry: CommandRegistry): void { registerCommands(registry: CommandRegistry): void {
registry.registerCommand(SaveAsSketch.Commands.SAVE_AS_SKETCH, { registry.registerCommand(SaveAsSketch.Commands.SAVE_AS_SKETCH, {
execute: (args) => this.saveAs(args), execute: (args) => this.saveAs(args),
}); });
} }
override registerMenus(registry: MenuModelRegistry): void { registerMenus(registry: MenuModelRegistry): void {
registry.registerMenuAction(ArduinoMenus.FILE__SKETCH_GROUP, { registry.registerMenuAction(ArduinoMenus.FILE__SKETCH_GROUP, {
commandId: SaveAsSketch.Commands.SAVE_AS_SKETCH.id, commandId: SaveAsSketch.Commands.SAVE_AS_SKETCH.id,
label: nls.localize('vscode/fileCommands/saveAs', 'Save As...'), label: nls.localize('vscode/fileCommands/saveAs', 'Save As...'),
@@ -42,7 +41,7 @@ export class SaveAsSketch extends SketchContribution {
}); });
} }
override registerKeybindings(registry: KeybindingRegistry): void { registerKeybindings(registry: KeybindingRegistry): void {
registry.registerKeybinding({ registry.registerKeybinding({
command: SaveAsSketch.Commands.SAVE_AS_SKETCH.id, command: SaveAsSketch.Commands.SAVE_AS_SKETCH.id,
keybinding: 'CtrlCmd+Shift+S', keybinding: 'CtrlCmd+Shift+S',
@@ -60,7 +59,7 @@ export class SaveAsSketch extends SketchContribution {
}: SaveAsSketch.Options = SaveAsSketch.Options.DEFAULT }: SaveAsSketch.Options = SaveAsSketch.Options.DEFAULT
): Promise<boolean> { ): Promise<boolean> {
const sketch = await this.sketchServiceClient.currentSketch(); const sketch = await this.sketchServiceClient.currentSketch();
if (!CurrentSketch.isValid(sketch)) { if (!sketch) {
return false; return false;
} }

View File

@@ -1,4 +1,4 @@
import { injectable } from '@theia/core/shared/inversify'; import { injectable } from 'inversify';
import { CommonCommands } from '@theia/core/lib/browser/common-frontend-contribution'; import { CommonCommands } from '@theia/core/lib/browser/common-frontend-contribution';
import { ArduinoMenus } from '../menu/arduino-menus'; import { ArduinoMenus } from '../menu/arduino-menus';
import { ArduinoToolbar } from '../toolbar/arduino-toolbar'; import { ArduinoToolbar } from '../toolbar/arduino-toolbar';
@@ -12,11 +12,10 @@ import {
TabBarToolbarRegistry, TabBarToolbarRegistry,
} from './contribution'; } from './contribution';
import { nls } from '@theia/core/lib/common'; import { nls } from '@theia/core/lib/common';
import { CurrentSketch } from '../../common/protocol/sketches-service-client-impl';
@injectable() @injectable()
export class SaveSketch extends SketchContribution { export class SaveSketch extends SketchContribution {
override registerCommands(registry: CommandRegistry): void { registerCommands(registry: CommandRegistry): void {
registry.registerCommand(SaveSketch.Commands.SAVE_SKETCH, { registry.registerCommand(SaveSketch.Commands.SAVE_SKETCH, {
execute: () => this.saveSketch(), execute: () => this.saveSketch(),
}); });
@@ -28,7 +27,7 @@ export class SaveSketch extends SketchContribution {
}); });
} }
override registerMenus(registry: MenuModelRegistry): void { registerMenus(registry: MenuModelRegistry): void {
registry.registerMenuAction(ArduinoMenus.FILE__SKETCH_GROUP, { registry.registerMenuAction(ArduinoMenus.FILE__SKETCH_GROUP, {
commandId: SaveSketch.Commands.SAVE_SKETCH.id, commandId: SaveSketch.Commands.SAVE_SKETCH.id,
label: nls.localize('vscode/fileCommands/save', 'Save'), label: nls.localize('vscode/fileCommands/save', 'Save'),
@@ -36,14 +35,14 @@ export class SaveSketch extends SketchContribution {
}); });
} }
override registerKeybindings(registry: KeybindingRegistry): void { registerKeybindings(registry: KeybindingRegistry): void {
registry.registerKeybinding({ registry.registerKeybinding({
command: SaveSketch.Commands.SAVE_SKETCH.id, command: SaveSketch.Commands.SAVE_SKETCH.id,
keybinding: 'CtrlCmd+S', keybinding: 'CtrlCmd+S',
}); });
} }
override registerToolbarItems(registry: TabBarToolbarRegistry): void { registerToolbarItems(registry: TabBarToolbarRegistry): void {
registry.registerItem({ registry.registerItem({
id: SaveSketch.Commands.SAVE_SKETCH__TOOLBAR.id, id: SaveSketch.Commands.SAVE_SKETCH__TOOLBAR.id,
command: SaveSketch.Commands.SAVE_SKETCH__TOOLBAR.id, command: SaveSketch.Commands.SAVE_SKETCH__TOOLBAR.id,
@@ -54,7 +53,7 @@ export class SaveSketch extends SketchContribution {
async saveSketch(): Promise<void> { async saveSketch(): Promise<void> {
const sketch = await this.sketchServiceClient.currentSketch(); const sketch = await this.sketchServiceClient.currentSketch();
if (!CurrentSketch.isValid(sketch)) { if (!sketch) {
return; return;
} }
const isTemp = await this.sketchService.isTemp(sketch); const isTemp = await this.sketchService.isTemp(sketch);

View File

@@ -1,4 +1,4 @@
import { inject, injectable } from '@theia/core/shared/inversify'; import { inject, injectable } from 'inversify';
import { import {
Command, Command,
MenuModelRegistry, MenuModelRegistry,
@@ -18,7 +18,7 @@ export class Settings extends SketchContribution {
protected settingsOpened = false; protected settingsOpened = false;
override registerCommands(registry: CommandRegistry): void { registerCommands(registry: CommandRegistry): void {
registry.registerCommand(Settings.Commands.OPEN, { registry.registerCommand(Settings.Commands.OPEN, {
execute: async () => { execute: async () => {
let settings: Preferences | undefined = undefined; let settings: Preferences | undefined = undefined;
@@ -39,7 +39,7 @@ export class Settings extends SketchContribution {
}); });
} }
override registerMenus(registry: MenuModelRegistry): void { registerMenus(registry: MenuModelRegistry): void {
registry.registerMenuAction(ArduinoMenus.FILE__PREFERENCES_GROUP, { registry.registerMenuAction(ArduinoMenus.FILE__PREFERENCES_GROUP, {
commandId: Settings.Commands.OPEN.id, commandId: Settings.Commands.OPEN.id,
label: label:
@@ -52,7 +52,7 @@ export class Settings extends SketchContribution {
registry.registerSubmenu(ArduinoMenus.FILE__ADVANCED_SUBMENU, 'Advanced'); registry.registerSubmenu(ArduinoMenus.FILE__ADVANCED_SUBMENU, 'Advanced');
} }
override registerKeybindings(registry: KeybindingRegistry): void { registerKeybindings(registry: KeybindingRegistry): void {
registry.registerKeybinding({ registry.registerKeybinding({
command: Settings.Commands.OPEN.id, command: Settings.Commands.OPEN.id,
keybinding: 'CtrlCmd+,', keybinding: 'CtrlCmd+,',

View File

@@ -1,4 +1,4 @@
import { inject, injectable } from '@theia/core/shared/inversify'; import { inject, injectable } from 'inversify';
import { CommonCommands } from '@theia/core/lib/browser/common-frontend-contribution'; import { CommonCommands } from '@theia/core/lib/browser/common-frontend-contribution';
import { ApplicationShell } from '@theia/core/lib/browser/shell/application-shell'; import { ApplicationShell } from '@theia/core/lib/browser/shell/application-shell';
import { WorkspaceCommands } from '@theia/workspace/lib/browser'; import { WorkspaceCommands } from '@theia/workspace/lib/browser';
@@ -19,10 +19,7 @@ import {
} from './contribution'; } from './contribution';
import { ArduinoMenus, PlaceholderMenuNode } from '../menu/arduino-menus'; import { ArduinoMenus, PlaceholderMenuNode } from '../menu/arduino-menus';
import { EditorManager } from '@theia/editor/lib/browser/editor-manager'; import { EditorManager } from '@theia/editor/lib/browser/editor-manager';
import { import { SketchesServiceClientImpl } from '../../common/protocol/sketches-service-client-impl';
CurrentSketch,
SketchesServiceClientImpl,
} from '../../common/protocol/sketches-service-client-impl';
import { LocalCacheFsProvider } from '../local-cache/local-cache-fs-provider'; import { LocalCacheFsProvider } from '../local-cache/local-cache-fs-provider';
import { nls } from '@theia/core/lib/common'; import { nls } from '@theia/core/lib/common';
@@ -38,7 +35,7 @@ export class SketchControl extends SketchContribution {
protected readonly contextMenuRenderer: ContextMenuRenderer; protected readonly contextMenuRenderer: ContextMenuRenderer;
@inject(EditorManager) @inject(EditorManager)
protected override readonly editorManager: EditorManager; protected readonly editorManager: EditorManager;
@inject(SketchesServiceClientImpl) @inject(SketchesServiceClientImpl)
protected readonly sketchesServiceClient: SketchesServiceClientImpl; protected readonly sketchesServiceClient: SketchesServiceClientImpl;
@@ -49,7 +46,7 @@ export class SketchControl extends SketchContribution {
protected readonly toDisposeBeforeCreateNewContextMenu = protected readonly toDisposeBeforeCreateNewContextMenu =
new DisposableCollection(); new DisposableCollection();
override registerCommands(registry: CommandRegistry): void { registerCommands(registry: CommandRegistry): void {
registry.registerCommand( registry.registerCommand(
SketchControl.Commands.OPEN_SKETCH_CONTROL__TOOLBAR, SketchControl.Commands.OPEN_SKETCH_CONTROL__TOOLBAR,
{ {
@@ -58,7 +55,7 @@ export class SketchControl extends SketchContribution {
execute: async () => { execute: async () => {
this.toDisposeBeforeCreateNewContextMenu.dispose(); this.toDisposeBeforeCreateNewContextMenu.dispose();
const sketch = await this.sketchServiceClient.currentSketch(); const sketch = await this.sketchServiceClient.currentSketch();
if (!CurrentSketch.isValid(sketch)) { if (!sketch) {
return; return;
} }
@@ -73,22 +70,25 @@ export class SketchControl extends SketchContribution {
return; return;
} }
const { mainFileUri, rootFolderFileUris } = sketch; const { mainFileUri, rootFolderFileUris } =
await this.sketchService.loadSketch(sketch.uri);
const uris = [mainFileUri, ...rootFolderFileUris]; const uris = [mainFileUri, ...rootFolderFileUris];
const parentSketchUri = this.editorManager.currentEditor const currentSketch =
await this.sketchesServiceClient.currentSketch();
const parentsketchUri = this.editorManager.currentEditor
?.getResourceUri() ?.getResourceUri()
?.toString(); ?.toString();
const parentSketch = await this.sketchService.getSketchFolder( const parentsketch = await this.sketchService.getSketchFolder(
parentSketchUri || '' parentsketchUri || ''
); );
// if the current file is in the current opened sketch, show extra menus // if the current file is in the current opened sketch, show extra menus
if ( if (
sketch && currentSketch &&
parentSketch && parentsketch &&
parentSketch.uri === sketch.uri && parentsketch.uri === currentSketch.uri &&
this.allowRename(parentSketch.uri) this.allowRename(parentsketch.uri)
) { ) {
this.menuRegistry.registerMenuAction( this.menuRegistry.registerMenuAction(
ArduinoMenus.SKETCH_CONTROL__CONTEXT__MAIN_GROUP, ArduinoMenus.SKETCH_CONTROL__CONTEXT__MAIN_GROUP,
@@ -122,10 +122,10 @@ export class SketchControl extends SketchContribution {
} }
if ( if (
sketch && currentSketch &&
parentSketch && parentsketch &&
parentSketch.uri === sketch.uri && parentsketch.uri === currentSketch.uri &&
this.allowDelete(parentSketch.uri) this.allowDelete(parentsketch.uri)
) { ) {
this.menuRegistry.registerMenuAction( this.menuRegistry.registerMenuAction(
ArduinoMenus.SKETCH_CONTROL__CONTEXT__MAIN_GROUP, ArduinoMenus.SKETCH_CONTROL__CONTEXT__MAIN_GROUP,
@@ -200,7 +200,7 @@ export class SketchControl extends SketchContribution {
); );
} }
override registerMenus(registry: MenuModelRegistry): void { registerMenus(registry: MenuModelRegistry): void {
registry.registerMenuAction( registry.registerMenuAction(
ArduinoMenus.SKETCH_CONTROL__CONTEXT__MAIN_GROUP, ArduinoMenus.SKETCH_CONTROL__CONTEXT__MAIN_GROUP,
{ {
@@ -228,7 +228,7 @@ export class SketchControl extends SketchContribution {
); );
} }
override registerKeybindings(registry: KeybindingRegistry): void { registerKeybindings(registry: KeybindingRegistry): void {
registry.registerKeybinding({ registry.registerKeybinding({
command: WorkspaceCommands.NEW_FILE.id, command: WorkspaceCommands.NEW_FILE.id,
keybinding: 'CtrlCmd+Shift+N', keybinding: 'CtrlCmd+Shift+N',
@@ -243,7 +243,7 @@ export class SketchControl extends SketchContribution {
}); });
} }
override registerToolbarItems(registry: TabBarToolbarRegistry): void { registerToolbarItems(registry: TabBarToolbarRegistry): void {
registry.registerItem({ registry.registerItem({
id: SketchControl.Commands.OPEN_SKETCH_CONTROL__TOOLBAR.id, id: SketchControl.Commands.OPEN_SKETCH_CONTROL__TOOLBAR.id,
command: SketchControl.Commands.OPEN_SKETCH_CONTROL__TOOLBAR.id, command: SketchControl.Commands.OPEN_SKETCH_CONTROL__TOOLBAR.id,

View File

@@ -1,4 +1,4 @@
import { inject, injectable } from '@theia/core/shared/inversify'; import { inject, injectable } from 'inversify';
import { CommandHandler } from '@theia/core/lib/common/command'; import { CommandHandler } from '@theia/core/lib/common/command';
import { CommandRegistry, MenuModelRegistry } from './contribution'; import { CommandRegistry, MenuModelRegistry } from './contribution';
import { ArduinoMenus } from '../menu/arduino-menus'; import { ArduinoMenus } from '../menu/arduino-menus';
@@ -12,10 +12,10 @@ import { nls } from '@theia/core/lib/common';
@injectable() @injectable()
export class Sketchbook extends Examples { export class Sketchbook extends Examples {
@inject(CommandRegistry) @inject(CommandRegistry)
protected override readonly commandRegistry: CommandRegistry; protected readonly commandRegistry: CommandRegistry;
@inject(MenuModelRegistry) @inject(MenuModelRegistry)
protected override readonly menuRegistry: MenuModelRegistry; protected readonly menuRegistry: MenuModelRegistry;
@inject(MainMenuManager) @inject(MainMenuManager)
protected readonly mainMenuManager: MainMenuManager; protected readonly mainMenuManager: MainMenuManager;
@@ -23,7 +23,11 @@ export class Sketchbook extends Examples {
@inject(NotificationCenter) @inject(NotificationCenter)
protected readonly notificationCenter: NotificationCenter; protected readonly notificationCenter: NotificationCenter;
override onStart(): void { onStart(): void {
this.sketchService.getSketches({}).then((container) => {
this.register(container);
this.mainMenuManager.update();
});
this.sketchServiceClient.onSketchbookDidChange(() => { this.sketchServiceClient.onSketchbookDidChange(() => {
this.sketchService.getSketches({}).then((container) => { this.sketchService.getSketches({}).then((container) => {
this.register(container); this.register(container);
@@ -32,14 +36,7 @@ export class Sketchbook extends Examples {
}); });
} }
override async onReady(): Promise<void> { registerMenus(registry: MenuModelRegistry): void {
this.sketchService.getSketches({}).then((container) => {
this.register(container);
this.mainMenuManager.update();
});
}
override registerMenus(registry: MenuModelRegistry): void {
registry.registerSubmenu( registry.registerSubmenu(
ArduinoMenus.FILE__SKETCHBOOK_SUBMENU, ArduinoMenus.FILE__SKETCHBOOK_SUBMENU,
nls.localize('arduino/sketch/sketchbook', 'Sketchbook'), nls.localize('arduino/sketch/sketchbook', 'Sketchbook'),
@@ -56,7 +53,7 @@ export class Sketchbook extends Examples {
); );
} }
protected override createHandler(uri: string): CommandHandler { protected createHandler(uri: string): CommandHandler {
return { return {
execute: async () => { execute: async () => {
const sketch = await this.sketchService.loadSketch(uri); const sketch = await this.sketchService.loadSketch(uri);

View File

@@ -1,78 +0,0 @@
import { MessageService } from '@theia/core';
import { FrontendApplicationContribution } from '@theia/core/lib/browser';
import { inject, injectable } from '@theia/core/shared/inversify';
import { LocalStorageService } from '@theia/core/lib/browser';
import { nls } from '@theia/core/lib/common';
import { WindowService } from '@theia/core/lib/browser/window/window-service';
import { ArduinoPreferences } from '../arduino-preferences';
import { SurveyNotificationService } from '../../common/protocol/survey-service';
const SURVEY_MESSAGE = nls.localize(
'arduino/survey/surveyMessage',
'Please help us improve by answering this super short survey. We value our community and would like to get to know our supporters a little better.'
);
const DO_NOT_SHOW_AGAIN = nls.localize(
'arduino/survey/dismissSurvey',
"Don't show again"
);
const GO_TO_SURVEY = nls.localize(
'arduino/survey/answerSurvey',
'Answer survey'
);
const SURVEY_BASE_URL = 'https://surveys.hotjar.com/';
const surveyId = '17887b40-e1f0-4bd6-b9f0-a37f229ccd8b';
@injectable()
export class SurveyNotification implements FrontendApplicationContribution {
@inject(MessageService)
private readonly messageService: MessageService;
@inject(LocalStorageService)
private readonly localStorageService: LocalStorageService;
@inject(WindowService)
private readonly windowService: WindowService;
@inject(ArduinoPreferences)
private readonly arduinoPreferences: ArduinoPreferences;
@inject(SurveyNotificationService)
private readonly surveyNotificationService: SurveyNotificationService;
onStart(): void {
this.arduinoPreferences.ready.then(async () => {
if (
(await this.surveyNotificationService.isFirstInstance()) &&
this.arduinoPreferences.get('arduino.survey.notification')
) {
const surveyAnswered = await this.localStorageService.getData(
this.surveyKey(surveyId)
);
if (surveyAnswered !== undefined) {
return;
}
const answer = await this.messageService.info(
SURVEY_MESSAGE,
DO_NOT_SHOW_AGAIN,
GO_TO_SURVEY
);
switch (answer) {
case GO_TO_SURVEY:
this.windowService.openNewWindow(SURVEY_BASE_URL + surveyId, {
external: true,
});
this.localStorageService.setData(this.surveyKey(surveyId), true);
break;
case DO_NOT_SHOW_AGAIN:
this.localStorageService.setData(this.surveyKey(surveyId), false);
break;
}
}
});
}
private surveyKey(id: string): string {
return `answered_survey:${id}`;
}
}

View File

@@ -1,4 +1,4 @@
import { inject, injectable } from '@theia/core/shared/inversify'; import { inject, injectable } from 'inversify';
import { import {
Command, Command,
MenuModelRegistry, MenuModelRegistry,
@@ -39,7 +39,7 @@ export class UploadCertificate extends Contribution {
protected dialogOpened = false; protected dialogOpened = false;
override registerCommands(registry: CommandRegistry): void { registerCommands(registry: CommandRegistry): void {
registry.registerCommand(UploadCertificate.Commands.OPEN, { registry.registerCommand(UploadCertificate.Commands.OPEN, {
execute: async () => { execute: async () => {
try { try {
@@ -93,7 +93,7 @@ export class UploadCertificate extends Contribution {
}); });
} }
override registerMenus(registry: MenuModelRegistry): void { registerMenus(registry: MenuModelRegistry): void {
registry.registerMenuAction(ArduinoMenus.TOOLS__FIRMWARE_UPLOADER_GROUP, { registry.registerMenuAction(ArduinoMenus.TOOLS__FIRMWARE_UPLOADER_GROUP, {
commandId: UploadCertificate.Commands.OPEN.id, commandId: UploadCertificate.Commands.OPEN.id,
label: UploadCertificate.Commands.OPEN.label, label: UploadCertificate.Commands.OPEN.label,

View File

@@ -1,4 +1,4 @@
import { inject, injectable } from '@theia/core/shared/inversify'; import { inject, injectable } from 'inversify';
import { import {
Command, Command,
MenuModelRegistry, MenuModelRegistry,
@@ -16,7 +16,7 @@ export class UploadFirmware extends Contribution {
protected dialogOpened = false; protected dialogOpened = false;
override registerCommands(registry: CommandRegistry): void { registerCommands(registry: CommandRegistry): void {
registry.registerCommand(UploadFirmware.Commands.OPEN, { registry.registerCommand(UploadFirmware.Commands.OPEN, {
execute: async () => { execute: async () => {
try { try {
@@ -30,7 +30,7 @@ export class UploadFirmware extends Contribution {
}); });
} }
override registerMenus(registry: MenuModelRegistry): void { registerMenus(registry: MenuModelRegistry): void {
registry.registerMenuAction(ArduinoMenus.TOOLS__FIRMWARE_UPLOADER_GROUP, { registry.registerMenuAction(ArduinoMenus.TOOLS__FIRMWARE_UPLOADER_GROUP, {
commandId: UploadFirmware.Commands.OPEN.id, commandId: UploadFirmware.Commands.OPEN.id,
label: UploadFirmware.Commands.OPEN.label, label: UploadFirmware.Commands.OPEN.label,

View File

@@ -1,4 +1,4 @@
import { inject, injectable } from '@theia/core/shared/inversify'; import { inject, injectable, postConstruct } from 'inversify';
import { Emitter } from '@theia/core/lib/common/event'; import { Emitter } from '@theia/core/lib/common/event';
import { BoardUserField, CoreService } from '../../common/protocol'; import { BoardUserField, CoreService } from '../../common/protocol';
import { ArduinoMenus, PlaceholderMenuNode } from '../menu/arduino-menus'; import { ArduinoMenus, PlaceholderMenuNode } from '../menu/arduino-menus';
@@ -6,7 +6,7 @@ import { ArduinoToolbar } from '../toolbar/arduino-toolbar';
import { BoardsDataStore } from '../boards/boards-data-store'; import { BoardsDataStore } from '../boards/boards-data-store';
import { BoardsServiceProvider } from '../boards/boards-service-provider'; import { BoardsServiceProvider } from '../boards/boards-service-provider';
import { import {
CoreServiceContribution, SketchContribution,
Command, Command,
CommandRegistry, CommandRegistry,
MenuModelRegistry, MenuModelRegistry,
@@ -15,10 +15,12 @@ import {
} from './contribution'; } from './contribution';
import { UserFieldsDialog } from '../dialogs/user-fields/user-fields-dialog'; import { UserFieldsDialog } from '../dialogs/user-fields/user-fields-dialog';
import { DisposableCollection, nls } from '@theia/core/lib/common'; import { DisposableCollection, nls } from '@theia/core/lib/common';
import { CurrentSketch } from '../../common/protocol/sketches-service-client-impl';
@injectable() @injectable()
export class UploadSketch extends CoreServiceContribution { export class UploadSketch extends SketchContribution {
@inject(CoreService)
protected readonly coreService: CoreService;
@inject(MenuModelRegistry) @inject(MenuModelRegistry)
protected readonly menuRegistry: MenuModelRegistry; protected readonly menuRegistry: MenuModelRegistry;
@@ -41,8 +43,8 @@ export class UploadSketch extends CoreServiceContribution {
protected readonly menuActionsDisposables = new DisposableCollection(); protected readonly menuActionsDisposables = new DisposableCollection();
protected override init(): void { @postConstruct()
super.init(); protected init(): void {
this.boardsServiceClientImpl.onBoardsConfigChanged(async () => { this.boardsServiceClientImpl.onBoardsConfigChanged(async () => {
const userFields = const userFields =
await this.boardsServiceClientImpl.selectedBoardUserFields(); await this.boardsServiceClientImpl.selectedBoardUserFields();
@@ -66,7 +68,7 @@ export class UploadSketch extends CoreServiceContribution {
return fqbn + '|' + address; return fqbn + '|' + address;
} }
override registerCommands(registry: CommandRegistry): void { registerCommands(registry: CommandRegistry): void {
registry.registerCommand(UploadSketch.Commands.UPLOAD_SKETCH, { registry.registerCommand(UploadSketch.Commands.UPLOAD_SKETCH, {
execute: async () => { execute: async () => {
const key = this.selectedFqbnAddress(); const key = this.selectedFqbnAddress();
@@ -128,7 +130,7 @@ export class UploadSketch extends CoreServiceContribution {
}); });
} }
override registerMenus(registry: MenuModelRegistry): void { registerMenus(registry: MenuModelRegistry): void {
this.menuActionsDisposables.dispose(); this.menuActionsDisposables.dispose();
this.menuActionsDisposables.push( this.menuActionsDisposables.push(
@@ -171,7 +173,7 @@ export class UploadSketch extends CoreServiceContribution {
); );
} }
override registerKeybindings(registry: KeybindingRegistry): void { registerKeybindings(registry: KeybindingRegistry): void {
registry.registerKeybinding({ registry.registerKeybinding({
command: UploadSketch.Commands.UPLOAD_SKETCH.id, command: UploadSketch.Commands.UPLOAD_SKETCH.id,
keybinding: 'CtrlCmd+U', keybinding: 'CtrlCmd+U',
@@ -182,7 +184,7 @@ export class UploadSketch extends CoreServiceContribution {
}); });
} }
override registerToolbarItems(registry: TabBarToolbarRegistry): void { registerToolbarItems(registry: TabBarToolbarRegistry): void {
registry.registerItem({ registry.registerItem({
id: UploadSketch.Commands.UPLOAD_SKETCH_TOOLBAR.id, id: UploadSketch.Commands.UPLOAD_SKETCH_TOOLBAR.id,
command: UploadSketch.Commands.UPLOAD_SKETCH_TOOLBAR.id, command: UploadSketch.Commands.UPLOAD_SKETCH_TOOLBAR.id,
@@ -198,17 +200,16 @@ export class UploadSketch extends CoreServiceContribution {
return; return;
} }
// toggle the toolbar button and menu item state.
// uploadInProgress will be set to false whether the upload fails or not
this.uploadInProgress = true;
this.onDidChangeEmitter.fire();
const sketch = await this.sketchServiceClient.currentSketch(); const sketch = await this.sketchServiceClient.currentSketch();
if (!CurrentSketch.isValid(sketch)) { if (!sketch) {
return; return;
} }
try { try {
// toggle the toolbar button and menu item state.
// uploadInProgress will be set to false whether the upload fails or not
this.uploadInProgress = true;
this.coreErrorHandler.reset();
this.onDidChangeEmitter.fire();
const { boardsConfig } = this.boardsServiceClientImpl; const { boardsConfig } = this.boardsServiceClientImpl;
const [fqbn, { selectedProgrammer }, verify, verbose, sourceOverride] = const [fqbn, { selectedProgrammer }, verify, verbose, sourceOverride] =
await Promise.all([ await Promise.all([
@@ -225,8 +226,9 @@ export class UploadSketch extends CoreServiceContribution {
...boardsConfig.selectedBoard, ...boardsConfig.selectedBoard,
name: boardsConfig.selectedBoard?.name || '', name: boardsConfig.selectedBoard?.name || '',
fqbn, fqbn,
}; }
let options: CoreService.Upload.Options | undefined = undefined; let options: CoreService.Upload.Options | undefined = undefined;
const sketchUri = sketch.uri;
const optimizeForDebug = this.editorMode.compileForDebug; const optimizeForDebug = this.editorMode.compileForDebug;
const { selectedPort } = boardsConfig; const { selectedPort } = boardsConfig;
const port = selectedPort; const port = selectedPort;
@@ -245,7 +247,7 @@ export class UploadSketch extends CoreServiceContribution {
if (usingProgrammer) { if (usingProgrammer) {
const programmer = selectedProgrammer; const programmer = selectedProgrammer;
options = { options = {
sketch, sketchUri,
board, board,
optimizeForDebug, optimizeForDebug,
programmer, programmer,
@@ -257,7 +259,7 @@ export class UploadSketch extends CoreServiceContribution {
}; };
} else { } else {
options = { options = {
sketch, sketchUri,
board, board,
optimizeForDebug, optimizeForDebug,
port, port,
@@ -278,7 +280,13 @@ export class UploadSketch extends CoreServiceContribution {
{ timeout: 3000 } { timeout: 3000 }
); );
} catch (e) { } catch (e) {
this.handleError(e); let errorMessage = '';
if (typeof e === 'string') {
errorMessage = e;
} else {
errorMessage = e.toString();
}
this.messageService.error(errorMessage);
} finally { } finally {
this.uploadInProgress = false; this.uploadInProgress = false;
this.onDidChangeEmitter.fire(); this.onDidChangeEmitter.fire();

View File

@@ -1,11 +1,12 @@
import { inject, injectable } from '@theia/core/shared/inversify'; import { inject, injectable } from 'inversify';
import { Emitter } from '@theia/core/lib/common/event'; import { Emitter } from '@theia/core/lib/common/event';
import { CoreService } from '../../common/protocol';
import { ArduinoMenus } from '../menu/arduino-menus'; import { ArduinoMenus } from '../menu/arduino-menus';
import { ArduinoToolbar } from '../toolbar/arduino-toolbar'; import { ArduinoToolbar } from '../toolbar/arduino-toolbar';
import { BoardsDataStore } from '../boards/boards-data-store'; import { BoardsDataStore } from '../boards/boards-data-store';
import { BoardsServiceProvider } from '../boards/boards-service-provider'; import { BoardsServiceProvider } from '../boards/boards-service-provider';
import { import {
CoreServiceContribution, SketchContribution,
Command, Command,
CommandRegistry, CommandRegistry,
MenuModelRegistry, MenuModelRegistry,
@@ -13,10 +14,12 @@ import {
TabBarToolbarRegistry, TabBarToolbarRegistry,
} from './contribution'; } from './contribution';
import { nls } from '@theia/core/lib/common'; import { nls } from '@theia/core/lib/common';
import { CurrentSketch } from '../../common/protocol/sketches-service-client-impl';
@injectable() @injectable()
export class VerifySketch extends CoreServiceContribution { export class VerifySketch extends SketchContribution {
@inject(CoreService)
protected readonly coreService: CoreService;
@inject(BoardsDataStore) @inject(BoardsDataStore)
protected readonly boardsDataStore: BoardsDataStore; protected readonly boardsDataStore: BoardsDataStore;
@@ -28,7 +31,7 @@ export class VerifySketch extends CoreServiceContribution {
protected verifyInProgress = false; protected verifyInProgress = false;
override registerCommands(registry: CommandRegistry): void { registerCommands(registry: CommandRegistry): void {
registry.registerCommand(VerifySketch.Commands.VERIFY_SKETCH, { registry.registerCommand(VerifySketch.Commands.VERIFY_SKETCH, {
execute: () => this.verifySketch(), execute: () => this.verifySketch(),
isEnabled: () => !this.verifyInProgress, isEnabled: () => !this.verifyInProgress,
@@ -47,7 +50,7 @@ export class VerifySketch extends CoreServiceContribution {
}); });
} }
override registerMenus(registry: MenuModelRegistry): void { registerMenus(registry: MenuModelRegistry): void {
registry.registerMenuAction(ArduinoMenus.SKETCH__MAIN_GROUP, { registry.registerMenuAction(ArduinoMenus.SKETCH__MAIN_GROUP, {
commandId: VerifySketch.Commands.VERIFY_SKETCH.id, commandId: VerifySketch.Commands.VERIFY_SKETCH.id,
label: nls.localize('arduino/sketch/verifyOrCompile', 'Verify/Compile'), label: nls.localize('arduino/sketch/verifyOrCompile', 'Verify/Compile'),
@@ -63,7 +66,7 @@ export class VerifySketch extends CoreServiceContribution {
}); });
} }
override registerKeybindings(registry: KeybindingRegistry): void { registerKeybindings(registry: KeybindingRegistry): void {
registry.registerKeybinding({ registry.registerKeybinding({
command: VerifySketch.Commands.VERIFY_SKETCH.id, command: VerifySketch.Commands.VERIFY_SKETCH.id,
keybinding: 'CtrlCmd+R', keybinding: 'CtrlCmd+R',
@@ -74,7 +77,7 @@ export class VerifySketch extends CoreServiceContribution {
}); });
} }
override registerToolbarItems(registry: TabBarToolbarRegistry): void { registerToolbarItems(registry: TabBarToolbarRegistry): void {
registry.registerItem({ registry.registerItem({
id: VerifySketch.Commands.VERIFY_SKETCH_TOOLBAR.id, id: VerifySketch.Commands.VERIFY_SKETCH_TOOLBAR.id,
command: VerifySketch.Commands.VERIFY_SKETCH_TOOLBAR.id, command: VerifySketch.Commands.VERIFY_SKETCH_TOOLBAR.id,
@@ -92,14 +95,14 @@ export class VerifySketch extends CoreServiceContribution {
// toggle the toolbar button and menu item state. // toggle the toolbar button and menu item state.
// verifyInProgress will be set to false whether the compilation fails or not // verifyInProgress will be set to false whether the compilation fails or not
this.verifyInProgress = true;
this.onDidChangeEmitter.fire();
const sketch = await this.sketchServiceClient.currentSketch(); const sketch = await this.sketchServiceClient.currentSketch();
if (!CurrentSketch.isValid(sketch)) {
if (!sketch) {
return; return;
} }
try { try {
this.verifyInProgress = true;
this.coreErrorHandler.reset();
this.onDidChangeEmitter.fire();
const { boardsConfig } = this.boardsServiceClientImpl; const { boardsConfig } = this.boardsServiceClientImpl;
const [fqbn, sourceOverride] = await Promise.all([ const [fqbn, sourceOverride] = await Promise.all([
this.boardsDataStore.appendConfigToFqbn( this.boardsDataStore.appendConfigToFqbn(
@@ -111,12 +114,12 @@ export class VerifySketch extends CoreServiceContribution {
...boardsConfig.selectedBoard, ...boardsConfig.selectedBoard,
name: boardsConfig.selectedBoard?.name || '', name: boardsConfig.selectedBoard?.name || '',
fqbn, fqbn,
}; }
const verbose = this.preferences.get('arduino.compile.verbose'); const verbose = this.preferences.get('arduino.compile.verbose');
const compilerWarnings = this.preferences.get('arduino.compile.warnings'); const compilerWarnings = this.preferences.get('arduino.compile.warnings');
this.outputChannelManager.getChannel('Arduino').clear(); this.outputChannelManager.getChannel('Arduino').clear();
await this.coreService.compile({ await this.coreService.compile({
sketch, sketchUri: sketch.uri,
board, board,
optimizeForDebug: this.editorMode.compileForDebug, optimizeForDebug: this.editorMode.compileForDebug,
verbose, verbose,
@@ -129,7 +132,13 @@ export class VerifySketch extends CoreServiceContribution {
{ timeout: 3000 } { timeout: 3000 }
); );
} catch (e) { } catch (e) {
this.handleError(e); let errorMessage = "";
if (typeof e === "string") {
errorMessage = e;
} else {
errorMessage = e.toString();
}
this.messageService.error(errorMessage);
} finally { } finally {
this.verifyInProgress = false; this.verifyInProgress = false;
this.onDidChangeEmitter.fire(); this.onDidChangeEmitter.fire();

View File

@@ -1,4 +1,4 @@
import { injectable, inject } from '@theia/core/shared/inversify'; import { injectable, inject } from 'inversify';
import * as createPaths from './create-paths'; import * as createPaths from './create-paths';
import { posix } from './create-paths'; import { posix } from './create-paths';
import { AuthenticationClientService } from '../auth/authentication-client-service'; import { AuthenticationClientService } from '../auth/authentication-client-service';
@@ -117,11 +117,11 @@ export class CreateApi {
headers, headers,
}) })
).sketches; ).sketches;
if (partialSketches.length !== 0) { if (partialSketches.length != 0) {
result.sketches = result.sketches.concat(partialSketches); result.sketches = result.sketches.concat(partialSketches);
} }
currentOffset = currentOffset + limit; currentOffset = currentOffset + limit;
} while (partialSketches.length !== 0); } while (partialSketches.length != 0);
result.sketches.forEach((sketch) => this.sketchCache.addSketch(sketch)); result.sketches.forEach((sketch) => this.sketchCache.addSketch(sketch));
return result.sketches; return result.sketches;

View File

@@ -1,4 +1,4 @@
import { inject, injectable } from '@theia/core/shared/inversify'; import { inject, injectable } from 'inversify';
import URI from '@theia/core/lib/common/uri'; import URI from '@theia/core/lib/common/uri';
import { Event } from '@theia/core/lib/common/event'; import { Event } from '@theia/core/lib/common/event';
import { import {

View File

@@ -1,149 +0,0 @@
{
"name": "Arduino dark",
"type": "dark",
"colors": {
"list.highlightForeground": "#0ca1a6",
"list.activeSelectionForeground": "#dae3e3",
"list.activeSelectionBackground": "#434f54",
"list.inactiveSelectionForeground": "#dae3e3",
"list.inactiveSelectionBackground": "#434f54",
"list.hoverBackground": "#1f272a",
"progressBar.background": "#005c5f",
"editor.background": "#1f272a",
"editor.foreground": "#dae3e3",
"editor.lineHighlightBackground": "#434f5410",
"editor.selectionBackground": "#f1c40f",
"editorCursor.foreground": "#434f54",
"editorWhitespace.foreground": "#bfbfbf",
"editorWidget.background": "#171e21",
"focusBorder": "#dae3e3",
"menubar.selectionBackground": "#ffffff",
"menubar.selectionForeground": "#212121",
"menu.selectionBackground": "#dae3e3",
"menu.selectionForeground": "#212121",
"editorGroupHeader.tabsBackground": "#171e21",
"button.background": "#0ca1a6",
"titleBar.activeBackground": "#171e21",
"titleBar.activeForeground": "#dae3e3",
"terminal.background": "#000000",
"terminal.foreground": "#e0e0e0",
"dropdown.border": "#7fcbcd",
"dropdown.background": "#2c353a",
"dropdown.foreground": "#dae3e3",
"activityBar.background": "#171e21",
"activityBar.foreground": "#dae3e3",
"activityBar.inactiveForeground": "#4e5b61",
"activityBar.activeBorder": "#0ca1a6",
"statusBar.background": "#171e21",
"secondaryButton.background": "#ff000000",
"secondaryButton.foreground": "#dae3e3",
"secondaryButton.hoverBackground": "#434f54",
"arduino.branding.primary": "#0ca1a6",
"arduino.branding.secondary": "#b5c8c9",
"arduino.foreground": "#edf1f1",
"arduino.output.foreground": "#ffffff",
"arduino.output.background": "#000000",
"arduino.toolbar.hoverBackground": "#dae3e3",
"sideBar.background": "#101618",
"input.background": "#000000",
"foreground": "#dae3e3",
"settings.headerForeground": "#dae3e3",
"tree.indentGuidesStroke": "#374146",
"tab.unfocusedActiveForeground": "#dae3e3",
"tab.inactiveBackground": "#171e21"
},
"tokenColors": [
{
"name": "",
"settings": {
"foreground": "#dae3e3"
}
},
{
"name": "Comments",
"scope": "comment",
"settings": {
"foreground": "#7f8c8d"
}
},
{
"name": "Keywords Attributes",
"scope": [
"storage",
"support",
"string.quoted.single.c"
],
"settings": {
"foreground": "#0ca1a6"
}
},
{
"name": "literal",
"scope": [
"meta.function.c",
"entity.name.function",
"meta.function-call.c",
"variable.other"
],
"settings": {
"foreground": "#F39C12"
}
},
{
"name": "punctuation",
"scope": [
"punctuation.section",
"meta.function-call.c",
"meta.block.c",
"meta.function.c",
"variable",
"variable.name"
],
"settings": {
"foreground": "#dae3e3"
}
},
{
"name": "function preprocessor",
"scope": [
"entity.name.function.preprocessor.c",
"meta.preprocessor.macro.c"
],
"settings": {
"foreground": "#569CD6"
}
},
{
"name": "constants",
"scope": [
"string.quoted.double",
"string.quoted.other.lt-gt",
"constant"
],
"settings": {
"foreground": "#7fcbcd"
}
},
{
"name": "meta keywords",
"scope": [
"keyword.control",
"meta.preprocessor.c"
],
"settings": {
"foreground": "#C586C0"
}
},
{
"name": "numeric preprocessor",
"scope": [
"meta.preprocessor.macro.c",
"constant.numeric.preprocessor.c",
"meta.preprocessor.c"
],
"settings": {
"foreground": "#434f54"
}
}
]
}

View File

@@ -1,149 +0,0 @@
{
"name": "Arduino default",
"type": "default",
"colors": {
"list.highlightForeground": "#008184",
"list.activeSelectionForeground": "#4e5b61",
"list.activeSelectionBackground": "#dae3e3",
"list.inactiveSelectionForeground": "#4e5b61",
"list.inactiveSelectionBackground": "#dae3e3",
"list.hoverBackground": "#ecf1f1",
"progressBar.background": "#005c5f",
"editor.background": "#ffffff",
"editor.foreground": "#4e5b61",
"editor.lineHighlightBackground": "#434f5410",
"editor.selectionBackground": "#f1c40f",
"editorCursor.foreground": "#434f54",
"editorWhitespace.foreground": "#bfbfbf",
"editorWidget.background": "#f7f9f9",
"focusBorder": "#7fcbcd",
"menubar.selectionBackground": "#ffffff",
"menubar.selectionForeground": "#212121",
"menu.selectionBackground": "#dae3e3",
"menu.selectionForeground": "#212121",
"editorGroupHeader.tabsBackground": "#ecf1f1",
"button.background": "#7fcbcd",
"titleBar.activeBackground": "#006d70",
"titleBar.activeForeground": "#f7f9f9",
"terminal.background": "#000000",
"terminal.foreground": "#e0e0e0",
"dropdown.border": "#f7f9f9",
"dropdown.background": "#ffffff",
"dropdown.foreground": "#4e5b61",
"activityBar.background": "#ecf1f1",
"activityBar.foreground": "#4e5b61",
"activityBar.inactiveForeground": "#bdc7c7",
"activityBar.activeBorder": "#008184",
"statusBar.background": "#006d70",
"secondaryButton.background": "#ff000000",
"secondaryButton.foreground": "#008184",
"secondaryButton.hoverBackground": "#dae3e3",
"arduino.branding.primary": "#008184",
"arduino.branding.secondary": "#b5c8c9",
"arduino.foreground": "#edf1f1",
"arduino.output.foreground": "#ffffff",
"arduino.output.background": "#000000",
"arduino.toolbar.hoverBackground": "#f7f9f9",
"sideBar.background": "#f7f9f9",
"input.background": "#ffffff",
"foreground": "#4e5b61",
"settings.headerForeground": "#4e5b61",
"tree.indentGuidesStroke": "#dae3e3",
"tab.unfocusedActiveForeground": "#4e5b61",
"tab.inactiveBackground": "#ecf1f1"
},
"tokenColors": [
{
"name": "",
"settings": {
"foreground": "#434f54"
}
},
{
"name": "Comments",
"scope": "comment",
"settings": {
"foreground": "#95a5a6cc"
}
},
{
"name": "Keywords Attributes",
"scope": [
"storage",
"support",
"string.quoted.single.c"
],
"settings": {
"foreground": "#00979D"
}
},
{
"name": "literal",
"scope": [
"meta.function.c",
"entity.name.function",
"meta.function-call.c",
"variable.other"
],
"settings": {
"foreground": "#D35400"
}
},
{
"name": "punctuation",
"scope": [
"punctuation.section",
"meta.function-call.c",
"meta.block.c",
"meta.function.c",
"variable",
"variable.name"
],
"settings": {
"foreground": "#434f54"
}
},
{
"name": "function preprocessor",
"scope": [
"entity.name.function.preprocessor.c",
"meta.preprocessor.macro.c"
],
"settings": {
"foreground": "#9e846d"
}
},
{
"name": "constants",
"scope": [
"string.quoted.double",
"string.quoted.other.lt-gt",
"constant"
],
"settings": {
"foreground": "#005C5F"
}
},
{
"name": "meta keywords",
"scope": [
"keyword.control",
"meta.preprocessor.c"
],
"settings": {
"foreground": "#728E00"
}
},
{
"name": "numeric preprocessor",
"scope": [
"meta.preprocessor.macro.c",
"constant.numeric.preprocessor.c",
"meta.preprocessor.c"
],
"settings": {
"foreground": "#434f54"
}
}
]
}

View File

@@ -1,5 +1,5 @@
import { nls } from '@theia/core/lib/common'; import { nls } from '@theia/core/lib/common';
import * as React from '@theia/core/shared/react'; import * as React from 'react';
export const CertificateAddComponent = ({ export const CertificateAddComponent = ({
addCertificate, addCertificate,
@@ -8,12 +8,9 @@ export const CertificateAddComponent = ({
}): React.ReactElement => { }): React.ReactElement => {
const [value, setValue] = React.useState(''); const [value, setValue] = React.useState('');
const handleChange = React.useCallback( const handleChange = React.useCallback((event) => {
(event: React.ChangeEvent<HTMLInputElement>) => { setValue(event.target.value);
setValue(event.target.value); }, []);
},
[]
);
return ( return (
<form <form

View File

@@ -1,4 +1,4 @@
import * as React from '@theia/core/shared/react'; import * as React from 'react';
export const CertificateListComponent = ({ export const CertificateListComponent = ({
certificates, certificates,

View File

@@ -1,4 +1,4 @@
import * as React from '@theia/core/shared/react'; import * as React from 'react';
import Tippy from '@tippyjs/react'; import Tippy from '@tippyjs/react';
import { AvailableBoard } from '../../boards/boards-service-provider'; import { AvailableBoard } from '../../boards/boards-service-provider';
import { CertificateListComponent } from './certificate-list'; import { CertificateListComponent } from './certificate-list';

View File

@@ -1,9 +1,9 @@
import * as React from '@theia/core/shared/react'; import * as React from 'react';
import { inject, injectable, postConstruct } from '@theia/core/shared/inversify'; import { inject, injectable, postConstruct } from 'inversify';
import { DialogProps } from '@theia/core/lib/browser/dialogs'; import { DialogProps } from '@theia/core/lib/browser/dialogs';
import { AbstractDialog } from '../../theia/dialogs/dialogs'; import { AbstractDialog } from '../../theia/dialogs/dialogs';
import { Widget } from '@theia/core/shared/@phosphor/widgets'; import { Widget } from '@phosphor/widgets';
import { Message } from '@theia/core/shared/@phosphor/messaging'; import { Message } from '@phosphor/messaging';
import { ReactWidget } from '@theia/core/lib/browser/widgets/react-widget'; import { ReactWidget } from '@theia/core/lib/browser/widgets/react-widget';
import { import {
AvailableBoard, AvailableBoard,
@@ -139,7 +139,7 @@ export class UploadCertificateDialog extends AbstractDialog<void> {
constructor( constructor(
@inject(UploadCertificateDialogProps) @inject(UploadCertificateDialogProps)
protected override readonly props: UploadCertificateDialogProps protected readonly props: UploadCertificateDialogProps
) { ) {
super({ super({
title: nls.localize( title: nls.localize(
@@ -155,7 +155,7 @@ export class UploadCertificateDialog extends AbstractDialog<void> {
return; return;
} }
protected override onAfterAttach(msg: Message): void { protected onAfterAttach(msg: Message): void {
if (this.widget.isAttached) { if (this.widget.isAttached) {
Widget.detach(this.widget); Widget.detach(this.widget);
} }
@@ -165,21 +165,21 @@ export class UploadCertificateDialog extends AbstractDialog<void> {
this.update(); this.update();
} }
protected override onUpdateRequest(msg: Message): void { protected onUpdateRequest(msg: Message): void {
super.onUpdateRequest(msg); super.onUpdateRequest(msg);
this.widget.update(); this.widget.update();
} }
protected override onActivateRequest(msg: Message): void { protected onActivateRequest(msg: Message): void {
super.onActivateRequest(msg); super.onActivateRequest(msg);
this.widget.activate(); this.widget.activate();
} }
protected override handleEnter(event: KeyboardEvent): boolean | void { protected handleEnter(event: KeyboardEvent): boolean | void {
return false; return false;
} }
override close(): void { close(): void {
if (this.busy) { if (this.busy) {
return; return;
} }

View File

@@ -1,5 +1,5 @@
import { nls } from '@theia/core/lib/common'; import { nls } from '@theia/core/lib/common';
import * as React from '@theia/core/shared/react'; import * as React from 'react';
import { AvailableBoard } from '../../boards/boards-service-provider'; import { AvailableBoard } from '../../boards/boards-service-provider';
import { ArduinoSelect } from '../../widgets/arduino-select'; import { ArduinoSelect } from '../../widgets/arduino-select';

View File

@@ -1,7 +1,7 @@
import * as React from '@theia/core/shared/react'; import * as React from 'react';
import { inject, injectable } from '@theia/core/shared/inversify'; import { inject, injectable } from 'inversify';
import { Widget } from '@theia/core/shared/@phosphor/widgets'; import { Widget } from '@phosphor/widgets';
import { Message } from '@theia/core/shared/@phosphor/messaging'; import { Message } from '@phosphor/messaging';
import { clipboard } from 'electron'; import { clipboard } from 'electron';
import { ReactWidget, DialogProps } from '@theia/core/lib/browser'; import { ReactWidget, DialogProps } from '@theia/core/lib/browser';
import { AbstractDialog } from '../theia/dialogs/dialogs'; import { AbstractDialog } from '../theia/dialogs/dialogs';
@@ -149,7 +149,7 @@ export class ShareSketchDialog extends AbstractDialog<void> {
constructor( constructor(
@inject(ShareSketchDialogProps) @inject(ShareSketchDialogProps)
protected override readonly props: ShareSketchDialogProps protected readonly props: ShareSketchDialogProps
) { ) {
super({ title: props.title }); super({ title: props.title });
this.contentNode.classList.add('arduino-share-sketch-dialog'); this.contentNode.classList.add('arduino-share-sketch-dialog');
@@ -159,7 +159,7 @@ export class ShareSketchDialog extends AbstractDialog<void> {
get value(): void { get value(): void {
return; return;
} }
protected override onAfterAttach(msg: Message): void { protected onAfterAttach(msg: Message): void {
if (this.widget.isAttached) { if (this.widget.isAttached) {
Widget.detach(this.widget); Widget.detach(this.widget);
} }
@@ -168,12 +168,12 @@ export class ShareSketchDialog extends AbstractDialog<void> {
this.update(); this.update();
} }
protected override onUpdateRequest(msg: Message): void { protected onUpdateRequest(msg: Message): void {
super.onUpdateRequest(msg); super.onUpdateRequest(msg);
this.widget.update(); this.widget.update();
} }
protected override onActivateRequest(msg: Message): void { protected onActivateRequest(msg: Message): void {
super.onActivateRequest(msg); super.onActivateRequest(msg);
this.widget.activate(); this.widget.activate();
} }

View File

@@ -1,5 +1,5 @@
import { inject, injectable } from '@theia/core/shared/inversify'; import { inject, injectable } from 'inversify';
import { Widget } from '@theia/core/shared/@phosphor/widgets'; import { Widget } from '@phosphor/widgets';
import { CancellationTokenSource } from '@theia/core/lib/common/cancellation'; import { CancellationTokenSource } from '@theia/core/lib/common/cancellation';
import { import {
ConfirmDialog, ConfirmDialog,
@@ -19,7 +19,7 @@ export class DoNotAskAgainConfirmDialog extends ConfirmDialog {
constructor( constructor(
@inject(DoNotAskAgainDialogProps) @inject(DoNotAskAgainDialogProps)
protected override readonly props: DoNotAskAgainDialogProps protected readonly props: DoNotAskAgainDialogProps
) { ) {
super(props); super(props);
this.controlPanel.removeChild(this.errorMessageNode); this.controlPanel.removeChild(this.errorMessageNode);
@@ -42,7 +42,7 @@ export class DoNotAskAgainConfirmDialog extends ConfirmDialog {
this.doNotAskAgainCheckbox.type = 'checkbox'; this.doNotAskAgainCheckbox.type = 'checkbox';
} }
protected override async accept(): Promise<void> { protected async accept(): Promise<void> {
if (!this.resolve) { if (!this.resolve) {
return; return;
} }
@@ -65,7 +65,7 @@ export class DoNotAskAgainConfirmDialog extends ConfirmDialog {
} }
} }
protected override setErrorMessage(error: DialogError): void { protected setErrorMessage(error: DialogError): void {
if (this.acceptButton) { if (this.acceptButton) {
this.acceptButton.disabled = !DialogError.getResult(error); this.acceptButton.disabled = !DialogError.getResult(error);
} }

View File

@@ -1,5 +1,5 @@
import { nls } from '@theia/core/lib/common'; import { nls } from '@theia/core/lib/common';
import * as React from '@theia/core/shared/react'; import * as React from 'react';
import { Port } from '../../../common/protocol'; import { Port } from '../../../common/protocol';
import { import {
ArduinoFirmwareUploader, ArduinoFirmwareUploader,

View File

@@ -1,13 +1,9 @@
import * as React from '@theia/core/shared/react'; import * as React from 'react';
import { import { inject, injectable, postConstruct } from 'inversify';
inject,
injectable,
postConstruct,
} from '@theia/core/shared/inversify';
import { DialogProps } from '@theia/core/lib/browser/dialogs'; import { DialogProps } from '@theia/core/lib/browser/dialogs';
import { AbstractDialog } from '../../theia/dialogs/dialogs'; import { AbstractDialog } from '../../theia/dialogs/dialogs';
import { Widget } from '@theia/core/shared/@phosphor/widgets'; import { Widget } from '@phosphor/widgets';
import { Message } from '@theia/core/shared/@phosphor/messaging'; import { Message } from '@phosphor/messaging';
import { ReactWidget } from '@theia/core/lib/browser/widgets/react-widget'; import { ReactWidget } from '@theia/core/lib/browser/widgets/react-widget';
import { import {
AvailableBoard, AvailableBoard,
@@ -20,7 +16,6 @@ import {
import { FirmwareUploaderComponent } from './firmware-uploader-component'; import { FirmwareUploaderComponent } from './firmware-uploader-component';
import { UploadFirmware } from '../../contributions/upload-firmware'; import { UploadFirmware } from '../../contributions/upload-firmware';
import { Port } from '../../../common/protocol'; import { Port } from '../../../common/protocol';
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
@injectable() @injectable()
export class UploadFirmwareDialogWidget extends ReactWidget { export class UploadFirmwareDialogWidget extends ReactWidget {
@@ -30,9 +25,6 @@ export class UploadFirmwareDialogWidget extends ReactWidget {
@inject(ArduinoFirmwareUploader) @inject(ArduinoFirmwareUploader)
protected readonly arduinoFirmwareUploader: ArduinoFirmwareUploader; protected readonly arduinoFirmwareUploader: ArduinoFirmwareUploader;
@inject(FrontendApplicationStateService)
private readonly appStatusService: FrontendApplicationStateService;
protected updatableFqbns: string[] = []; protected updatableFqbns: string[] = [];
protected availableBoards: AvailableBoard[] = []; protected availableBoards: AvailableBoard[] = [];
protected isOpen = new Object(); protected isOpen = new Object();
@@ -47,8 +39,7 @@ export class UploadFirmwareDialogWidget extends ReactWidget {
@postConstruct() @postConstruct()
protected init(): void { protected init(): void {
this.appStatusService.reachedState('ready').then(async () => { this.arduinoFirmwareUploader.updatableBoards().then((fqbns) => {
const fqbns = await this.arduinoFirmwareUploader.updatableBoards();
this.updatableFqbns = fqbns; this.updatableFqbns = fqbns;
this.update(); this.update();
}); });
@@ -66,7 +57,7 @@ export class UploadFirmwareDialogWidget extends ReactWidget {
.finally(() => this.busyCallback(false)); .finally(() => this.busyCallback(false));
} }
protected override onCloseRequest(msg: Message): void { onCloseRequest(msg: Message): void {
super.onCloseRequest(msg); super.onCloseRequest(msg);
this.isOpen = new Object(); this.isOpen = new Object();
} }
@@ -98,7 +89,7 @@ export class UploadFirmwareDialog extends AbstractDialog<void> {
constructor( constructor(
@inject(UploadFirmwareDialogProps) @inject(UploadFirmwareDialogProps)
protected override readonly props: UploadFirmwareDialogProps protected readonly props: UploadFirmwareDialogProps
) { ) {
super({ title: UploadFirmware.Commands.OPEN.label || '' }); super({ title: UploadFirmware.Commands.OPEN.label || '' });
this.contentNode.classList.add('firmware-uploader-dialog'); this.contentNode.classList.add('firmware-uploader-dialog');
@@ -109,7 +100,7 @@ export class UploadFirmwareDialog extends AbstractDialog<void> {
return; return;
} }
protected override onAfterAttach(msg: Message): void { protected onAfterAttach(msg: Message): void {
if (this.widget.isAttached) { if (this.widget.isAttached) {
Widget.detach(this.widget); Widget.detach(this.widget);
} }
@@ -119,21 +110,21 @@ export class UploadFirmwareDialog extends AbstractDialog<void> {
this.update(); this.update();
} }
protected override onUpdateRequest(msg: Message): void { protected onUpdateRequest(msg: Message): void {
super.onUpdateRequest(msg); super.onUpdateRequest(msg);
this.widget.update(); this.widget.update();
} }
protected override onActivateRequest(msg: Message): void { protected onActivateRequest(msg: Message): void {
super.onActivateRequest(msg); super.onActivateRequest(msg);
this.widget.activate(); this.widget.activate();
} }
protected override handleEnter(event: KeyboardEvent): boolean | void { protected handleEnter(event: KeyboardEvent): boolean | void {
return false; return false;
} }
override close(): void { close(): void {
if (this.busy) { if (this.busy) {
return; return;
} }

View File

@@ -1,8 +1,8 @@
import { WindowService } from '@theia/core/lib/browser/window/window-service'; import { WindowService } from '@theia/core/lib/browser/window/window-service';
import { nls } from '@theia/core/lib/common'; import { nls } from '@theia/core/lib/common';
import { shell } from 'electron'; import { shell } from 'electron';
import * as React from '@theia/core/shared/react'; import * as React from 'react';
import * as ReactDOM from '@theia/core/shared/react-dom'; import * as ReactDOM from 'react-dom';
import ReactMarkdown from 'react-markdown'; import ReactMarkdown from 'react-markdown';
import { ProgressInfo, UpdateInfo } from '../../../common/protocol/ide-updater'; import { ProgressInfo, UpdateInfo } from '../../../common/protocol/ide-updater';
import ProgressBar from '../../components/ProgressBar'; import ProgressBar from '../../components/ProgressBar';

View File

@@ -1,9 +1,9 @@
import * as React from '@theia/core/shared/react'; import * as React from 'react';
import { inject, injectable } from '@theia/core/shared/inversify'; import { inject, injectable } from 'inversify';
import { DialogProps } from '@theia/core/lib/browser/dialogs'; import { DialogProps } from '@theia/core/lib/browser/dialogs';
import { AbstractDialog } from '../../theia/dialogs/dialogs'; import { AbstractDialog } from '../../theia/dialogs/dialogs';
import { Widget } from '@theia/core/shared/@phosphor/widgets'; import { Widget } from '@phosphor/widgets';
import { Message } from '@theia/core/shared/@phosphor/messaging'; import { Message } from '@phosphor/messaging';
import { ReactWidget } from '@theia/core/lib/browser/widgets/react-widget'; import { ReactWidget } from '@theia/core/lib/browser/widgets/react-widget';
import { nls } from '@theia/core'; import { nls } from '@theia/core';
import { IDEUpdaterComponent } from './ide-updater-component'; import { IDEUpdaterComponent } from './ide-updater-component';
@@ -70,7 +70,7 @@ export class IDEUpdaterDialogWidget extends ReactWidget {
this.close(); this.close();
} }
override close(): void { close(): void {
super.close(); super.close();
this.onClose(); this.onClose();
} }
@@ -122,7 +122,7 @@ export class IDEUpdaterDialog extends AbstractDialog<UpdateInfo> {
constructor( constructor(
@inject(IDEUpdaterDialogProps) @inject(IDEUpdaterDialogProps)
protected override readonly props: IDEUpdaterDialogProps protected readonly props: IDEUpdaterDialogProps
) { ) {
super({ super({
title: nls.localize( title: nls.localize(
@@ -138,7 +138,7 @@ export class IDEUpdaterDialog extends AbstractDialog<UpdateInfo> {
return this.widget.updateInfo; return this.widget.updateInfo;
} }
protected override onAfterAttach(msg: Message): void { protected onAfterAttach(msg: Message): void {
if (this.widget.isAttached) { if (this.widget.isAttached) {
Widget.detach(this.widget); Widget.detach(this.widget);
} }
@@ -147,7 +147,7 @@ export class IDEUpdaterDialog extends AbstractDialog<UpdateInfo> {
this.update(); this.update();
} }
override async open( async open(
data: UpdateInfo | undefined = undefined data: UpdateInfo | undefined = undefined
): Promise<UpdateInfo | undefined> { ): Promise<UpdateInfo | undefined> {
if (data && data.version) { if (data && data.version) {
@@ -156,17 +156,17 @@ export class IDEUpdaterDialog extends AbstractDialog<UpdateInfo> {
} }
} }
protected override onUpdateRequest(msg: Message): void { protected onUpdateRequest(msg: Message): void {
super.onUpdateRequest(msg); super.onUpdateRequest(msg);
this.widget.update(); this.widget.update();
} }
protected override onActivateRequest(msg: Message): void { protected onActivateRequest(msg: Message): void {
super.onActivateRequest(msg); super.onActivateRequest(msg);
this.widget.activate(); this.widget.activate();
} }
override close(): void { close(): void {
this.widget.dispose(); this.widget.dispose();
super.close(); super.close();
} }

View File

@@ -1,4 +1,4 @@
import * as React from '@theia/core/shared/react'; import * as React from 'react';
import { Tab, Tabs, TabList, TabPanel } from 'react-tabs'; import { Tab, Tabs, TabList, TabPanel } from 'react-tabs';
import 'react-tabs/style/react-tabs.css'; import 'react-tabs/style/react-tabs.css';
import { Disable } from 'react-disable'; import { Disable } from 'react-disable';
@@ -17,10 +17,7 @@ import {
import { nls } from '@theia/core/lib/common'; import { nls } from '@theia/core/lib/common';
import { Settings, SettingsService } from './settings'; import { Settings, SettingsService } from './settings';
import { AdditionalUrlsDialog } from './settings-dialog'; import { AdditionalUrlsDialog } from './settings-dialog';
import { import { AsyncLocalizationProvider } from '@theia/core/lib/common/i18n/localization';
AsyncLocalizationProvider,
LanguageInfo,
} from '@theia/core/lib/common/i18n/localization';
export class SettingsComponent extends React.Component< export class SettingsComponent extends React.Component<
SettingsComponent.Props, SettingsComponent.Props,
@@ -32,7 +29,7 @@ export class SettingsComponent extends React.Component<
super(props); super(props);
} }
override componentDidUpdate( componentDidUpdate(
_: SettingsComponent.Props, _: SettingsComponent.Props,
prevState: SettingsComponent.State prevState: SettingsComponent.State
): void { ): void {
@@ -49,7 +46,7 @@ export class SettingsComponent extends React.Component<
} }
} }
override componentDidMount(): void { componentDidMount(): void {
this.props.settingsService this.props.settingsService
.settings() .settings()
.then((settings) => .then((settings) =>
@@ -67,11 +64,11 @@ export class SettingsComponent extends React.Component<
]); ]);
} }
override componentWillUnmount(): void { componentWillUnmount(): void {
this.toDispose.dispose(); this.toDispose.dispose();
} }
override render(): React.ReactNode { render(): React.ReactNode {
if (!this.state) { if (!this.state) {
return <div />; return <div />;
} }
@@ -216,9 +213,11 @@ export class SettingsComponent extends React.Component<
value={this.state.currentLanguage} value={this.state.currentLanguage}
onChange={this.languageDidChange} onChange={this.languageDidChange}
> >
{this.state.languages.map((label) => {this.state.languages.map((label) => (
this.toSelectOptions(label) <option key={label} value={label}>
)} {label}
</option>
))}
</select> </select>
<span style={{ marginLeft: '5px' }}> <span style={{ marginLeft: '5px' }}>
( (
@@ -276,7 +275,7 @@ export class SettingsComponent extends React.Component<
<label className="flex-line"> <label className="flex-line">
<input <input
type="checkbox" type="checkbox"
checked={this.state.autoSave !== 'off'} checked={this.state.autoSave === 'on'}
onChange={this.autoSaveDidChange} onChange={this.autoSaveDidChange}
/> />
{nls.localize( {nls.localize(
@@ -315,24 +314,6 @@ export class SettingsComponent extends React.Component<
); );
} }
private toSelectOptions(language: string | LanguageInfo): JSX.Element {
const plain = typeof language === 'string';
const key = plain ? language : language.languageId;
const value = plain ? language : language.languageId;
const label = plain
? language === 'en'
? 'English'
: language
: language.localizedLanguageName ||
language.languageName ||
language.languageId;
return (
<option key={key} value={value}>
{label}
</option>
);
}
protected renderNetwork(): React.ReactNode { protected renderNetwork(): React.ReactNode {
return ( return (
<div className="content noselect"> <div className="content noselect">
@@ -568,9 +549,7 @@ export class SettingsComponent extends React.Component<
protected autoSaveDidChange = ( protected autoSaveDidChange = (
event: React.ChangeEvent<HTMLInputElement> event: React.ChangeEvent<HTMLInputElement>
): void => { ): void => {
this.setState({ this.setState({ autoSave: event.target.checked ? 'on' : 'off' });
autoSave: event.target.checked ? Settings.AutoSave.DEFAULT_ON : 'off',
});
}; };
protected quickSuggestionsOtherDidChange = ( protected quickSuggestionsOtherDidChange = (

View File

@@ -1,7 +1,7 @@
import * as React from '@theia/core/shared/react'; import * as React from 'react';
import { injectable, inject, postConstruct } from '@theia/core/shared/inversify'; import { injectable, inject, postConstruct } from 'inversify';
import { Widget } from '@theia/core/shared/@phosphor/widgets'; import { Widget } from '@phosphor/widgets';
import { Message } from '@theia/core/shared/@phosphor/messaging'; import { Message } from '@phosphor/messaging';
import { DialogError, ReactWidget } from '@theia/core/lib/browser'; import { DialogError, ReactWidget } from '@theia/core/lib/browser';
import { AbstractDialog, DialogProps } from '@theia/core/lib/browser'; import { AbstractDialog, DialogProps } from '@theia/core/lib/browser';
import { Settings, SettingsService } from './settings'; import { Settings, SettingsService } from './settings';
@@ -56,7 +56,7 @@ export class SettingsDialog extends AbstractDialog<Promise<Settings>> {
constructor( constructor(
@inject(SettingsDialogProps) @inject(SettingsDialogProps)
protected override readonly props: SettingsDialogProps protected readonly props: SettingsDialogProps
) { ) {
super(props); super(props);
this.contentNode.classList.add('arduino-settings-dialog'); this.contentNode.classList.add('arduino-settings-dialog');
@@ -73,7 +73,7 @@ export class SettingsDialog extends AbstractDialog<Promise<Settings>> {
); );
} }
protected override async isValid(settings: Promise<Settings>): Promise<DialogError> { protected async isValid(settings: Promise<Settings>): Promise<DialogError> {
const result = await this.settingsService.validate(settings); const result = await this.settingsService.validate(settings);
if (typeof result === 'string') { if (typeof result === 'string') {
return result; return result;
@@ -85,7 +85,7 @@ export class SettingsDialog extends AbstractDialog<Promise<Settings>> {
return this.settingsService.settings(); return this.settingsService.settings();
} }
protected override onAfterAttach(msg: Message): void { protected onAfterAttach(msg: Message): void {
if (this.widget.isAttached) { if (this.widget.isAttached) {
Widget.detach(this.widget); Widget.detach(this.widget);
} }
@@ -97,12 +97,12 @@ export class SettingsDialog extends AbstractDialog<Promise<Settings>> {
this.update(); this.update();
} }
protected override onUpdateRequest(msg: Message): void { protected onUpdateRequest(msg: Message): void {
super.onUpdateRequest(msg); super.onUpdateRequest(msg);
this.widget.update(); this.widget.update();
} }
protected override onActivateRequest(msg: Message): void { protected onActivateRequest(msg: Message): void {
super.onActivateRequest(msg); super.onActivateRequest(msg);
// calling settingsService.reset() in order to reload the settings from the preferenceService // calling settingsService.reset() in order to reload the settings from the preferenceService
@@ -172,17 +172,17 @@ export class AdditionalUrlsDialog extends AbstractDialog<string[]> {
return AdditionalUrls.parse(this.textArea.value, 'newline'); return AdditionalUrls.parse(this.textArea.value, 'newline');
} }
protected override onAfterAttach(message: Message): void { protected onAfterAttach(message: Message): void {
super.onAfterAttach(message); super.onAfterAttach(message);
this.addUpdateListener(this.textArea, 'input'); this.addUpdateListener(this.textArea, 'input');
} }
protected override onActivateRequest(message: Message): void { protected onActivateRequest(message: Message): void {
super.onActivateRequest(message); super.onActivateRequest(message);
this.textArea.focus(); this.textArea.focus();
} }
protected override handleEnter(event: KeyboardEvent): boolean | void { protected handleEnter(event: KeyboardEvent): boolean | void {
if (event.target instanceof HTMLInputElement) { if (event.target instanceof HTMLInputElement) {
return super.handleEnter(event); return super.handleEnter(event);
} }

View File

@@ -1,8 +1,4 @@
import { import { injectable, inject, postConstruct } from 'inversify';
injectable,
inject,
postConstruct,
} from '@theia/core/shared/inversify';
import URI from '@theia/core/lib/common/uri'; import URI from '@theia/core/lib/common/uri';
import { Emitter } from '@theia/core/lib/common/event'; import { Emitter } from '@theia/core/lib/common/event';
import { Deferred, timeout } from '@theia/core/lib/common/promise-util'; import { Deferred, timeout } from '@theia/core/lib/common/promise-util';
@@ -20,15 +16,12 @@ import {
Network, Network,
} from '../../../common/protocol'; } from '../../../common/protocol';
import { CommandService, nls } from '@theia/core/lib/common'; import { CommandService, nls } from '@theia/core/lib/common';
import { import { AsyncLocalizationProvider } from '@theia/core/lib/common/i18n/localization';
AsyncLocalizationProvider,
LanguageInfo,
} from '@theia/core/lib/common/i18n/localization';
import { ElectronCommands } from '@theia/core/lib/electron-browser/menu/electron-menu-contribution'; import { ElectronCommands } from '@theia/core/lib/electron-browser/menu/electron-menu-contribution';
export const EDITOR_SETTING = 'editor'; export const EDITOR_SETTING = 'editor';
export const FONT_SIZE_SETTING = `${EDITOR_SETTING}.fontSize`; export const FONT_SIZE_SETTING = `${EDITOR_SETTING}.fontSize`;
export const AUTO_SAVE_SETTING = `files.autoSave`; export const AUTO_SAVE_SETTING = `${EDITOR_SETTING}.autoSave`;
export const QUICK_SUGGESTIONS_SETTING = `${EDITOR_SETTING}.quickSuggestions`; export const QUICK_SUGGESTIONS_SETTING = `${EDITOR_SETTING}.quickSuggestions`;
export const ARDUINO_SETTING = 'arduino'; export const ARDUINO_SETTING = 'arduino';
export const WINDOW_SETTING = `${ARDUINO_SETTING}.window`; export const WINDOW_SETTING = `${ARDUINO_SETTING}.window`;
@@ -46,10 +39,10 @@ export const SHOW_ALL_FILES_SETTING = `${SKETCHBOOK_SETTING}.showAllFiles`;
export interface Settings { export interface Settings {
editorFontSize: number; // `editor.fontSize` editorFontSize: number; // `editor.fontSize`
themeId: string; // `workbench.colorTheme` themeId: string; // `workbench.colorTheme`
autoSave: Settings.AutoSave; // `files.autoSave` autoSave: 'on' | 'off'; // `editor.autoSave`
quickSuggestions: Record<'other' | 'comments' | 'strings', boolean>; // `editor.quickSuggestions` quickSuggestions: Record<'other' | 'comments' | 'strings', boolean>; // `editor.quickSuggestions`
languages: (string | LanguageInfo)[]; // `languages from the plugins` languages: string[]; // `languages from the plugins`
currentLanguage: string; currentLanguage: string;
autoScaleInterface: boolean; // `arduino.window.autoScale` autoScaleInterface: boolean; // `arduino.window.autoScale`
@@ -68,14 +61,6 @@ export namespace Settings {
export function belongsToCli<K extends keyof Settings>(key: K): boolean { export function belongsToCli<K extends keyof Settings>(key: K): boolean {
return key === 'sketchbookPath' || key === 'additionalUrls'; return key === 'sketchbookPath' || key === 'additionalUrls';
} }
export type AutoSave =
| 'off'
| 'afterDelay'
| 'onFocusChange'
| 'onWindowChange';
export namespace AutoSave {
export const DEFAULT_ON: AutoSave = 'afterDelay'; // https://github.com/eclipse-theia/theia/issues/10812
}
} }
@injectable() @injectable()
@@ -141,10 +126,7 @@ export class SettingsService {
'workbench.colorTheme', 'workbench.colorTheme',
'arduino-theme' 'arduino-theme'
), ),
this.preferenceService.get<Settings.AutoSave>( this.preferenceService.get<'on' | 'off'>(AUTO_SAVE_SETTING, 'on'),
AUTO_SAVE_SETTING,
Settings.AutoSave.DEFAULT_ON
),
this.preferenceService.get< this.preferenceService.get<
Record<'other' | 'comments' | 'strings', boolean> Record<'other' | 'comments' | 'strings', boolean>
>(QUICK_SUGGESTIONS_SETTING, { >(QUICK_SUGGESTIONS_SETTING, {
@@ -280,7 +262,7 @@ export class SettingsService {
await this.savePreference('editor.fontSize', editorFontSize); await this.savePreference('editor.fontSize', editorFontSize);
await this.savePreference('workbench.colorTheme', themeId); await this.savePreference('workbench.colorTheme', themeId);
await this.savePreference(AUTO_SAVE_SETTING, autoSave); await this.savePreference('editor.autoSave', autoSave);
await this.savePreference('editor.quickSuggestions', quickSuggestions); await this.savePreference('editor.quickSuggestions', quickSuggestions);
await this.savePreference(AUTO_SCALE_SETTING, autoScaleInterface); await this.savePreference(AUTO_SCALE_SETTING, autoScaleInterface);
await this.savePreference(ZOOM_LEVEL_SETTING, interfaceScale); await this.savePreference(ZOOM_LEVEL_SETTING, interfaceScale);

View File

@@ -1,4 +1,4 @@
import * as React from '@theia/core/shared/react'; import * as React from 'react';
import { BoardUserField } from '../../../common/protocol'; import { BoardUserField } from '../../../common/protocol';
import { nls } from '@theia/core/lib/common'; import { nls } from '@theia/core/lib/common';

View File

@@ -1,12 +1,12 @@
import * as React from '@theia/core/shared/react'; import * as React from 'react';
import { inject, injectable } from '@theia/core/shared/inversify'; import { inject, injectable } from 'inversify';
import { import {
AbstractDialog, AbstractDialog,
DialogProps, DialogProps,
ReactWidget, ReactWidget,
} from '@theia/core/lib/browser'; } from '@theia/core/lib/browser';
import { Widget } from '@theia/core/shared/@phosphor/widgets'; import { Widget } from '@phosphor/widgets';
import { Message } from '@theia/core/shared/@phosphor/messaging'; import { Message } from '@phosphor/messaging';
import { UploadSketch } from '../../contributions/upload-sketch'; import { UploadSketch } from '../../contributions/upload-sketch';
import { UserFieldsComponent } from './user-fields-component'; import { UserFieldsComponent } from './user-fields-component';
import { BoardUserField } from '../../../common/protocol'; import { BoardUserField } from '../../../common/protocol';
@@ -61,7 +61,7 @@ export class UserFieldsDialog extends AbstractDialog<BoardUserField[]> {
constructor( constructor(
@inject(UserFieldsDialogProps) @inject(UserFieldsDialogProps)
protected override readonly props: UserFieldsDialogProps protected readonly props: UserFieldsDialogProps
) { ) {
super({ super({
title: UploadSketch.Commands.UPLOAD_WITH_CONFIGURATION.label || '', title: UploadSketch.Commands.UPLOAD_WITH_CONFIGURATION.label || '',
@@ -83,7 +83,7 @@ export class UserFieldsDialog extends AbstractDialog<BoardUserField[]> {
return this.widget.currentUserFields; return this.widget.currentUserFields;
} }
protected override onAfterAttach(msg: Message): void { protected onAfterAttach(msg: Message): void {
if (this.widget.isAttached) { if (this.widget.isAttached) {
Widget.detach(this.widget); Widget.detach(this.widget);
} }
@@ -92,17 +92,17 @@ export class UserFieldsDialog extends AbstractDialog<BoardUserField[]> {
this.update(); this.update();
} }
protected override onUpdateRequest(msg: Message): void { protected onUpdateRequest(msg: Message): void {
super.onUpdateRequest(msg); super.onUpdateRequest(msg);
this.widget.update(); this.widget.update();
} }
protected override onActivateRequest(msg: Message): void { protected onActivateRequest(msg: Message): void {
super.onActivateRequest(msg); super.onActivateRequest(msg);
this.widget.activate(); this.widget.activate();
} }
protected override async accept(): Promise<void> { protected async accept(): Promise<void> {
// If the user presses enter and at least // If the user presses enter and at least
// a field is empty don't accept the input // a field is empty don't accept the input
for (const field of this.value) { for (const field of this.value) {
@@ -113,7 +113,7 @@ export class UserFieldsDialog extends AbstractDialog<BoardUserField[]> {
return super.accept(); return super.accept();
} }
override close(): void { close(): void {
this.widget.resetUserFieldsValue(); this.widget.resetUserFieldsValue();
this.widget.close(); this.widget.close();
super.close(); super.close();

View File

@@ -1,4 +1,4 @@
import { injectable, inject } from '@theia/core/shared/inversify'; import { injectable, inject } from 'inversify';
import { import {
FrontendApplicationContribution, FrontendApplicationContribution,
FrontendApplication, FrontendApplication,

View File

@@ -1,74 +0,0 @@
import { DisposableCollection, Emitter, Event } from '@theia/core';
import { FrontendApplicationContribution } from '@theia/core/lib/browser';
import { inject, injectable } from '@theia/core/shared/inversify';
import { HostedPluginSupport } from './theia/plugin-ext/hosted-plugin';
/**
* Frontend contribution to watch VS Code extension start/stop events from Theia.
*
* In Theia, there are no events when a VS Code extension is loaded, started, unloaded, and stopped.
* Currently, it's possible to `@inject` the `HostedPluginSupport` service from Theia and `await`
* for the `didStart` promise to resolve. But if the OS goes to sleep, the VS Code extensions will
* be unloaded and loaded and started again when the OS awakes. Theia reloads the VS Code extensions
* after the OS awake event, but the `didStart` promise was already resolved, so IDE2 cannot restart the LS.
* This service is meant to work around the limitation of Theia and fire an event every time the VS Code extensions
* loaded and started.
*/
@injectable()
export class HostedPluginEvents implements FrontendApplicationContribution {
@inject(HostedPluginSupport)
private readonly hostedPluginSupport: HostedPluginSupport;
private firstStart = true;
private readonly onPluginsDidStartEmitter = new Emitter<void>();
private readonly onPluginsWillUnloadEmitter = new Emitter<void>();
private readonly toDispose = new DisposableCollection(
this.onPluginsDidStartEmitter,
this.onPluginsWillUnloadEmitter
);
onStart(): void {
this.hostedPluginSupport.onDidLoad(() => {
// Fire the first event, when `didStart` resolves.
if (!this.firstStart) {
console.debug('HostedPluginEvents', "Received 'onDidLoad' event.");
this.onPluginsDidStartEmitter.fire();
} else {
console.debug(
'HostedPluginEvents',
"Received 'onDidLoad' event before the first start. Skipping."
);
}
});
this.hostedPluginSupport.didStart.then(() => {
console.debug('HostedPluginEvents', "Hosted plugins 'didStart'.");
if (!this.firstStart) {
throw new Error(
'Unexpectedly received a `didStart` event after the first start.'
);
}
this.firstStart = false;
this.onPluginsDidStartEmitter.fire();
});
this.hostedPluginSupport.onDidCloseConnection(() => {
console.debug('HostedPluginEvents', "Received 'onDidCloseConnection'.");
this.onPluginsWillUnloadEmitter.fire();
});
}
onStop(): void {
this.toDispose.dispose();
}
get onPluginsDidStart(): Event<void> {
return this.onPluginsDidStartEmitter.event;
}
get onPluginsWillUnload(): Event<void> {
return this.onPluginsWillUnloadEmitter.event;
}
get didStart(): Promise<void> {
return this.hostedPluginSupport.didStart;
}
}

View File

@@ -5,7 +5,7 @@ import {
MessageService, MessageService,
nls, nls,
} from '@theia/core'; } from '@theia/core';
import { injectable, inject } from '@theia/core/shared/inversify'; import { injectable, inject } from 'inversify';
import { IDEUpdater, UpdateInfo } from '../../common/protocol/ide-updater'; import { IDEUpdater, UpdateInfo } from '../../common/protocol/ide-updater';
import { IDEUpdaterDialog } from '../dialogs/ide-updater/ide-updater-dialog'; import { IDEUpdaterDialog } from '../dialogs/ide-updater/ide-updater-dialog';

View File

@@ -1,13 +0,0 @@
import * as monaco from '@theia/monaco-editor-core';
/**
* Exclusive "ino" document selector for monaco.
*/
export const InoSelector = selectorOf('ino', 'c', 'cpp', 'h', 'hpp', 'pde');
function selectorOf(
...languageId: string[]
): monaco.languages.LanguageSelector {
return languageId.map((language) => ({
language,
exclusive: true, // <-- this should make sure the custom formatter has higher precedence over the LS formatter.
}));
}

View File

@@ -1,5 +1,5 @@
import { injectable, postConstruct, inject } from '@theia/core/shared/inversify'; import { injectable, postConstruct, inject } from 'inversify';
import { Message } from '@theia/core/shared/@phosphor/messaging'; import { Message } from '@phosphor/messaging';
import { addEventListener } from '@theia/core/lib/browser/widgets/widget'; import { addEventListener } from '@theia/core/lib/browser/widgets/widget';
import { DialogProps } from '@theia/core/lib/browser/dialogs'; import { DialogProps } from '@theia/core/lib/browser/dialogs';
import { AbstractDialog } from '../theia/dialogs/dialogs'; import { AbstractDialog } from '../theia/dialogs/dialogs';
@@ -38,7 +38,7 @@ export class LibraryListWidget extends ListWidget<LibraryPackage> {
} }
@postConstruct() @postConstruct()
protected override init(): void { protected init(): void {
super.init(); super.init();
this.toDispose.pushAll([ this.toDispose.pushAll([
this.notificationCenter.onLibraryInstalled(() => this.refresh(undefined)), this.notificationCenter.onLibraryInstalled(() => this.refresh(undefined)),
@@ -48,7 +48,7 @@ export class LibraryListWidget extends ListWidget<LibraryPackage> {
]); ]);
} }
protected override async install({ protected async install({
item, item,
progressId, progressId,
version, version,
@@ -158,7 +158,7 @@ export class LibraryListWidget extends ListWidget<LibraryPackage> {
} }
} }
protected override async uninstall({ protected async uninstall({
item, item,
progressId, progressId,
}: { }: {
@@ -199,7 +199,7 @@ class MessageBoxDialog extends AbstractDialog<MessageBoxDialog.Result> {
}); });
} }
protected override onCloseRequest(message: Message): void { protected onCloseRequest(message: Message): void {
super.onCloseRequest(message); super.onCloseRequest(message);
this.accept(); this.accept();
} }
@@ -217,7 +217,7 @@ class MessageBoxDialog extends AbstractDialog<MessageBoxDialog.Result> {
return message; return message;
} }
protected override handleEnter(event: KeyboardEvent): boolean | void { protected handleEnter(event: KeyboardEvent): boolean | void {
this.response = 0; this.response = 0;
super.handleEnter(event); super.handleEnter(event);
} }

View File

@@ -1,4 +1,4 @@
import { injectable } from '@theia/core/shared/inversify'; import { injectable } from 'inversify';
import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application'; import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application';
import { AbstractViewContribution } from '@theia/core/lib/browser/shell/view-contribution'; import { AbstractViewContribution } from '@theia/core/lib/browser/shell/view-contribution';
import { MenuModelRegistry } from '@theia/core'; import { MenuModelRegistry } from '@theia/core';
@@ -28,7 +28,7 @@ export class LibraryListWidgetFrontendContribution
this.openView(); this.openView();
} }
override registerMenus(menus: MenuModelRegistry): void { registerMenus(menus: MenuModelRegistry): void {
if (this.toggleCommand) { if (this.toggleCommand) {
menus.registerMenuAction(ArduinoMenus.TOOLS__MAIN_GROUP, { menus.registerMenuAction(ArduinoMenus.TOOLS__MAIN_GROUP, {
commandId: this.toggleCommand.id, commandId: this.toggleCommand.id,

View File

@@ -1,4 +1,4 @@
import { inject, injectable } from '@theia/core/shared/inversify'; import { inject, injectable } from 'inversify';
import { URI as Uri } from 'vscode-uri'; import { URI as Uri } from 'vscode-uri';
import URI from '@theia/core/lib/common/uri'; import URI from '@theia/core/lib/common/uri';
import { Deferred } from '@theia/core/lib/common/promise-util'; import { Deferred } from '@theia/core/lib/common/promise-util';

View File

@@ -1,10 +1,4 @@
import { import { Emitter, MessageService } from '@theia/core';
CommandRegistry,
Disposable,
Emitter,
MessageService,
nls,
} from '@theia/core';
import { inject, injectable } from '@theia/core/shared/inversify'; import { inject, injectable } from '@theia/core/shared/inversify';
import { Board, Port } from '../common/protocol'; import { Board, Port } from '../common/protocol';
import { import {
@@ -16,8 +10,6 @@ import {
PluggableMonitorSettings, PluggableMonitorSettings,
MonitorSettings, MonitorSettings,
} from '../node/monitor-settings/monitor-settings-provider'; } from '../node/monitor-settings/monitor-settings-provider';
import { BoardsConfig } from './boards/boards-config';
import { BoardsServiceProvider } from './boards/boards-service-provider';
@injectable() @injectable()
export class MonitorManagerProxyClientImpl export class MonitorManagerProxyClientImpl
@@ -32,20 +24,13 @@ export class MonitorManagerProxyClientImpl
}>(); }>();
readonly onMessagesReceived = this.onMessagesReceivedEmitter.event; readonly onMessagesReceived = this.onMessagesReceivedEmitter.event;
protected readonly onMonitorSettingsDidChangeEmitter = protected readonly onWSConnectionChangedEmitter = new Emitter<boolean>();
new Emitter<MonitorSettings>(); readonly onWSConnectionChanged = this.onWSConnectionChangedEmitter.event;
readonly onMonitorSettingsDidChange =
this.onMonitorSettingsDidChangeEmitter.event;
protected readonly onMonitorShouldResetEmitter = new Emitter();
readonly onMonitorShouldReset = this.onMonitorShouldResetEmitter.event;
// WebSocket used to handle pluggable monitor communication between // WebSocket used to handle pluggable monitor communication between
// frontend and backend. // frontend and backend.
private webSocket?: WebSocket; private webSocket?: WebSocket;
private wsPort?: number; private wsPort?: number;
private lastConnectedBoard: BoardsConfig.Config;
private onBoardsConfigChanged: Disposable | undefined;
getWebSocketPort(): number | undefined { getWebSocketPort(): number | undefined {
return this.wsPort; return this.wsPort;
@@ -57,46 +42,28 @@ export class MonitorManagerProxyClientImpl
// This is necessary to call the backend methods from the frontend // This is necessary to call the backend methods from the frontend
@inject(MonitorManagerProxyFactory) @inject(MonitorManagerProxyFactory)
protected server: MonitorManagerProxyFactory, protected server: MonitorManagerProxyFactory
@inject(CommandRegistry)
protected readonly commandRegistry: CommandRegistry,
@inject(BoardsServiceProvider)
protected readonly boardsServiceProvider: BoardsServiceProvider
) {} ) {}
/** /**
* Connects a localhost WebSocket using the specified port. * Connects a localhost WebSocket using the specified port.
* @param addressPort port of the WebSocket * @param addressPort port of the WebSocket
*/ */
async connect(addressPort: number): Promise<void> { connect(addressPort: number): void {
if (!!this.webSocket) { if (this.webSocket) {
if (this.wsPort === addressPort) return; return;
else this.disconnect();
} }
try { try {
this.webSocket = new WebSocket(`ws://localhost:${addressPort}`); this.webSocket = new WebSocket(`ws://localhost:${addressPort}`);
this.onWSConnectionChangedEmitter.fire(true);
} catch { } catch {
this.messageService.error( this.messageService.error('Unable to connect to websocket');
nls.localize(
'arduino/monitor/unableToConnectToWebSocket',
'Unable to connect to websocket'
)
);
return; return;
} }
this.webSocket.onmessage = (message) => { this.webSocket.onmessage = (res) => {
const parsedMessage = JSON.parse(message.data); const messages = JSON.parse(res.data);
if (Array.isArray(parsedMessage)) this.onMessagesReceivedEmitter.fire({ messages });
this.onMessagesReceivedEmitter.fire({ messages: parsedMessage });
else if (
parsedMessage.command ===
Monitor.MiddlewareCommand.ON_SETTINGS_DID_CHANGE
) {
this.onMonitorSettingsDidChangeEmitter.fire(parsedMessage.data);
}
}; };
this.wsPort = addressPort; this.wsPort = addressPort;
} }
@@ -105,19 +72,12 @@ export class MonitorManagerProxyClientImpl
* Disconnects the WebSocket if connected. * Disconnects the WebSocket if connected.
*/ */
disconnect(): void { disconnect(): void {
if (!this.webSocket) return;
this.onBoardsConfigChanged?.dispose();
this.onBoardsConfigChanged = undefined;
try { try {
this.webSocket?.close(); this.webSocket?.close();
this.webSocket = undefined; this.webSocket = undefined;
this.onWSConnectionChangedEmitter.fire(false);
} catch { } catch {
this.messageService.error( this.messageService.error('Unable to close websocket');
nls.localize(
'arduino/monitor/unableToCloseWebSocket',
'Unable to close websocket'
)
);
} }
} }
@@ -125,49 +85,15 @@ export class MonitorManagerProxyClientImpl
return !!this.webSocket; return !!this.webSocket;
} }
async startMonitor(settings?: PluggableMonitorSettings): Promise<void> { async startMonitor(
this.lastConnectedBoard = { board: Board,
selectedBoard: this.boardsServiceProvider.boardsConfig.selectedBoard, port: Port,
selectedPort: this.boardsServiceProvider.boardsConfig.selectedPort, settings?: PluggableMonitorSettings
}; ): Promise<void> {
return this.server().startMonitor(board, port, settings);
if (!this.onBoardsConfigChanged) {
this.onBoardsConfigChanged =
this.boardsServiceProvider.onBoardsConfigChanged(
async ({ selectedBoard, selectedPort }) => {
if (
typeof selectedBoard === 'undefined' ||
typeof selectedPort === 'undefined'
)
return;
// a board is plugged and it's different from the old connected board
if (
selectedBoard?.fqbn !==
this.lastConnectedBoard?.selectedBoard?.fqbn ||
selectedPort?.id !== this.lastConnectedBoard?.selectedPort?.id
) {
this.onMonitorShouldResetEmitter.fire(null);
this.lastConnectedBoard = {
selectedBoard: selectedBoard,
selectedPort: selectedPort,
};
} else {
// a board is plugged and it's the same as prev, rerun "this.startMonitor" to
// recreate the listener callback
this.startMonitor();
}
}
);
}
const { selectedBoard, selectedPort } =
this.boardsServiceProvider.boardsConfig;
if (!selectedBoard || !selectedBoard.fqbn || !selectedPort) return;
await this.server().startMonitor(selectedBoard, selectedPort, settings);
} }
getCurrentSettings(board: Board, port: Port): Promise<MonitorSettings> { getCurrentSettings(board: Board, port: Port): MonitorSettings {
return this.server().getCurrentSettings(board, port); return this.server().getCurrentSettings(board, port);
} }
@@ -178,7 +104,7 @@ export class MonitorManagerProxyClientImpl
this.webSocket.send( this.webSocket.send(
JSON.stringify({ JSON.stringify({
command: Monitor.ClientCommand.SEND_MESSAGE, command: Monitor.Command.SEND_MESSAGE,
data: message, data: message,
}) })
); );
@@ -191,7 +117,9 @@ export class MonitorManagerProxyClientImpl
this.webSocket.send( this.webSocket.send(
JSON.stringify({ JSON.stringify({
command: Monitor.ClientCommand.CHANGE_SETTINGS, command: Monitor.Command.CHANGE_SETTINGS,
// TODO: This might be wrong, verify if it works
// SPOILER: It doesn't
data: settings, data: settings,
}) })
); );

View File

@@ -4,9 +4,6 @@ import {
LocalStorageService, LocalStorageService,
} from '@theia/core/lib/browser'; } from '@theia/core/lib/browser';
import { inject, injectable } from '@theia/core/shared/inversify'; import { inject, injectable } from '@theia/core/shared/inversify';
import { MonitorManagerProxyClient } from '../common/protocol';
import { isNullOrUndefined } from '../common/utils';
import { MonitorSettings } from '../node/monitor-settings/monitor-settings-provider';
@injectable() @injectable()
export class MonitorModel implements FrontendApplicationContribution { export class MonitorModel implements FrontendApplicationContribution {
@@ -15,9 +12,6 @@ export class MonitorModel implements FrontendApplicationContribution {
@inject(LocalStorageService) @inject(LocalStorageService)
protected readonly localStorageService: LocalStorageService; protected readonly localStorageService: LocalStorageService;
@inject(MonitorManagerProxyClient)
protected readonly monitorManagerProxy: MonitorManagerProxyClient;
protected readonly onChangeEmitter: Emitter< protected readonly onChangeEmitter: Emitter<
MonitorModel.State.Change<keyof MonitorModel.State> MonitorModel.State.Change<keyof MonitorModel.State>
>; >;
@@ -26,20 +20,12 @@ export class MonitorModel implements FrontendApplicationContribution {
protected _timestamp: boolean; protected _timestamp: boolean;
protected _lineEnding: MonitorModel.EOL; protected _lineEnding: MonitorModel.EOL;
protected _interpolate: boolean; protected _interpolate: boolean;
protected _darkTheme: boolean;
protected _wsPort: number;
protected _serialPort: string;
protected _connected: boolean;
constructor() { constructor() {
this._autoscroll = true; this._autoscroll = true;
this._timestamp = false; this._timestamp = false;
this._interpolate = false; this._interpolate = false;
this._lineEnding = MonitorModel.EOL.DEFAULT; this._lineEnding = MonitorModel.EOL.DEFAULT;
this._darkTheme = false;
this._wsPort = 0;
this._serialPort = '';
this._connected = true;
this.onChangeEmitter = new Emitter< this.onChangeEmitter = new Emitter<
MonitorModel.State.Change<keyof MonitorModel.State> MonitorModel.State.Change<keyof MonitorModel.State>
@@ -49,11 +35,7 @@ export class MonitorModel implements FrontendApplicationContribution {
onStart(): void { onStart(): void {
this.localStorageService this.localStorageService
.getData<MonitorModel.State>(MonitorModel.STORAGE_ID) .getData<MonitorModel.State>(MonitorModel.STORAGE_ID)
.then(this.restoreState.bind(this)); .then(this.restoreState);
this.monitorManagerProxy.onMonitorSettingsDidChange(
this.onMonitorSettingsDidChange.bind(this)
);
} }
get onChange(): Event<MonitorModel.State.Change<keyof MonitorModel.State>> { get onChange(): Event<MonitorModel.State.Change<keyof MonitorModel.State>> {
@@ -68,7 +50,6 @@ export class MonitorModel implements FrontendApplicationContribution {
this._timestamp = state.timestamp; this._timestamp = state.timestamp;
this._lineEnding = state.lineEnding; this._lineEnding = state.lineEnding;
this._interpolate = state.interpolate; this._interpolate = state.interpolate;
this._serialPort = state.serialPort;
} }
protected async storeState(): Promise<void> { protected async storeState(): Promise<void> {
@@ -77,7 +58,6 @@ export class MonitorModel implements FrontendApplicationContribution {
timestamp: this._timestamp, timestamp: this._timestamp,
lineEnding: this._lineEnding, lineEnding: this._lineEnding,
interpolate: this._interpolate, interpolate: this._interpolate,
serialPort: this._serialPort,
}); });
} }
@@ -85,34 +65,22 @@ export class MonitorModel implements FrontendApplicationContribution {
return this._autoscroll; return this._autoscroll;
} }
set autoscroll(autoscroll: boolean) { toggleAutoscroll(): void {
if (autoscroll === this._autoscroll) return; this._autoscroll = !this._autoscroll;
this._autoscroll = autoscroll;
this.monitorManagerProxy.changeSettings({
monitorUISettings: { autoscroll },
});
this.storeState().then(() => { this.storeState().then(() => {
this.onChangeEmitter.fire({ this.onChangeEmitter.fire({
property: 'autoscroll', property: 'autoscroll',
value: this._autoscroll, value: this._timestamp,
}); });
}); });
} }
toggleAutoscroll(): void {
this.autoscroll = !this._autoscroll;
}
get timestamp(): boolean { get timestamp(): boolean {
return this._timestamp; return this._timestamp;
} }
set timestamp(timestamp: boolean) { toggleTimestamp(): void {
if (timestamp === this._timestamp) return; this._timestamp = !this._timestamp;
this._timestamp = timestamp;
this.monitorManagerProxy.changeSettings({
monitorUISettings: { timestamp },
});
this.storeState().then(() => this.storeState().then(() =>
this.onChangeEmitter.fire({ this.onChangeEmitter.fire({
property: 'timestamp', property: 'timestamp',
@@ -121,20 +89,12 @@ export class MonitorModel implements FrontendApplicationContribution {
); );
} }
toggleTimestamp(): void {
this.timestamp = !this._timestamp;
}
get lineEnding(): MonitorModel.EOL { get lineEnding(): MonitorModel.EOL {
return this._lineEnding; return this._lineEnding;
} }
set lineEnding(lineEnding: MonitorModel.EOL) { set lineEnding(lineEnding: MonitorModel.EOL) {
if (lineEnding === this._lineEnding) return;
this._lineEnding = lineEnding; this._lineEnding = lineEnding;
this.monitorManagerProxy.changeSettings({
monitorUISettings: { lineEnding },
});
this.storeState().then(() => this.storeState().then(() =>
this.onChangeEmitter.fire({ this.onChangeEmitter.fire({
property: 'lineEnding', property: 'lineEnding',
@@ -147,12 +107,8 @@ export class MonitorModel implements FrontendApplicationContribution {
return this._interpolate; return this._interpolate;
} }
set interpolate(interpolate: boolean) { set interpolate(i: boolean) {
if (interpolate === this._interpolate) return; this._interpolate = i;
this._interpolate = interpolate;
this.monitorManagerProxy.changeSettings({
monitorUISettings: { interpolate },
});
this.storeState().then(() => this.storeState().then(() =>
this.onChangeEmitter.fire({ this.onChangeEmitter.fire({
property: 'interpolate', property: 'interpolate',
@@ -160,96 +116,6 @@ export class MonitorModel implements FrontendApplicationContribution {
}) })
); );
} }
get darkTheme(): boolean {
return this._darkTheme;
}
set darkTheme(darkTheme: boolean) {
if (darkTheme === this._darkTheme) return;
this._darkTheme = darkTheme;
this.monitorManagerProxy.changeSettings({
monitorUISettings: { darkTheme },
});
this.onChangeEmitter.fire({
property: 'darkTheme',
value: this._darkTheme,
});
}
get wsPort(): number {
return this._wsPort;
}
set wsPort(wsPort: number) {
if (wsPort === this._wsPort) return;
this._wsPort = wsPort;
this.monitorManagerProxy.changeSettings({
monitorUISettings: { wsPort },
});
this.onChangeEmitter.fire({
property: 'wsPort',
value: this._wsPort,
});
}
get serialPort(): string {
return this._serialPort;
}
set serialPort(serialPort: string) {
if (serialPort === this._serialPort) return;
this._serialPort = serialPort;
this.monitorManagerProxy.changeSettings({
monitorUISettings: { serialPort },
});
this.storeState().then(() =>
this.onChangeEmitter.fire({
property: 'serialPort',
value: this._serialPort,
})
);
}
get connected(): boolean {
return this._connected;
}
set connected(connected: boolean) {
if (connected === this._connected) return;
this._connected = connected;
this.monitorManagerProxy.changeSettings({
monitorUISettings: { connected },
});
this.onChangeEmitter.fire({
property: 'connected',
value: this._connected,
});
}
protected onMonitorSettingsDidChange = (settings: MonitorSettings): void => {
const { monitorUISettings } = settings;
if (!monitorUISettings) return;
const {
autoscroll,
interpolate,
lineEnding,
timestamp,
darkTheme,
wsPort,
serialPort,
connected,
} = monitorUISettings;
if (!isNullOrUndefined(autoscroll)) this.autoscroll = autoscroll;
if (!isNullOrUndefined(interpolate)) this.interpolate = interpolate;
if (!isNullOrUndefined(lineEnding)) this.lineEnding = lineEnding;
if (!isNullOrUndefined(timestamp)) this.timestamp = timestamp;
if (!isNullOrUndefined(darkTheme)) this.darkTheme = darkTheme;
if (!isNullOrUndefined(wsPort)) this.wsPort = wsPort;
if (!isNullOrUndefined(serialPort)) this.serialPort = serialPort;
if (!isNullOrUndefined(connected)) this.connected = connected;
};
} }
// TODO: Move this to /common // TODO: Move this to /common
@@ -259,10 +125,6 @@ export namespace MonitorModel {
timestamp: boolean; timestamp: boolean;
lineEnding: EOL; lineEnding: EOL;
interpolate: boolean; interpolate: boolean;
darkTheme: boolean;
wsPort: number;
serialPort: string;
connected: boolean;
} }
export namespace State { export namespace State {
export interface Change<K extends keyof State> { export interface Change<K extends keyof State> {

View File

@@ -1,8 +1,4 @@
import { import { inject, injectable, postConstruct } from 'inversify';
inject,
injectable,
postConstruct,
} from '@theia/core/shared/inversify';
import { Emitter } from '@theia/core/lib/common/event'; import { Emitter } from '@theia/core/lib/common/event';
import { JsonRpcProxy } from '@theia/core/lib/common/messaging/proxy-factory'; import { JsonRpcProxy } from '@theia/core/lib/common/messaging/proxy-factory';
import { DisposableCollection } from '@theia/core/lib/common/disposable'; import { DisposableCollection } from '@theia/core/lib/common/disposable';
@@ -18,10 +14,6 @@ import {
Config, Config,
Sketch, Sketch,
} from '../common/protocol'; } from '../common/protocol';
import {
FrontendApplicationStateService,
FrontendApplicationState,
} from '@theia/core/lib/browser/frontend-application-state';
@injectable() @injectable()
export class NotificationCenter export class NotificationCenter
@@ -30,11 +22,8 @@ export class NotificationCenter
@inject(NotificationServiceServer) @inject(NotificationServiceServer)
protected readonly server: JsonRpcProxy<NotificationServiceServer>; protected readonly server: JsonRpcProxy<NotificationServiceServer>;
@inject(FrontendApplicationStateService)
private readonly appStateService: FrontendApplicationStateService;
protected readonly indexUpdatedEmitter = new Emitter<void>(); protected readonly indexUpdatedEmitter = new Emitter<void>();
protected readonly daemonStartedEmitter = new Emitter<string>(); protected readonly daemonStartedEmitter = new Emitter<void>();
protected readonly daemonStoppedEmitter = new Emitter<void>(); protected readonly daemonStoppedEmitter = new Emitter<void>();
protected readonly configChangedEmitter = new Emitter<{ protected readonly configChangedEmitter = new Emitter<{
config: Config | undefined; config: Config | undefined;
@@ -56,8 +45,6 @@ export class NotificationCenter
protected readonly recentSketchesChangedEmitter = new Emitter<{ protected readonly recentSketchesChangedEmitter = new Emitter<{
sketches: Sketch[]; sketches: Sketch[];
}>(); }>();
private readonly onAppStateDidChangeEmitter =
new Emitter<FrontendApplicationState>();
protected readonly toDispose = new DisposableCollection( protected readonly toDispose = new DisposableCollection(
this.indexUpdatedEmitter, this.indexUpdatedEmitter,
@@ -81,16 +68,10 @@ export class NotificationCenter
readonly onLibraryUninstalled = this.libraryUninstalledEmitter.event; readonly onLibraryUninstalled = this.libraryUninstalledEmitter.event;
readonly onAttachedBoardsChanged = this.attachedBoardsChangedEmitter.event; readonly onAttachedBoardsChanged = this.attachedBoardsChangedEmitter.event;
readonly onRecentSketchesChanged = this.recentSketchesChangedEmitter.event; readonly onRecentSketchesChanged = this.recentSketchesChangedEmitter.event;
readonly onAppStateDidChange = this.onAppStateDidChangeEmitter.event;
@postConstruct() @postConstruct()
protected init(): void { protected init(): void {
this.server.setClient(this); this.server.setClient(this);
this.toDispose.push(
this.appStateService.onStateChanged((state) =>
this.onAppStateDidChangeEmitter.fire(state)
)
);
} }
onStop(): void { onStop(): void {
@@ -101,8 +82,8 @@ export class NotificationCenter
this.indexUpdatedEmitter.fire(); this.indexUpdatedEmitter.fire();
} }
notifyDaemonStarted(port: string): void { notifyDaemonStarted(): void {
this.daemonStartedEmitter.fire(port); this.daemonStartedEmitter.fire();
} }
notifyDaemonStopped(): void { notifyDaemonStopped(): void {

View File

@@ -1,9 +1,7 @@
import { inject, injectable } from '@theia/core/shared/inversify'; import { inject, injectable } from 'inversify';
import { Emitter } from '@theia/core/lib/common/event'; import { Emitter } from '@theia/core/lib/common/event';
import { import { OutputContribution } from '@theia/output/lib/browser/output-contribution';
OutputChannelManager, import { OutputChannelManager } from '@theia/output/lib/browser/output-channel';
OutputChannelSeverity,
} from '@theia/output/lib/browser/output-channel';
import { import {
OutputMessage, OutputMessage,
ProgressMessage, ProgressMessage,
@@ -12,10 +10,13 @@ import {
@injectable() @injectable()
export class ResponseServiceImpl implements ResponseServiceArduino { export class ResponseServiceImpl implements ResponseServiceArduino {
@inject(OutputChannelManager) @inject(OutputContribution)
private readonly outputChannelManager: OutputChannelManager; protected outputContribution: OutputContribution;
private readonly progressDidChangeEmitter = new Emitter<ProgressMessage>(); @inject(OutputChannelManager)
protected outputChannelManager: OutputChannelManager;
protected readonly progressDidChangeEmitter = new Emitter<ProgressMessage>();
readonly onProgressDidChange = this.progressDidChangeEmitter.event; readonly onProgressDidChange = this.progressDidChangeEmitter.event;
@@ -24,22 +25,13 @@ export class ResponseServiceImpl implements ResponseServiceArduino {
} }
appendToOutput(message: OutputMessage): void { appendToOutput(message: OutputMessage): void {
const { chunk, severity } = message; const { chunk } = message;
const channel = this.outputChannelManager.getChannel('Arduino'); const channel = this.outputChannelManager.getChannel('Arduino');
channel.show({ preserveFocus: true }); channel.show({ preserveFocus: true });
channel.append(chunk, mapSeverity(severity)); channel.append(chunk);
} }
reportProgress(progress: ProgressMessage): void { reportProgress(progress: ProgressMessage): void {
this.progressDidChangeEmitter.fire(progress); this.progressDidChangeEmitter.fire(progress);
} }
} }
function mapSeverity(severity?: OutputMessage.Severity): OutputChannelSeverity {
if (severity === OutputMessage.Severity.Error) {
return OutputChannelSeverity.Error;
} else if (severity === OutputMessage.Severity.Warning) {
return OutputChannelSeverity.Warning;
}
return OutputChannelSeverity.Info;
}

View File

@@ -1,5 +1,5 @@
import * as React from '@theia/core/shared/react'; import * as React from 'react';
import { injectable, inject } from '@theia/core/shared/inversify'; import { injectable, inject } from 'inversify';
import { AbstractViewContribution, codicon } from '@theia/core/lib/browser'; import { AbstractViewContribution, codicon } from '@theia/core/lib/browser';
import { MonitorWidget } from './monitor-widget'; import { MonitorWidget } from './monitor-widget';
import { MenuModelRegistry, Command, CommandRegistry } from '@theia/core'; import { MenuModelRegistry, Command, CommandRegistry } from '@theia/core';
@@ -11,7 +11,6 @@ import { ArduinoToolbar } from '../../toolbar/arduino-toolbar';
import { ArduinoMenus } from '../../menu/arduino-menus'; import { ArduinoMenus } from '../../menu/arduino-menus';
import { nls } from '@theia/core/lib/common'; import { nls } from '@theia/core/lib/common';
import { MonitorModel } from '../../monitor-model'; import { MonitorModel } from '../../monitor-model';
import { MonitorManagerProxyClient } from '../../../common/protocol';
export namespace SerialMonitor { export namespace SerialMonitor {
export namespace Commands { export namespace Commands {
@@ -48,15 +47,11 @@ export class MonitorViewContribution
static readonly TOGGLE_SERIAL_MONITOR = MonitorWidget.ID + ':toggle'; static readonly TOGGLE_SERIAL_MONITOR = MonitorWidget.ID + ':toggle';
static readonly TOGGLE_SERIAL_MONITOR_TOOLBAR = static readonly TOGGLE_SERIAL_MONITOR_TOOLBAR =
MonitorWidget.ID + ':toggle-toolbar'; MonitorWidget.ID + ':toggle-toolbar';
static readonly RESET_SERIAL_MONITOR = MonitorWidget.ID + ':reset';
constructor( @inject(MonitorModel)
@inject(MonitorModel) protected readonly model: MonitorModel;
protected readonly model: MonitorModel,
@inject(MonitorManagerProxyClient) constructor() {
protected readonly monitorManagerProxy: MonitorManagerProxyClient
) {
super({ super({
widgetId: MonitorWidget.ID, widgetId: MonitorWidget.ID,
widgetName: MonitorWidget.LABEL, widgetName: MonitorWidget.LABEL,
@@ -66,10 +61,9 @@ export class MonitorViewContribution
toggleCommandId: MonitorViewContribution.TOGGLE_SERIAL_MONITOR, toggleCommandId: MonitorViewContribution.TOGGLE_SERIAL_MONITOR,
toggleKeybinding: 'CtrlCmd+Shift+M', toggleKeybinding: 'CtrlCmd+Shift+M',
}); });
this.monitorManagerProxy.onMonitorShouldReset(() => this.reset());
} }
override registerMenus(menus: MenuModelRegistry): void { registerMenus(menus: MenuModelRegistry): void {
if (this.toggleCommand) { if (this.toggleCommand) {
menus.registerMenuAction(ArduinoMenus.TOOLS__MAIN_GROUP, { menus.registerMenuAction(ArduinoMenus.TOOLS__MAIN_GROUP, {
commandId: this.toggleCommand.id, commandId: this.toggleCommand.id,
@@ -102,7 +96,7 @@ export class MonitorViewContribution
}); });
} }
override registerCommands(commands: CommandRegistry): void { registerCommands(commands: CommandRegistry): void {
commands.registerCommand(SerialMonitor.Commands.CLEAR_OUTPUT, { commands.registerCommand(SerialMonitor.Commands.CLEAR_OUTPUT, {
isEnabled: (widget) => widget instanceof MonitorWidget, isEnabled: (widget) => widget instanceof MonitorWidget,
isVisible: (widget) => widget instanceof MonitorWidget, isVisible: (widget) => widget instanceof MonitorWidget,
@@ -125,10 +119,6 @@ export class MonitorViewContribution
} }
); );
} }
commands.registerCommand(
{ id: MonitorViewContribution.RESET_SERIAL_MONITOR },
{ execute: () => this.reset() }
);
} }
protected async toggle(): Promise<void> { protected async toggle(): Promise<void> {
@@ -140,14 +130,6 @@ export class MonitorViewContribution
} }
} }
protected async reset(): Promise<void> {
const widget = this.tryGetWidget();
if (widget) {
widget.dispose();
await this.openView({ activate: true, reveal: true });
}
}
protected renderAutoScrollButton(): React.ReactNode { protected renderAutoScrollButton(): React.ReactNode {
return ( return (
<React.Fragment key="autoscroll-toolbar-item"> <React.Fragment key="autoscroll-toolbar-item">

View File

@@ -1,5 +1,5 @@
import * as React from '@theia/core/shared/react'; import * as React from 'react';
import { injectable, inject } from '@theia/core/shared/inversify'; import { postConstruct, injectable, inject } from 'inversify';
import { OptionsType } from 'react-select/src/types'; import { OptionsType } from 'react-select/src/types';
import { Emitter } from '@theia/core/lib/common/event'; import { Emitter } from '@theia/core/lib/common/event';
import { Disposable } from '@theia/core/lib/common/disposable'; import { Disposable } from '@theia/core/lib/common/disposable';
@@ -26,8 +26,6 @@ export class MonitorWidget extends ReactWidget {
); );
static readonly ID = 'serial-monitor'; static readonly ID = 'serial-monitor';
protected settings: MonitorSettings = {};
protected widgetHeight: number; protected widgetHeight: number;
/** /**
@@ -61,28 +59,33 @@ export class MonitorWidget extends ReactWidget {
this.toDispose.push( this.toDispose.push(
Disposable.create(() => this.monitorManagerProxy.disconnect()) Disposable.create(() => this.monitorManagerProxy.disconnect())
); );
// Start monitor right away if there is already a board/port combination selected
const { selectedBoard, selectedPort } =
this.boardsServiceProvider.boardsConfig;
if (selectedBoard && selectedBoard.fqbn && selectedPort) {
this.monitorManagerProxy.startMonitor(selectedBoard, selectedPort);
}
this.toDispose.push(
this.boardsServiceProvider.onBoardsConfigChanged(
async ({ selectedBoard, selectedPort }) => {
if (selectedBoard && selectedBoard.fqbn && selectedPort) {
await this.monitorManagerProxy.startMonitor(
selectedBoard,
selectedPort
);
this.update();
}
}
)
);
} }
protected override onBeforeAttach(msg: Message): void { @postConstruct()
protected init(): void {
this.update(); this.update();
this.toDispose.push(this.monitorModel.onChange(() => this.update())); this.toDispose.push(this.monitorModel.onChange(() => this.update()));
this.getCurrentSettings().then(this.onMonitorSettingsDidChange.bind(this));
this.monitorManagerProxy.onMonitorSettingsDidChange(
this.onMonitorSettingsDidChange.bind(this)
);
this.monitorManagerProxy.startMonitor();
}
onMonitorSettingsDidChange(settings: MonitorSettings): void {
this.settings = {
...this.settings,
pluggableMonitorSettings: {
...this.settings.pluggableMonitorSettings,
...settings.pluggableMonitorSettings,
},
};
this.update();
} }
clearConsole(): void { clearConsole(): void {
@@ -90,16 +93,16 @@ export class MonitorWidget extends ReactWidget {
this.update(); this.update();
} }
override dispose(): void { dispose(): void {
super.dispose(); super.dispose();
} }
protected override onCloseRequest(msg: Message): void { onCloseRequest(msg: Message): void {
this.closing = true; this.closing = true;
super.onCloseRequest(msg); super.onCloseRequest(msg);
} }
protected override onUpdateRequest(msg: Message): void { protected onUpdateRequest(msg: Message): void {
// TODO: `this.isAttached` // TODO: `this.isAttached`
// See: https://github.com/eclipse-theia/theia/issues/6704#issuecomment-562574713 // See: https://github.com/eclipse-theia/theia/issues/6704#issuecomment-562574713
if (!this.closing && this.isAttached) { if (!this.closing && this.isAttached) {
@@ -107,13 +110,13 @@ export class MonitorWidget extends ReactWidget {
} }
} }
protected override onResize(msg: Widget.ResizeMessage): void { protected onResize(msg: Widget.ResizeMessage): void {
super.onResize(msg); super.onResize(msg);
this.widgetHeight = msg.height; this.widgetHeight = msg.height;
this.update(); this.update();
} }
protected override onActivateRequest(msg: Message): void { protected onActivateRequest(msg: Message): void {
super.onActivateRequest(msg); super.onActivateRequest(msg);
(this.focusNode || this.node).focus(); (this.focusNode || this.node).focus();
} }
@@ -154,40 +157,63 @@ export class MonitorWidget extends ReactWidget {
]; ];
} }
private getCurrentSettings(): Promise<MonitorSettings> { private getCurrentSettings(): MonitorSettings {
const board = this.boardsServiceProvider.boardsConfig.selectedBoard; const board = this.boardsServiceProvider.boardsConfig.selectedBoard;
const port = this.boardsServiceProvider.boardsConfig.selectedPort; const port = this.boardsServiceProvider.boardsConfig.selectedPort;
if (!board || !port) { if (!board || !port) {
return Promise.resolve(this.settings || {}); return {};
} }
return this.monitorManagerProxy.getCurrentSettings(board, port); return this.monitorManagerProxy.getCurrentSettings(board, port);
} }
//////////////////////////////////////////////////
////////////////////IMPORTANT/////////////////////
//////////////////////////////////////////////////
// baudRates and selectedBaudRates as of now are hardcoded
// like this to retrieve the baudrate settings from the ones
// received by the monitor.
// We're doing it like since the frontend as of now doesn't
// support a fully customizable list of options that would
// be require to support pluggable monitors completely.
// As soon as the frontend UI is updated to support
// any custom settings this methods MUST be removed and
// made generic.
//
// This breaks if the user tries to open a monitor that
// doesn't support the baudrate setting.
protected get baudRates(): string[] {
const { pluggableMonitorSettings } = this.getCurrentSettings();
if (!pluggableMonitorSettings || !pluggableMonitorSettings['baudrate']) {
return [];
}
const baudRateSettings = pluggableMonitorSettings['baudrate'];
return baudRateSettings.values;
}
protected get selectedBaudRate(): string {
const { pluggableMonitorSettings } = this.getCurrentSettings();
if (!pluggableMonitorSettings || !pluggableMonitorSettings['baudrate']) {
return '';
}
const baudRateSettings = pluggableMonitorSettings['baudrate'];
return baudRateSettings.selectedValue;
}
protected render(): React.ReactNode { protected render(): React.ReactNode {
const baudrate = this.settings?.pluggableMonitorSettings const { baudRates, lineEndings } = this;
? this.settings.pluggableMonitorSettings.baudrate
: undefined;
const baudrateOptions = baudrate?.values.map((b) => ({
label: b + ' baud',
value: b,
}));
const baudrateSelectedOption = baudrateOptions?.find(
(b) => b.value === baudrate?.selectedValue
);
const lineEnding = const lineEnding =
this.lineEndings.find( lineEndings.find((item) => item.value === this.monitorModel.lineEnding) ||
(item) => item.value === this.monitorModel.lineEnding lineEndings[1]; // Defaults to `\n`.
) || this.lineEndings[1]; // Defaults to `\n`. const baudRate = baudRates.find((item) => item === this.selectedBaudRate);
return ( return (
<div className="serial-monitor"> <div className="serial-monitor">
<div className="head"> <div className="head">
<div className="send"> <div className="send">
<SerialMonitorSendInput <SerialMonitorSendInput
boardsServiceProvider={this.boardsServiceProvider} boardsServiceProvider={this.boardsServiceProvider}
monitorModel={this.monitorModel} monitorManagerProxy={this.monitorManagerProxy}
resolveFocus={this.onFocusResolved} resolveFocus={this.onFocusResolved}
onSend={this.onSend} onSend={this.onSend}
/> />
@@ -196,22 +222,20 @@ export class MonitorWidget extends ReactWidget {
<div className="select"> <div className="select">
<ArduinoSelect <ArduinoSelect
maxMenuHeight={this.widgetHeight - 40} maxMenuHeight={this.widgetHeight - 40}
options={this.lineEndings} options={lineEndings}
value={lineEnding} value={lineEnding}
onChange={this.onChangeLineEnding} onChange={this.onChangeLineEnding}
/> />
</div> </div>
{baudrateOptions && baudrateSelectedOption && ( <div className="select">
<div className="select"> <ArduinoSelect
<ArduinoSelect className="select"
className="select" maxMenuHeight={this.widgetHeight - 40}
maxMenuHeight={this.widgetHeight - 40} options={baudRates}
options={baudrateOptions} value={baudRate}
value={baudrateSelectedOption} onChange={this.onChangeBaudRate}
onChange={this.onChangeBaudRate} />
/> </div>
</div>
)}
</div> </div>
</div> </div>
<div className="body"> <div className="body">
@@ -233,21 +257,16 @@ export class MonitorWidget extends ReactWidget {
protected readonly onChangeLineEnding = ( protected readonly onChangeLineEnding = (
option: SerialMonitorOutput.SelectOption<MonitorModel.EOL> option: SerialMonitorOutput.SelectOption<MonitorModel.EOL>
): void => { ) => {
this.monitorModel.lineEnding = option.value; this.monitorModel.lineEnding = option.value;
}; };
protected readonly onChangeBaudRate = ({ protected readonly onChangeBaudRate = (value: string) => {
value, const { pluggableMonitorSettings } = this.getCurrentSettings();
}: { if (!pluggableMonitorSettings || !pluggableMonitorSettings['baudrate'])
value: string; return;
}): void => { const baudRateSettings = pluggableMonitorSettings['baudrate'];
this.getCurrentSettings().then(({ pluggableMonitorSettings }) => { baudRateSettings.selectedValue = value;
if (!pluggableMonitorSettings || !pluggableMonitorSettings['baudrate']) this.monitorManagerProxy.changeSettings(pluggableMonitorSettings);
return;
const baudRateSettings = pluggableMonitorSettings['baudrate'];
baudRateSettings.selectedValue = value;
this.monitorManagerProxy.changeSettings({ pluggableMonitorSettings });
});
}; };
} }

View File

@@ -1,15 +1,16 @@
import * as React from '@theia/core/shared/react'; import * as React from 'react';
import { Key, KeyCode } from '@theia/core/lib/browser/keys'; import { Key, KeyCode } from '@theia/core/lib/browser/keys';
import { Board } from '../../../common/protocol/boards-service'; import { Board } from '../../../common/protocol/boards-service';
import { isOSX } from '@theia/core/lib/common/os'; import { isOSX } from '@theia/core/lib/common/os';
import { DisposableCollection, nls } from '@theia/core/lib/common'; import { DisposableCollection, nls } from '@theia/core/lib/common';
import { MonitorManagerProxyClient } from '../../../common/protocol';
import { BoardsServiceProvider } from '../../boards/boards-service-provider'; import { BoardsServiceProvider } from '../../boards/boards-service-provider';
import { MonitorModel } from '../../monitor-model'; import { timeout } from '@theia/core/lib/common/promise-util';
export namespace SerialMonitorSendInput { export namespace SerialMonitorSendInput {
export interface Props { export interface Props {
readonly boardsServiceProvider: BoardsServiceProvider; readonly boardsServiceProvider: BoardsServiceProvider;
readonly monitorModel: MonitorModel; readonly monitorManagerProxy: MonitorManagerProxyClient;
readonly onSend: (text: string) => void; readonly onSend: (text: string) => void;
readonly resolveFocus: (element: HTMLElement | undefined) => void; readonly resolveFocus: (element: HTMLElement | undefined) => void;
} }
@@ -33,22 +34,35 @@ export class SerialMonitorSendInput extends React.Component<
this.onKeyDown = this.onKeyDown.bind(this); this.onKeyDown = this.onKeyDown.bind(this);
} }
override componentDidMount(): void { componentDidMount(): void {
this.setState({ connected: this.props.monitorModel.connected }); this.setState({ connected: true });
this.toDisposeBeforeUnmount.push(
this.props.monitorModel.onChange(({ property }) => { const checkWSConnection = new Promise<boolean>((resolve) => {
if (property === 'connected') this.props.monitorManagerProxy.onWSConnectionChanged((connected) => {
this.setState({ connected: this.props.monitorModel.connected }); this.setState({ connected });
}) resolve(true);
});
});
const checkWSTimeout = timeout(1000).then(() => false);
Promise.race<boolean>([checkWSConnection, checkWSTimeout]).then(
async (resolved) => {
if (!resolved) {
const connected =
await this.props.monitorManagerProxy.isWSConnected();
this.setState({ connected });
}
}
); );
} }
override componentWillUnmount(): void { componentWillUnmount(): void {
// TODO: "Your preferred browser's local storage is almost full." Discard `content` before saving layout? // TODO: "Your preferred browser's local storage is almost full." Discard `content` before saving layout?
this.toDisposeBeforeUnmount.dispose(); this.toDisposeBeforeUnmount.dispose();
} }
override render(): React.ReactNode { render(): React.ReactNode {
return ( return (
<input <input
ref={this.setRef} ref={this.setRef}
@@ -102,7 +116,7 @@ export class SerialMonitorSendInput extends React.Component<
} }
protected onSend(): void { protected onSend(): void {
this.props.onSend(this.state.text + this.props.monitorModel.lineEnding); this.props.onSend(this.state.text);
this.setState({ text: '' }); this.setState({ text: '' });
} }

View File

@@ -1,4 +1,4 @@
import * as React from '@theia/core/shared/react'; import * as React from 'react';
import { Event } from '@theia/core/lib/common/event'; import { Event } from '@theia/core/lib/common/event';
import { DisposableCollection } from '@theia/core/lib/common/disposable'; import { DisposableCollection } from '@theia/core/lib/common/disposable';
import { areEqual, FixedSizeList as List } from 'react-window'; import { areEqual, FixedSizeList as List } from 'react-window';
@@ -29,7 +29,7 @@ export class SerialMonitorOutput extends React.Component<
}; };
} }
override render(): React.ReactNode { render(): React.ReactNode {
return ( return (
<List <List
className="serial-monitor-messages" className="serial-monitor-messages"
@@ -51,11 +51,11 @@ export class SerialMonitorOutput extends React.Component<
); );
} }
override shouldComponentUpdate(): boolean { shouldComponentUpdate(): boolean {
return true; return true;
} }
override componentDidMount(): void { componentDidMount(): void {
this.scrollToBottom(); this.scrollToBottom();
this.toDisposeBeforeUnmount.pushAll([ this.toDisposeBeforeUnmount.pushAll([
this.props.monitorManagerProxy.onMessagesReceived(({ messages }) => { this.props.monitorManagerProxy.onMessagesReceived(({ messages }) => {
@@ -86,7 +86,7 @@ export class SerialMonitorOutput extends React.Component<
]); ]);
} }
override componentWillUnmount(): void { componentWillUnmount(): void {
// TODO: "Your preferred browser's local storage is almost full." Discard `content` before saving layout? // TODO: "Your preferred browser's local storage is almost full." Discard `content` before saving layout?
this.toDisposeBeforeUnmount.dispose(); this.toDisposeBeforeUnmount.dispose();
} }

View File

@@ -1,5 +1,5 @@
import { ThemeService } from '@theia/core/lib/browser/theming'; import { ThemeService } from '@theia/core/lib/browser/theming';
import { injectable, inject } from '@theia/core/shared/inversify'; import { injectable, inject } from 'inversify';
import { import {
Command, Command,
CommandRegistry, CommandRegistry,
@@ -11,9 +11,9 @@ import { Contribution } from '../../contributions/contribution';
import { Endpoint, FrontendApplication } from '@theia/core/lib/browser'; import { Endpoint, FrontendApplication } from '@theia/core/lib/browser';
import { ipcRenderer } from '@theia/electron/shared/electron'; import { ipcRenderer } from '@theia/electron/shared/electron';
import { MonitorManagerProxyClient } from '../../../common/protocol'; import { MonitorManagerProxyClient } from '../../../common/protocol';
import { SerialPlotter } from './protocol';
import { BoardsServiceProvider } from '../../boards/boards-service-provider'; import { BoardsServiceProvider } from '../../boards/boards-service-provider';
import { MonitorModel } from '../../monitor-model'; import { MonitorModel } from '../../monitor-model';
const queryString = require('query-string'); const queryString = require('query-string');
export namespace SerialPlotterContribution { export namespace SerialPlotterContribution {
@@ -23,11 +23,6 @@ export namespace SerialPlotterContribution {
label: 'Serial Plotter', label: 'Serial Plotter',
category: 'Arduino', category: 'Arduino',
}; };
export const RESET: Command = {
id: 'serial-plotter-reset',
label: 'Reset Serial Plotter',
category: 'Arduino',
};
} }
} }
@@ -49,7 +44,7 @@ export class PlotterFrontendContribution extends Contribution {
@inject(BoardsServiceProvider) @inject(BoardsServiceProvider)
protected readonly boardsServiceProvider: BoardsServiceProvider; protected readonly boardsServiceProvider: BoardsServiceProvider;
override onStart(app: FrontendApplication): MaybePromise<void> { onStart(app: FrontendApplication): MaybePromise<void> {
this.url = new Endpoint({ path: '/plotter' }).getRestUrl().toString(); this.url = new Endpoint({ path: '/plotter' }).getRestUrl().toString();
ipcRenderer.on('CLOSE_CHILD_WINDOW', async () => { ipcRenderer.on('CLOSE_CHILD_WINDOW', async () => {
@@ -57,21 +52,16 @@ export class PlotterFrontendContribution extends Contribution {
this.window = null; this.window = null;
} }
}); });
this.monitorManagerProxy.onMonitorShouldReset(() => this.reset());
return super.onStart(app); return super.onStart(app);
} }
override registerCommands(registry: CommandRegistry): void { registerCommands(registry: CommandRegistry): void {
registry.registerCommand(SerialPlotterContribution.Commands.OPEN, { registry.registerCommand(SerialPlotterContribution.Commands.OPEN, {
execute: this.startPlotter.bind(this), execute: this.connect.bind(this),
});
registry.registerCommand(SerialPlotterContribution.Commands.RESET, {
execute: () => this.reset(),
}); });
} }
override registerMenus(menus: MenuModelRegistry): void { registerMenus(menus: MenuModelRegistry): void {
menus.registerMenuAction(ArduinoMenus.TOOLS__MAIN_GROUP, { menus.registerMenuAction(ArduinoMenus.TOOLS__MAIN_GROUP, {
commandId: SerialPlotterContribution.Commands.OPEN.id, commandId: SerialPlotterContribution.Commands.OPEN.id,
label: SerialPlotterContribution.Commands.OPEN.label, label: SerialPlotterContribution.Commands.OPEN.label,
@@ -79,8 +69,7 @@ export class PlotterFrontendContribution extends Contribution {
}); });
} }
async startPlotter(): Promise<void> { async connect(): Promise<void> {
await this.monitorManagerProxy.startMonitor();
if (!!this.window) { if (!!this.window) {
this.window.focus(); this.window.focus();
return; return;
@@ -94,10 +83,29 @@ export class PlotterFrontendContribution extends Contribution {
} }
protected async open(wsPort: number): Promise<void> { protected async open(wsPort: number): Promise<void> {
const initConfig = { const board = this.boardsServiceProvider.boardsConfig.selectedBoard;
const port = this.boardsServiceProvider.boardsConfig.selectedPort;
let baudrates: number[] = [];
let currentBaudrate = -1;
if (board && port) {
const { pluggableMonitorSettings } =
this.monitorManagerProxy.getCurrentSettings(board, port);
if (pluggableMonitorSettings && 'baudrate' in pluggableMonitorSettings) {
// Convert from string to numbers
baudrates = pluggableMonitorSettings['baudrate'].values.map((b) => +b);
currentBaudrate = +pluggableMonitorSettings['baudrate'].selectedValue;
}
}
const initConfig: Partial<SerialPlotter.Config> = {
baudrates,
currentBaudrate,
currentLineEnding: this.model.lineEnding,
darkTheme: this.themeService.getCurrentTheme().type === 'dark', darkTheme: this.themeService.getCurrentTheme().type === 'dark',
wsPort, wsPort,
serialPort: this.model.serialPort, interpolate: this.model.interpolate,
connected: await this.monitorManagerProxy.isWSConnected(),
serialPort: this.boardsServiceProvider.boardsConfig.selectedPort?.address,
}; };
const urlWithParams = queryString.stringifyUrl( const urlWithParams = queryString.stringifyUrl(
{ {
@@ -108,11 +116,4 @@ export class PlotterFrontendContribution extends Contribution {
); );
this.window = window.open(urlWithParams, 'serialPlotter'); this.window = window.open(urlWithParams, 'serialPlotter');
} }
protected async reset(): Promise<void> {
if (!!this.window) {
this.window.close();
await this.startPlotter();
}
}
} }

View File

@@ -0,0 +1,26 @@
export namespace SerialPlotter {
export type Config = {
currentBaudrate: number;
baudrates: number[];
currentLineEnding: string;
darkTheme: boolean;
wsPort: number;
interpolate: boolean;
serialPort: string;
connected: boolean;
generate?: boolean;
};
export namespace Protocol {
export enum Command {
PLOTTER_SET_BAUDRATE = 'PLOTTER_SET_BAUDRATE',
PLOTTER_SET_LINE_ENDING = 'PLOTTER_SET_LINE_ENDING',
PLOTTER_SET_INTERPOLATE = 'PLOTTER_SET_INTERPOLATE',
PLOTTER_SEND_MESSAGE = 'PLOTTER_SEND_MESSAGE',
MIDDLEWARE_CONFIG_CHANGED = 'MIDDLEWARE_CONFIG_CHANGED',
}
export type Message = {
command: SerialPlotter.Protocol.Command;
data?: any;
};
}
}

View File

@@ -1,4 +1,4 @@
import { injectable, inject } from '@theia/core/shared/inversify'; import { injectable, inject } from 'inversify';
import { StorageService } from '@theia/core/lib/browser/storage-service'; import { StorageService } from '@theia/core/lib/browser/storage-service';
import { import {
Command, Command,

View File

@@ -8,8 +8,3 @@
.monaco-list-row.show-file-icons.focused { .monaco-list-row.show-file-icons.focused {
background-color: #d6ebff; background-color: #d6ebff;
} }
.monaco-editor .view-overlays .compiler-error {
background-color: var(--theia-inputValidation-errorBackground);
opacity: 0.4 !important;
}

View File

@@ -27,9 +27,8 @@
} }
.ide-updater-dialog .changelog-container { .ide-updater-dialog .changelog-container {
color: var(--theia-dropdown-foreground); background: white;
background-color: var(--theia-dropdown-background); border: 1px solid #dae3e3;
border: 1px solid var(--theia-tree-indentGuidesStroke);
border-radius: 2px; border-radius: 2px;
font-size: 12px; font-size: 12px;
height: 180px; height: 180px;

View File

@@ -108,21 +108,3 @@ button.theia-button.main {
.fa-reload { .fa-reload {
font-size: 14px; font-size: 14px;
} }
/* restore the old Theia spinner */
/* https://github.com/eclipse-theia/theia/pull/10761#issuecomment-1131476318 */
.old-theia-preload {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 50000;
background: var(--theia-editor-background);
background-image: var(--theia-preloader);
background-size: 60px 60px;
background-repeat: no-repeat;
background-attachment: fixed;
background-position: center;
transition: opacity 0.8s;
}

View File

@@ -1,10 +0,0 @@
import { AboutDialog as TheiaAboutDialog } from '@theia/core/lib/browser/about-dialog';
import { duration } from '../../../common/decorators';
export class AboutDialog extends TheiaAboutDialog {
@duration({ name: 'theia-about#init' })
protected override async init(): Promise<void> {
// NOOP
// IDE2 has a custom about dialog, so it does not make sense to collect Theia extensions at startup time.
}
}

View File

@@ -1,4 +1,4 @@
import { injectable, inject } from '@theia/core/shared/inversify'; import { injectable, inject } from 'inversify';
import { EditorWidget } from '@theia/editor/lib/browser'; import { EditorWidget } from '@theia/editor/lib/browser';
import { CommandService } from '@theia/core/lib/common/command'; import { CommandService } from '@theia/core/lib/common/command';
import { MessageService } from '@theia/core/lib/common/message-service'; import { MessageService } from '@theia/core/lib/common/message-service';
@@ -15,7 +15,7 @@ import {
} from '@theia/core/lib/browser'; } from '@theia/core/lib/browser';
import { Sketch } from '../../../common/protocol'; import { Sketch } from '../../../common/protocol';
import { SaveAsSketch } from '../../contributions/save-as-sketch'; import { SaveAsSketch } from '../../contributions/save-as-sketch';
import { CurrentSketch, SketchesServiceClientImpl } from '../../../common/protocol/sketches-service-client-impl'; import { SketchesServiceClientImpl } from '../../../common/protocol/sketches-service-client-impl';
import { nls } from '@theia/core/lib/common'; import { nls } from '@theia/core/lib/common';
import URI from '@theia/core/lib/common/uri'; import URI from '@theia/core/lib/common/uri';
@@ -33,7 +33,7 @@ export class ApplicationShell extends TheiaApplicationShell {
@inject(ConnectionStatusService) @inject(ConnectionStatusService)
protected readonly connectionStatusService: ConnectionStatusService; protected readonly connectionStatusService: ConnectionStatusService;
protected override track(widget: Widget): void { protected track(widget: Widget): void {
super.track(widget); super.track(widget);
if (widget instanceof OutputWidget) { if (widget instanceof OutputWidget) {
widget.title.closable = false; // TODO: https://arduino.slack.com/archives/C01698YT7S4/p1598011990133700 widget.title.closable = false; // TODO: https://arduino.slack.com/archives/C01698YT7S4/p1598011990133700
@@ -41,7 +41,7 @@ export class ApplicationShell extends TheiaApplicationShell {
if (widget instanceof EditorWidget) { if (widget instanceof EditorWidget) {
// Make the editor un-closeable asynchronously. // Make the editor un-closeable asynchronously.
this.sketchesServiceClient.currentSketch().then((sketch) => { this.sketchesServiceClient.currentSketch().then((sketch) => {
if (CurrentSketch.isValid(sketch)) { if (sketch) {
if (!this.isSketchFile(widget.editor.uri, sketch.uri)) { if (!this.isSketchFile(widget.editor.uri, sketch.uri)) {
return; return;
} }
@@ -61,7 +61,7 @@ export class ApplicationShell extends TheiaApplicationShell {
return false; return false;
} }
override async addWidget( async addWidget(
widget: Widget, widget: Widget,
options: Readonly<TheiaApplicationShell.WidgetOptions> = {} options: Readonly<TheiaApplicationShell.WidgetOptions> = {}
): Promise<void> { ): Promise<void> {
@@ -87,19 +87,19 @@ export class ApplicationShell extends TheiaApplicationShell {
return super.addWidget(widget, { ...options, ref }); return super.addWidget(widget, { ...options, ref });
} }
override handleEvent(): boolean { handleEvent(): boolean {
// NOOP, dragging has been disabled // NOOP, dragging has been disabled
return false; return false
} }
// Avoid hiding top panel as we use it for arduino toolbar // Avoid hiding top panel as we use it for arduino toolbar
protected override createTopPanel(): Panel { protected createTopPanel(): Panel {
const topPanel = super.createTopPanel(); const topPanel = super.createTopPanel();
topPanel.show(); topPanel.show();
return topPanel; return topPanel;
} }
override async saveAll(): Promise<void> { async saveAll(): Promise<void> {
if ( if (
this.connectionStatusService.currentStatus === ConnectionStatus.OFFLINE this.connectionStatusService.currentStatus === ConnectionStatus.OFFLINE
) { ) {
@@ -130,5 +130,5 @@ DockPanel.prototype.handleEvent = function (event) {
case 'p-drop': case 'p-drop':
return; return;
} }
originalHandleEvent.bind(this)(event); originalHandleEvent(event);
}; };

View File

@@ -1,4 +1,4 @@
import { injectable } from '@theia/core/shared/inversify'; import { injectable } from 'inversify';
import { import {
BrowserMainMenuFactory as TheiaBrowserMainMenuFactory, BrowserMainMenuFactory as TheiaBrowserMainMenuFactory,
MenuBarWidget, MenuBarWidget,
@@ -12,12 +12,12 @@ export class BrowserMainMenuFactory
{ {
protected menuBar: MenuBarWidget | undefined; protected menuBar: MenuBarWidget | undefined;
override createMenuBar(): MenuBarWidget { createMenuBar(): MenuBarWidget {
this.menuBar = super.createMenuBar(); this.menuBar = super.createMenuBar();
return this.menuBar; return this.menuBar;
} }
update(): void { update() {
if (this.menuBar) { if (this.menuBar) {
this.menuBar.clearMenus(); this.menuBar.clearMenus();
this.fillMenuBar(this.menuBar); this.fillMenuBar(this.menuBar);

View File

@@ -1,5 +1,5 @@
import '../../../../src/browser/style/browser-menu.css'; import '../../../../src/browser/style/browser-menu.css';
import { ContainerModule } from '@theia/core/shared/inversify'; import { ContainerModule } from 'inversify';
import { import {
BrowserMenuBarContribution, BrowserMenuBarContribution,
BrowserMainMenuFactory as TheiaBrowserMainMenuFactory, BrowserMainMenuFactory as TheiaBrowserMainMenuFactory,

View File

@@ -1,10 +1,10 @@
import { injectable } from '@theia/core/shared/inversify'; import { injectable } from 'inversify';
import { FrontendApplication } from '@theia/core/lib/browser'; import { FrontendApplication } from '@theia/core/lib/browser';
import { BrowserMenuBarContribution } from '@theia/core/lib/browser/menu/browser-menu-plugin'; import { BrowserMenuBarContribution } from '@theia/core/lib/browser/menu/browser-menu-plugin';
@injectable() @injectable()
export class ArduinoMenuContribution extends BrowserMenuBarContribution { export class ArduinoMenuContribution extends BrowserMenuBarContribution {
override onStart(app: FrontendApplication): void { onStart(app: FrontendApplication): void {
const menu = this.factory.createMenuBar(); const menu = this.factory.createMenuBar();
app.shell.addWidget(menu, { area: 'top' }); app.shell.addWidget(menu, { area: 'top' });
} }

Some files were not shown because too many files have changed in this diff Show More