mirror of
https://github.com/arduino/arduino-ide.git
synced 2025-07-19 09:16:40 +00:00
chore: updated to Theia 1.37.0
- Updated `@theia/*` to `1.37.0`. - Fixed all `yarn audit` security vulnerabilities. - Updated to `electron@23.2.4`: - `contextIsolation` is `true`, - `nodeIntegration` is `false`, and the - `webpack` target is moved from `electron-renderer` to `web`. - Updated to `typescript@4.9.3`. - Updated the `eslint` plugins. - Added the new `Light High Contrast` theme to the IDE2. - High contrast themes use Theia APIs for style adjustments. - Support for ESM modules: `"moduleResolution": "node16"`. - Node.js >= 16.14 is required. - VISX langage packs were bumped to `1.70.0`. - Removed undesired editor context menu items. (Closes #1394) Signed-off-by: Akos Kitta <a.kitta@arduino.cc>
This commit is contained in:
parent
964ea3bc0c
commit
192aac5a81
@ -16,6 +16,7 @@ module.exports = {
|
|||||||
'docs/*',
|
'docs/*',
|
||||||
'scripts/*',
|
'scripts/*',
|
||||||
'electron-app/*',
|
'electron-app/*',
|
||||||
|
'!electron-app/webpack.config.js',
|
||||||
'plugins/*',
|
'plugins/*',
|
||||||
'arduino-ide-extension/src/node/cli-protocol',
|
'arduino-ide-extension/src/node/cli-protocol',
|
||||||
],
|
],
|
||||||
|
4
.github/workflows/build.yml
vendored
4
.github/workflows/build.yml
vendored
@ -57,10 +57,10 @@ jobs:
|
|||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Install Node.js 16.x
|
- name: Install Node.js 16.14
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: '16.x'
|
node-version: '16.14'
|
||||||
registry-url: 'https://registry.npmjs.org'
|
registry-url: 'https://registry.npmjs.org'
|
||||||
|
|
||||||
- name: Install Python 3.x
|
- name: Install Python 3.x
|
||||||
|
4
.github/workflows/check-i18n-task.yml
vendored
4
.github/workflows/check-i18n-task.yml
vendored
@ -29,10 +29,10 @@ jobs:
|
|||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Install Node.js 16.x
|
- name: Install Node.js 16.14
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: '16.x'
|
node-version: '16.14'
|
||||||
registry-url: 'https://registry.npmjs.org'
|
registry-url: 'https://registry.npmjs.org'
|
||||||
|
|
||||||
- name: Install Go
|
- name: Install Go
|
||||||
|
4
.github/workflows/i18n-nightly-push.yml
vendored
4
.github/workflows/i18n-nightly-push.yml
vendored
@ -16,10 +16,10 @@ jobs:
|
|||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Install Node.js 16.x
|
- name: Install Node.js 16.14
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: '16.x'
|
node-version: '16.14'
|
||||||
registry-url: 'https://registry.npmjs.org'
|
registry-url: 'https://registry.npmjs.org'
|
||||||
|
|
||||||
- name: Install Go
|
- name: Install Go
|
||||||
|
4
.github/workflows/i18n-weekly-pull.yml
vendored
4
.github/workflows/i18n-weekly-pull.yml
vendored
@ -16,10 +16,10 @@ jobs:
|
|||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Install Node.js 16.x
|
- name: Install Node.js 16.14
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: '16.x'
|
node-version: '16.14'
|
||||||
registry-url: 'https://registry.npmjs.org'
|
registry-url: 'https://registry.npmjs.org'
|
||||||
|
|
||||||
- name: Install Go
|
- name: Install Go
|
||||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -7,7 +7,7 @@ build/
|
|||||||
arduino-ide-extension/Examples/
|
arduino-ide-extension/Examples/
|
||||||
!electron/build/
|
!electron/build/
|
||||||
src-gen/
|
src-gen/
|
||||||
webpack.config.js
|
electron/build/webpack.config.js
|
||||||
gen-webpack.config.js
|
gen-webpack.config.js
|
||||||
.DS_Store
|
.DS_Store
|
||||||
# switching from `electron` to `browser` in dev mode.
|
# switching from `electron` to `browser` in dev mode.
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
"compose-changelog": "node ./scripts/compose-changelog.js",
|
"compose-changelog": "node ./scripts/compose-changelog.js",
|
||||||
"download-cli": "node ./scripts/download-cli.js",
|
"download-cli": "node ./scripts/download-cli.js",
|
||||||
"download-fwuploader": "node ./scripts/download-fwuploader.js",
|
"download-fwuploader": "node ./scripts/download-fwuploader.js",
|
||||||
"copy-i18n": "npx ncp ../i18n ./build/i18n",
|
"copy-i18n": "ncp ../i18n ./build/i18n",
|
||||||
"download-ls": "node ./scripts/download-ls.js",
|
"download-ls": "node ./scripts/download-ls.js",
|
||||||
"download-examples": "node ./scripts/download-examples.js",
|
"download-examples": "node ./scripts/download-examples.js",
|
||||||
"generate-protocol": "node ./scripts/generate-protocol.js",
|
"generate-protocol": "node ./scripts/generate-protocol.js",
|
||||||
@ -21,28 +21,28 @@
|
|||||||
"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.8.14",
|
||||||
"@theia/application-package": "1.31.1",
|
"@theia/application-package": "1.37.0",
|
||||||
"@theia/core": "1.31.1",
|
"@theia/core": "1.37.0",
|
||||||
"@theia/debug": "1.31.1",
|
"@theia/debug": "1.37.0",
|
||||||
"@theia/editor": "1.31.1",
|
"@theia/editor": "1.37.0",
|
||||||
"@theia/electron": "1.31.1",
|
"@theia/electron": "1.37.0",
|
||||||
"@theia/filesystem": "1.31.1",
|
"@theia/filesystem": "1.37.0",
|
||||||
"@theia/keymaps": "1.31.1",
|
"@theia/keymaps": "1.37.0",
|
||||||
"@theia/markers": "1.31.1",
|
"@theia/markers": "1.37.0",
|
||||||
"@theia/messages": "1.31.1",
|
"@theia/messages": "1.37.0",
|
||||||
"@theia/monaco": "1.31.1",
|
"@theia/monaco": "1.37.0",
|
||||||
"@theia/monaco-editor-core": "1.67.2",
|
"@theia/monaco-editor-core": "1.72.3",
|
||||||
"@theia/navigator": "1.31.1",
|
"@theia/navigator": "1.37.0",
|
||||||
"@theia/outline-view": "1.31.1",
|
"@theia/outline-view": "1.37.0",
|
||||||
"@theia/output": "1.31.1",
|
"@theia/output": "1.37.0",
|
||||||
"@theia/plugin-ext": "1.31.1",
|
"@theia/plugin-ext": "1.37.0",
|
||||||
"@theia/preferences": "1.31.1",
|
"@theia/preferences": "1.37.0",
|
||||||
"@theia/scm": "1.31.1",
|
"@theia/scm": "1.37.0",
|
||||||
"@theia/search-in-workspace": "1.31.1",
|
"@theia/search-in-workspace": "1.37.0",
|
||||||
"@theia/terminal": "1.31.1",
|
"@theia/terminal": "1.37.0",
|
||||||
"@theia/typehierarchy": "1.31.1",
|
"@theia/typehierarchy": "1.37.0",
|
||||||
"@theia/workspace": "1.31.1",
|
"@theia/workspace": "1.37.0",
|
||||||
"@tippyjs/react": "^4.2.5",
|
"@tippyjs/react": "^4.2.5",
|
||||||
"@types/auth0-js": "^9.14.0",
|
"@types/auth0-js": "^9.14.0",
|
||||||
"@types/btoa": "^1.2.3",
|
"@types/btoa": "^1.2.3",
|
||||||
@ -51,6 +51,7 @@
|
|||||||
"@types/glob": "^7.2.0",
|
"@types/glob": "^7.2.0",
|
||||||
"@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/jsdom": "^21.1.1",
|
||||||
"@types/keytar": "^4.4.0",
|
"@types/keytar": "^4.4.0",
|
||||||
"@types/lodash.debounce": "^4.0.6",
|
"@types/lodash.debounce": "^4.0.6",
|
||||||
"@types/node-fetch": "^2.5.7",
|
"@types/node-fetch": "^2.5.7",
|
||||||
@ -65,7 +66,7 @@
|
|||||||
"auth0-js": "^9.14.0",
|
"auth0-js": "^9.14.0",
|
||||||
"btoa": "^1.2.1",
|
"btoa": "^1.2.1",
|
||||||
"classnames": "^2.3.1",
|
"classnames": "^2.3.1",
|
||||||
"cpy": "^8.1.2",
|
"cpy": "^10.0.0",
|
||||||
"cross-fetch": "^3.1.5",
|
"cross-fetch": "^3.1.5",
|
||||||
"dateformat": "^3.0.3",
|
"dateformat": "^3.0.3",
|
||||||
"deepmerge": "^4.2.2",
|
"deepmerge": "^4.2.2",
|
||||||
@ -76,8 +77,9 @@
|
|||||||
"glob": "^7.1.6",
|
"glob": "^7.1.6",
|
||||||
"google-protobuf": "^3.20.1",
|
"google-protobuf": "^3.20.1",
|
||||||
"hash.js": "^1.1.7",
|
"hash.js": "^1.1.7",
|
||||||
"is-online": "^9.0.1",
|
"is-online": "^10.0.0",
|
||||||
"js-yaml": "^3.13.1",
|
"js-yaml": "^3.13.1",
|
||||||
|
"jsdom": "^21.1.1",
|
||||||
"jsonc-parser": "^2.2.0",
|
"jsonc-parser": "^2.2.0",
|
||||||
"just-diff": "^5.1.1",
|
"just-diff": "^5.1.1",
|
||||||
"jwt-decode": "^3.1.2",
|
"jwt-decode": "^3.1.2",
|
||||||
@ -88,6 +90,7 @@
|
|||||||
"open": "^8.0.6",
|
"open": "^8.0.6",
|
||||||
"p-debounce": "^2.1.0",
|
"p-debounce": "^2.1.0",
|
||||||
"p-queue": "^2.4.2",
|
"p-queue": "^2.4.2",
|
||||||
|
"process": "^0.11.10",
|
||||||
"ps-tree": "^1.2.0",
|
"ps-tree": "^1.2.0",
|
||||||
"query-string": "^7.0.1",
|
"query-string": "^7.0.1",
|
||||||
"react-disable": "^0.1.1",
|
"react-disable": "^0.1.1",
|
||||||
@ -101,6 +104,7 @@
|
|||||||
"temp": "^0.9.1",
|
"temp": "^0.9.1",
|
||||||
"temp-dir": "^2.0.0",
|
"temp-dir": "^2.0.0",
|
||||||
"tree-kill": "^1.2.1",
|
"tree-kill": "^1.2.1",
|
||||||
|
"util": "^0.12.5",
|
||||||
"which": "^1.3.1"
|
"which": "^1.3.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@ -147,6 +151,9 @@
|
|||||||
"examples"
|
"examples"
|
||||||
],
|
],
|
||||||
"theiaExtensions": [
|
"theiaExtensions": [
|
||||||
|
{
|
||||||
|
"preload": "lib/electron-browser/preload"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"backend": "lib/node/arduino-ide-backend-module",
|
"backend": "lib/node/arduino-ide-backend-module",
|
||||||
"frontend": "lib/browser/arduino-ide-frontend-module"
|
"frontend": "lib/browser/arduino-ide-frontend-module"
|
||||||
@ -157,6 +164,9 @@
|
|||||||
{
|
{
|
||||||
"frontendElectron": "lib/electron-browser/theia/core/electron-window-module"
|
"frontendElectron": "lib/electron-browser/theia/core/electron-window-module"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"frontendElectron": "lib/electron-browser/electron-arduino-module"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"electronMain": "lib/electron-main/arduino-electron-main-module"
|
"electronMain": "lib/electron-main/arduino-electron-main-module"
|
||||||
}
|
}
|
||||||
|
13
arduino-ide-extension/src/browser/app-service.ts
Normal file
13
arduino-ide-extension/src/browser/app-service.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import type { Disposable } from '@theia/core/lib/common/disposable';
|
||||||
|
import type { StartupTasks } from '../electron-common/startup-task';
|
||||||
|
import type { Sketch } from './contributions/contribution';
|
||||||
|
|
||||||
|
export const AppService = Symbol('AppService');
|
||||||
|
export interface AppService {
|
||||||
|
quit(): void;
|
||||||
|
version(): Promise<string>;
|
||||||
|
registerStartupTasksHandler(
|
||||||
|
handler: (tasks: StartupTasks) => void
|
||||||
|
): Disposable;
|
||||||
|
scheduleDeletion(sketch: Sketch): void; // TODO: find a better place
|
||||||
|
}
|
@ -1,41 +1,47 @@
|
|||||||
import * as remote from '@theia/core/electron-shared/@electron/remote';
|
import { ColorContribution } from '@theia/core/lib/browser/color-application-contribution';
|
||||||
|
import { ColorRegistry } from '@theia/core/lib/browser/color-registry';
|
||||||
|
import { CommonMenus } from '@theia/core/lib/browser/common-frontend-contribution';
|
||||||
|
import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application';
|
||||||
|
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
|
||||||
|
import {
|
||||||
|
TabBarToolbarContribution,
|
||||||
|
TabBarToolbarRegistry,
|
||||||
|
} from '@theia/core/lib/browser/shell/tab-bar-toolbar';
|
||||||
|
import {
|
||||||
|
ColorTheme,
|
||||||
|
CssStyleCollector,
|
||||||
|
StylingParticipant,
|
||||||
|
} from '@theia/core/lib/browser/styling-service';
|
||||||
|
import {
|
||||||
|
CommandContribution,
|
||||||
|
CommandRegistry,
|
||||||
|
} from '@theia/core/lib/common/command';
|
||||||
|
import {
|
||||||
|
MAIN_MENU_BAR,
|
||||||
|
MenuContribution,
|
||||||
|
MenuModelRegistry,
|
||||||
|
} from '@theia/core/lib/common/menu';
|
||||||
|
import { MessageService } from '@theia/core/lib/common/message-service';
|
||||||
|
import { nls } from '@theia/core/lib/common/nls';
|
||||||
|
import { isHighContrast } from '@theia/core/lib/common/theme';
|
||||||
|
import { ElectronWindowPreferences } from '@theia/core/lib/electron-browser/window/electron-window-preferences';
|
||||||
import {
|
import {
|
||||||
inject,
|
inject,
|
||||||
injectable,
|
injectable,
|
||||||
postConstruct,
|
postConstruct,
|
||||||
} from '@theia/core/shared/inversify';
|
} from '@theia/core/shared/inversify';
|
||||||
import * as React from '@theia/core/shared/react';
|
import * as React from '@theia/core/shared/react';
|
||||||
import {
|
import { EditorCommands } from '@theia/editor/lib/browser/editor-command';
|
||||||
MAIN_MENU_BAR,
|
import { EditorMainMenu } from '@theia/editor/lib/browser/editor-menu';
|
||||||
MenuContribution,
|
|
||||||
MenuModelRegistry,
|
|
||||||
} from '@theia/core';
|
|
||||||
import { FrontendApplicationContribution } from '@theia/core/lib/browser';
|
|
||||||
import { ColorContribution } from '@theia/core/lib/browser/color-application-contribution';
|
|
||||||
import { ColorRegistry } from '@theia/core/lib/browser/color-registry';
|
|
||||||
import { CommonMenus } from '@theia/core/lib/browser/common-frontend-contribution';
|
|
||||||
import {
|
|
||||||
TabBarToolbarContribution,
|
|
||||||
TabBarToolbarRegistry,
|
|
||||||
} from '@theia/core/lib/browser/shell/tab-bar-toolbar';
|
|
||||||
import { nls } from '@theia/core/lib/common';
|
|
||||||
import {
|
|
||||||
CommandContribution,
|
|
||||||
CommandRegistry,
|
|
||||||
} from '@theia/core/lib/common/command';
|
|
||||||
import { MessageService } from '@theia/core/lib/common/message-service';
|
|
||||||
import { EditorCommands, EditorMainMenu } from '@theia/editor/lib/browser';
|
|
||||||
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 } from '@theia/navigator/lib/browser/navigator-contribution';
|
||||||
import { TerminalMenus } from '@theia/terminal/lib/browser/terminal-frontend-contribution';
|
import { TerminalMenus } from '@theia/terminal/lib/browser/terminal-frontend-contribution';
|
||||||
import { ElectronWindowPreferences } from '@theia/core/lib/electron-browser/window/electron-window-preferences';
|
|
||||||
import { BoardsServiceProvider } from './boards/boards-service-provider';
|
import { BoardsServiceProvider } from './boards/boards-service-provider';
|
||||||
import { BoardsToolBarItem } from './boards/boards-toolbar-item';
|
import { BoardsToolBarItem } from './boards/boards-toolbar-item';
|
||||||
import { ArduinoMenus } from './menu/arduino-menus';
|
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 { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
|
|
||||||
import { SerialPlotterContribution } from './serial/plotter/plotter-frontend-contribution';
|
import { SerialPlotterContribution } from './serial/plotter/plotter-frontend-contribution';
|
||||||
|
import { ArduinoToolbar } from './toolbar/arduino-toolbar';
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class ArduinoFrontendContribution
|
export class ArduinoFrontendContribution
|
||||||
@ -44,7 +50,8 @@ export class ArduinoFrontendContribution
|
|||||||
TabBarToolbarContribution,
|
TabBarToolbarContribution,
|
||||||
CommandContribution,
|
CommandContribution,
|
||||||
MenuContribution,
|
MenuContribution,
|
||||||
ColorContribution
|
ColorContribution,
|
||||||
|
StylingParticipant
|
||||||
{
|
{
|
||||||
@inject(MessageService)
|
@inject(MessageService)
|
||||||
private readonly messageService: MessageService;
|
private readonly messageService: MessageService;
|
||||||
@ -80,8 +87,7 @@ export class ArduinoFrontendContribution
|
|||||||
switch (event.preferenceName) {
|
switch (event.preferenceName) {
|
||||||
case 'window.zoomLevel':
|
case 'window.zoomLevel':
|
||||||
if (typeof event.newValue === 'number') {
|
if (typeof event.newValue === 'number') {
|
||||||
const webContents = remote.getCurrentWebContents();
|
window.electronTheiaCore.setZoomLevel(event.newValue || 0);
|
||||||
webContents.setZoomLevel(event.newValue || 0);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -89,10 +95,9 @@ export class ArduinoFrontendContribution
|
|||||||
});
|
});
|
||||||
this.appStateService.reachedState('ready').then(() =>
|
this.appStateService.reachedState('ready').then(() =>
|
||||||
this.electronWindowPreferences.ready.then(() => {
|
this.electronWindowPreferences.ready.then(() => {
|
||||||
const webContents = remote.getCurrentWebContents();
|
|
||||||
const zoomLevel =
|
const zoomLevel =
|
||||||
this.electronWindowPreferences.get('window.zoomLevel');
|
this.electronWindowPreferences.get('window.zoomLevel');
|
||||||
webContents.setZoomLevel(zoomLevel);
|
window.electronTheiaCore.setZoomLevel(zoomLevel);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -168,7 +173,8 @@ export class ArduinoFrontendContribution
|
|||||||
defaults: {
|
defaults: {
|
||||||
dark: 'button.background',
|
dark: 'button.background',
|
||||||
light: 'button.background',
|
light: 'button.background',
|
||||||
hc: 'activityBar.inactiveForeground',
|
hcDark: 'activityBar.inactiveForeground',
|
||||||
|
hcLight: 'activityBar.inactiveForeground',
|
||||||
},
|
},
|
||||||
description:
|
description:
|
||||||
'Background color of the toolbar items. Such as Upload, Verify, etc.',
|
'Background color of the toolbar items. Such as Upload, Verify, etc.',
|
||||||
@ -178,7 +184,8 @@ export class ArduinoFrontendContribution
|
|||||||
defaults: {
|
defaults: {
|
||||||
dark: 'button.hoverBackground',
|
dark: 'button.hoverBackground',
|
||||||
light: 'button.hoverBackground',
|
light: 'button.hoverBackground',
|
||||||
hc: 'button.background',
|
hcDark: 'button.background',
|
||||||
|
hcLight: 'button.background',
|
||||||
},
|
},
|
||||||
description:
|
description:
|
||||||
'Background color of the toolbar items when hovering over them. Such as Upload, Verify, etc.',
|
'Background color of the toolbar items when hovering over them. Such as Upload, Verify, etc.',
|
||||||
@ -188,7 +195,8 @@ export class ArduinoFrontendContribution
|
|||||||
defaults: {
|
defaults: {
|
||||||
dark: 'secondaryButton.foreground',
|
dark: 'secondaryButton.foreground',
|
||||||
light: 'button.foreground',
|
light: 'button.foreground',
|
||||||
hc: 'activityBar.inactiveForeground',
|
hcDark: 'activityBar.inactiveForeground',
|
||||||
|
hcLight: 'activityBar.inactiveForeground',
|
||||||
},
|
},
|
||||||
description:
|
description:
|
||||||
'Foreground color of the toolbar items. Such as Serial Monitor and Serial Plotter',
|
'Foreground color of the toolbar items. Such as Serial Monitor and Serial Plotter',
|
||||||
@ -198,7 +206,8 @@ export class ArduinoFrontendContribution
|
|||||||
defaults: {
|
defaults: {
|
||||||
dark: 'secondaryButton.hoverBackground',
|
dark: 'secondaryButton.hoverBackground',
|
||||||
light: 'button.hoverBackground',
|
light: 'button.hoverBackground',
|
||||||
hc: 'textLink.foreground',
|
hcDark: 'textLink.foreground',
|
||||||
|
hcLight: 'textLink.foreground',
|
||||||
},
|
},
|
||||||
description:
|
description:
|
||||||
'Background color of the toolbar items when hovering over them, such as "Serial Monitor" and "Serial Plotter"',
|
'Background color of the toolbar items when hovering over them, such as "Serial Monitor" and "Serial Plotter"',
|
||||||
@ -208,7 +217,8 @@ export class ArduinoFrontendContribution
|
|||||||
defaults: {
|
defaults: {
|
||||||
dark: 'editor.selectionBackground',
|
dark: 'editor.selectionBackground',
|
||||||
light: 'editor.selectionBackground',
|
light: 'editor.selectionBackground',
|
||||||
hc: 'textPreformat.foreground',
|
hcDark: 'textPreformat.foreground',
|
||||||
|
hcLight: 'textPreformat.foreground',
|
||||||
},
|
},
|
||||||
description:
|
description:
|
||||||
'Toggle color of the toolbar items when they are currently toggled (the command is in progress)',
|
'Toggle color of the toolbar items when they are currently toggled (the command is in progress)',
|
||||||
@ -218,37 +228,38 @@ export class ArduinoFrontendContribution
|
|||||||
defaults: {
|
defaults: {
|
||||||
dark: 'dropdown.border',
|
dark: 'dropdown.border',
|
||||||
light: 'dropdown.border',
|
light: 'dropdown.border',
|
||||||
hc: 'dropdown.border',
|
hcDark: 'dropdown.border',
|
||||||
|
hcLight: 'dropdown.border',
|
||||||
},
|
},
|
||||||
description: 'Border color of the Board Selector.',
|
description: 'Border color of the Board Selector.',
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
id: 'arduino.toolbar.dropdown.borderActive',
|
id: 'arduino.toolbar.dropdown.borderActive',
|
||||||
defaults: {
|
defaults: {
|
||||||
dark: 'focusBorder',
|
dark: 'focusBorder',
|
||||||
light: 'focusBorder',
|
light: 'focusBorder',
|
||||||
hc: 'focusBorder',
|
hcDark: 'focusBorder',
|
||||||
|
hcLight: 'focusBorder',
|
||||||
},
|
},
|
||||||
description: "Border color of the Board Selector when it's active",
|
description: "Border color of the Board Selector when it's active",
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
id: 'arduino.toolbar.dropdown.background',
|
id: 'arduino.toolbar.dropdown.background',
|
||||||
defaults: {
|
defaults: {
|
||||||
dark: 'tab.unfocusedActiveBackground',
|
dark: 'tab.unfocusedActiveBackground',
|
||||||
light: 'dropdown.background',
|
light: 'dropdown.background',
|
||||||
hc: 'dropdown.background',
|
hcDark: 'dropdown.background',
|
||||||
|
hcLight: 'dropdown.background',
|
||||||
},
|
},
|
||||||
description: 'Background color of the Board Selector.',
|
description: 'Background color of the Board Selector.',
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
id: 'arduino.toolbar.dropdown.label',
|
id: 'arduino.toolbar.dropdown.label',
|
||||||
defaults: {
|
defaults: {
|
||||||
dark: 'dropdown.foreground',
|
dark: 'dropdown.foreground',
|
||||||
light: 'dropdown.foreground',
|
light: 'dropdown.foreground',
|
||||||
hc: 'dropdown.foreground',
|
hcDark: 'dropdown.foreground',
|
||||||
|
hcLight: 'dropdown.foreground',
|
||||||
},
|
},
|
||||||
description: 'Font color of the Board Selector.',
|
description: 'Font color of the Board Selector.',
|
||||||
},
|
},
|
||||||
@ -257,7 +268,8 @@ export class ArduinoFrontendContribution
|
|||||||
defaults: {
|
defaults: {
|
||||||
dark: 'list.activeSelectionIconForeground',
|
dark: 'list.activeSelectionIconForeground',
|
||||||
light: 'list.activeSelectionIconForeground',
|
light: 'list.activeSelectionIconForeground',
|
||||||
hc: 'list.activeSelectionIconForeground',
|
hcDark: 'list.activeSelectionIconForeground',
|
||||||
|
hcLight: 'list.activeSelectionIconForeground',
|
||||||
},
|
},
|
||||||
description:
|
description:
|
||||||
'Color of the selected protocol icon in the Board Selector.',
|
'Color of the selected protocol icon in the Board Selector.',
|
||||||
@ -267,7 +279,8 @@ export class ArduinoFrontendContribution
|
|||||||
defaults: {
|
defaults: {
|
||||||
dark: 'list.hoverBackground',
|
dark: 'list.hoverBackground',
|
||||||
light: 'list.hoverBackground',
|
light: 'list.hoverBackground',
|
||||||
hc: 'list.hoverBackground',
|
hcDark: 'list.hoverBackground',
|
||||||
|
hcLight: 'list.hoverBackground',
|
||||||
},
|
},
|
||||||
description: 'Background color on hover of the Board Selector options.',
|
description: 'Background color on hover of the Board Selector options.',
|
||||||
},
|
},
|
||||||
@ -276,11 +289,191 @@ export class ArduinoFrontendContribution
|
|||||||
defaults: {
|
defaults: {
|
||||||
dark: 'list.activeSelectionBackground',
|
dark: 'list.activeSelectionBackground',
|
||||||
light: 'list.activeSelectionBackground',
|
light: 'list.activeSelectionBackground',
|
||||||
hc: 'list.activeSelectionBackground',
|
hcDark: 'list.activeSelectionBackground',
|
||||||
|
hcLight: 'list.activeSelectionBackground',
|
||||||
},
|
},
|
||||||
description:
|
description:
|
||||||
'Background color of the selected board in the Board Selector.',
|
'Background color of the selected board in the Board Selector.',
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
registerThemeStyle(theme: ColorTheme, collector: CssStyleCollector): void {
|
||||||
|
const warningForeground = theme.getColor('warningForeground');
|
||||||
|
const warningBackground = theme.getColor('warningBackground');
|
||||||
|
const focusBorder = theme.getColor('focusBorder');
|
||||||
|
const contrastBorder = theme.getColor('contrastBorder');
|
||||||
|
const notificationsBackground = theme.getColor('notifications.background');
|
||||||
|
const buttonBorder = theme.getColor('button.border');
|
||||||
|
const buttonBackground = theme.getColor('button.background') || 'none';
|
||||||
|
const dropdownBackground = theme.getColor('dropdown.background');
|
||||||
|
const arduinoToolbarButtonBackground = theme.getColor(
|
||||||
|
'arduino.toolbar.button.background'
|
||||||
|
);
|
||||||
|
if (isHighContrast(theme.type)) {
|
||||||
|
// toolbar items
|
||||||
|
collector.addRule(`
|
||||||
|
.p-TabBar-toolbar .item.arduino-tool-item.enabled:hover > div.toggle-serial-monitor,
|
||||||
|
.p-TabBar-toolbar .item.arduino-tool-item.enabled:hover > div.toggle-serial-plotter {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
if (contrastBorder) {
|
||||||
|
collector.addRule(`
|
||||||
|
.quick-input-widget {
|
||||||
|
outline: 1px solid ${contrastBorder};
|
||||||
|
outline-offset: -1px;
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
if (focusBorder) {
|
||||||
|
// customized react-select widget
|
||||||
|
collector.addRule(`
|
||||||
|
.arduino-select__option--is-selected {
|
||||||
|
outline: 1px solid ${focusBorder};
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
collector.addRule(`
|
||||||
|
.arduino-select__option--is-focused {
|
||||||
|
outline: 1px dashed ${focusBorder};
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
// boards selector dropdown
|
||||||
|
collector.addRule(`
|
||||||
|
#select-board-dialog .selectBoardContainer .list .item:hover {
|
||||||
|
outline: 1px dashed ${focusBorder};
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
// button hover
|
||||||
|
collector.addRule(`
|
||||||
|
.theia-button:hover,
|
||||||
|
button.theia-button:hover {
|
||||||
|
outline: 1px dashed ${focusBorder};
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
collector.addRule(`
|
||||||
|
.theia-button {
|
||||||
|
border: 1px solid ${focusBorder};
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
collector.addRule(`
|
||||||
|
.component-list-item .header .installed-version:hover:before {
|
||||||
|
background-color: transparent;
|
||||||
|
outline: 1px dashed ${focusBorder};
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
// tree node
|
||||||
|
collector.addRule(`
|
||||||
|
.theia-TreeNode:hover {
|
||||||
|
outline: 1px dashed ${focusBorder};
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
collector.addRule(`
|
||||||
|
.quick-input-list .monaco-list-row.focused,
|
||||||
|
.theia-Tree .theia-TreeNode.theia-mod-selected {
|
||||||
|
outline: 1px dotted ${focusBorder};
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
collector.addRule(`
|
||||||
|
div#select-board-dialog .selectBoardContainer .list .item.selected,
|
||||||
|
.theia-Tree:focus .theia-TreeNode.theia-mod-selected,
|
||||||
|
.theia-Tree .ReactVirtualized__List:focus .theia-TreeNode.theia-mod-selected {
|
||||||
|
outline: 1px solid ${focusBorder};
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
// quick input
|
||||||
|
collector.addRule(`
|
||||||
|
.quick-input-list .monaco-list-row:hover {
|
||||||
|
outline: 1px dashed ${focusBorder};
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
// editor tab-bar
|
||||||
|
collector.addRule(`
|
||||||
|
.p-TabBar.theia-app-centers .p-TabBar-tab.p-mod-closable > .p-TabBar-tabCloseIcon:hover {
|
||||||
|
outline: 1px dashed ${focusBorder};
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
collector.addRule(`
|
||||||
|
#theia-main-content-panel .p-TabBar .p-TabBar-tab:hover {
|
||||||
|
outline: 1px dashed ${focusBorder};
|
||||||
|
outline-offset: -4px;
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
collector.addRule(`
|
||||||
|
#theia-main-content-panel .p-TabBar .p-TabBar-tab.p-mod-current {
|
||||||
|
outline: 1px solid ${focusBorder};
|
||||||
|
outline-offset: -4px;
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
// boards selector dropdown
|
||||||
|
collector.addRule(`
|
||||||
|
.arduino-boards-dropdown-item:hover {
|
||||||
|
outline: 1px dashed ${focusBorder};
|
||||||
|
outline-offset: -2px;
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
if (notificationsBackground) {
|
||||||
|
// notification
|
||||||
|
collector.addRule(`
|
||||||
|
.theia-notification-list-item:hover:not(:focus) {
|
||||||
|
background-color: ${notificationsBackground};
|
||||||
|
outline: 1px dashed ${focusBorder};
|
||||||
|
outline-offset: -2px;
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
if (arduinoToolbarButtonBackground) {
|
||||||
|
// toolbar item
|
||||||
|
collector.addRule(`
|
||||||
|
.item.arduino-tool-item.toggled .arduino-upload-sketch--toolbar,
|
||||||
|
.item.arduino-tool-item.toggled .arduino-verify-sketch--toolbar {
|
||||||
|
background-color: ${arduinoToolbarButtonBackground} !important;
|
||||||
|
outline: 1px solid ${focusBorder};
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
collector.addRule(`
|
||||||
|
.p-TabBar-toolbar .item.arduino-tool-item.enabled:hover > div {
|
||||||
|
background: ${arduinoToolbarButtonBackground};
|
||||||
|
outline: 1px dashed ${focusBorder};
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (dropdownBackground) {
|
||||||
|
// boards selector dropdown
|
||||||
|
collector.addRule(`
|
||||||
|
.arduino-boards-dropdown-item:hover {
|
||||||
|
background: ${dropdownBackground};
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
if (warningForeground && warningBackground) {
|
||||||
|
// <input> widget with inverted foreground and background colors
|
||||||
|
collector.addRule(`
|
||||||
|
.theia-input.warning:focus,
|
||||||
|
.theia-input.warning::placeholder,
|
||||||
|
.theia-input.warning {
|
||||||
|
color: ${warningBackground};
|
||||||
|
background-color: ${warningForeground};
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
if (buttonBorder) {
|
||||||
|
collector.addRule(`
|
||||||
|
button.theia-button,
|
||||||
|
button.theia-button.secondary,
|
||||||
|
.component-list-item .theia-button.secondary.no-border,
|
||||||
|
.component-list-item .theia-button.secondary.no-border:hover {
|
||||||
|
border: 1px solid ${buttonBorder};
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
collector.addRule(`
|
||||||
|
.component-list-item .header .installed-version:before {
|
||||||
|
color: ${buttonBackground};
|
||||||
|
border: 1px solid ${buttonBorder};
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import '../../src/browser/style/index.css';
|
import '../../src/browser/style/index.css';
|
||||||
import { Container, ContainerModule } from '@theia/core/shared/inversify';
|
import { ContainerModule } from '@theia/core/shared/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';
|
||||||
@ -295,7 +295,7 @@ import { CoreErrorHandler } from './contributions/core-error-handler';
|
|||||||
import { CompilerErrors } from './contributions/compiler-errors';
|
import { CompilerErrors } from './contributions/compiler-errors';
|
||||||
import { WidgetManager } from './theia/core/widget-manager';
|
import { WidgetManager } from './theia/core/widget-manager';
|
||||||
import { WidgetManager as TheiaWidgetManager } from '@theia/core/lib/browser/widget-manager';
|
import { WidgetManager as TheiaWidgetManager } from '@theia/core/lib/browser/widget-manager';
|
||||||
import { StartupTasks } from './contributions/startup-task';
|
import { StartupTasksExecutor } from './contributions/startup-tasks-executor';
|
||||||
import { IndexesUpdateProgress } from './contributions/indexes-update-progress';
|
import { IndexesUpdateProgress } from './contributions/indexes-update-progress';
|
||||||
import { Daemon } from './contributions/daemon';
|
import { Daemon } from './contributions/daemon';
|
||||||
import { FirstStartupInstaller } from './contributions/first-startup-installer';
|
import { FirstStartupInstaller } from './contributions/first-startup-installer';
|
||||||
@ -341,16 +341,6 @@ import { TypeHierarchyContribution } from './theia/typehierarchy/type-hierarchy-
|
|||||||
import { TypeHierarchyContribution as TheiaTypeHierarchyContribution } from '@theia/typehierarchy/lib/browser/typehierarchy-contribution';
|
import { TypeHierarchyContribution as TheiaTypeHierarchyContribution } from '@theia/typehierarchy/lib/browser/typehierarchy-contribution';
|
||||||
import { DefaultDebugSessionFactory } from './theia/debug/debug-session-contribution';
|
import { DefaultDebugSessionFactory } from './theia/debug/debug-session-contribution';
|
||||||
import { DebugSessionFactory } from '@theia/debug/lib/browser/debug-session-contribution';
|
import { DebugSessionFactory } from '@theia/debug/lib/browser/debug-session-contribution';
|
||||||
import { DebugToolbar } from './theia/debug/debug-toolbar-widget';
|
|
||||||
import { DebugToolBar as TheiaDebugToolbar } from '@theia/debug/lib/browser/view/debug-toolbar-widget';
|
|
||||||
import { PluginMenuCommandAdapter } from './theia/plugin-ext/plugin-menu-command-adapter';
|
|
||||||
import { PluginMenuCommandAdapter as TheiaPluginMenuCommandAdapter } from '@theia/plugin-ext/lib/main/browser/menus/plugin-menu-command-adapter';
|
|
||||||
import { DebugSessionManager } from './theia/debug/debug-session-manager';
|
|
||||||
import { DebugSessionManager as TheiaDebugSessionManager } from '@theia/debug/lib/browser/debug-session-manager';
|
|
||||||
import { DebugWidget } from '@theia/debug/lib/browser/view/debug-widget';
|
|
||||||
import { DebugViewModel } from '@theia/debug/lib/browser/view/debug-view-model';
|
|
||||||
import { DebugSessionWidget } from '@theia/debug/lib/browser/view/debug-session-widget';
|
|
||||||
import { DebugConfigurationWidget } from '@theia/debug/lib/browser/view/debug-configuration-widget';
|
|
||||||
import { ConfigServiceClient } from './config/config-service-client';
|
import { ConfigServiceClient } from './config/config-service-client';
|
||||||
import { ValidateSketch } from './contributions/validate-sketch';
|
import { ValidateSketch } from './contributions/validate-sketch';
|
||||||
import { RenameCloudSketch } from './contributions/rename-cloud-sketch';
|
import { RenameCloudSketch } from './contributions/rename-cloud-sketch';
|
||||||
@ -361,15 +351,29 @@ import { SidebarBottomMenuWidget as TheiaSidebarBottomMenuWidget } from '@theia/
|
|||||||
import { CreateCloudCopy } from './contributions/create-cloud-copy';
|
import { CreateCloudCopy } from './contributions/create-cloud-copy';
|
||||||
import { FileResourceResolver } from './theia/filesystem/file-resource';
|
import { FileResourceResolver } from './theia/filesystem/file-resource';
|
||||||
import { FileResourceResolver as TheiaFileResourceResolver } from '@theia/filesystem/lib/browser/file-resource';
|
import { FileResourceResolver as TheiaFileResourceResolver } from '@theia/filesystem/lib/browser/file-resource';
|
||||||
|
import { StylingParticipant } from '@theia/core/lib/browser/styling-service';
|
||||||
|
import { MonacoEditorMenuContribution } from './theia/monaco/monaco-menu';
|
||||||
|
import { MonacoEditorMenuContribution as TheiaMonacoEditorMenuContribution } from '@theia/monaco/lib/browser/monaco-menu';
|
||||||
|
|
||||||
|
// Hack to fix copy/cut/paste issue after electron version update in Theia.
|
||||||
|
// https://github.com/eclipse-theia/theia/issues/12487
|
||||||
|
import('@theia/core/lib/browser/common-frontend-contribution.js').then(
|
||||||
|
(theiaCommonContribution) => {
|
||||||
|
theiaCommonContribution['supportCopy'] = true;
|
||||||
|
theiaCommonContribution['supportCut'] = true;
|
||||||
|
theiaCommonContribution['supportPaste'] = true;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||||
// Commands and toolbar items
|
// Commands, colors, theme adjustments, and toolbar items
|
||||||
bind(ArduinoFrontendContribution).toSelf().inSingletonScope();
|
bind(ArduinoFrontendContribution).toSelf().inSingletonScope();
|
||||||
bind(CommandContribution).toService(ArduinoFrontendContribution);
|
bind(CommandContribution).toService(ArduinoFrontendContribution);
|
||||||
bind(MenuContribution).toService(ArduinoFrontendContribution);
|
bind(MenuContribution).toService(ArduinoFrontendContribution);
|
||||||
bind(TabBarToolbarContribution).toService(ArduinoFrontendContribution);
|
bind(TabBarToolbarContribution).toService(ArduinoFrontendContribution);
|
||||||
bind(FrontendApplicationContribution).toService(ArduinoFrontendContribution);
|
bind(FrontendApplicationContribution).toService(ArduinoFrontendContribution);
|
||||||
bind(ColorContribution).toService(ArduinoFrontendContribution);
|
bind(ColorContribution).toService(ArduinoFrontendContribution);
|
||||||
|
bind(StylingParticipant).toService(ArduinoFrontendContribution);
|
||||||
|
|
||||||
bind(ArduinoToolbarContribution).toSelf().inSingletonScope();
|
bind(ArduinoToolbarContribution).toSelf().inSingletonScope();
|
||||||
bind(FrontendApplicationContribution).toService(ArduinoToolbarContribution);
|
bind(FrontendApplicationContribution).toService(ArduinoToolbarContribution);
|
||||||
@ -722,7 +726,7 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
|||||||
Contribution.configure(bind, PlotterFrontendContribution);
|
Contribution.configure(bind, PlotterFrontendContribution);
|
||||||
Contribution.configure(bind, Format);
|
Contribution.configure(bind, Format);
|
||||||
Contribution.configure(bind, CompilerErrors);
|
Contribution.configure(bind, CompilerErrors);
|
||||||
Contribution.configure(bind, StartupTasks);
|
Contribution.configure(bind, StartupTasksExecutor);
|
||||||
Contribution.configure(bind, IndexesUpdateProgress);
|
Contribution.configure(bind, IndexesUpdateProgress);
|
||||||
Contribution.configure(bind, Daemon);
|
Contribution.configure(bind, Daemon);
|
||||||
Contribution.configure(bind, FirstStartupInstaller);
|
Contribution.configure(bind, FirstStartupInstaller);
|
||||||
@ -982,9 +986,7 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
|||||||
// workaround for themes cannot be removed after registration
|
// workaround for themes cannot be removed after registration
|
||||||
// https://github.com/eclipse-theia/theia/issues/11151
|
// https://github.com/eclipse-theia/theia/issues/11151
|
||||||
bind(CleanupObsoleteThemes).toSelf().inSingletonScope();
|
bind(CleanupObsoleteThemes).toSelf().inSingletonScope();
|
||||||
bind(FrontendApplicationContribution).toService(
|
bind(FrontendApplicationContribution).toService(CleanupObsoleteThemes);
|
||||||
CleanupObsoleteThemes
|
|
||||||
);
|
|
||||||
bind(ThemesRegistrationSummary).toSelf().inSingletonScope();
|
bind(ThemesRegistrationSummary).toSelf().inSingletonScope();
|
||||||
bind(MonacoThemeRegistry).toSelf().inSingletonScope();
|
bind(MonacoThemeRegistry).toSelf().inSingletonScope();
|
||||||
rebind(TheiaMonacoThemeRegistry).toService(MonacoThemeRegistry);
|
rebind(TheiaMonacoThemeRegistry).toService(MonacoThemeRegistry);
|
||||||
@ -998,37 +1000,8 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
|||||||
bind(TypeHierarchyContribution).toSelf().inSingletonScope();
|
bind(TypeHierarchyContribution).toSelf().inSingletonScope();
|
||||||
rebind(TheiaTypeHierarchyContribution).toService(TypeHierarchyContribution);
|
rebind(TheiaTypeHierarchyContribution).toService(TypeHierarchyContribution);
|
||||||
|
|
||||||
// patched the debugger for `cortex-debug@1.5.1`
|
|
||||||
// https://github.com/eclipse-theia/theia/issues/11871
|
|
||||||
// https://github.com/eclipse-theia/theia/issues/11879
|
|
||||||
// https://github.com/eclipse-theia/theia/issues/11880
|
|
||||||
// https://github.com/eclipse-theia/theia/issues/11885
|
|
||||||
// https://github.com/eclipse-theia/theia/issues/11886
|
|
||||||
// https://github.com/eclipse-theia/theia/issues/11916
|
|
||||||
// based on: https://github.com/eclipse-theia/theia/compare/master...kittaakos:theia:%2311871
|
|
||||||
bind(DefaultDebugSessionFactory).toSelf().inSingletonScope();
|
bind(DefaultDebugSessionFactory).toSelf().inSingletonScope();
|
||||||
rebind(DebugSessionFactory).toService(DefaultDebugSessionFactory);
|
rebind(DebugSessionFactory).toService(DefaultDebugSessionFactory);
|
||||||
bind(DebugSessionManager).toSelf().inSingletonScope();
|
|
||||||
rebind(TheiaDebugSessionManager).toService(DebugSessionManager);
|
|
||||||
bind(DebugToolbar).toSelf().inSingletonScope();
|
|
||||||
rebind(TheiaDebugToolbar).toService(DebugToolbar);
|
|
||||||
bind(PluginMenuCommandAdapter).toSelf().inSingletonScope();
|
|
||||||
rebind(TheiaPluginMenuCommandAdapter).toService(PluginMenuCommandAdapter);
|
|
||||||
bind(WidgetFactory)
|
|
||||||
.toDynamicValue(({ container }) => ({
|
|
||||||
id: DebugWidget.ID,
|
|
||||||
createWidget: () => {
|
|
||||||
const child = new Container({ defaultScope: 'Singleton' });
|
|
||||||
child.parent = container;
|
|
||||||
child.bind(DebugViewModel).toSelf();
|
|
||||||
child.bind(DebugToolbar).toSelf(); // patched toolbar
|
|
||||||
child.bind(DebugSessionWidget).toSelf();
|
|
||||||
child.bind(DebugConfigurationWidget).toSelf();
|
|
||||||
child.bind(DebugWidget).toSelf();
|
|
||||||
return child.get(DebugWidget);
|
|
||||||
},
|
|
||||||
}))
|
|
||||||
.inSingletonScope();
|
|
||||||
|
|
||||||
bind(SidebarBottomMenuWidget).toSelf();
|
bind(SidebarBottomMenuWidget).toSelf();
|
||||||
rebind(TheiaSidebarBottomMenuWidget).toService(SidebarBottomMenuWidget);
|
rebind(TheiaSidebarBottomMenuWidget).toService(SidebarBottomMenuWidget);
|
||||||
@ -1043,4 +1016,12 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
|||||||
// https://github.com/arduino/arduino-ide/issues/437
|
// https://github.com/arduino/arduino-ide/issues/437
|
||||||
bind(FileResourceResolver).toSelf().inSingletonScope();
|
bind(FileResourceResolver).toSelf().inSingletonScope();
|
||||||
rebind(TheiaFileResourceResolver).toService(FileResourceResolver);
|
rebind(TheiaFileResourceResolver).toService(FileResourceResolver);
|
||||||
|
|
||||||
|
// Full control over the editor context menu to filter undesired menu items contributed by Theia.
|
||||||
|
// https://github.com/arduino/arduino-ide/issues/1394
|
||||||
|
// https://github.com/arduino/arduino-ide/pull/2027#pullrequestreview-1414246614
|
||||||
|
bind(MonacoEditorMenuContribution).toSelf().inSingletonScope();
|
||||||
|
rebind(TheiaMonacoEditorMenuContribution).toService(
|
||||||
|
MonacoEditorMenuContribution
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
@ -9,13 +9,13 @@ import {
|
|||||||
CommandContribution,
|
CommandContribution,
|
||||||
} from '@theia/core/lib/common/command';
|
} from '@theia/core/lib/common/command';
|
||||||
import {
|
import {
|
||||||
|
AuthOptions,
|
||||||
AuthenticationService,
|
AuthenticationService,
|
||||||
AuthenticationServiceClient,
|
AuthenticationServiceClient,
|
||||||
AuthenticationSession,
|
AuthenticationSession,
|
||||||
|
authServerPort,
|
||||||
} from '../../common/protocol/authentication-service';
|
} from '../../common/protocol/authentication-service';
|
||||||
import { CloudUserCommands } from './cloud-user-commands';
|
import { CloudUserCommands } from './cloud-user-commands';
|
||||||
import { serverPort } from '../../node/auth/authentication-server';
|
|
||||||
import { AuthOptions } from '../../node/auth/types';
|
|
||||||
import { ArduinoPreferences } from '../arduino-preferences';
|
import { ArduinoPreferences } from '../arduino-preferences';
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
@ -61,7 +61,7 @@ export class AuthenticationClientService
|
|||||||
|
|
||||||
setOptions(): Promise<void> {
|
setOptions(): Promise<void> {
|
||||||
return this.service.setOptions({
|
return this.service.setOptions({
|
||||||
redirectUri: `http://localhost:${serverPort}/callback`,
|
redirectUri: `http://localhost:${authServerPort}/callback`,
|
||||||
responseType: 'code',
|
responseType: 'code',
|
||||||
clientID: this.arduinoPreferences['arduino.auth.clientID'],
|
clientID: this.arduinoPreferences['arduino.auth.clientID'],
|
||||||
domain: this.arduinoPreferences['arduino.auth.domain'],
|
domain: this.arduinoPreferences['arduino.auth.domain'],
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import * as PQueue from 'p-queue';
|
import PQueue from 'p-queue';
|
||||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
import { inject, injectable } from '@theia/core/shared/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';
|
||||||
|
@ -1,26 +1,27 @@
|
|||||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
|
||||||
import * as moment from 'moment';
|
|
||||||
import * as remote from '@theia/core/electron-shared/@electron/remote';
|
|
||||||
import { isOSX, isWindows } from '@theia/core/lib/common/os';
|
|
||||||
import { ClipboardService } from '@theia/core/lib/browser/clipboard-service';
|
import { ClipboardService } from '@theia/core/lib/browser/clipboard-service';
|
||||||
import { FrontendApplicationConfigProvider } from '@theia/core/lib/browser/frontend-application-config-provider';
|
import { FrontendApplicationConfigProvider } from '@theia/core/lib/browser/frontend-application-config-provider';
|
||||||
import {
|
import { nls } from '@theia/core/lib/common/nls';
|
||||||
Contribution,
|
import { isOSX, isWindows } from '@theia/core/lib/common/os';
|
||||||
Command,
|
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||||
MenuModelRegistry,
|
import moment from 'moment';
|
||||||
CommandRegistry,
|
|
||||||
} from './contribution';
|
|
||||||
import { ArduinoMenus } from '../menu/arduino-menus';
|
|
||||||
import { ConfigService } from '../../common/protocol';
|
import { ConfigService } from '../../common/protocol';
|
||||||
import { nls } from '@theia/core/lib/common';
|
import { AppService } from '../app-service';
|
||||||
|
import { ArduinoMenus } from '../menu/arduino-menus';
|
||||||
|
import {
|
||||||
|
Command,
|
||||||
|
CommandRegistry,
|
||||||
|
Contribution,
|
||||||
|
MenuModelRegistry,
|
||||||
|
} from './contribution';
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class About extends Contribution {
|
export class About extends Contribution {
|
||||||
@inject(ClipboardService)
|
@inject(ClipboardService)
|
||||||
protected readonly clipboardService: ClipboardService;
|
private readonly clipboardService: ClipboardService;
|
||||||
|
|
||||||
@inject(ConfigService)
|
@inject(ConfigService)
|
||||||
protected readonly configService: ConfigService;
|
private readonly configService: ConfigService;
|
||||||
|
@inject(AppService)
|
||||||
|
private readonly appService: AppService;
|
||||||
|
|
||||||
override registerCommands(registry: CommandRegistry): void {
|
override registerCommands(registry: CommandRegistry): void {
|
||||||
registry.registerCommand(About.Commands.ABOUT_APP, {
|
registry.registerCommand(About.Commands.ABOUT_APP, {
|
||||||
@ -40,17 +41,20 @@ export class About extends Contribution {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async showAbout(): Promise<void> {
|
private async showAbout(): Promise<void> {
|
||||||
const version = await this.configService.getVersion();
|
const [appVersion, cliVersion] = await Promise.all([
|
||||||
|
this.appService.version(),
|
||||||
|
this.configService.getVersion(),
|
||||||
|
]);
|
||||||
const buildDate = this.buildDate;
|
const buildDate = this.buildDate;
|
||||||
const detail = (showAll: boolean) =>
|
const detail = (showAll: boolean) =>
|
||||||
nls.localize(
|
nls.localize(
|
||||||
'arduino/about/detail',
|
'arduino/about/detail',
|
||||||
'Version: {0}\nDate: {1}{2}\nCLI Version: {3}\n\n{4}',
|
'Version: {0}\nDate: {1}{2}\nCLI Version: {3}\n\n{4}',
|
||||||
remote.app.getVersion(),
|
appVersion,
|
||||||
buildDate ? buildDate : nls.localize('', 'dev build'),
|
buildDate ? buildDate : nls.localize('', 'dev build'),
|
||||||
buildDate && showAll ? ` (${this.ago(buildDate)})` : '',
|
buildDate && showAll ? ` (${this.ago(buildDate)})` : '',
|
||||||
version,
|
cliVersion,
|
||||||
nls.localize(
|
nls.localize(
|
||||||
'arduino/about/copyright',
|
'arduino/about/copyright',
|
||||||
'Copyright © {0} Arduino SA',
|
'Copyright © {0} Arduino SA',
|
||||||
@ -60,9 +64,7 @@ export class About extends Contribution {
|
|||||||
const ok = nls.localize('vscode/issueMainService/ok', 'OK');
|
const ok = nls.localize('vscode/issueMainService/ok', 'OK');
|
||||||
const copy = nls.localize('vscode/textInputActions/copy', 'Copy');
|
const copy = nls.localize('vscode/textInputActions/copy', 'Copy');
|
||||||
const buttons = !isWindows && !isOSX ? [copy, ok] : [ok, copy];
|
const buttons = !isWindows && !isOSX ? [copy, ok] : [ok, copy];
|
||||||
const { response } = await remote.dialog.showMessageBox(
|
const { response } = await this.dialogService.showMessageBox({
|
||||||
remote.getCurrentWindow(),
|
|
||||||
{
|
|
||||||
message: `${this.applicationName}`,
|
message: `${this.applicationName}`,
|
||||||
title: `${this.applicationName}`,
|
title: `${this.applicationName}`,
|
||||||
type: 'info',
|
type: 'info',
|
||||||
@ -71,23 +73,22 @@ export class About extends Contribution {
|
|||||||
noLink: true,
|
noLink: true,
|
||||||
defaultId: buttons.indexOf(ok),
|
defaultId: buttons.indexOf(ok),
|
||||||
cancelId: buttons.indexOf(ok),
|
cancelId: buttons.indexOf(ok),
|
||||||
}
|
});
|
||||||
);
|
|
||||||
|
|
||||||
if (buttons[response] === copy) {
|
if (buttons[response] === copy) {
|
||||||
await this.clipboardService.writeText(detail(false).trim());
|
await this.clipboardService.writeText(detail(false).trim());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected get applicationName(): string {
|
private get applicationName(): string {
|
||||||
return FrontendApplicationConfigProvider.get().applicationName;
|
return FrontendApplicationConfigProvider.get().applicationName;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected get buildDate(): string | undefined {
|
private get buildDate(): string | undefined {
|
||||||
return FrontendApplicationConfigProvider.get().buildDate;
|
return FrontendApplicationConfigProvider.get().buildDate;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected ago(isoTime: string): string {
|
private ago(isoTime: string): string {
|
||||||
const now = moment(Date.now());
|
const now = moment(Date.now());
|
||||||
const other = moment(isoTime);
|
const other = moment(isoTime);
|
||||||
let result = now.diff(other, 'minute');
|
let result = now.diff(other, 'minute');
|
||||||
|
@ -1,22 +1,21 @@
|
|||||||
|
import { nls } from '@theia/core/lib/common/nls';
|
||||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||||
import * as remote from '@theia/core/electron-shared/@electron/remote';
|
import { FileDialogService } from '@theia/filesystem/lib/browser';
|
||||||
import { ArduinoMenus } from '../menu/arduino-menus';
|
import { ArduinoMenus } from '../menu/arduino-menus';
|
||||||
|
import { CurrentSketch } from '../sketches-service-client-impl';
|
||||||
import {
|
import {
|
||||||
SketchContribution,
|
|
||||||
Command,
|
Command,
|
||||||
CommandRegistry,
|
CommandRegistry,
|
||||||
MenuModelRegistry,
|
MenuModelRegistry,
|
||||||
URI,
|
|
||||||
Sketch,
|
Sketch,
|
||||||
|
SketchContribution,
|
||||||
|
URI,
|
||||||
} from './contribution';
|
} from './contribution';
|
||||||
import { FileDialogService } from '@theia/filesystem/lib/browser';
|
|
||||||
import { nls } from '@theia/core/lib/common';
|
|
||||||
import { CurrentSketch } from '../sketches-service-client-impl';
|
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class AddFile extends SketchContribution {
|
export class AddFile extends SketchContribution {
|
||||||
@inject(FileDialogService)
|
@inject(FileDialogService)
|
||||||
private readonly fileDialogService: FileDialogService;
|
private readonly fileDialogService: FileDialogService; // TODO: use dialogService
|
||||||
|
|
||||||
override registerCommands(registry: CommandRegistry): void {
|
override registerCommands(registry: CommandRegistry): void {
|
||||||
registry.registerCommand(AddFile.Commands.ADD_FILE, {
|
registry.registerCommand(AddFile.Commands.ADD_FILE, {
|
||||||
@ -50,7 +49,7 @@ export class AddFile extends SketchContribution {
|
|||||||
const { uri: targetUri, filename } = this.resolveTarget(sketch, toAddUri);
|
const { uri: targetUri, filename } = this.resolveTarget(sketch, toAddUri);
|
||||||
const exists = await this.fileService.exists(targetUri);
|
const exists = await this.fileService.exists(targetUri);
|
||||||
if (exists) {
|
if (exists) {
|
||||||
const { response } = await remote.dialog.showMessageBox({
|
const { response } = await this.dialogService.showMessageBox({
|
||||||
type: 'question',
|
type: 'question',
|
||||||
title: nls.localize('arduino/contributions/replaceTitle', 'Replace'),
|
title: nls.localize('arduino/contributions/replaceTitle', 'Replace'),
|
||||||
buttons: [
|
buttons: [
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||||
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';
|
||||||
import { ArduinoMenus } from '../menu/arduino-menus';
|
import { ArduinoMenus } from '../menu/arduino-menus';
|
||||||
@ -42,9 +41,7 @@ export class AddZipLibrary extends SketchContribution {
|
|||||||
private async addZipLibrary(): Promise<void> {
|
private async addZipLibrary(): Promise<void> {
|
||||||
const homeUri = await this.envVariableServer.getHomeDirUri();
|
const homeUri = await this.envVariableServer.getHomeDirUri();
|
||||||
const defaultPath = await this.fileService.fsPath(new URI(homeUri));
|
const defaultPath = await this.fileService.fsPath(new URI(homeUri));
|
||||||
const { canceled, filePaths } = await remote.dialog.showOpenDialog(
|
const { canceled, filePaths } = await this.dialogService.showOpenDialog({
|
||||||
remote.getCurrentWindow(),
|
|
||||||
{
|
|
||||||
title: nls.localize(
|
title: nls.localize(
|
||||||
'arduino/selectZip',
|
'arduino/selectZip',
|
||||||
"Select a zip file containing the library you'd like to add"
|
"Select a zip file containing the library you'd like to add"
|
||||||
@ -57,8 +54,7 @@ export class AddZipLibrary extends SketchContribution {
|
|||||||
extensions: ['zip'],
|
extensions: ['zip'],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}
|
});
|
||||||
);
|
|
||||||
if (!canceled && filePaths.length) {
|
if (!canceled && filePaths.length) {
|
||||||
const zipUri = await this.fileSystemExt.getUri(filePaths[0]);
|
const zipUri = await this.fileSystemExt.getUri(filePaths[0]);
|
||||||
try {
|
try {
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { injectable } from '@theia/core/shared/inversify';
|
import { injectable } from '@theia/core/shared/inversify';
|
||||||
import * as remote from '@theia/core/electron-shared/@electron/remote';
|
import dateFormat from 'dateformat';
|
||||||
import * as dateFormat from 'dateformat';
|
|
||||||
import { ArduinoMenus } from '../menu/arduino-menus';
|
import { ArduinoMenus } from '../menu/arduino-menus';
|
||||||
import {
|
import {
|
||||||
SketchContribution,
|
SketchContribution,
|
||||||
@ -39,16 +38,13 @@ export class ArchiveSketch extends SketchContribution {
|
|||||||
const defaultContainerUri = await this.defaultUri();
|
const defaultContainerUri = await this.defaultUri();
|
||||||
const defaultUri = defaultContainerUri.resolve(archiveBasename);
|
const defaultUri = defaultContainerUri.resolve(archiveBasename);
|
||||||
const defaultPath = await this.fileService.fsPath(defaultUri);
|
const defaultPath = await this.fileService.fsPath(defaultUri);
|
||||||
const { filePath, canceled } = await remote.dialog.showSaveDialog(
|
const { filePath, canceled } = await this.dialogService.showSaveDialog({
|
||||||
remote.getCurrentWindow(),
|
|
||||||
{
|
|
||||||
title: nls.localize(
|
title: nls.localize(
|
||||||
'arduino/sketch/saveSketchAs',
|
'arduino/sketch/saveSketchAs',
|
||||||
'Save sketch folder as...'
|
'Save sketch folder as...'
|
||||||
),
|
),
|
||||||
defaultPath,
|
defaultPath,
|
||||||
}
|
});
|
||||||
);
|
|
||||||
if (!filePath || canceled) {
|
if (!filePath || canceled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||||
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 {
|
||||||
DisposableCollection,
|
DisposableCollection,
|
||||||
@ -65,7 +64,7 @@ VID: ${VID}
|
|||||||
PID: ${PID}
|
PID: ${PID}
|
||||||
SN: ${SN}
|
SN: ${SN}
|
||||||
`.trim();
|
`.trim();
|
||||||
await remote.dialog.showMessageBox(remote.getCurrentWindow(), {
|
await this.dialogService.showMessageBox({
|
||||||
message: nls.localize('arduino/board/boardInfo', 'Board Info'),
|
message: nls.localize('arduino/board/boardInfo', 'Board Info'),
|
||||||
title: nls.localize('arduino/board/boardInfo', 'Board Info'),
|
title: nls.localize('arduino/board/boardInfo', 'Board Info'),
|
||||||
type: 'info',
|
type: 'info',
|
||||||
|
@ -1,26 +1,26 @@
|
|||||||
import { injectable } from '@theia/core/shared/inversify';
|
import { Dialog } from '@theia/core/lib/browser/dialogs';
|
||||||
import { toArray } from '@theia/core/shared/@phosphor/algorithm';
|
|
||||||
import * as remote from '@theia/core/electron-shared/@electron/remote';
|
|
||||||
import { MonacoEditor } from '@theia/monaco/lib/browser/monaco-editor';
|
|
||||||
import type { MaybePromise } from '@theia/core/lib/common/types';
|
|
||||||
import type {
|
import type {
|
||||||
FrontendApplication,
|
FrontendApplication,
|
||||||
OnWillStopAction,
|
OnWillStopAction,
|
||||||
} from '@theia/core/lib/browser/frontend-application';
|
} from '@theia/core/lib/browser/frontend-application';
|
||||||
import { nls } from '@theia/core/lib/common/nls';
|
|
||||||
import { ApplicationShell } from '@theia/core/lib/browser/shell/application-shell';
|
import { ApplicationShell } from '@theia/core/lib/browser/shell/application-shell';
|
||||||
|
import { nls } from '@theia/core/lib/common/nls';
|
||||||
|
import type { MaybePromise } from '@theia/core/lib/common/types';
|
||||||
|
import { toArray } from '@theia/core/shared/@phosphor/algorithm';
|
||||||
|
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||||
|
import { MonacoEditor } from '@theia/monaco/lib/browser/monaco-editor';
|
||||||
import { ArduinoMenus } from '../menu/arduino-menus';
|
import { ArduinoMenus } from '../menu/arduino-menus';
|
||||||
|
import { CurrentSketch } from '../sketches-service-client-impl';
|
||||||
|
import { WindowServiceExt } from '../theia/core/window-service-ext';
|
||||||
import {
|
import {
|
||||||
SketchContribution,
|
|
||||||
Command,
|
Command,
|
||||||
CommandRegistry,
|
CommandRegistry,
|
||||||
MenuModelRegistry,
|
|
||||||
KeybindingRegistry,
|
KeybindingRegistry,
|
||||||
|
MenuModelRegistry,
|
||||||
Sketch,
|
Sketch,
|
||||||
|
SketchContribution,
|
||||||
URI,
|
URI,
|
||||||
} from './contribution';
|
} from './contribution';
|
||||||
import { Dialog } from '@theia/core/lib/browser/dialogs';
|
|
||||||
import { CurrentSketch } from '../sketches-service-client-impl';
|
|
||||||
import { SaveAsSketch } from './save-as-sketch';
|
import { SaveAsSketch } from './save-as-sketch';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -28,6 +28,9 @@ import { SaveAsSketch } from './save-as-sketch';
|
|||||||
*/
|
*/
|
||||||
@injectable()
|
@injectable()
|
||||||
export class Close extends SketchContribution {
|
export class Close extends SketchContribution {
|
||||||
|
@inject(WindowServiceExt)
|
||||||
|
private readonly windowServiceExt: WindowServiceExt;
|
||||||
|
|
||||||
private shell: ApplicationShell | undefined;
|
private shell: ApplicationShell | undefined;
|
||||||
|
|
||||||
override onStart(app: FrontendApplication): MaybePromise<void> {
|
override onStart(app: FrontendApplication): MaybePromise<void> {
|
||||||
@ -56,7 +59,7 @@ export class Close extends SketchContribution {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return remote.getCurrentWindow().close();
|
return this.windowServiceExt.close();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -150,9 +153,7 @@ export class Close extends SketchContribution {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async prompt(isTemp: boolean): Promise<Prompt> {
|
private async prompt(isTemp: boolean): Promise<Prompt> {
|
||||||
const { response } = await remote.dialog.showMessageBox(
|
const { response } = await this.dialogService.showMessageBox({
|
||||||
remote.getCurrentWindow(),
|
|
||||||
{
|
|
||||||
message: nls.localize(
|
message: nls.localize(
|
||||||
'arduino/sketch/saveSketch',
|
'arduino/sketch/saveSketch',
|
||||||
'Save your sketch to open it again later.'
|
'Save your sketch to open it again later.'
|
||||||
@ -168,8 +169,7 @@ export class Close extends SketchContribution {
|
|||||||
nls.localizeByDefault(isTemp ? 'Save As...' : 'Save'),
|
nls.localizeByDefault(isTemp ? 'Save As...' : 'Save'),
|
||||||
],
|
],
|
||||||
defaultId: 2, // `Save`/`Save As...` button index is the default.
|
defaultId: 2, // `Save`/`Save As...` button index is the default.
|
||||||
}
|
});
|
||||||
);
|
|
||||||
switch (response) {
|
switch (response) {
|
||||||
case 0:
|
case 0:
|
||||||
return Prompt.DoNotSave;
|
return Prompt.DoNotSave;
|
||||||
|
@ -67,6 +67,7 @@ import { WorkspaceService } from '../theia/workspace/workspace-service';
|
|||||||
import { MainMenuManager } from '../../common/main-menu-manager';
|
import { MainMenuManager } from '../../common/main-menu-manager';
|
||||||
import { ConfigServiceClient } from '../config/config-service-client';
|
import { ConfigServiceClient } from '../config/config-service-client';
|
||||||
import { ApplicationShell } from '@theia/core/lib/browser/shell/application-shell';
|
import { ApplicationShell } from '@theia/core/lib/browser/shell/application-shell';
|
||||||
|
import { DialogService } from '../dialog-service';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
Command,
|
Command,
|
||||||
@ -115,6 +116,9 @@ export abstract class Contribution
|
|||||||
@inject(MainMenuManager)
|
@inject(MainMenuManager)
|
||||||
protected readonly menuManager: MainMenuManager;
|
protected readonly menuManager: MainMenuManager;
|
||||||
|
|
||||||
|
@inject(DialogService)
|
||||||
|
protected readonly dialogService: DialogService;
|
||||||
|
|
||||||
@postConstruct()
|
@postConstruct()
|
||||||
protected init(): void {
|
protected init(): void {
|
||||||
this.appStateService.reachedState('ready').then(() => this.onReady());
|
this.appStateService.reachedState('ready').then(() => this.onReady());
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
import * as remote from '@theia/core/electron-shared/@electron/remote';
|
|
||||||
import { ipcRenderer } from '@theia/core/electron-shared/electron';
|
|
||||||
import { Dialog } from '@theia/core/lib/browser/dialogs';
|
import { Dialog } from '@theia/core/lib/browser/dialogs';
|
||||||
import { NavigatableWidget } from '@theia/core/lib/browser/navigatable-types';
|
import { NavigatableWidget } from '@theia/core/lib/browser/navigatable-types';
|
||||||
import { ApplicationShell } from '@theia/core/lib/browser/shell/application-shell';
|
import { ApplicationShell } from '@theia/core/lib/browser/shell/application-shell';
|
||||||
@ -10,11 +8,11 @@ import URI from '@theia/core/lib/common/uri';
|
|||||||
import type { Widget } from '@theia/core/shared/@phosphor/widgets';
|
import type { Widget } from '@theia/core/shared/@phosphor/widgets';
|
||||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||||
import { SketchesError } from '../../common/protocol';
|
import { SketchesError } from '../../common/protocol';
|
||||||
import { SCHEDULE_DELETION_SIGNAL } from '../../electron-common/electron-messages';
|
|
||||||
import { Sketch } from '../contributions/contribution';
|
import { Sketch } from '../contributions/contribution';
|
||||||
import { isNotFound } from '../create/typings';
|
import { isNotFound } from '../create/typings';
|
||||||
import { Command, CommandRegistry } from './contribution';
|
import { Command, CommandRegistry } from './contribution';
|
||||||
import { CloudSketchContribution } from './cloud-contribution';
|
import { CloudSketchContribution } from './cloud-contribution';
|
||||||
|
import { AppService } from '../app-service';
|
||||||
|
|
||||||
export interface DeleteSketchParams {
|
export interface DeleteSketchParams {
|
||||||
/**
|
/**
|
||||||
@ -38,6 +36,8 @@ export class DeleteSketch extends CloudSketchContribution {
|
|||||||
private readonly shell: ApplicationShell;
|
private readonly shell: ApplicationShell;
|
||||||
@inject(WindowService)
|
@inject(WindowService)
|
||||||
private readonly windowService: WindowService;
|
private readonly windowService: WindowService;
|
||||||
|
@inject(AppService)
|
||||||
|
private readonly appService: AppService;
|
||||||
|
|
||||||
override registerCommands(registry: CommandRegistry): void {
|
override registerCommands(registry: CommandRegistry): void {
|
||||||
registry.registerCommand(DeleteSketch.Commands.DELETE_SKETCH, {
|
registry.registerCommand(DeleteSketch.Commands.DELETE_SKETCH, {
|
||||||
@ -66,7 +66,7 @@ export class DeleteSketch extends CloudSketchContribution {
|
|||||||
}
|
}
|
||||||
const cloudUri = this.createFeatures.cloudUri(sketch);
|
const cloudUri = this.createFeatures.cloudUri(sketch);
|
||||||
if (willNavigateAway !== 'force') {
|
if (willNavigateAway !== 'force') {
|
||||||
const { response } = await remote.dialog.showMessageBox({
|
const { response } = await this.dialogService.showMessageBox({
|
||||||
title: nls.localizeByDefault('Delete'),
|
title: nls.localizeByDefault('Delete'),
|
||||||
type: 'question',
|
type: 'question',
|
||||||
buttons: [Dialog.CANCEL, Dialog.OK],
|
buttons: [Dialog.CANCEL, Dialog.OK],
|
||||||
@ -120,7 +120,7 @@ export class DeleteSketch extends CloudSketchContribution {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private scheduleDeletion(sketch: Sketch): void {
|
private scheduleDeletion(sketch: Sketch): void {
|
||||||
ipcRenderer.send(SCHEDULE_DELETION_SIGNAL, sketch);
|
this.appService.scheduleDeletion(sketch);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async loadSketch(uri: string): Promise<Sketch | undefined> {
|
private async loadSketch(uri: string): Promise<Sketch | undefined> {
|
||||||
|
@ -1,11 +1,7 @@
|
|||||||
import * as PQueue from 'p-queue';
|
import PQueue from 'p-queue';
|
||||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||||
import { CommandHandler, CommandService } from '@theia/core/lib/common/command';
|
import { CommandHandler, CommandService } from '@theia/core/lib/common/command';
|
||||||
import {
|
import { MenuPath, SubMenuOptions } from '@theia/core/lib/common/menu';
|
||||||
MenuPath,
|
|
||||||
CompositeMenuNode,
|
|
||||||
SubMenuOptions,
|
|
||||||
} from '@theia/core/lib/common/menu';
|
|
||||||
import {
|
import {
|
||||||
Disposable,
|
Disposable,
|
||||||
DisposableCollection,
|
DisposableCollection,
|
||||||
@ -143,19 +139,6 @@ export abstract class Examples extends SketchContribution {
|
|||||||
}): void;
|
}): void;
|
||||||
|
|
||||||
override registerMenus(registry: MenuModelRegistry): void {
|
override registerMenus(registry: MenuModelRegistry): void {
|
||||||
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.
|
|
||||||
const index = ArduinoMenus.FILE__EXAMPLES_SUBMENU.length - 1;
|
|
||||||
const menuId = ArduinoMenus.FILE__EXAMPLES_SUBMENU[index];
|
|
||||||
const groupPath =
|
|
||||||
index === 0 ? [] : ArduinoMenus.FILE__EXAMPLES_SUBMENU.slice(0, index);
|
|
||||||
const parent: CompositeMenuNode = (registry as any).findGroup(groupPath);
|
|
||||||
const examples = new CompositeMenuNode(menuId, '', { order: '4' });
|
|
||||||
parent.addNode(examples);
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
console.warn('Could not patch menu ordering.');
|
|
||||||
}
|
|
||||||
// Registering the same submenu multiple times has no side-effect.
|
// Registering the same submenu multiple times has no side-effect.
|
||||||
// TODO: unregister submenu? https://github.com/eclipse-theia/theia/issues/7300
|
// TODO: unregister submenu? https://github.com/eclipse-theia/theia/issues/7300
|
||||||
registry.registerSubmenu(
|
registry.registerSubmenu(
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import * as PQueue from 'p-queue';
|
import PQueue from 'p-queue';
|
||||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
import { inject, injectable } from '@theia/core/shared/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';
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
import { DisposableCollection } from '@theia/core/lib/common/disposable';
|
import {
|
||||||
|
Disposable,
|
||||||
|
DisposableCollection,
|
||||||
|
} from '@theia/core/lib/common/disposable';
|
||||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||||
import { Mutex } from 'async-mutex';
|
import { Mutex } from 'async-mutex';
|
||||||
import {
|
import {
|
||||||
@ -120,6 +123,7 @@ export class InoLanguage extends SketchContribution {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const release = await this.languageServerStartMutex.acquire();
|
const release = await this.languageServerStartMutex.acquire();
|
||||||
|
const toDisposeOnRelease = new DisposableCollection();
|
||||||
try {
|
try {
|
||||||
await this.hostedPluginEvents.didStart;
|
await this.hostedPluginEvents.didStart;
|
||||||
const details = await this.boardsService.getBoardDetails({ fqbn });
|
const details = await this.boardsService.getBoardDetails({ fqbn });
|
||||||
@ -179,12 +183,13 @@ export class InoLanguage extends SketchContribution {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
this.languageServerFqbn = await Promise.race([
|
this.languageServerFqbn = await Promise.race([
|
||||||
new Promise<undefined>((_, reject) =>
|
new Promise<undefined>((_, reject) => {
|
||||||
setTimeout(
|
const timer = setTimeout(
|
||||||
() => reject(new Error(`Timeout after ${20_000} ms.`)),
|
() => reject(new Error(`Timeout after ${20_000} ms.`)),
|
||||||
20_000
|
20_000
|
||||||
)
|
);
|
||||||
),
|
toDisposeOnRelease.push(Disposable.create(() => clearTimeout(timer)));
|
||||||
|
}),
|
||||||
this.commandService.executeCommand<string>(
|
this.commandService.executeCommand<string>(
|
||||||
'arduino.languageserver.start',
|
'arduino.languageserver.start',
|
||||||
{
|
{
|
||||||
@ -206,6 +211,7 @@ export class InoLanguage extends SketchContribution {
|
|||||||
console.log(`Failed to start language server. Original FQBN: ${fqbn}`, e);
|
console.log(`Failed to start language server. Original FQBN: ${fqbn}`, e);
|
||||||
this.languageServerFqbn = undefined;
|
this.languageServerFqbn = undefined;
|
||||||
} finally {
|
} finally {
|
||||||
|
toDisposeOnRelease.dispose();
|
||||||
release();
|
release();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ import {
|
|||||||
import { ArduinoMenus } from '../menu/arduino-menus';
|
import { ArduinoMenus } from '../menu/arduino-menus';
|
||||||
import { CommandRegistry, MaybePromise, nls } from '@theia/core/lib/common';
|
import { CommandRegistry, MaybePromise, nls } from '@theia/core/lib/common';
|
||||||
import { Settings } from '../dialogs/settings/settings';
|
import { Settings } from '../dialogs/settings/settings';
|
||||||
import debounce = require('lodash.debounce');
|
import debounce from 'lodash.debounce';
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class InterfaceScale extends Contribution {
|
export class InterfaceScale extends Contribution {
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { injectable } from '@theia/core/shared/inversify';
|
import { injectable } from '@theia/core/shared/inversify';
|
||||||
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';
|
||||||
import {
|
import {
|
||||||
@ -9,7 +8,7 @@ import {
|
|||||||
MenuModelRegistry,
|
MenuModelRegistry,
|
||||||
KeybindingRegistry,
|
KeybindingRegistry,
|
||||||
} from './contribution';
|
} from './contribution';
|
||||||
import { nls } from '@theia/core/lib/common';
|
import { nls } from '@theia/core/lib/common/nls';
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class OpenSketchExternal extends SketchContribution {
|
export class OpenSketchExternal extends SketchContribution {
|
||||||
@ -41,7 +40,7 @@ export class OpenSketchExternal extends SketchContribution {
|
|||||||
if (exists) {
|
if (exists) {
|
||||||
const fsPath = await this.fileService.fsPath(new URI(uri));
|
const fsPath = await this.fileService.fsPath(new URI(uri));
|
||||||
if (fsPath) {
|
if (fsPath) {
|
||||||
remote.shell.showItemInFolder(fsPath);
|
window.electronTheiaCore.showItemInFolder(fsPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -118,6 +118,7 @@ export class OpenSketchFiles extends SketchContribution {
|
|||||||
fileService: this.fileService,
|
fileService: this.fileService,
|
||||||
sketchesService: this.sketchesService,
|
sketchesService: this.sketchesService,
|
||||||
labelProvider: this.labelProvider,
|
labelProvider: this.labelProvider,
|
||||||
|
dialogService: this.dialogService,
|
||||||
});
|
});
|
||||||
if (movedSketch) {
|
if (movedSketch) {
|
||||||
this.workspaceService.open(new URI(movedSketch.uri), {
|
this.workspaceService.open(new URI(movedSketch.uri), {
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import * as remote from '@theia/core/electron-shared/@electron/remote';
|
|
||||||
import { nls } from '@theia/core/lib/common/nls';
|
import { nls } from '@theia/core/lib/common/nls';
|
||||||
import { injectable } from '@theia/core/shared/inversify';
|
import { injectable } from '@theia/core/shared/inversify';
|
||||||
import { FileService } from '@theia/filesystem/lib/browser/file-service';
|
import { FileService } from '@theia/filesystem/lib/browser/file-service';
|
||||||
@ -18,6 +17,7 @@ import {
|
|||||||
SketchContribution,
|
SketchContribution,
|
||||||
URI,
|
URI,
|
||||||
} from './contribution';
|
} from './contribution';
|
||||||
|
import { DialogService } from '../dialog-service';
|
||||||
|
|
||||||
export type SketchLocation = string | URI | SketchRef;
|
export type SketchLocation = string | URI | SketchRef;
|
||||||
export namespace SketchLocation {
|
export namespace SketchLocation {
|
||||||
@ -83,9 +83,7 @@ export class OpenSketch extends SketchContribution {
|
|||||||
|
|
||||||
private async selectSketch(): Promise<Sketch | undefined> {
|
private async selectSketch(): Promise<Sketch | undefined> {
|
||||||
const defaultPath = await this.defaultPath();
|
const defaultPath = await this.defaultPath();
|
||||||
const { filePaths } = await remote.dialog.showOpenDialog(
|
const { filePaths } = await this.dialogService.showOpenDialog({
|
||||||
remote.getCurrentWindow(),
|
|
||||||
{
|
|
||||||
defaultPath,
|
defaultPath,
|
||||||
properties: ['createDirectory', 'openFile'],
|
properties: ['createDirectory', 'openFile'],
|
||||||
filters: [
|
filters: [
|
||||||
@ -94,8 +92,7 @@ export class OpenSketch extends SketchContribution {
|
|||||||
extensions: ['ino', 'pde'],
|
extensions: ['ino', 'pde'],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}
|
});
|
||||||
);
|
|
||||||
if (!filePaths.length) {
|
if (!filePaths.length) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
@ -115,6 +112,7 @@ export class OpenSketch extends SketchContribution {
|
|||||||
fileService: this.fileService,
|
fileService: this.fileService,
|
||||||
sketchesService: this.sketchesService,
|
sketchesService: this.sketchesService,
|
||||||
labelProvider: this.labelProvider,
|
labelProvider: this.labelProvider,
|
||||||
|
dialogService: this.dialogService,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -134,14 +132,16 @@ export async function promptMoveSketch(
|
|||||||
fileService: FileService;
|
fileService: FileService;
|
||||||
sketchesService: SketchesService;
|
sketchesService: SketchesService;
|
||||||
labelProvider: LabelProvider;
|
labelProvider: LabelProvider;
|
||||||
|
dialogService: DialogService;
|
||||||
}
|
}
|
||||||
): Promise<Sketch | undefined> {
|
): Promise<Sketch | undefined> {
|
||||||
const { fileService, sketchesService, labelProvider } = options;
|
const { fileService, sketchesService, labelProvider, dialogService } =
|
||||||
|
options;
|
||||||
const uri =
|
const uri =
|
||||||
sketchFileUri instanceof URI ? sketchFileUri : new URI(sketchFileUri);
|
sketchFileUri instanceof URI ? sketchFileUri : new URI(sketchFileUri);
|
||||||
const name = uri.path.name;
|
const name = uri.path.name;
|
||||||
const nameWithExt = labelProvider.getName(uri);
|
const nameWithExt = labelProvider.getName(uri);
|
||||||
const { response } = await remote.dialog.showMessageBox({
|
const { response } = await dialogService.showMessageBox({
|
||||||
title: nls.localize('arduino/sketch/moving', 'Moving'),
|
title: nls.localize('arduino/sketch/moving', 'Moving'),
|
||||||
type: 'question',
|
type: 'question',
|
||||||
buttons: [
|
buttons: [
|
||||||
@ -160,7 +160,7 @@ export async function promptMoveSketch(
|
|||||||
const newSketchUri = uri.parent.resolve(name);
|
const newSketchUri = uri.parent.resolve(name);
|
||||||
const exists = await fileService.exists(newSketchUri);
|
const exists = await fileService.exists(newSketchUri);
|
||||||
if (exists) {
|
if (exists) {
|
||||||
await remote.dialog.showMessageBox({
|
await dialogService.showMessageBox({
|
||||||
type: 'error',
|
type: 'error',
|
||||||
title: nls.localize('vscode/dialog/dialogErrorMessage', 'Error'),
|
title: nls.localize('vscode/dialog/dialogErrorMessage', 'Error'),
|
||||||
message: nls.localize(
|
message: nls.localize(
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { injectable } from '@theia/core/shared/inversify';
|
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||||
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 {
|
||||||
Contribution,
|
Contribution,
|
||||||
@ -9,14 +8,18 @@ import {
|
|||||||
CommandRegistry,
|
CommandRegistry,
|
||||||
} 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/nls';
|
||||||
|
import { AppService } from '../app-service';
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class QuitApp extends Contribution {
|
export class QuitApp extends Contribution {
|
||||||
|
@inject(AppService)
|
||||||
|
private readonly appService: AppService;
|
||||||
|
|
||||||
override registerCommands(registry: CommandRegistry): void {
|
override 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: () => this.appService.quit(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import * as remote from '@theia/core/electron-shared/@electron/remote';
|
|
||||||
import { Dialog } from '@theia/core/lib/browser/dialogs';
|
import { Dialog } from '@theia/core/lib/browser/dialogs';
|
||||||
import { NavigatableWidget } from '@theia/core/lib/browser/navigatable';
|
import { NavigatableWidget } from '@theia/core/lib/browser/navigatable';
|
||||||
import { Saveable } from '@theia/core/lib/browser/saveable';
|
import { Saveable } from '@theia/core/lib/browser/saveable';
|
||||||
@ -8,7 +7,7 @@ import { nls } from '@theia/core/lib/common/nls';
|
|||||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||||
import { EditorManager } from '@theia/editor/lib/browser/editor-manager';
|
import { EditorManager } from '@theia/editor/lib/browser/editor-manager';
|
||||||
import { WorkspaceInput } from '@theia/workspace/lib/browser/workspace-service';
|
import { WorkspaceInput } from '@theia/workspace/lib/browser/workspace-service';
|
||||||
import { StartupTask } from '../../electron-common/startup-task';
|
import { StartupTasks } from '../../electron-common/startup-task';
|
||||||
import { ArduinoMenus } from '../menu/arduino-menus';
|
import { ArduinoMenus } from '../menu/arduino-menus';
|
||||||
import { CurrentSketch } from '../sketches-service-client-impl';
|
import { CurrentSketch } from '../sketches-service-client-impl';
|
||||||
import { CloudSketchContribution } from './cloud-contribution';
|
import { CloudSketchContribution } from './cloud-contribution';
|
||||||
@ -95,7 +94,7 @@ export class SaveAsSketch extends CloudSketchContribution {
|
|||||||
if (markAsRecentlyOpened) {
|
if (markAsRecentlyOpened) {
|
||||||
this.sketchesService.markAsRecentlyOpened(newWorkspaceUri);
|
this.sketchesService.markAsRecentlyOpened(newWorkspaceUri);
|
||||||
}
|
}
|
||||||
const options: WorkspaceInput & StartupTask.Owner = {
|
const options: WorkspaceInput & StartupTasks = {
|
||||||
preserveWindow: true,
|
preserveWindow: true,
|
||||||
tasks: [],
|
tasks: [],
|
||||||
};
|
};
|
||||||
@ -165,16 +164,13 @@ export class SaveAsSketch extends CloudSketchContribution {
|
|||||||
): Promise<string | undefined> {
|
): Promise<string | undefined> {
|
||||||
let sketchFolderDestinationUri: string | undefined;
|
let sketchFolderDestinationUri: string | undefined;
|
||||||
while (!sketchFolderDestinationUri) {
|
while (!sketchFolderDestinationUri) {
|
||||||
const { filePath } = await remote.dialog.showSaveDialog(
|
const { filePath } = await this.dialogService.showSaveDialog({
|
||||||
remote.getCurrentWindow(),
|
|
||||||
{
|
|
||||||
title: nls.localize(
|
title: nls.localize(
|
||||||
'arduino/sketch/saveFolderAs',
|
'arduino/sketch/saveFolderAs',
|
||||||
'Save sketch folder as...'
|
'Save sketch folder as...'
|
||||||
),
|
),
|
||||||
defaultPath,
|
defaultPath,
|
||||||
}
|
});
|
||||||
);
|
|
||||||
if (!filePath) {
|
if (!filePath) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
@ -225,13 +221,10 @@ ${dialogContent.details}
|
|||||||
|
|
||||||
${dialogContent.question}`.trim();
|
${dialogContent.question}`.trim();
|
||||||
defaultPath = filePath;
|
defaultPath = filePath;
|
||||||
const { response } = await remote.dialog.showMessageBox(
|
const { response } = await this.dialogService.showMessageBox({
|
||||||
remote.getCurrentWindow(),
|
|
||||||
{
|
|
||||||
message,
|
message,
|
||||||
buttons: [Dialog.CANCEL, Dialog.YES],
|
buttons: [Dialog.CANCEL, Dialog.YES],
|
||||||
}
|
});
|
||||||
);
|
|
||||||
// cancel
|
// cancel
|
||||||
if (response === 0) {
|
if (response === 0) {
|
||||||
return undefined;
|
return undefined;
|
||||||
|
@ -1,52 +0,0 @@
|
|||||||
import * as remote from '@theia/core/electron-shared/@electron/remote';
|
|
||||||
import type { IpcRendererEvent } from '@theia/core/electron-shared/electron';
|
|
||||||
import { ipcRenderer } from '@theia/core/electron-shared/electron';
|
|
||||||
import { injectable } from '@theia/core/shared/inversify';
|
|
||||||
import { StartupTask } from '../../electron-common/startup-task';
|
|
||||||
import { Contribution } from './contribution';
|
|
||||||
|
|
||||||
@injectable()
|
|
||||||
export class StartupTasks extends Contribution {
|
|
||||||
override onReady(): void {
|
|
||||||
ipcRenderer.once(
|
|
||||||
StartupTask.Messaging.STARTUP_TASKS_SIGNAL,
|
|
||||||
(_: IpcRendererEvent, args: unknown) => {
|
|
||||||
console.debug(
|
|
||||||
`Received the startup tasks from the electron main process. Args: ${JSON.stringify(
|
|
||||||
args
|
|
||||||
)}`
|
|
||||||
);
|
|
||||||
if (!StartupTask.has(args)) {
|
|
||||||
console.warn(`Could not detect 'tasks' from the signal. Skipping.`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const tasks = args.tasks;
|
|
||||||
if (tasks.length) {
|
|
||||||
console.log(`Executing startup tasks:`);
|
|
||||||
tasks.forEach(({ command, args = [] }) => {
|
|
||||||
console.log(
|
|
||||||
` - '${command}' ${
|
|
||||||
args.length ? `, args: ${JSON.stringify(args)}` : ''
|
|
||||||
}`
|
|
||||||
);
|
|
||||||
this.commandService
|
|
||||||
.executeCommand(command, ...args)
|
|
||||||
.catch((err) =>
|
|
||||||
console.error(
|
|
||||||
`Error occurred when executing the startup task '${command}'${
|
|
||||||
args?.length ? ` with args: '${JSON.stringify(args)}` : ''
|
|
||||||
}.`,
|
|
||||||
err
|
|
||||||
)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
const { id } = remote.getCurrentWindow();
|
|
||||||
console.debug(
|
|
||||||
`Signalling app ready event to the electron main process. Sender ID: ${id}.`
|
|
||||||
);
|
|
||||||
ipcRenderer.send(StartupTask.Messaging.APP_READY_SIGNAL(id));
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,65 @@
|
|||||||
|
import { DisposableCollection } from '@theia/core/lib/common/disposable';
|
||||||
|
import {
|
||||||
|
inject,
|
||||||
|
injectable,
|
||||||
|
postConstruct,
|
||||||
|
} from '@theia/core/shared/inversify';
|
||||||
|
import {
|
||||||
|
hasStartupTasks,
|
||||||
|
StartupTasks,
|
||||||
|
} from '../../electron-common/startup-task';
|
||||||
|
import { AppService } from '../app-service';
|
||||||
|
import { Contribution } from './contribution';
|
||||||
|
|
||||||
|
@injectable()
|
||||||
|
export class StartupTasksExecutor extends Contribution {
|
||||||
|
@inject(AppService)
|
||||||
|
private readonly appService: AppService;
|
||||||
|
|
||||||
|
private readonly toDispose = new DisposableCollection();
|
||||||
|
|
||||||
|
@postConstruct()
|
||||||
|
protected override init(): void {
|
||||||
|
super.init();
|
||||||
|
this.toDispose.push(
|
||||||
|
this.appService.registerStartupTasksHandler((tasks) =>
|
||||||
|
this.handleStartupTasks(tasks)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
onStop(): void {
|
||||||
|
this.toDispose.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async handleStartupTasks(tasks: StartupTasks): Promise<void> {
|
||||||
|
console.debug(
|
||||||
|
`Received the startup tasks from the electron main process. Args: ${JSON.stringify(
|
||||||
|
tasks
|
||||||
|
)}`
|
||||||
|
);
|
||||||
|
if (!hasStartupTasks(tasks)) {
|
||||||
|
console.warn(`Could not detect 'tasks' from the signal. Skipping.`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await this.appStateService.reachedState('ready');
|
||||||
|
console.log(`Executing startup tasks:`);
|
||||||
|
tasks.tasks.forEach(({ command, args = [] }) => {
|
||||||
|
console.log(
|
||||||
|
` - '${command}' ${
|
||||||
|
args.length ? `, args: ${JSON.stringify(args)}` : ''
|
||||||
|
}`
|
||||||
|
);
|
||||||
|
this.commandService
|
||||||
|
.executeCommand(command, ...args)
|
||||||
|
.catch((err) =>
|
||||||
|
console.error(
|
||||||
|
`Error occurred when executing the startup task '${command}'${
|
||||||
|
args?.length ? ` with args: '${JSON.stringify(args)}` : ''
|
||||||
|
}.`,
|
||||||
|
err
|
||||||
|
)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,3 @@
|
|||||||
import * as remote from '@theia/core/electron-shared/@electron/remote';
|
|
||||||
import { Dialog } from '@theia/core/lib/browser/dialogs';
|
import { Dialog } from '@theia/core/lib/browser/dialogs';
|
||||||
import { nls } from '@theia/core/lib/common/nls';
|
import { nls } from '@theia/core/lib/common/nls';
|
||||||
import { Deferred, waitForEvent } from '@theia/core/lib/common/promise-util';
|
import { Deferred, waitForEvent } from '@theia/core/lib/common/promise-util';
|
||||||
@ -180,15 +179,12 @@ export class ValidateSketch extends CloudSketchContribution {
|
|||||||
message: string,
|
message: string,
|
||||||
buttons: string[] = [Dialog.CANCEL, Dialog.OK]
|
buttons: string[] = [Dialog.CANCEL, Dialog.OK]
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
const { response } = await remote.dialog.showMessageBox(
|
const { response } = await this.dialogService.showMessageBox({
|
||||||
remote.getCurrentWindow(),
|
|
||||||
{
|
|
||||||
title,
|
title,
|
||||||
message,
|
message,
|
||||||
type: 'warning',
|
type: 'warning',
|
||||||
buttons,
|
buttons,
|
||||||
}
|
});
|
||||||
);
|
|
||||||
// cancel
|
// cancel
|
||||||
if (response === 0) {
|
if (response === 0) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -4,7 +4,7 @@ import { Emitter, Event } from '@theia/core/lib/common/event';
|
|||||||
import URI from '@theia/core/lib/common/uri';
|
import URI from '@theia/core/lib/common/uri';
|
||||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||||
import { Sketch } from '../../common/protocol';
|
import { Sketch } from '../../common/protocol';
|
||||||
import { AuthenticationSession } from '../../node/auth/types';
|
import { AuthenticationSession } from '../../common/protocol/authentication-service';
|
||||||
import { ArduinoPreferences } from '../arduino-preferences';
|
import { ArduinoPreferences } from '../arduino-preferences';
|
||||||
import { AuthenticationClientService } from '../auth/authentication-client-service';
|
import { AuthenticationClientService } from '../auth/authentication-client-service';
|
||||||
import { LocalCacheFsProvider } from '../local-cache/local-cache-fs-provider';
|
import { LocalCacheFsProvider } from '../local-cache/local-cache-fs-provider';
|
||||||
|
15
arduino-ide-extension/src/browser/dialog-service.ts
Normal file
15
arduino-ide-extension/src/browser/dialog-service.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import type {
|
||||||
|
MessageBoxOptions,
|
||||||
|
MessageBoxReturnValue,
|
||||||
|
OpenDialogOptions,
|
||||||
|
OpenDialogReturnValue,
|
||||||
|
SaveDialogOptions,
|
||||||
|
SaveDialogReturnValue,
|
||||||
|
} from '../electron-common/electron-arduino';
|
||||||
|
|
||||||
|
export const DialogService = Symbol('DialogService');
|
||||||
|
export interface DialogService {
|
||||||
|
showMessageBox(options: MessageBoxOptions): Promise<MessageBoxReturnValue>;
|
||||||
|
showOpenDialog(options: OpenDialogOptions): Promise<OpenDialogReturnValue>;
|
||||||
|
showSaveDialog(options: SaveDialogOptions): Promise<SaveDialogReturnValue>;
|
||||||
|
}
|
@ -1,12 +1,14 @@
|
|||||||
import * as React from '@theia/core/shared/react';
|
import { ClipboardService } from '@theia/core/lib/browser/clipboard-service';
|
||||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
import { DialogProps } from '@theia/core/lib/browser/dialogs';
|
||||||
import { Widget } from '@theia/core/shared/@phosphor/widgets';
|
import { TreeNode } from '@theia/core/lib/browser/tree/tree';
|
||||||
|
import { ReactWidget } from '@theia/core/lib/browser/widgets/react-widget';
|
||||||
|
import { nls } from '@theia/core/lib/common/nls';
|
||||||
|
import { MaybePromise } from '@theia/core/lib/common/types';
|
||||||
import { Message } from '@theia/core/shared/@phosphor/messaging';
|
import { Message } from '@theia/core/shared/@phosphor/messaging';
|
||||||
import { clipboard } from '@theia/core/electron-shared/@electron/remote';
|
import { Widget } from '@theia/core/shared/@phosphor/widgets';
|
||||||
import { ReactWidget, DialogProps } from '@theia/core/lib/browser';
|
import * as React from '@theia/core/shared/react';
|
||||||
import { AbstractDialog } from '../theia/dialogs/dialogs';
|
|
||||||
import { CreateApi } from '../create/create-api';
|
import { CreateApi } from '../create/create-api';
|
||||||
import { nls } from '@theia/core/lib/common';
|
import { AbstractDialog } from '../theia/dialogs/dialogs';
|
||||||
|
|
||||||
const RadioButton = (props: {
|
const RadioButton = (props: {
|
||||||
id: string;
|
id: string;
|
||||||
@ -35,15 +37,18 @@ export const ShareSketchComponent = ({
|
|||||||
treeNode,
|
treeNode,
|
||||||
createApi,
|
createApi,
|
||||||
domain = 'https://create.arduino.cc',
|
domain = 'https://create.arduino.cc',
|
||||||
|
writeClipboard,
|
||||||
}: {
|
}: {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
treeNode: any;
|
treeNode: any;
|
||||||
createApi: CreateApi;
|
createApi: CreateApi;
|
||||||
domain?: string;
|
domain?: string;
|
||||||
|
writeClipboard: (text: string) => MaybePromise<void>;
|
||||||
}): React.ReactElement => {
|
}): React.ReactElement => {
|
||||||
const [loading, setloading] = React.useState<boolean>(false);
|
const [loading, setLoading] = React.useState<boolean>(false);
|
||||||
|
|
||||||
const radioChangeHandler = async (event: React.BaseSyntheticEvent) => {
|
const radioChangeHandler = async (event: React.BaseSyntheticEvent) => {
|
||||||
setloading(true);
|
setLoading(true);
|
||||||
const sketch = await createApi.editSketch({
|
const sketch = await createApi.editSketch({
|
||||||
id: treeNode.sketchId,
|
id: treeNode.sketchId,
|
||||||
params: {
|
params: {
|
||||||
@ -52,7 +57,7 @@ export const ShareSketchComponent = ({
|
|||||||
});
|
});
|
||||||
// setPublicVisibility(sketch.is_public);
|
// setPublicVisibility(sketch.is_public);
|
||||||
treeNode.isPublic = sketch.is_public;
|
treeNode.isPublic = sketch.is_public;
|
||||||
setloading(false);
|
setLoading(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
const sketchLink = `${domain}/editor/_/${treeNode.sketchId}/preview`;
|
const sketchLink = `${domain}/editor/_/${treeNode.sketchId}/preview`;
|
||||||
@ -100,7 +105,7 @@ export const ShareSketchComponent = ({
|
|||||||
className="theia-input"
|
className="theia-input"
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
onClick={() => clipboard.writeText(sketchLink)}
|
onClick={() => writeClipboard(sketchLink)}
|
||||||
value="copy"
|
value="copy"
|
||||||
className="theia-button secondary"
|
className="theia-button secondary"
|
||||||
>
|
>
|
||||||
@ -121,44 +126,52 @@ export const ShareSketchComponent = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@injectable()
|
|
||||||
export class ShareSketchWidget extends ReactWidget {
|
export class ShareSketchWidget extends ReactWidget {
|
||||||
constructor(private treeNode: any, private createApi: CreateApi) {
|
private readonly writeClipboard = (text: string) =>
|
||||||
|
this.clipboardService.writeText(text);
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private treeNode: TreeNode,
|
||||||
|
private createApi: CreateApi,
|
||||||
|
private clipboardService: ClipboardService
|
||||||
|
) {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected render(): React.ReactNode {
|
protected override render(): React.ReactNode {
|
||||||
return (
|
return (
|
||||||
<ShareSketchComponent
|
<ShareSketchComponent
|
||||||
treeNode={this.treeNode}
|
treeNode={this.treeNode}
|
||||||
createApi={this.createApi}
|
createApi={this.createApi}
|
||||||
|
writeClipboard={this.writeClipboard}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@injectable()
|
|
||||||
export class ShareSketchDialogProps extends DialogProps {
|
export class ShareSketchDialogProps extends DialogProps {
|
||||||
readonly node: any;
|
readonly node: TreeNode;
|
||||||
readonly createApi: CreateApi;
|
readonly createApi: CreateApi;
|
||||||
|
readonly clipboardService: ClipboardService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@injectable()
|
|
||||||
export class ShareSketchDialog extends AbstractDialog<void> {
|
export class ShareSketchDialog extends AbstractDialog<void> {
|
||||||
protected widget: ShareSketchWidget;
|
protected widget: ShareSketchWidget;
|
||||||
|
|
||||||
constructor(
|
constructor(protected override readonly props: ShareSketchDialogProps) {
|
||||||
@inject(ShareSketchDialogProps)
|
|
||||||
protected override 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');
|
||||||
this.widget = new ShareSketchWidget(props.node, props.createApi);
|
this.widget = new ShareSketchWidget(
|
||||||
|
props.node,
|
||||||
|
props.createApi,
|
||||||
|
props.clipboardService
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
get value(): void {
|
override get value(): void {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override onAfterAttach(msg: Message): void {
|
protected override onAfterAttach(msg: Message): void {
|
||||||
if (this.widget.isAttached) {
|
if (this.widget.isAttached) {
|
||||||
Widget.detach(this.widget);
|
Widget.detach(this.widget);
|
||||||
|
@ -157,7 +157,7 @@ export const FirmwareUploaderComponent = ({
|
|||||||
options={firmwareOptions}
|
options={firmwareOptions}
|
||||||
value={selectedFirmware}
|
value={selectedFirmware}
|
||||||
tabSelectsValue={false}
|
tabSelectsValue={false}
|
||||||
onChange={(value) => {
|
onChange={(value: FirmwareOption | null) => {
|
||||||
if (value) {
|
if (value) {
|
||||||
setInstallFeedback(null);
|
setInstallFeedback(null);
|
||||||
setSelectedFirmware(value);
|
setSelectedFirmware(value);
|
||||||
|
@ -1,10 +1,15 @@
|
|||||||
import { nls } from '@theia/core/lib/common';
|
import { nls } from '@theia/core/lib/common/nls';
|
||||||
import { shell } from '@theia/core/electron-shared/@electron/remote';
|
|
||||||
import * as React from '@theia/core/shared/react';
|
import * as React from '@theia/core/shared/react';
|
||||||
import ReactMarkdown from 'react-markdown';
|
// @ts-expect-error see https://github.com/microsoft/TypeScript/issues/49721#issuecomment-1319854183
|
||||||
|
import type { Options } 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';
|
||||||
|
|
||||||
|
const ReactMarkdown = React.lazy<React.ComponentType<Options>>(
|
||||||
|
// @ts-expect-error see above
|
||||||
|
() => import('react-markdown')
|
||||||
|
);
|
||||||
|
|
||||||
export interface UpdateProgress {
|
export interface UpdateProgress {
|
||||||
progressInfo?: ProgressInfo | undefined;
|
progressInfo?: ProgressInfo | undefined;
|
||||||
downloadFinished?: boolean;
|
downloadFinished?: boolean;
|
||||||
@ -15,6 +20,7 @@ export interface UpdateProgress {
|
|||||||
export interface IDEUpdaterComponentProps {
|
export interface IDEUpdaterComponentProps {
|
||||||
updateInfo: UpdateInfo;
|
updateInfo: UpdateInfo;
|
||||||
updateProgress: UpdateProgress;
|
updateProgress: UpdateProgress;
|
||||||
|
openExternal: (url: string) => undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const IDEUpdaterComponent = ({
|
export const IDEUpdaterComponent = ({
|
||||||
@ -25,6 +31,7 @@ export const IDEUpdaterComponent = ({
|
|||||||
progressInfo,
|
progressInfo,
|
||||||
error,
|
error,
|
||||||
},
|
},
|
||||||
|
openExternal,
|
||||||
}: IDEUpdaterComponentProps): React.ReactElement => {
|
}: IDEUpdaterComponentProps): React.ReactElement => {
|
||||||
const { version, releaseNotes } = updateInfo;
|
const { version, releaseNotes } = updateInfo;
|
||||||
const [changelog, setChangelog] = React.useState<string>('');
|
const [changelog, setChangelog] = React.useState<string>('');
|
||||||
@ -95,13 +102,18 @@ export const IDEUpdaterComponent = ({
|
|||||||
{changelog && (
|
{changelog && (
|
||||||
<div className="dialogRow changelog-container">
|
<div className="dialogRow changelog-container">
|
||||||
<div className="changelog">
|
<div className="changelog">
|
||||||
|
<React.Suspense
|
||||||
|
fallback={
|
||||||
|
<div className="fallback">
|
||||||
|
<div className="spinner" />
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
>
|
||||||
<ReactMarkdown
|
<ReactMarkdown
|
||||||
components={{
|
components={{
|
||||||
|
// @ts-expect-error see imports. There is no ESM type-only import in CommonJS modules.
|
||||||
a: ({ href, children, ...props }) => (
|
a: ({ href, children, ...props }) => (
|
||||||
<a
|
<a onClick={() => href && openExternal(href)} {...props}>
|
||||||
onClick={() => href && shell.openExternal(href)}
|
|
||||||
{...props}
|
|
||||||
>
|
|
||||||
{children}
|
{children}
|
||||||
</a>
|
</a>
|
||||||
),
|
),
|
||||||
@ -109,6 +121,7 @@ export const IDEUpdaterComponent = ({
|
|||||||
>
|
>
|
||||||
{changelog}
|
{changelog}
|
||||||
</ReactMarkdown>
|
</ReactMarkdown>
|
||||||
|
</React.Suspense>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
@ -18,8 +18,6 @@ import {
|
|||||||
import { LocalStorageService } from '@theia/core/lib/browser';
|
import { LocalStorageService } from '@theia/core/lib/browser';
|
||||||
import { WindowService } from '@theia/core/lib/browser/window/window-service';
|
import { WindowService } from '@theia/core/lib/browser/window/window-service';
|
||||||
|
|
||||||
const DOWNLOAD_PAGE_URL = 'https://www.arduino.cc/en/software';
|
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class IDEUpdaterDialogProps extends DialogProps {}
|
export class IDEUpdaterDialogProps extends DialogProps {}
|
||||||
|
|
||||||
@ -76,11 +74,15 @@ export class IDEUpdaterDialog extends ReactDialog<UpdateInfo | undefined> {
|
|||||||
<IDEUpdaterComponent
|
<IDEUpdaterComponent
|
||||||
updateInfo={this.updateInfo}
|
updateInfo={this.updateInfo}
|
||||||
updateProgress={this.updateProgress}
|
updateProgress={this.updateProgress}
|
||||||
|
openExternal={this.openExternal}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private readonly openExternal = (url: string) =>
|
||||||
|
this.windowService.openNewWindow(url, { external: true });
|
||||||
|
|
||||||
get value(): UpdateInfo | undefined {
|
get value(): UpdateInfo | undefined {
|
||||||
return this.updateInfo;
|
return this.updateInfo;
|
||||||
}
|
}
|
||||||
@ -164,7 +166,7 @@ export class IDEUpdaterDialog extends ReactDialog<UpdateInfo | undefined> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private openDownloadPage(): void {
|
private openDownloadPage(): void {
|
||||||
this.windowService.openNewWindow(DOWNLOAD_PAGE_URL, { external: true });
|
this.openExternal('https://www.arduino.cc/en/software');
|
||||||
this.close();
|
this.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,11 +14,9 @@ import {
|
|||||||
Monitor,
|
Monitor,
|
||||||
MonitorManagerProxyClient,
|
MonitorManagerProxyClient,
|
||||||
MonitorManagerProxyFactory,
|
MonitorManagerProxyFactory,
|
||||||
} from '../common/protocol/monitor-service';
|
|
||||||
import {
|
|
||||||
PluggableMonitorSettings,
|
|
||||||
MonitorSettings,
|
MonitorSettings,
|
||||||
} from '../node/monitor-settings/monitor-settings-provider';
|
PluggableMonitorSettings,
|
||||||
|
} from '../common/protocol/monitor-service';
|
||||||
import { BoardsConfig } from './boards/boards-config';
|
import { BoardsConfig } from './boards/boards-config';
|
||||||
import { BoardsServiceProvider } from './boards/boards-service-provider';
|
import { BoardsServiceProvider } from './boards/boards-service-provider';
|
||||||
|
|
||||||
|
@ -10,10 +10,10 @@ import {
|
|||||||
monitorConnectionStatusEquals,
|
monitorConnectionStatusEquals,
|
||||||
MonitorEOL,
|
MonitorEOL,
|
||||||
MonitorManagerProxyClient,
|
MonitorManagerProxyClient,
|
||||||
|
MonitorSettings,
|
||||||
MonitorState,
|
MonitorState,
|
||||||
} from '../common/protocol';
|
} from '../common/protocol';
|
||||||
import { isNullOrUndefined } from '../common/utils';
|
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 {
|
||||||
|
@ -23,9 +23,9 @@ import { nls } from '@theia/core/lib/common';
|
|||||||
import {
|
import {
|
||||||
MonitorEOL,
|
MonitorEOL,
|
||||||
MonitorManagerProxyClient,
|
MonitorManagerProxyClient,
|
||||||
|
MonitorSettings,
|
||||||
} from '../../../common/protocol';
|
} from '../../../common/protocol';
|
||||||
import { MonitorModel } from '../../monitor-model';
|
import { MonitorModel } from '../../monitor-model';
|
||||||
import { MonitorSettings } from '../../../node/monitor-settings/monitor-settings-provider';
|
|
||||||
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
|
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
|
@ -2,7 +2,7 @@ import * as React from '@theia/core/shared/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';
|
||||||
import dateFormat = require('dateformat');
|
import dateFormat from 'dateformat';
|
||||||
import { messagesToLines, truncateLines } from './monitor-utils';
|
import { messagesToLines, truncateLines } from './monitor-utils';
|
||||||
import { MonitorManagerProxyClient } from '../../../common/protocol';
|
import { MonitorManagerProxyClient } from '../../../common/protocol';
|
||||||
import { MonitorModel } from '../../monitor-model';
|
import { MonitorModel } from '../../monitor-model';
|
||||||
|
@ -1,26 +1,16 @@
|
|||||||
|
import { Endpoint } from '@theia/core/lib/browser/endpoint';
|
||||||
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 { Command, CommandRegistry } from '@theia/core/lib/common/command';
|
||||||
import {
|
import { DisposableCollection } from '@theia/core/lib/common/disposable';
|
||||||
Command,
|
import type { MenuModelRegistry } from '@theia/core/lib/common/menu';
|
||||||
CommandRegistry,
|
import { nls } from '@theia/core/lib/common/nls';
|
||||||
MaybePromise,
|
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||||
MenuModelRegistry,
|
import queryString from 'query-string';
|
||||||
} from '@theia/core';
|
|
||||||
import { ArduinoMenus } from '../../menu/arduino-menus';
|
|
||||||
import { Contribution } from '../../contributions/contribution';
|
|
||||||
import { Endpoint, FrontendApplication } from '@theia/core/lib/browser';
|
|
||||||
import { ipcRenderer } from '@theia/electron/shared/electron';
|
|
||||||
import { MonitorManagerProxyClient } from '../../../common/protocol';
|
import { MonitorManagerProxyClient } from '../../../common/protocol';
|
||||||
import { BoardsServiceProvider } from '../../boards/boards-service-provider';
|
import { Contribution } from '../../contributions/contribution';
|
||||||
|
import { ArduinoMenus } from '../../menu/arduino-menus';
|
||||||
import { MonitorModel } from '../../monitor-model';
|
import { MonitorModel } from '../../monitor-model';
|
||||||
import { ArduinoToolbar } from '../../toolbar/arduino-toolbar';
|
import { ArduinoToolbar } from '../../toolbar/arduino-toolbar';
|
||||||
import {
|
|
||||||
CLOSE_PLOTTER_WINDOW,
|
|
||||||
SHOW_PLOTTER_WINDOW,
|
|
||||||
} from '../../../common/ipc-communication';
|
|
||||||
import { nls } from '@theia/core/lib/common/nls';
|
|
||||||
|
|
||||||
const queryString = require('query-string');
|
|
||||||
|
|
||||||
export namespace SerialPlotterContribution {
|
export namespace SerialPlotterContribution {
|
||||||
export namespace Commands {
|
export namespace Commands {
|
||||||
@ -44,38 +34,31 @@ export namespace SerialPlotterContribution {
|
|||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class PlotterFrontendContribution extends Contribution {
|
export class PlotterFrontendContribution extends Contribution {
|
||||||
protected window: Window | null;
|
private readonly endpointUrl = new Endpoint({ path: '/plotter' })
|
||||||
protected url: string;
|
.getRestUrl()
|
||||||
protected wsPort: number;
|
.toString();
|
||||||
|
private readonly toDispose = new DisposableCollection();
|
||||||
|
private _plotterUrl: string | undefined;
|
||||||
|
|
||||||
@inject(MonitorModel)
|
@inject(MonitorModel)
|
||||||
protected readonly model: MonitorModel;
|
private readonly model: MonitorModel;
|
||||||
|
|
||||||
@inject(ThemeService)
|
@inject(ThemeService)
|
||||||
protected readonly themeService: ThemeService;
|
private readonly themeService: ThemeService;
|
||||||
|
|
||||||
@inject(MonitorManagerProxyClient)
|
@inject(MonitorManagerProxyClient)
|
||||||
protected readonly monitorManagerProxy: MonitorManagerProxyClient;
|
private readonly monitorManagerProxy: MonitorManagerProxyClient;
|
||||||
|
|
||||||
@inject(BoardsServiceProvider)
|
override onStart(): void {
|
||||||
protected readonly boardsServiceProvider: BoardsServiceProvider;
|
this.toDispose.push(
|
||||||
|
window.electronArduino.registerPlotterWindowCloseHandler(() => {
|
||||||
override onStart(app: FrontendApplication): MaybePromise<void> {
|
this._plotterUrl = undefined;
|
||||||
this.url = new Endpoint({ path: '/plotter' }).getRestUrl().toString();
|
})
|
||||||
|
);
|
||||||
ipcRenderer.on(CLOSE_PLOTTER_WINDOW, async () => {
|
|
||||||
if (!!this.window) {
|
|
||||||
this.window = null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this.monitorManagerProxy.onMonitorShouldReset(() => this.reset());
|
this.monitorManagerProxy.onMonitorShouldReset(() => this.reset());
|
||||||
|
|
||||||
return super.onStart(app);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override registerCommands(registry: CommandRegistry): void {
|
override registerCommands(registry: CommandRegistry): void {
|
||||||
registry.registerCommand(SerialPlotterContribution.Commands.OPEN, {
|
registry.registerCommand(SerialPlotterContribution.Commands.OPEN, {
|
||||||
execute: this.startPlotter.bind(this),
|
execute: () => this.startPlotter(),
|
||||||
});
|
});
|
||||||
registry.registerCommand(SerialPlotterContribution.Commands.RESET, {
|
registry.registerCommand(SerialPlotterContribution.Commands.RESET, {
|
||||||
execute: () => this.reset(),
|
execute: () => this.reset(),
|
||||||
@ -85,7 +68,7 @@ export class PlotterFrontendContribution extends Contribution {
|
|||||||
{
|
{
|
||||||
isVisible: (widget) =>
|
isVisible: (widget) =>
|
||||||
ArduinoToolbar.is(widget) && widget.side === 'right',
|
ArduinoToolbar.is(widget) && widget.side === 'right',
|
||||||
execute: this.startPlotter.bind(this),
|
execute: () => this.startPlotter(),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -98,10 +81,13 @@ export class PlotterFrontendContribution extends Contribution {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async startPlotter(): Promise<void> {
|
private async startPlotter(forceReload = false): Promise<void> {
|
||||||
await this.monitorManagerProxy.startMonitor();
|
await this.monitorManagerProxy.startMonitor();
|
||||||
if (!!this.window) {
|
if (this._plotterUrl) {
|
||||||
ipcRenderer.send(SHOW_PLOTTER_WINDOW);
|
window.electronArduino.showPlotterWindow({
|
||||||
|
url: this._plotterUrl,
|
||||||
|
forceReload,
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const wsPort = this.monitorManagerProxy.getWebSocketPort();
|
const wsPort = this.monitorManagerProxy.getWebSocketPort();
|
||||||
@ -117,26 +103,30 @@ export class PlotterFrontendContribution extends Contribution {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async open(wsPort: number): Promise<void> {
|
private open(wsPort: number): void {
|
||||||
const initConfig = {
|
const initConfig = {
|
||||||
darkTheme: this.themeService.getCurrentTheme().type === 'dark',
|
darkTheme: this.isDarkTheme,
|
||||||
wsPort,
|
wsPort,
|
||||||
serialPort: this.model.serialPort,
|
serialPort: this.model.serialPort,
|
||||||
};
|
};
|
||||||
const urlWithParams = queryString.stringifyUrl(
|
this._plotterUrl = queryString.stringifyUrl(
|
||||||
{
|
{
|
||||||
url: this.url,
|
url: this.endpointUrl,
|
||||||
query: initConfig,
|
query: initConfig,
|
||||||
},
|
},
|
||||||
{ arrayFormat: 'comma' }
|
{ arrayFormat: 'comma' }
|
||||||
);
|
);
|
||||||
this.window = window.open(urlWithParams, 'serialPlotter');
|
window.electronArduino.showPlotterWindow({ url: this._plotterUrl });
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async reset(): Promise<void> {
|
private get isDarkTheme(): boolean {
|
||||||
if (!!this.window) {
|
const themeType = this.themeService.getCurrentTheme().type;
|
||||||
this.window.close();
|
return themeType === 'dark' || themeType === 'hc';
|
||||||
await this.startPlotter();
|
}
|
||||||
|
|
||||||
|
private async reset(): Promise<void> {
|
||||||
|
if (this._plotterUrl) {
|
||||||
|
await this.startPlotter(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,14 +49,3 @@
|
|||||||
padding-top: 0 !important;
|
padding-top: 0 !important;
|
||||||
padding-bottom: 0 !important;
|
padding-bottom: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* High Contrast Theme rules */
|
|
||||||
/* TODO: Remove it when the Theia version is upgraded to 1.27.0 and use Theia APIs to implement it*/
|
|
||||||
.hc-black.hc-theia.theia-hc .arduino-select__option--is-selected {
|
|
||||||
outline: 1px solid var(--theia-focusBorder);
|
|
||||||
}
|
|
||||||
|
|
||||||
.hc-black.hc-theia.theia-hc .arduino-select__option--is-focused {
|
|
||||||
outline: 1px dashed var(--theia-focusBorder);
|
|
||||||
}
|
|
||||||
|
@ -276,16 +276,6 @@ div#select-board-dialog .selectBoardContainer .list .item.selected i {
|
|||||||
border-top: 1px solid var(--theia-dropdown-border);
|
border-top: 1px solid var(--theia-dropdown-border);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* High Contrast Theme rules */
|
|
||||||
/* TODO: Remove it when the Theia version is upgraded to 1.27.0 and use Theia APIs to implement it*/
|
|
||||||
.hc-black.hc-theia.theia-hc #select-board-dialog .selectBoardContainer .list .item:hover {
|
|
||||||
outline: 1px dashed var(--theia-focusBorder);
|
|
||||||
}
|
|
||||||
|
|
||||||
.hc-black.hc-theia.theia-hc div#select-board-dialog .selectBoardContainer .list .item.selected {
|
|
||||||
outline: 1px solid var(--theia-focusBorder);
|
|
||||||
}
|
|
||||||
|
|
||||||
@media only screen and (max-height: 400px) {
|
@media only screen and (max-height: 400px) {
|
||||||
div.dialogContent.select-board-dialog > div.head {
|
div.dialogContent.select-board-dialog > div.head {
|
||||||
display: none;
|
display: none;
|
||||||
|
@ -41,6 +41,17 @@
|
|||||||
overflow: auto;
|
overflow: auto;
|
||||||
padding: 0 12px;
|
padding: 0 12px;
|
||||||
cursor: text;
|
cursor: text;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ide-updater-dialog .changelog .fallback {
|
||||||
|
min-height: 180px;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ide-updater-dialog .changelog .fallback .spinner {
|
||||||
|
align-self: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dialogContent.ide-updater-dialog
|
.dialogContent.ide-updater-dialog
|
||||||
@ -104,3 +115,10 @@
|
|||||||
margin-left: 79px;
|
margin-left: 79px;
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* https://github.com/arduino/arduino-ide/pull/2027#issuecomment-1533174638 */
|
||||||
|
/* https://github.com/eclipse-theia/theia/commit/1b5ff9ee459df14cedc0e8266dd02dae93fcd1bf#diff-d8d45a890507a01141c010ad4a6891edf2ae727cfa6dfe604cebbd667812cccbR68 */
|
||||||
|
/* Use normal whitespace handling for the changelog content in the update dialog. */
|
||||||
|
.p-Widget.dialogOverlay .dialogContent.ide-updater-dialog {
|
||||||
|
white-space: normal;
|
||||||
|
}
|
||||||
|
@ -54,12 +54,6 @@ body.theia-dark {
|
|||||||
background-color: var(--theia-warningBackground);
|
background-color: var(--theia-warningBackground);
|
||||||
}
|
}
|
||||||
|
|
||||||
.hc-black.hc-theia.theia-hc .theia-input.warning,
|
|
||||||
.hc-black.hc-theia.theia-hc .theia-input.warning::placeholder {
|
|
||||||
color: var(--theia-warningBackground);
|
|
||||||
background-color: var(--theia-warningForeground);
|
|
||||||
}
|
|
||||||
|
|
||||||
.theia-input.error:focus {
|
.theia-input.error:focus {
|
||||||
outline-width: 1px;
|
outline-width: 1px;
|
||||||
outline-style: solid;
|
outline-style: solid;
|
||||||
@ -170,25 +164,6 @@ button.theia-button.message-box-dialog-button {
|
|||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* High Contrast Theme rules */
|
|
||||||
/* TODO: Remove it when the Theia version is upgraded to 1.27.0 and use Theia APIs to implement it*/
|
|
||||||
.hc-black.hc-theia.theia-hc button.theia-button:hover,
|
|
||||||
.hc-black.hc-theia.theia-hc .theia-button:hover {
|
|
||||||
outline: 1px dashed var(--theia-focusBorder);
|
|
||||||
}
|
|
||||||
|
|
||||||
.hc-black.hc-theia.theia-hc button.theia-button,
|
|
||||||
.hc-black.hc-theia.theia-hc .theia-button,
|
|
||||||
.hc-black.hc-theia.theia-hc button.theia-button.secondary {
|
|
||||||
border: 1px solid var(--theia-button-border);
|
|
||||||
}
|
|
||||||
|
|
||||||
.hc-black.hc-theia.theia-hc .theia-notification-list-item:hover:not(:focus) {
|
|
||||||
background-color: var(--theia-notifications-background);
|
|
||||||
outline: 1px dashed var(--theia-focusBorder);
|
|
||||||
outline-offset: -2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.debug-toolbar .debug-action>div {
|
.debug-toolbar .debug-action>div {
|
||||||
font-family: var(--theia-ui-font-family);
|
font-family: var(--theia-ui-font-family);
|
||||||
font-size: var(--theia-ui-font-size0);
|
font-size: var(--theia-ui-font-size0);
|
||||||
|
@ -219,15 +219,3 @@ div.filterable-list-container > div > div > div > div:nth-child(1) > div.separat
|
|||||||
width: 65px;
|
width: 65px;
|
||||||
min-width: 65px;
|
min-width: 65px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* High Contrast Theme rules */
|
|
||||||
/* TODO: Remove it when the Theia version is upgraded to 1.27.0 and use Theia APIs to implement it*/
|
|
||||||
.hc-black.hc-theia.theia-hc .component-list-item .header .installed-version:hover:before {
|
|
||||||
background-color: transparent;
|
|
||||||
outline: 1px dashed var(--theia-focusBorder);
|
|
||||||
}
|
|
||||||
|
|
||||||
.hc-black.hc-theia.theia-hc .component-list-item .header .installed-version:before {
|
|
||||||
color: var(--theia-button-background);
|
|
||||||
border: 1px solid var(--theia-button-border);
|
|
||||||
}
|
|
||||||
|
@ -137,24 +137,6 @@
|
|||||||
color: var(--theia-titleBar-activeForeground);
|
color: var(--theia-titleBar-activeForeground);
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-TabBar-toolbar .item > div.arduino-toggle-advanced-mode {
|
|
||||||
display: flex;
|
|
||||||
width: 24px;
|
|
||||||
height: 24px;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.arduino-toggle-advanced-mode-icon {
|
|
||||||
mask: none;
|
|
||||||
-webkit-mask: none;
|
|
||||||
background: none;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
color: var(--theia-titleBar-activeBackground);
|
|
||||||
}
|
|
||||||
|
|
||||||
.arduino-open-boards-control-icon {
|
.arduino-open-boards-control-icon {
|
||||||
mask: none;
|
mask: none;
|
||||||
-webkit-mask: none;
|
-webkit-mask: none;
|
||||||
@ -202,61 +184,6 @@
|
|||||||
background-color: var(--theia-terminal-background);
|
background-color: var(--theia-terminal-background);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* High Contrast Theme rules */
|
|
||||||
/* TODO: Remove it when the Theia version is upgraded to 1.27.0 and use Theia APIs to implement it*/
|
|
||||||
.hc-black.hc-theia.theia-hc .p-TabBar-toolbar .item.arduino-tool-item.enabled:hover > div {
|
|
||||||
background: var(--theia-arduino-toolbar-button-background);
|
|
||||||
outline: 1px dashed var(--theia-focusBorder);
|
|
||||||
}
|
|
||||||
|
|
||||||
.hc-black.hc-theia.theia-hc .p-TabBar-toolbar .item.arduino-tool-item.enabled:hover > div.toggle-serial-plotter,
|
|
||||||
.hc-black.hc-theia.theia-hc .p-TabBar-toolbar .item.arduino-tool-item.enabled:hover > div.toggle-serial-monitor {
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hc-black.hc-theia.theia-hc .item.arduino-tool-item.toggled .arduino-verify-sketch--toolbar,
|
|
||||||
.hc-black.hc-theia.theia-hc .item.arduino-tool-item.toggled .arduino-upload-sketch--toolbar {
|
|
||||||
background-color: var(--theia-arduino-toolbar-button-background) !important;
|
|
||||||
outline: 1px solid var(--theia-focusBorder);
|
|
||||||
}
|
|
||||||
|
|
||||||
.hc-black.hc-theia.theia-hc .arduino-boards-dropdown-item:hover {
|
|
||||||
background: var(--theia-dropdown-background);
|
|
||||||
}
|
|
||||||
|
|
||||||
.hc-black.hc-theia.theia-hc .arduino-boards-dropdown-item:hover {
|
|
||||||
outline: 1px dashed var(--theia-focusBorder);
|
|
||||||
outline-offset: -2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hc-black.hc-theia.theia-hc #theia-main-content-panel .p-TabBar .p-TabBar-tab.p-mod-current {
|
|
||||||
outline: 1px solid var(--theia-focusBorder);
|
|
||||||
outline-offset: -4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hc-black.hc-theia.theia-hc #theia-main-content-panel .p-TabBar .p-TabBar-tab:hover {
|
|
||||||
outline: 1px dashed var(--theia-focusBorder);
|
|
||||||
outline-offset: -4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hc-black.hc-theia.theia-hc .p-TabBar.theia-app-centers .p-TabBar-tab.p-mod-closable > .p-TabBar-tabCloseIcon:hover {
|
|
||||||
outline: 1px dashed var(--theia-focusBorder);
|
|
||||||
}
|
|
||||||
|
|
||||||
.hc-black.hc-theia.theia-hc .quick-input-list .monaco-list-row.focused {
|
|
||||||
outline: 1px dotted var(--theia-focusBorder);
|
|
||||||
}
|
|
||||||
|
|
||||||
.hc-black.hc-theia.theia-hc .quick-input-list .monaco-list-row:hover {
|
|
||||||
outline: 1px dashed var(--theia-focusBorder);
|
|
||||||
}
|
|
||||||
|
|
||||||
.hc-black.hc-theia.theia-hc .quick-input-widget {
|
|
||||||
outline: 1px solid var(--theia-contrastBorder);
|
|
||||||
outline-offset: -1px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.monaco-hover .hover-row.markdown-hover:first-child p {
|
.monaco-hover .hover-row.markdown-hover:first-child p {
|
||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
}
|
}
|
||||||
|
@ -80,19 +80,3 @@ widget width.
|
|||||||
background: var(--theia-list-inactiveSelectionBackground);
|
background: var(--theia-list-inactiveSelectionBackground);
|
||||||
color: var(--theia-list-inactiveSelectionForeground) !important;
|
color: var(--theia-list-inactiveSelectionForeground) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* High Contrast Theme rules */
|
|
||||||
/* TODO: Remove it when the Theia version is upgraded to 1.27.0 and use Theia APIs to implement it*/
|
|
||||||
.hc-black.hc-theia.theia-hc .theia-TreeNode:hover {
|
|
||||||
outline: 1px dashed var(--theia-focusBorder);
|
|
||||||
}
|
|
||||||
|
|
||||||
.hc-black.hc-theia.theia-hc .theia-Tree .theia-TreeNode.theia-mod-selected {
|
|
||||||
outline: 1px dotted var(--theia-focusBorder);
|
|
||||||
}
|
|
||||||
|
|
||||||
.hc-black.hc-theia.theia-hc .theia-Tree:focus .theia-TreeNode.theia-mod-selected,
|
|
||||||
.hc-black.hc-theia.theia-hc .theia-Tree .ReactVirtualized__List:focus .theia-TreeNode.theia-mod-selected {
|
|
||||||
outline: 1px solid var(--theia-focusBorder);
|
|
||||||
}
|
|
||||||
|
@ -20,8 +20,7 @@ import { ArduinoDaemon } from '../../../common/protocol';
|
|||||||
import { assertUnreachable } from '../../../common/utils';
|
import { assertUnreachable } from '../../../common/utils';
|
||||||
import { CreateFeatures } from '../../create/create-features';
|
import { CreateFeatures } from '../../create/create-features';
|
||||||
import { NotificationCenter } from '../../notification-center';
|
import { NotificationCenter } from '../../notification-center';
|
||||||
import debounce = require('lodash.debounce');
|
import debounce from 'lodash.debounce';
|
||||||
import isOnline = require('is-online');
|
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class IsOnline implements FrontendApplicationContribution {
|
export class IsOnline implements FrontendApplicationContribution {
|
||||||
@ -30,10 +29,11 @@ export class IsOnline implements FrontendApplicationContribution {
|
|||||||
private stopped = false;
|
private stopped = false;
|
||||||
|
|
||||||
onStart(): void {
|
onStart(): void {
|
||||||
|
import('is-online').then((module) => {
|
||||||
const checkOnline = async () => {
|
const checkOnline = async () => {
|
||||||
if (!this.stopped) {
|
if (!this.stopped) {
|
||||||
try {
|
try {
|
||||||
const online = await isOnline();
|
const online = await module.default();
|
||||||
this.setOnline(online);
|
this.setOnline(online);
|
||||||
} finally {
|
} finally {
|
||||||
window.setTimeout(() => checkOnline(), 6_000); // 6 seconds poll interval
|
window.setTimeout(() => checkOnline(), 6_000); // 6 seconds poll interval
|
||||||
@ -41,6 +41,7 @@ export class IsOnline implements FrontendApplicationContribution {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
checkOnline();
|
checkOnline();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onStop(): void {
|
onStop(): void {
|
||||||
@ -56,7 +57,7 @@ export class IsOnline implements FrontendApplicationContribution {
|
|||||||
return this.onDidChangeOnlineEmitter.event;
|
return this.onDidChangeOnlineEmitter.event;
|
||||||
}
|
}
|
||||||
|
|
||||||
private setOnline(online: boolean) {
|
private setOnline(online: boolean): void {
|
||||||
const oldOnline = this._online;
|
const oldOnline = this._online;
|
||||||
this._online = online;
|
this._online = online;
|
||||||
if (!this.stopped && this._online !== oldOnline) {
|
if (!this.stopped && this._online !== oldOnline) {
|
||||||
|
@ -4,7 +4,7 @@ import {
|
|||||||
TabBarRenderer as TheiaTabBarRenderer,
|
TabBarRenderer as TheiaTabBarRenderer,
|
||||||
ToolbarAwareTabBar as TheiaToolbarAwareTabBar,
|
ToolbarAwareTabBar as TheiaToolbarAwareTabBar,
|
||||||
} from '@theia/core/lib/browser/shell/tab-bars';
|
} from '@theia/core/lib/browser/shell/tab-bars';
|
||||||
import debounce = require('lodash.debounce');
|
import debounce from 'lodash.debounce';
|
||||||
|
|
||||||
export class TabBarRenderer extends TheiaTabBarRenderer {
|
export class TabBarRenderer extends TheiaTabBarRenderer {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
@ -25,8 +25,8 @@ const builtInThemeIds = new Set(
|
|||||||
[
|
[
|
||||||
ArduinoThemes.light,
|
ArduinoThemes.light,
|
||||||
ArduinoThemes.dark,
|
ArduinoThemes.dark,
|
||||||
|
BuiltinThemeProvider.hcLightTheme,
|
||||||
BuiltinThemeProvider.hcTheme,
|
BuiltinThemeProvider.hcTheme,
|
||||||
// TODO: add the HC light theme after Theia 1.36
|
|
||||||
].map(({ id }) => id)
|
].map(({ id }) => id)
|
||||||
);
|
);
|
||||||
const deprecatedThemeIds = new Set(
|
const deprecatedThemeIds = new Set(
|
||||||
@ -37,7 +37,14 @@ const deprecatedThemeIds = new Set(
|
|||||||
|
|
||||||
export const lightThemeLabel = nls.localize('arduino/theme/light', 'Light');
|
export const lightThemeLabel = nls.localize('arduino/theme/light', 'Light');
|
||||||
export const darkThemeLabel = nls.localize('arduino/theme/dark', 'Dark');
|
export const darkThemeLabel = nls.localize('arduino/theme/dark', 'Dark');
|
||||||
export const hcThemeLabel = nls.localize('arduino/theme/hc', 'High Contrast');
|
export const hcLightThemeLabel = nls.localize(
|
||||||
|
'arduino/theme/hcLight',
|
||||||
|
'Light High Contrast'
|
||||||
|
);
|
||||||
|
export const hcThemeLabel = nls.localize(
|
||||||
|
'arduino/theme/hc',
|
||||||
|
'Dark High Contrast'
|
||||||
|
);
|
||||||
export function userThemeLabel(theme: Theme): string {
|
export function userThemeLabel(theme: Theme): string {
|
||||||
return nls.localize('arduino/theme/user', '{0} (user)', theme.label);
|
return nls.localize('arduino/theme/user', '{0} (user)', theme.label);
|
||||||
}
|
}
|
||||||
@ -57,6 +64,8 @@ export function themeLabelForSettings(theme: Theme): string {
|
|||||||
return darkThemeLabel;
|
return darkThemeLabel;
|
||||||
case BuiltinThemeProvider.hcTheme.id:
|
case BuiltinThemeProvider.hcTheme.id:
|
||||||
return hcThemeLabel;
|
return hcThemeLabel;
|
||||||
|
case BuiltinThemeProvider.hcLightTheme.id:
|
||||||
|
return hcLightThemeLabel;
|
||||||
case BuiltinThemeProvider.lightTheme.id: // fall-through
|
case BuiltinThemeProvider.lightTheme.id: // fall-through
|
||||||
case BuiltinThemeProvider.darkTheme.id:
|
case BuiltinThemeProvider.darkTheme.id:
|
||||||
return deprecatedThemeLabel(theme);
|
return deprecatedThemeLabel(theme);
|
||||||
@ -73,6 +82,8 @@ export function compatibleBuiltInTheme(theme: Theme): Theme {
|
|||||||
return ArduinoThemes.dark;
|
return ArduinoThemes.dark;
|
||||||
case 'hc':
|
case 'hc':
|
||||||
return BuiltinThemeProvider.hcTheme;
|
return BuiltinThemeProvider.hcTheme;
|
||||||
|
case 'hcLight':
|
||||||
|
return BuiltinThemeProvider.hcLightTheme;
|
||||||
default: {
|
default: {
|
||||||
console.warn(
|
console.warn(
|
||||||
`Unhandled theme type: ${theme.type}. Theme ID: ${theme.id}, label: ${theme.label}`
|
`Unhandled theme type: ${theme.type}. Theme ID: ${theme.id}, label: ${theme.label}`
|
||||||
@ -91,7 +102,7 @@ interface ThemeProvider {
|
|||||||
/**
|
/**
|
||||||
* Returns with a list of built-in themes officially supported by IDE2 (https://github.com/arduino/arduino-ide/issues/1283).
|
* Returns with a list of built-in themes officially supported by IDE2 (https://github.com/arduino/arduino-ide/issues/1283).
|
||||||
* The themes in the array follow the following order:
|
* The themes in the array follow the following order:
|
||||||
* - built-in themes first (in `Light`, `Dark`, `High Contrast`), // TODO -> High Contrast will be split up to HC Dark and HC Light after the Theia version uplift
|
* - built-in themes first (in `Light`, `Dark`, `Light High Contrast`, and `Dark High Contrast`),
|
||||||
* - followed by user installed (VSIX) themes grouped by theme type, then alphabetical order,
|
* - followed by user installed (VSIX) themes grouped by theme type, then alphabetical order,
|
||||||
* - if the `currentTheme` is either Light (Theia) or Dark (Theia), the last item of the array will be the selected theme with `(deprecated)` suffix.
|
* - if the `currentTheme` is either Light (Theia) or Dark (Theia), the last item of the array will be the selected theme with `(deprecated)` suffix.
|
||||||
*/
|
*/
|
||||||
@ -171,7 +182,8 @@ const arduinoThemeTypeOrder: Record<ArduinoThemeType, number> = {
|
|||||||
const themeTypeOrder: Record<ThemeType, number> = {
|
const themeTypeOrder: Record<ThemeType, number> = {
|
||||||
light: 0,
|
light: 0,
|
||||||
dark: 1,
|
dark: 1,
|
||||||
hc: 2,
|
hcLight: 2,
|
||||||
|
hc: 3,
|
||||||
};
|
};
|
||||||
|
|
||||||
export function arduinoThemeTypeOf(theme: Theme | string): ArduinoThemeType {
|
export function arduinoThemeTypeOf(theme: Theme | string): ArduinoThemeType {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import type { StartupTask } from '../../../electron-common/startup-task';
|
import type { StartupTasks } from '../../../electron-common/startup-task';
|
||||||
|
|
||||||
export const WindowServiceExt = Symbol('WindowServiceExt');
|
export const WindowServiceExt = Symbol('WindowServiceExt');
|
||||||
export interface WindowServiceExt {
|
export interface WindowServiceExt {
|
||||||
@ -6,5 +6,6 @@ export interface WindowServiceExt {
|
|||||||
* Returns with a promise that resolves to `true` if the current window is the first window.
|
* Returns with a promise that resolves to `true` if the current window is the first window.
|
||||||
*/
|
*/
|
||||||
isFirstWindow(): Promise<boolean>;
|
isFirstWindow(): Promise<boolean>;
|
||||||
reload(options?: StartupTask.Owner): void;
|
reload(tasks?: StartupTasks): void;
|
||||||
|
close(): void;
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import * as remote from '@theia/core/electron-shared/@electron/remote';
|
|
||||||
import { FrontendApplication } from '@theia/core/lib/browser/frontend-application';
|
import { FrontendApplication } from '@theia/core/lib/browser/frontend-application';
|
||||||
import { FrontendApplicationConfigProvider } from '@theia/core/lib/browser/frontend-application-config-provider';
|
import { FrontendApplicationConfigProvider } from '@theia/core/lib/browser/frontend-application-config-provider';
|
||||||
import { NavigatableWidget } from '@theia/core/lib/browser/navigatable-types';
|
import { NavigatableWidget } from '@theia/core/lib/browser/navigatable-types';
|
||||||
@ -118,10 +117,8 @@ export class WindowTitleUpdater extends TheiaWindowTitleUpdater {
|
|||||||
if (widget instanceof EditorWidget) {
|
if (widget instanceof EditorWidget) {
|
||||||
const { uri } = widget.editor;
|
const { uri } = widget.editor;
|
||||||
const filename = uri.path.toString();
|
const filename = uri.path.toString();
|
||||||
// Do not necessarily require the current window if not needed. It's a synchronous, blocking call.
|
|
||||||
if (this.previousRepresentedFilename !== filename) {
|
if (this.previousRepresentedFilename !== filename) {
|
||||||
const currentWindow = remote.getCurrentWindow();
|
window.electronArduino.setRepresentedFilename(uri.path.fsPath());
|
||||||
currentWindow.setRepresentedFilename(uri.path.toString());
|
|
||||||
this.previousRepresentedFilename = filename;
|
this.previousRepresentedFilename = filename;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,29 +0,0 @@
|
|||||||
import * as React from '@theia/core/shared/react';
|
|
||||||
import { DebugAction as TheiaDebugAction } from '@theia/debug/lib/browser/view/debug-action';
|
|
||||||
import {
|
|
||||||
codiconArray,
|
|
||||||
DISABLED_CLASS,
|
|
||||||
} from '@theia/core/lib/browser/widgets/widget';
|
|
||||||
|
|
||||||
// customized debug action to show the contributed command's label when there is no icon
|
|
||||||
export class DebugAction extends TheiaDebugAction {
|
|
||||||
override render(): React.ReactNode {
|
|
||||||
const { enabled, label, iconClass } = this.props;
|
|
||||||
const classNames = ['debug-action', ...codiconArray(iconClass, true)];
|
|
||||||
if (enabled === false) {
|
|
||||||
classNames.push(DISABLED_CLASS);
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<span
|
|
||||||
tabIndex={0}
|
|
||||||
className={classNames.join(' ')}
|
|
||||||
title={label}
|
|
||||||
onClick={this.props.run}
|
|
||||||
ref={this.setRef}
|
|
||||||
>
|
|
||||||
{!iconClass ||
|
|
||||||
(iconClass.match(/plugin-icon-\d+/) && <div>{label}</div>)}
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,4 +1,4 @@
|
|||||||
import debounce = require('p-debounce');
|
import debounce from 'p-debounce';
|
||||||
import {
|
import {
|
||||||
inject,
|
inject,
|
||||||
injectable,
|
injectable,
|
||||||
|
@ -43,7 +43,8 @@ export class DefaultDebugSessionFactory extends TheiaDefaultDebugSessionFactory
|
|||||||
this.messages,
|
this.messages,
|
||||||
this.fileService,
|
this.fileService,
|
||||||
this.debugContributionProvider,
|
this.debugContributionProvider,
|
||||||
this.workspaceService
|
this.workspaceService,
|
||||||
|
2_000
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,120 +0,0 @@
|
|||||||
import type { ContextKey } from '@theia/core/lib/browser/context-key-service';
|
|
||||||
import { injectable, postConstruct } from '@theia/core/shared/inversify';
|
|
||||||
import {
|
|
||||||
DebugSession,
|
|
||||||
DebugState,
|
|
||||||
} from '@theia/debug/lib/browser/debug-session';
|
|
||||||
import { DebugSessionManager as TheiaDebugSessionManager } from '@theia/debug/lib/browser/debug-session-manager';
|
|
||||||
import type { DebugConfigurationSessionOptions } from '@theia/debug/lib/browser/debug-session-options';
|
|
||||||
|
|
||||||
function debugStateLabel(state: DebugState): string {
|
|
||||||
switch (state) {
|
|
||||||
case DebugState.Initializing:
|
|
||||||
return 'initializing';
|
|
||||||
case DebugState.Stopped:
|
|
||||||
return 'stopped';
|
|
||||||
case DebugState.Running:
|
|
||||||
return 'running';
|
|
||||||
default:
|
|
||||||
return 'inactive';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@injectable()
|
|
||||||
export class DebugSessionManager extends TheiaDebugSessionManager {
|
|
||||||
protected debugStateKey: ContextKey<string>;
|
|
||||||
|
|
||||||
@postConstruct()
|
|
||||||
protected override init(): void {
|
|
||||||
this.debugStateKey = this.contextKeyService.createKey<string>(
|
|
||||||
'debugState',
|
|
||||||
debugStateLabel(this.state)
|
|
||||||
);
|
|
||||||
super.init();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override fireDidChange(current: DebugSession | undefined): void {
|
|
||||||
this.debugTypeKey.set(current?.configuration.type);
|
|
||||||
this.inDebugModeKey.set(this.inDebugMode);
|
|
||||||
this.debugStateKey.set(debugStateLabel(this.state));
|
|
||||||
this.onDidChangeEmitter.fire(current);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override async doStart(
|
|
||||||
sessionId: string,
|
|
||||||
options: DebugConfigurationSessionOptions
|
|
||||||
): Promise<DebugSession> {
|
|
||||||
const parentSession =
|
|
||||||
options.configuration.parentSession &&
|
|
||||||
this._sessions.get(options.configuration.parentSession.id);
|
|
||||||
const contrib = this.sessionContributionRegistry.get(
|
|
||||||
options.configuration.type
|
|
||||||
);
|
|
||||||
const sessionFactory = contrib
|
|
||||||
? contrib.debugSessionFactory()
|
|
||||||
: this.debugSessionFactory;
|
|
||||||
const session = sessionFactory.get(sessionId, options, parentSession);
|
|
||||||
this._sessions.set(sessionId, session);
|
|
||||||
|
|
||||||
this.debugTypeKey.set(session.configuration.type);
|
|
||||||
// this.onDidCreateDebugSessionEmitter.fire(session); // defer the didCreate event after start https://github.com/eclipse-theia/theia/issues/11916
|
|
||||||
|
|
||||||
let state = DebugState.Inactive;
|
|
||||||
session.onDidChange(() => {
|
|
||||||
if (state !== session.state) {
|
|
||||||
state = session.state;
|
|
||||||
if (state === DebugState.Stopped) {
|
|
||||||
this.onDidStopDebugSessionEmitter.fire(session);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.updateCurrentSession(session);
|
|
||||||
});
|
|
||||||
session.onDidChangeBreakpoints((uri) =>
|
|
||||||
this.fireDidChangeBreakpoints({ session, uri })
|
|
||||||
);
|
|
||||||
session.on('terminated', async (event) => {
|
|
||||||
const restart = event.body && event.body.restart;
|
|
||||||
if (restart) {
|
|
||||||
// postDebugTask isn't run in case of auto restart as well as preLaunchTask
|
|
||||||
this.doRestart(session, !!restart);
|
|
||||||
} else {
|
|
||||||
await session.disconnect(false, () =>
|
|
||||||
this.debug.terminateDebugSession(session.id)
|
|
||||||
);
|
|
||||||
await this.runTask(
|
|
||||||
session.options.workspaceFolderUri,
|
|
||||||
session.configuration.postDebugTask
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// eslint-disable-next-line unused-imports/no-unused-vars, @typescript-eslint/no-unused-vars
|
|
||||||
session.on('exited', async (event) => {
|
|
||||||
await session.disconnect(false, () =>
|
|
||||||
this.debug.terminateDebugSession(session.id)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
session.onDispose(() => this.cleanup(session));
|
|
||||||
session
|
|
||||||
.start()
|
|
||||||
.then(() => {
|
|
||||||
this.onDidCreateDebugSessionEmitter.fire(session); // now fire the didCreate event
|
|
||||||
this.onDidStartDebugSessionEmitter.fire(session);
|
|
||||||
})
|
|
||||||
// eslint-disable-next-line unused-imports/no-unused-vars, @typescript-eslint/no-unused-vars
|
|
||||||
.catch((e) => {
|
|
||||||
session.stop(false, () => {
|
|
||||||
this.debug.terminateDebugSession(session.id);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
session.onDidCustomEvent(({ event, body }) =>
|
|
||||||
this.onDidReceiveDebugSessionCustomEventEmitter.fire({
|
|
||||||
event,
|
|
||||||
body,
|
|
||||||
session,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
return session;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,231 +1,12 @@
|
|||||||
import { FrontendApplicationConfigProvider } from '@theia/core/lib/browser/frontend-application-config-provider';
|
|
||||||
import { Deferred } from '@theia/core/lib/common/promise-util';
|
|
||||||
import { Mutable } from '@theia/core/lib/common/types';
|
|
||||||
import { URI } from '@theia/core/lib/common/uri';
|
|
||||||
import { DebugSession as TheiaDebugSession } from '@theia/debug/lib/browser/debug-session';
|
import { DebugSession as TheiaDebugSession } from '@theia/debug/lib/browser/debug-session';
|
||||||
import { DebugFunctionBreakpoint } from '@theia/debug/lib/browser/model/debug-function-breakpoint';
|
|
||||||
import { DebugSourceBreakpoint } from '@theia/debug/lib/browser/model/debug-source-breakpoint';
|
|
||||||
import {
|
|
||||||
DebugThreadData,
|
|
||||||
StoppedDetails,
|
|
||||||
} from '@theia/debug/lib/browser/model/debug-thread';
|
|
||||||
import { DebugProtocol } from '@vscode/debugprotocol';
|
|
||||||
import { DebugThread } from './debug-thread';
|
|
||||||
|
|
||||||
export class DebugSession extends TheiaDebugSession {
|
export class DebugSession extends TheiaDebugSession {
|
||||||
/**
|
// eslint-disable-next-line unused-imports/no-unused-vars, @typescript-eslint/no-unused-vars
|
||||||
* The `send('initialize')` request resolves later than `on('initialized')` emits the event.
|
protected override handleDisconnectError(err: unknown): void {
|
||||||
* Hence, the `configure` would use the empty object `capabilities`.
|
// NOOP
|
||||||
* Using the empty `capabilities` could result in missing exception breakpoint filters, as
|
}
|
||||||
* always `capabilities.exceptionBreakpointFilters` is falsy. This deferred promise works
|
// eslint-disable-next-line unused-imports/no-unused-vars, @typescript-eslint/no-unused-vars
|
||||||
* around this timing issue.
|
protected override handleTerminateError(err: unknown): void {
|
||||||
* See: https://github.com/eclipse-theia/theia/issues/11886.
|
// NOOP
|
||||||
*/
|
|
||||||
protected didReceiveCapabilities = new Deferred();
|
|
||||||
|
|
||||||
protected override async initialize(): Promise<void> {
|
|
||||||
const clientName = FrontendApplicationConfigProvider.get().applicationName;
|
|
||||||
try {
|
|
||||||
const response = await this.connection.sendRequest('initialize', {
|
|
||||||
clientID: clientName.toLocaleLowerCase().replace(/ /g, '_'),
|
|
||||||
clientName,
|
|
||||||
adapterID: this.configuration.type,
|
|
||||||
locale: 'en-US',
|
|
||||||
linesStartAt1: true,
|
|
||||||
columnsStartAt1: true,
|
|
||||||
pathFormat: 'path',
|
|
||||||
supportsVariableType: false,
|
|
||||||
supportsVariablePaging: false,
|
|
||||||
supportsRunInTerminalRequest: true,
|
|
||||||
});
|
|
||||||
this.updateCapabilities(response?.body || {});
|
|
||||||
this.didReceiveCapabilities.resolve();
|
|
||||||
} catch (err) {
|
|
||||||
this.didReceiveCapabilities.reject(err);
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override async configure(): Promise<void> {
|
|
||||||
await this.didReceiveCapabilities.promise;
|
|
||||||
return super.configure();
|
|
||||||
}
|
|
||||||
|
|
||||||
override async stop(isRestart: boolean, callback: () => void): Promise<void> {
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
const _this = this as any;
|
|
||||||
if (!_this.isStopping) {
|
|
||||||
_this.isStopping = true;
|
|
||||||
if (this.configuration.lifecycleManagedByParent && this.parentSession) {
|
|
||||||
await this.parentSession.stop(isRestart, callback);
|
|
||||||
} else {
|
|
||||||
if (this.canTerminate()) {
|
|
||||||
const terminated = this.waitFor('terminated', 5000);
|
|
||||||
try {
|
|
||||||
await this.connection.sendRequest(
|
|
||||||
'terminate',
|
|
||||||
{ restart: isRestart },
|
|
||||||
5000
|
|
||||||
);
|
|
||||||
await terminated;
|
|
||||||
} catch (e) {
|
|
||||||
console.error('Did not receive terminated event in time', e);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const terminateDebuggee =
|
|
||||||
this.initialized && this.capabilities.supportTerminateDebuggee;
|
|
||||||
// Related https://github.com/microsoft/vscode/issues/165138
|
|
||||||
try {
|
|
||||||
await this.sendRequest(
|
|
||||||
'disconnect',
|
|
||||||
{ restart: isRestart, terminateDebuggee },
|
|
||||||
2000
|
|
||||||
);
|
|
||||||
} catch (err) {
|
|
||||||
if (
|
|
||||||
'message' in err &&
|
|
||||||
typeof err.message === 'string' &&
|
|
||||||
err.message.test(err.message)
|
|
||||||
) {
|
|
||||||
// VS Code ignores errors when sending the `disconnect` request.
|
|
||||||
// Debug adapter might not send the `disconnected` event as a response.
|
|
||||||
} else {
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override async sendFunctionBreakpoints(
|
|
||||||
affectedUri: URI
|
|
||||||
): Promise<void> {
|
|
||||||
const all = this.breakpoints
|
|
||||||
.getFunctionBreakpoints()
|
|
||||||
.map(
|
|
||||||
(origin) =>
|
|
||||||
new DebugFunctionBreakpoint(origin, this.asDebugBreakpointOptions())
|
|
||||||
);
|
|
||||||
const enabled = all.filter((b) => b.enabled);
|
|
||||||
if (this.capabilities.supportsFunctionBreakpoints) {
|
|
||||||
try {
|
|
||||||
const response = await this.sendRequest('setFunctionBreakpoints', {
|
|
||||||
breakpoints: enabled.map((b) => b.origin.raw),
|
|
||||||
});
|
|
||||||
// Apparently, `body` and `breakpoints` can be missing.
|
|
||||||
// https://github.com/eclipse-theia/theia/issues/11885
|
|
||||||
// https://github.com/microsoft/vscode/blob/80004351ccf0884b58359f7c8c801c91bb827d83/src/vs/workbench/contrib/debug/browser/debugSession.ts#L448-L449
|
|
||||||
if (response && response.body) {
|
|
||||||
response.body.breakpoints.forEach((raw, index) => {
|
|
||||||
// node debug adapter returns more breakpoints sometimes
|
|
||||||
if (enabled[index]) {
|
|
||||||
enabled[index].update({ raw });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
// could be error or promise rejection of DebugProtocol.SetFunctionBreakpoints
|
|
||||||
if (error instanceof Error) {
|
|
||||||
console.error(`Error setting breakpoints: ${error.message}`);
|
|
||||||
} else {
|
|
||||||
// handle adapters that send failed DebugProtocol.SetFunctionBreakpoints for invalid breakpoints
|
|
||||||
const genericMessage =
|
|
||||||
'Function breakpoint not valid for current debug session';
|
|
||||||
const message = error.message ? `${error.message}` : genericMessage;
|
|
||||||
console.warn(
|
|
||||||
`Could not handle function breakpoints: ${message}, disabling...`
|
|
||||||
);
|
|
||||||
enabled.forEach((b) =>
|
|
||||||
b.update({
|
|
||||||
raw: {
|
|
||||||
verified: false,
|
|
||||||
message,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.setBreakpoints(affectedUri, all);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override async sendSourceBreakpoints(
|
|
||||||
affectedUri: URI,
|
|
||||||
sourceModified?: boolean
|
|
||||||
): Promise<void> {
|
|
||||||
const source = await this.toSource(affectedUri);
|
|
||||||
const all = this.breakpoints
|
|
||||||
.findMarkers({ uri: affectedUri })
|
|
||||||
.map(
|
|
||||||
({ data }) =>
|
|
||||||
new DebugSourceBreakpoint(data, this.asDebugBreakpointOptions())
|
|
||||||
);
|
|
||||||
const enabled = all.filter((b) => b.enabled);
|
|
||||||
try {
|
|
||||||
const breakpoints = enabled.map(({ origin }) => origin.raw);
|
|
||||||
const response = await this.sendRequest('setBreakpoints', {
|
|
||||||
source: source.raw,
|
|
||||||
sourceModified,
|
|
||||||
breakpoints,
|
|
||||||
lines: breakpoints.map(({ line }) => line),
|
|
||||||
});
|
|
||||||
response.body.breakpoints.forEach((raw, index) => {
|
|
||||||
// node debug adapter returns more breakpoints sometimes
|
|
||||||
if (enabled[index]) {
|
|
||||||
enabled[index].update({ raw });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
// could be error or promise rejection of DebugProtocol.SetBreakpointsResponse
|
|
||||||
if (error instanceof Error) {
|
|
||||||
console.error(`Error setting breakpoints: ${error.message}`);
|
|
||||||
} else {
|
|
||||||
// handle adapters that send failed DebugProtocol.SetBreakpointsResponse for invalid breakpoints
|
|
||||||
const genericMessage = 'Breakpoint not valid for current debug session';
|
|
||||||
const message = error.message ? `${error.message}` : genericMessage;
|
|
||||||
console.warn(
|
|
||||||
`Could not handle breakpoints for ${affectedUri}: ${message}, disabling...`
|
|
||||||
);
|
|
||||||
enabled.forEach((b) =>
|
|
||||||
b.update({
|
|
||||||
raw: {
|
|
||||||
verified: false,
|
|
||||||
message,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.setSourceBreakpoints(affectedUri, all);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override doUpdateThreads(
|
|
||||||
threads: DebugProtocol.Thread[],
|
|
||||||
stoppedDetails?: StoppedDetails
|
|
||||||
): void {
|
|
||||||
const existing = this._threads;
|
|
||||||
this._threads = new Map();
|
|
||||||
for (const raw of threads) {
|
|
||||||
const id = raw.id;
|
|
||||||
const thread = existing.get(id) || new DebugThread(this); // patched debug thread
|
|
||||||
this._threads.set(id, thread);
|
|
||||||
const data: Partial<Mutable<DebugThreadData>> = { raw };
|
|
||||||
if (stoppedDetails) {
|
|
||||||
if (stoppedDetails.threadId === id) {
|
|
||||||
data.stoppedDetails = stoppedDetails;
|
|
||||||
} else if (stoppedDetails.allThreadsStopped) {
|
|
||||||
data.stoppedDetails = {
|
|
||||||
// When a debug adapter notifies us that all threads are stopped,
|
|
||||||
// we do not know why the others are stopped, so we should default
|
|
||||||
// to something generic.
|
|
||||||
reason: '',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
thread.update(data);
|
|
||||||
}
|
|
||||||
this.updateCurrentThread(stoppedDetails);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,32 +0,0 @@
|
|||||||
import { WidgetOpenerOptions } from '@theia/core/lib/browser/widget-open-handler';
|
|
||||||
import { Range } from '@theia/core/shared/vscode-languageserver-types';
|
|
||||||
import { DebugStackFrame as TheiaDebugStackFrame } from '@theia/debug/lib/browser/model/debug-stack-frame';
|
|
||||||
import { EditorWidget } from '@theia/editor/lib/browser/editor-widget';
|
|
||||||
|
|
||||||
export class DebugStackFrame extends TheiaDebugStackFrame {
|
|
||||||
override async open(
|
|
||||||
options: WidgetOpenerOptions = {
|
|
||||||
mode: 'reveal',
|
|
||||||
}
|
|
||||||
): Promise<EditorWidget | undefined> {
|
|
||||||
if (!this.source) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
const { line, column, endLine, endColumn, source } = this.raw;
|
|
||||||
if (!source) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
// create selection based on VS Code
|
|
||||||
// https://github.com/eclipse-theia/theia/issues/11880
|
|
||||||
const selection = Range.create(
|
|
||||||
line,
|
|
||||||
column,
|
|
||||||
endLine || line,
|
|
||||||
endColumn || column
|
|
||||||
);
|
|
||||||
this.source.open({
|
|
||||||
...options,
|
|
||||||
selection,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
import { DebugStackFrame as TheiaDebugStackFrame } from '@theia/debug/lib/browser/model/debug-stack-frame';
|
|
||||||
import { DebugThread as TheiaDebugThread } from '@theia/debug/lib/browser/model/debug-thread';
|
|
||||||
import { DebugProtocol } from '@vscode/debugprotocol';
|
|
||||||
import { DebugStackFrame } from './debug-stack-frame';
|
|
||||||
|
|
||||||
export class DebugThread extends TheiaDebugThread {
|
|
||||||
protected override doUpdateFrames(
|
|
||||||
frames: DebugProtocol.StackFrame[]
|
|
||||||
): TheiaDebugStackFrame[] {
|
|
||||||
const result = new Set<TheiaDebugStackFrame>();
|
|
||||||
for (const raw of frames) {
|
|
||||||
const id = raw.id;
|
|
||||||
const frame =
|
|
||||||
this._frames.get(id) || new DebugStackFrame(this, this.session); // patched debug stack frame
|
|
||||||
this._frames.set(id, frame);
|
|
||||||
frame.update({ raw });
|
|
||||||
result.add(frame);
|
|
||||||
}
|
|
||||||
this.updateCurrentFrame();
|
|
||||||
return [...result.values()];
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,85 +0,0 @@
|
|||||||
import { ContextKeyService } from '@theia/core/lib/browser/context-key-service';
|
|
||||||
import { CommandRegistry } from '@theia/core/lib/common/command';
|
|
||||||
import {
|
|
||||||
ActionMenuNode,
|
|
||||||
CompositeMenuNode,
|
|
||||||
MenuModelRegistry,
|
|
||||||
} from '@theia/core/lib/common/menu';
|
|
||||||
import { nls } from '@theia/core/lib/common/nls';
|
|
||||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
|
||||||
import * as React from '@theia/core/shared/react';
|
|
||||||
import { DebugState } from '@theia/debug/lib/browser/debug-session';
|
|
||||||
import { DebugAction } from './debug-action';
|
|
||||||
import { DebugToolBar as TheiaDebugToolbar } from '@theia/debug/lib/browser/view/debug-toolbar-widget';
|
|
||||||
|
|
||||||
@injectable()
|
|
||||||
export class DebugToolbar extends TheiaDebugToolbar {
|
|
||||||
@inject(CommandRegistry) private readonly commandRegistry: CommandRegistry;
|
|
||||||
@inject(MenuModelRegistry)
|
|
||||||
private readonly menuModelRegistry: MenuModelRegistry;
|
|
||||||
@inject(ContextKeyService)
|
|
||||||
private readonly contextKeyService: ContextKeyService;
|
|
||||||
|
|
||||||
protected override render(): React.ReactNode {
|
|
||||||
const { state } = this.model;
|
|
||||||
return (
|
|
||||||
<React.Fragment>
|
|
||||||
{this.renderContributedCommands()}
|
|
||||||
{this.renderContinue()}
|
|
||||||
<DebugAction
|
|
||||||
enabled={state === DebugState.Stopped}
|
|
||||||
run={this.stepOver}
|
|
||||||
label={nls.localizeByDefault('Step Over')}
|
|
||||||
iconClass="debug-step-over"
|
|
||||||
ref={this.setStepRef}
|
|
||||||
/>
|
|
||||||
<DebugAction
|
|
||||||
enabled={state === DebugState.Stopped}
|
|
||||||
run={this.stepIn}
|
|
||||||
label={nls.localizeByDefault('Step Into')}
|
|
||||||
iconClass="debug-step-into"
|
|
||||||
/>
|
|
||||||
<DebugAction
|
|
||||||
enabled={state === DebugState.Stopped}
|
|
||||||
run={this.stepOut}
|
|
||||||
label={nls.localizeByDefault('Step Out')}
|
|
||||||
iconClass="debug-step-out"
|
|
||||||
/>
|
|
||||||
<DebugAction
|
|
||||||
enabled={state !== DebugState.Inactive}
|
|
||||||
run={this.restart}
|
|
||||||
label={nls.localizeByDefault('Restart')}
|
|
||||||
iconClass="debug-restart"
|
|
||||||
/>
|
|
||||||
{this.renderStart()}
|
|
||||||
</React.Fragment>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private renderContributedCommands(): React.ReactNode {
|
|
||||||
return this.menuModelRegistry
|
|
||||||
.getMenu(TheiaDebugToolbar.MENU)
|
|
||||||
.children.filter((node) => node instanceof CompositeMenuNode)
|
|
||||||
.map((node) => (node as CompositeMenuNode).children)
|
|
||||||
.reduce((acc, curr) => acc.concat(curr), [])
|
|
||||||
.filter((node) => node instanceof ActionMenuNode)
|
|
||||||
.map((node) => this.debugAction(node as ActionMenuNode));
|
|
||||||
}
|
|
||||||
|
|
||||||
private debugAction(node: ActionMenuNode): React.ReactNode {
|
|
||||||
const { label, command, when, icon: iconClass = '' } = node;
|
|
||||||
const run = () => this.commandRegistry.executeCommand(command);
|
|
||||||
const enabled = when ? this.contextKeyService.match(when) : true;
|
|
||||||
return (
|
|
||||||
enabled && (
|
|
||||||
<DebugAction
|
|
||||||
key={command}
|
|
||||||
enabled={enabled}
|
|
||||||
label={label}
|
|
||||||
iconClass={iconClass}
|
|
||||||
run={run}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,5 +1,3 @@
|
|||||||
import { ResourceSaveOptions } from '@theia/core/lib/common/resource';
|
|
||||||
import { Readable } from '@theia/core/lib/common/stream';
|
|
||||||
import URI from '@theia/core/lib/common/uri';
|
import URI from '@theia/core/lib/common/uri';
|
||||||
import { injectable } from '@theia/core/shared/inversify';
|
import { injectable } from '@theia/core/shared/inversify';
|
||||||
import {
|
import {
|
||||||
@ -11,14 +9,13 @@ import { FileService } from '@theia/filesystem/lib/browser/file-service';
|
|||||||
import {
|
import {
|
||||||
FileOperationError,
|
FileOperationError,
|
||||||
FileOperationResult,
|
FileOperationResult,
|
||||||
FileStat,
|
|
||||||
} from '@theia/filesystem/lib/common/files';
|
} from '@theia/filesystem/lib/common/files';
|
||||||
import * as PQueue from 'p-queue';
|
import PQueue from 'p-queue';
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class FileResourceResolver extends TheiaFileResourceResolver {
|
export class FileResourceResolver extends TheiaFileResourceResolver {
|
||||||
override async resolve(uri: URI): Promise<WriteQueuedFileResource> {
|
override async resolve(uri: URI): Promise<WriteQueuedFileResource> {
|
||||||
let stat: FileStat | undefined;
|
let stat;
|
||||||
try {
|
try {
|
||||||
stat = await this.fileService.resolve(uri);
|
stat = await this.fileService.resolve(uri);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@ -37,6 +34,7 @@ export class FileResourceResolver extends TheiaFileResourceResolver {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
return new WriteQueuedFileResource(uri, this.fileService, {
|
return new WriteQueuedFileResource(uri, this.fileService, {
|
||||||
|
isReadonly: stat?.isReadonly ?? false,
|
||||||
shouldOverwrite: () => this.shouldOverwrite(uri),
|
shouldOverwrite: () => this.shouldOverwrite(uri),
|
||||||
shouldOpenAsText: (error) => this.shouldOpenAsText(uri, error),
|
shouldOpenAsText: (error) => this.shouldOpenAsText(uri, error),
|
||||||
});
|
});
|
||||||
@ -52,23 +50,32 @@ class WriteQueuedFileResource extends FileResource {
|
|||||||
options: FileResourceOptions
|
options: FileResourceOptions
|
||||||
) {
|
) {
|
||||||
super(uri, fileService, options);
|
super(uri, fileService, options);
|
||||||
|
const originalDoWrite = this['doWrite'];
|
||||||
|
this['doWrite'] = (content, options) =>
|
||||||
|
this.writeQueue.add(() => originalDoWrite.bind(this)(content, options));
|
||||||
|
const originalSaveStream = this['saveStream'];
|
||||||
|
if (originalSaveStream) {
|
||||||
|
this['saveStream'] = (content, options) =>
|
||||||
|
this.writeQueue.add(() =>
|
||||||
|
originalSaveStream.bind(this)(content, options)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const originalSaveContents = this['saveContents'];
|
||||||
|
if (originalSaveContents) {
|
||||||
|
this['saveContents'] = (content, options) =>
|
||||||
|
this.writeQueue.add(() =>
|
||||||
|
originalSaveContents.bind(this)(content, options)
|
||||||
|
);
|
||||||
|
}
|
||||||
const originalSaveContentChanges = this['saveContentChanges'];
|
const originalSaveContentChanges = this['saveContentChanges'];
|
||||||
if (originalSaveContentChanges) {
|
if (originalSaveContentChanges) {
|
||||||
this['saveContentChanges'] = (changes, options) => {
|
this['saveContentChanges'] = (changes, options) =>
|
||||||
return this.writeQueue.add(() =>
|
this.writeQueue.add(() =>
|
||||||
originalSaveContentChanges.bind(this)(changes, options)
|
originalSaveContentChanges.bind(this)(changes, options)
|
||||||
);
|
);
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override async doWrite(
|
|
||||||
content: string | Readable<string>,
|
|
||||||
options?: ResourceSaveOptions
|
|
||||||
): Promise<void> {
|
|
||||||
return this.writeQueue.add(() => super.doWrite(content, options));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override async isInSync(): Promise<boolean> {
|
protected override async isInSync(): Promise<boolean> {
|
||||||
// Let all the write operations finish to update the version (mtime) before checking whether the resource is in sync.
|
// Let all the write operations finish to update the version (mtime) before checking whether the resource is in sync.
|
||||||
// https://github.com/eclipse-theia/theia/issues/12327
|
// https://github.com/eclipse-theia/theia/issues/12327
|
||||||
|
@ -8,7 +8,7 @@ import URI from '@theia/core/lib/common/uri';
|
|||||||
import { Marker } from '@theia/markers/lib/common/marker';
|
import { Marker } from '@theia/markers/lib/common/marker';
|
||||||
import { ProblemManager as TheiaProblemManager } from '@theia/markers/lib/browser/problem/problem-manager';
|
import { ProblemManager as TheiaProblemManager } from '@theia/markers/lib/browser/problem/problem-manager';
|
||||||
import { ConfigServiceClient } from '../../config/config-service-client';
|
import { ConfigServiceClient } from '../../config/config-service-client';
|
||||||
import debounce = require('lodash.debounce');
|
import debounce from 'lodash.debounce';
|
||||||
import {
|
import {
|
||||||
ARDUINO_CLOUD_FOLDER,
|
ARDUINO_CLOUD_FOLDER,
|
||||||
REMOTE_SKETCHBOOK_FOLDER,
|
REMOTE_SKETCHBOOK_FOLDER,
|
||||||
|
@ -0,0 +1,24 @@
|
|||||||
|
import type { MenuModelRegistry } from '@theia/core/lib/common/menu/menu-model-registry';
|
||||||
|
import { injectable } from '@theia/core/shared/inversify';
|
||||||
|
import { MonacoEditorMenuContribution as TheiaMonacoEditorMenuContribution } from '@theia/monaco/lib/browser/monaco-menu';
|
||||||
|
|
||||||
|
@injectable()
|
||||||
|
export class MonacoEditorMenuContribution extends TheiaMonacoEditorMenuContribution {
|
||||||
|
override registerMenus(registry: MenuModelRegistry): void {
|
||||||
|
super.registerMenus(registry);
|
||||||
|
// https://github.com/arduino/arduino-ide/issues/1394
|
||||||
|
registry.unregisterMenuAction('editor.action.refactor'); // Refactor...
|
||||||
|
registry.unregisterMenuAction('editor.action.sourceAction'); // Source Action...
|
||||||
|
// https://github.com/arduino/arduino-ide/pull/2027#pullrequestreview-1414246614
|
||||||
|
// Root editor context menu
|
||||||
|
registry.unregisterMenuAction('editor.action.revealDeclaration'); // Go to Declaration
|
||||||
|
registry.unregisterMenuAction('editor.action.goToTypeDefinition'); // Go to Type Definition
|
||||||
|
registry.unregisterMenuAction('editor.action.goToImplementation'); // Go to Implementations
|
||||||
|
registry.unregisterMenuAction('editor.action.goToReferences'); // Go to References
|
||||||
|
// Peek submenu
|
||||||
|
registry.unregisterMenuAction('editor.action.peekDeclaration'); // Peek Declaration
|
||||||
|
registry.unregisterMenuAction('editor.action.peekTypeDefinition'); // Peek Type Definition
|
||||||
|
registry.unregisterMenuAction('editor.action.peekImplementation'); // Peek Implementation
|
||||||
|
registry.unregisterMenuAction('editor.action.referenceSearch.trigger'); // Peek References
|
||||||
|
}
|
||||||
|
}
|
@ -1,66 +0,0 @@
|
|||||||
import {
|
|
||||||
Disposable,
|
|
||||||
DisposableCollection,
|
|
||||||
} from '@theia/core/lib/common/disposable';
|
|
||||||
import { DebuggerDescription } from '@theia/debug/lib/common/debug-service';
|
|
||||||
import { DebugMainImpl as TheiaDebugMainImpl } from '@theia/plugin-ext/lib/main/browser/debug/debug-main';
|
|
||||||
import { PluginDebugAdapterContribution } from '@theia/plugin-ext/lib/main/browser/debug/plugin-debug-adapter-contribution';
|
|
||||||
import { PluginDebugSessionFactory } from './plugin-debug-session-factory';
|
|
||||||
|
|
||||||
export class DebugMainImpl extends TheiaDebugMainImpl {
|
|
||||||
override async $registerDebuggerContribution(
|
|
||||||
description: DebuggerDescription
|
|
||||||
): Promise<void> {
|
|
||||||
const debugType = description.type;
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
const _this = <any>this;
|
|
||||||
const terminalOptionsExt = await _this.debugExt.$getTerminalCreationOptions(
|
|
||||||
debugType
|
|
||||||
);
|
|
||||||
|
|
||||||
if (_this.toDispose.disposed) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const debugSessionFactory = new PluginDebugSessionFactory(
|
|
||||||
_this.terminalService,
|
|
||||||
_this.editorManager,
|
|
||||||
_this.breakpointsManager,
|
|
||||||
_this.labelProvider,
|
|
||||||
_this.messages,
|
|
||||||
_this.outputChannelManager,
|
|
||||||
_this.debugPreferences,
|
|
||||||
async (sessionId: string) => {
|
|
||||||
const connection = await _this.connectionMain.ensureConnection(
|
|
||||||
sessionId
|
|
||||||
);
|
|
||||||
return connection;
|
|
||||||
},
|
|
||||||
_this.fileService,
|
|
||||||
terminalOptionsExt,
|
|
||||||
_this.debugContributionProvider,
|
|
||||||
_this.workspaceService
|
|
||||||
);
|
|
||||||
|
|
||||||
const toDispose = new DisposableCollection(
|
|
||||||
Disposable.create(() => _this.debuggerContributions.delete(debugType))
|
|
||||||
);
|
|
||||||
_this.debuggerContributions.set(debugType, toDispose);
|
|
||||||
toDispose.pushAll([
|
|
||||||
_this.pluginDebugService.registerDebugAdapterContribution(
|
|
||||||
new PluginDebugAdapterContribution(
|
|
||||||
description,
|
|
||||||
_this.debugExt,
|
|
||||||
_this.pluginService
|
|
||||||
)
|
|
||||||
),
|
|
||||||
_this.sessionContributionRegistrator.registerDebugSessionContribution({
|
|
||||||
debugType: description.type,
|
|
||||||
debugSessionFactory: () => debugSessionFactory,
|
|
||||||
}),
|
|
||||||
]);
|
|
||||||
_this.toDispose.push(
|
|
||||||
Disposable.create(() => this.$unregisterDebuggerConfiguration(debugType))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,16 +1,7 @@
|
|||||||
import { Emitter, Event, JsonRpcProxy } from '@theia/core';
|
import { Emitter, Event, JsonRpcProxy } from '@theia/core';
|
||||||
import { injectable, interfaces } from '@theia/core/shared/inversify';
|
import { injectable, interfaces } from '@theia/core/shared/inversify';
|
||||||
import { HostedPluginServer } from '@theia/plugin-ext/lib/common/plugin-protocol';
|
import { HostedPluginServer } from '@theia/plugin-ext/lib/common/plugin-protocol';
|
||||||
import { RPCProtocol } from '@theia/plugin-ext/lib/common/rpc-protocol';
|
import { HostedPluginSupport as TheiaHostedPluginSupport } from '@theia/plugin-ext/lib/hosted/browser/hosted-plugin';
|
||||||
import {
|
|
||||||
HostedPluginSupport as TheiaHostedPluginSupport,
|
|
||||||
PluginHost,
|
|
||||||
} from '@theia/plugin-ext/lib/hosted/browser/hosted-plugin';
|
|
||||||
import { PluginWorker } from '@theia/plugin-ext/lib/hosted/browser/plugin-worker';
|
|
||||||
import { setUpPluginApi } from '@theia/plugin-ext/lib/main/browser/main-context';
|
|
||||||
import { PLUGIN_RPC_CONTEXT } from '@theia/plugin-ext/lib/common/plugin-api-rpc';
|
|
||||||
import { DebugMainImpl } from './debug-main';
|
|
||||||
import { ConnectionImpl } from '@theia/plugin-ext/lib/common/connection';
|
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class HostedPluginSupport extends TheiaHostedPluginSupport {
|
export class HostedPluginSupport extends TheiaHostedPluginSupport {
|
||||||
@ -41,26 +32,4 @@ export class HostedPluginSupport extends TheiaHostedPluginSupport {
|
|||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
return (this as any).server;
|
return (this as any).server;
|
||||||
}
|
}
|
||||||
|
|
||||||
// to patch the VS Code extension based debugger
|
|
||||||
// eslint-disable-next-line unused-imports/no-unused-vars, @typescript-eslint/no-unused-vars
|
|
||||||
protected override initRpc(host: PluginHost, pluginId: string): RPCProtocol {
|
|
||||||
const rpc =
|
|
||||||
host === 'frontend' ? new PluginWorker().rpc : this.createServerRpc(host);
|
|
||||||
setUpPluginApi(rpc, this.container);
|
|
||||||
this.patchDebugMain(rpc);
|
|
||||||
this.mainPluginApiProviders
|
|
||||||
.getContributions()
|
|
||||||
.forEach((p) => p.initialize(rpc, this.container));
|
|
||||||
return rpc;
|
|
||||||
}
|
|
||||||
|
|
||||||
private patchDebugMain(rpc: RPCProtocol): void {
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
const connectionMain = (rpc as any).locals.get(
|
|
||||||
PLUGIN_RPC_CONTEXT.CONNECTION_MAIN.id
|
|
||||||
) as ConnectionImpl;
|
|
||||||
const debugMain = new DebugMainImpl(rpc, connectionMain, this.container);
|
|
||||||
rpc.set(PLUGIN_RPC_CONTEXT.DEBUG_MAIN, debugMain);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,37 +0,0 @@
|
|||||||
import { DebugSession } from '@theia/debug/lib/browser/debug-session';
|
|
||||||
import { DebugSessionConnection } from '@theia/debug/lib/browser/debug-session-connection';
|
|
||||||
import { DebugConfigurationSessionOptions } from '@theia/debug/lib/browser/debug-session-options';
|
|
||||||
import { PluginDebugSessionFactory as TheiaPluginDebugSessionFactory } from '@theia/plugin-ext/lib/main/browser/debug/plugin-debug-session-factory';
|
|
||||||
import { PluginDebugSession } from './plugin-debug-session';
|
|
||||||
|
|
||||||
export class PluginDebugSessionFactory extends TheiaPluginDebugSessionFactory {
|
|
||||||
override get(
|
|
||||||
sessionId: string,
|
|
||||||
options: DebugConfigurationSessionOptions,
|
|
||||||
parentSession?: DebugSession
|
|
||||||
): DebugSession {
|
|
||||||
const connection = new DebugSessionConnection(
|
|
||||||
sessionId,
|
|
||||||
this.connectionFactory,
|
|
||||||
this.getTraceOutputChannel()
|
|
||||||
);
|
|
||||||
|
|
||||||
return new PluginDebugSession(
|
|
||||||
sessionId,
|
|
||||||
options,
|
|
||||||
parentSession,
|
|
||||||
connection,
|
|
||||||
this.terminalService,
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
this.editorManager as any,
|
|
||||||
this.breakpoints,
|
|
||||||
this.labelProvider,
|
|
||||||
this.messages,
|
|
||||||
this.fileService,
|
|
||||||
this.terminalOptionsExt,
|
|
||||||
this.debugContributionProvider,
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
this.workspaceService as any
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,62 +0,0 @@
|
|||||||
import { ContributionProvider, MessageClient } from '@theia/core';
|
|
||||||
import { LabelProvider } from '@theia/core/lib/browser';
|
|
||||||
import { BreakpointManager } from '@theia/debug/lib/browser/breakpoint/breakpoint-manager';
|
|
||||||
import { DebugContribution } from '@theia/debug/lib/browser/debug-contribution';
|
|
||||||
import { DebugSession as TheiaDebugSession } from '@theia/debug/lib/browser/debug-session';
|
|
||||||
import { DebugSessionConnection } from '@theia/debug/lib/browser/debug-session-connection';
|
|
||||||
import { DebugConfigurationSessionOptions } from '@theia/debug/lib/browser/debug-session-options';
|
|
||||||
import { FileService } from '@theia/filesystem/lib/browser/file-service';
|
|
||||||
import { TerminalOptionsExt } from '@theia/plugin-ext';
|
|
||||||
import { TerminalService } from '@theia/terminal/lib/browser/base/terminal-service';
|
|
||||||
import {
|
|
||||||
TerminalWidget,
|
|
||||||
TerminalWidgetOptions,
|
|
||||||
} from '@theia/terminal/lib/browser/base/terminal-widget';
|
|
||||||
import { DebugSession } from '../debug/debug-session';
|
|
||||||
import { EditorManager } from '../editor/editor-manager';
|
|
||||||
import { WorkspaceService } from '../workspace/workspace-service';
|
|
||||||
|
|
||||||
// This class extends the patched debug session, and not the default debug session from Theia
|
|
||||||
export class PluginDebugSession extends DebugSession {
|
|
||||||
constructor(
|
|
||||||
override readonly id: string,
|
|
||||||
override readonly options: DebugConfigurationSessionOptions,
|
|
||||||
override readonly parentSession: TheiaDebugSession | undefined,
|
|
||||||
protected override readonly connection: DebugSessionConnection,
|
|
||||||
protected override readonly terminalServer: TerminalService,
|
|
||||||
protected override readonly editorManager: EditorManager,
|
|
||||||
protected override readonly breakpoints: BreakpointManager,
|
|
||||||
protected override readonly labelProvider: LabelProvider,
|
|
||||||
protected override readonly messages: MessageClient,
|
|
||||||
protected override readonly fileService: FileService,
|
|
||||||
protected readonly terminalOptionsExt: TerminalOptionsExt | undefined,
|
|
||||||
protected override readonly debugContributionProvider: ContributionProvider<DebugContribution>,
|
|
||||||
protected override readonly workspaceService: WorkspaceService
|
|
||||||
) {
|
|
||||||
super(
|
|
||||||
id,
|
|
||||||
options,
|
|
||||||
parentSession,
|
|
||||||
connection,
|
|
||||||
terminalServer,
|
|
||||||
editorManager,
|
|
||||||
breakpoints,
|
|
||||||
labelProvider,
|
|
||||||
messages,
|
|
||||||
fileService,
|
|
||||||
debugContributionProvider,
|
|
||||||
workspaceService
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override async doCreateTerminal(
|
|
||||||
terminalWidgetOptions: TerminalWidgetOptions
|
|
||||||
): Promise<TerminalWidget> {
|
|
||||||
terminalWidgetOptions = Object.assign(
|
|
||||||
{},
|
|
||||||
terminalWidgetOptions,
|
|
||||||
this.terminalOptionsExt
|
|
||||||
);
|
|
||||||
return super.doCreateTerminal(terminalWidgetOptions);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,73 +0,0 @@
|
|||||||
import { MenuPath } from '@theia/core';
|
|
||||||
import { TAB_BAR_TOOLBAR_CONTEXT_MENU } from '@theia/core/lib/browser/shell/tab-bar-toolbar';
|
|
||||||
import { injectable, postConstruct } from '@theia/core/shared/inversify';
|
|
||||||
import { DebugToolBar } from '@theia/debug/lib/browser/view/debug-toolbar-widget';
|
|
||||||
import { DebugVariablesWidget } from '@theia/debug/lib/browser/view/debug-variables-widget';
|
|
||||||
import {
|
|
||||||
ArgumentAdapter,
|
|
||||||
PluginMenuCommandAdapter as TheiaPluginMenuCommandAdapter,
|
|
||||||
} from '@theia/plugin-ext/lib/main/browser/menus/plugin-menu-command-adapter';
|
|
||||||
import {
|
|
||||||
codeToTheiaMappings,
|
|
||||||
ContributionPoint,
|
|
||||||
} from '@theia/plugin-ext/lib/main/browser/menus/vscode-theia-menu-mappings';
|
|
||||||
|
|
||||||
function patch(
|
|
||||||
toPatch: typeof codeToTheiaMappings,
|
|
||||||
key: string,
|
|
||||||
value: MenuPath[]
|
|
||||||
): void {
|
|
||||||
const loose = toPatch as Map<string, MenuPath[]>;
|
|
||||||
if (!loose.has(key)) {
|
|
||||||
loose.set(key, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// mappings is a const and cannot be customized with DI
|
|
||||||
patch(codeToTheiaMappings, 'debug/variables/context', [
|
|
||||||
DebugVariablesWidget.CONTEXT_MENU,
|
|
||||||
]);
|
|
||||||
patch(codeToTheiaMappings, 'debug/toolBar', [DebugToolBar.MENU]);
|
|
||||||
|
|
||||||
@injectable()
|
|
||||||
export class PluginMenuCommandAdapter extends TheiaPluginMenuCommandAdapter {
|
|
||||||
@postConstruct()
|
|
||||||
protected override init(): void {
|
|
||||||
const toCommentArgs: ArgumentAdapter = (...args) =>
|
|
||||||
this.toCommentArgs(...args);
|
|
||||||
const firstArgOnly: ArgumentAdapter = (...args) => [args[0]];
|
|
||||||
const noArgs: ArgumentAdapter = () => [];
|
|
||||||
const toScmArgs: ArgumentAdapter = (...args) => this.toScmArgs(...args);
|
|
||||||
const selectedResource = () => this.getSelectedResources();
|
|
||||||
const widgetURI: ArgumentAdapter = (widget) =>
|
|
||||||
this.codeEditorUtil.is(widget)
|
|
||||||
? [this.codeEditorUtil.getResourceUri(widget)]
|
|
||||||
: [];
|
|
||||||
(<Array<[ContributionPoint, ArgumentAdapter | undefined]>>[
|
|
||||||
['comments/comment/context', toCommentArgs],
|
|
||||||
['comments/comment/title', toCommentArgs],
|
|
||||||
['comments/commentThread/context', toCommentArgs],
|
|
||||||
['debug/callstack/context', firstArgOnly],
|
|
||||||
['debug/variables/context', firstArgOnly],
|
|
||||||
['debug/toolBar', noArgs],
|
|
||||||
['editor/context', selectedResource],
|
|
||||||
['editor/title', widgetURI],
|
|
||||||
['editor/title/context', selectedResource],
|
|
||||||
['explorer/context', selectedResource],
|
|
||||||
['scm/resourceFolder/context', toScmArgs],
|
|
||||||
['scm/resourceGroup/context', toScmArgs],
|
|
||||||
['scm/resourceState/context', toScmArgs],
|
|
||||||
['scm/title', () => this.toScmArg(this.scmService.selectedRepository)],
|
|
||||||
['timeline/item/context', (...args) => this.toTimelineArgs(...args)],
|
|
||||||
['view/item/context', (...args) => this.toTreeArgs(...args)],
|
|
||||||
['view/title', noArgs],
|
|
||||||
]).forEach(([contributionPoint, adapter]) => {
|
|
||||||
if (adapter) {
|
|
||||||
const paths = codeToTheiaMappings.get(contributionPoint);
|
|
||||||
if (paths) {
|
|
||||||
paths.forEach((path) => this.addArgumentAdapter(path, adapter));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this.addArgumentAdapter(TAB_BAR_TOOLBAR_CONTEXT_MENU, widgetURI);
|
|
||||||
}
|
|
||||||
}
|
|
@ -15,8 +15,9 @@ import {
|
|||||||
SketchesService,
|
SketchesService,
|
||||||
} from '../../../common/protocol/sketches-service';
|
} from '../../../common/protocol/sketches-service';
|
||||||
import {
|
import {
|
||||||
StartupTask,
|
|
||||||
StartupTaskProvider,
|
StartupTaskProvider,
|
||||||
|
hasStartupTasks,
|
||||||
|
StartupTask,
|
||||||
} from '../../../electron-common/startup-task';
|
} from '../../../electron-common/startup-task';
|
||||||
import { WindowServiceExt } from '../core/window-service-ext';
|
import { WindowServiceExt } from '../core/window-service-ext';
|
||||||
|
|
||||||
@ -128,7 +129,7 @@ export class WorkspaceService extends TheiaWorkspaceService {
|
|||||||
.getContributions()
|
.getContributions()
|
||||||
.map((contribution) => contribution.tasks())
|
.map((contribution) => contribution.tasks())
|
||||||
.reduce((prev, curr) => prev.concat(curr), []);
|
.reduce((prev, curr) => prev.concat(curr), []);
|
||||||
if (StartupTask.has(options)) {
|
if (hasStartupTasks(options)) {
|
||||||
tasks.push(...options.tasks);
|
tasks.push(...options.tasks);
|
||||||
}
|
}
|
||||||
return tasks;
|
return tasks;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { notEmpty } from '@theia/core';
|
import { notEmpty } from '@theia/core/lib/common/objects';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds the closest child HTMLButtonElement representing a Theia button.
|
* Finds the closest child HTMLButtonElement representing a Theia button.
|
||||||
|
@ -1,16 +1,9 @@
|
|||||||
import * as React from '@theia/core/shared/react';
|
import * as React from '@theia/core/shared/react';
|
||||||
|
import type { Props, StylesConfig, ThemeConfig } from 'react-select';
|
||||||
import Select from 'react-select';
|
import Select from 'react-select';
|
||||||
import type { StylesConfig } from 'react-select/dist/declarations/src/styles';
|
|
||||||
import type { ThemeConfig } from 'react-select/dist/declarations/src/theme';
|
|
||||||
import type { GroupBase } from 'react-select/dist/declarations/src/types';
|
|
||||||
import type { StateManagerProps } from 'react-select/dist/declarations/src/useStateManager';
|
|
||||||
|
|
||||||
export class ArduinoSelect<
|
export class ArduinoSelect extends React.Component<Props> {
|
||||||
Option,
|
constructor(props: Props) {
|
||||||
IsMulti extends boolean = false,
|
|
||||||
Group extends GroupBase<Option> = GroupBase<Option>
|
|
||||||
> extends React.Component<StateManagerProps<Option, IsMulti, Group>> {
|
|
||||||
constructor(props: StateManagerProps<Option, IsMulti, Group>) {
|
|
||||||
super(props);
|
super(props);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,8 +12,8 @@ import { AuthenticationClientService } from '../../auth/authentication-client-se
|
|||||||
import { CloudSketchbookTreeModel } from './cloud-sketchbook-tree-model';
|
import { CloudSketchbookTreeModel } from './cloud-sketchbook-tree-model';
|
||||||
import { BaseSketchbookCompositeWidget } from '../sketchbook/sketchbook-composite-widget';
|
import { BaseSketchbookCompositeWidget } from '../sketchbook/sketchbook-composite-widget';
|
||||||
import { CreateNew } from '../sketchbook/create-new';
|
import { CreateNew } from '../sketchbook/create-new';
|
||||||
import { AuthenticationSession } from '../../../node/auth/types';
|
|
||||||
import { ApplicationConnectionStatusContribution } from '../../theia/core/connection-status-service';
|
import { ApplicationConnectionStatusContribution } from '../../theia/core/connection-status-service';
|
||||||
|
import { AuthenticationSession } from '../../../common/protocol/authentication-service';
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class CloudSketchbookCompositeWidget extends BaseSketchbookCompositeWidget<CloudSketchbookTreeWidget> {
|
export class CloudSketchbookCompositeWidget extends BaseSketchbookCompositeWidget<CloudSketchbookTreeWidget> {
|
||||||
|
@ -34,6 +34,7 @@ import { SketchbookCommands } from '../sketchbook/sketchbook-commands';
|
|||||||
import { CloudSketchbookCommands } from './cloud-sketchbook-commands';
|
import { CloudSketchbookCommands } from './cloud-sketchbook-commands';
|
||||||
import { CloudSketchbookTree } from './cloud-sketchbook-tree';
|
import { CloudSketchbookTree } from './cloud-sketchbook-tree';
|
||||||
import { CreateUri } from '../../create/create-uri';
|
import { CreateUri } from '../../create/create-uri';
|
||||||
|
import { ClipboardService } from '@theia/core/lib/browser/clipboard-service';
|
||||||
|
|
||||||
const SKETCHBOOKSYNC__CONTEXT = ['arduino-sketchbook-sync--context'];
|
const SKETCHBOOKSYNC__CONTEXT = ['arduino-sketchbook-sync--context'];
|
||||||
|
|
||||||
@ -61,6 +62,8 @@ export class CloudSketchbookContribution extends CloudSketchContribution {
|
|||||||
private readonly configServiceClient: ConfigServiceClient;
|
private readonly configServiceClient: ConfigServiceClient;
|
||||||
@inject(ApplicationConnectionStatusContribution)
|
@inject(ApplicationConnectionStatusContribution)
|
||||||
private readonly connectionStatus: ApplicationConnectionStatusContribution;
|
private readonly connectionStatus: ApplicationConnectionStatusContribution;
|
||||||
|
@inject(ClipboardService)
|
||||||
|
private readonly clipboardService: ClipboardService;
|
||||||
|
|
||||||
private readonly onDidChangeToolbarEmitter = new Emitter<void>();
|
private readonly onDidChangeToolbarEmitter = new Emitter<void>();
|
||||||
private readonly toDisposeBeforeNewContextMenu = new DisposableCollection();
|
private readonly toDisposeBeforeNewContextMenu = new DisposableCollection();
|
||||||
@ -176,6 +179,7 @@ export class CloudSketchbookContribution extends CloudSketchContribution {
|
|||||||
node: arg.node,
|
node: arg.node,
|
||||||
title: nls.localize('arduino/cloud/shareSketch', 'Share Sketch'),
|
title: nls.localize('arduino/cloud/shareSketch', 'Share Sketch'),
|
||||||
createApi: this.createApi,
|
createApi: this.createApi,
|
||||||
|
clipboardService: this.clipboardService,
|
||||||
}).open();
|
}).open();
|
||||||
},
|
},
|
||||||
isEnabled: (arg) => this.isCloudSketchDirNodeCommandArg(arg),
|
isEnabled: (arg) => this.isCloudSketchDirNodeCommandArg(arg),
|
||||||
|
@ -35,6 +35,7 @@ function sketchBaseDir(sketch: Create.Sketch): FileStat {
|
|||||||
isDirectory: true,
|
isDirectory: true,
|
||||||
isFile: false,
|
isFile: false,
|
||||||
isSymbolicLink: false,
|
isSymbolicLink: false,
|
||||||
|
isReadonly: false,
|
||||||
resource: createPath,
|
resource: createPath,
|
||||||
mtime,
|
mtime,
|
||||||
ctime,
|
ctime,
|
||||||
|
@ -12,7 +12,6 @@ import {
|
|||||||
import { NodeProps } from '@theia/core/lib/browser/tree/tree-widget';
|
import { NodeProps } from '@theia/core/lib/browser/tree/tree-widget';
|
||||||
import { TreeNode } from '@theia/core/lib/browser/tree';
|
import { TreeNode } from '@theia/core/lib/browser/tree';
|
||||||
import { CompositeTreeNode } from '@theia/core/lib/browser';
|
import { CompositeTreeNode } from '@theia/core/lib/browser';
|
||||||
import { shell } from '@theia/core/electron-shared/@electron/remote';
|
|
||||||
import { SketchbookTreeWidget } from '../sketchbook/sketchbook-tree-widget';
|
import { SketchbookTreeWidget } from '../sketchbook/sketchbook-tree-widget';
|
||||||
import { nls } from '@theia/core/lib/common';
|
import { nls } from '@theia/core/lib/common';
|
||||||
import { ApplicationConnectionStatusContribution } from '../../theia/core/connection-status-service';
|
import { ApplicationConnectionStatusContribution } from '../../theia/core/connection-status-service';
|
||||||
@ -60,7 +59,12 @@ export class CloudSketchbookTreeWidget extends SketchbookTreeWidget {
|
|||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
className="theia-button uppercase"
|
className="theia-button uppercase"
|
||||||
onClick={() => shell.openExternal('https://create.arduino.cc/editor')}
|
onClick={() =>
|
||||||
|
this.windowService.openNewWindow(
|
||||||
|
'https://create.arduino.cc/editor',
|
||||||
|
{ external: true }
|
||||||
|
)
|
||||||
|
}
|
||||||
>
|
>
|
||||||
{nls.localize('arduino/cloud/goToCloud', 'Go to Cloud')}
|
{nls.localize('arduino/cloud/goToCloud', 'Go to Cloud')}
|
||||||
</button>
|
</button>
|
||||||
@ -81,7 +85,10 @@ export class CloudSketchbookTreeWidget extends SketchbookTreeWidget {
|
|||||||
return CompositeTreeNode.is(node) && node.children.length === 0;
|
return CompositeTreeNode.is(node) && node.children.length === 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override createNodeClassNames(node: any, props: NodeProps): string[] {
|
protected override createNodeClassNames(
|
||||||
|
node: any,
|
||||||
|
props: NodeProps
|
||||||
|
): string[] {
|
||||||
const classNames = super.createNodeClassNames(node, props);
|
const classNames = super.createNodeClassNames(node, props);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import * as React from '@theia/core/shared/react';
|
import * as React from '@theia/core/shared/react';
|
||||||
import debounce = require('lodash.debounce');
|
import debounce from 'lodash.debounce';
|
||||||
import { Event } from '@theia/core/lib/common/event';
|
import { Event } from '@theia/core/lib/common/event';
|
||||||
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';
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import * as remote from '@theia/core/electron-shared/@electron/remote';
|
|
||||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
import { inject, injectable } from '@theia/core/shared/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';
|
||||||
@ -120,7 +119,7 @@ export class SketchbookWidgetContribution
|
|||||||
if (exists) {
|
if (exists) {
|
||||||
const fsPath = await this.fileService.fsPath(new URI(arg.node.uri));
|
const fsPath = await this.fileService.fsPath(new URI(arg.node.uri));
|
||||||
if (fsPath) {
|
if (fsPath) {
|
||||||
remote.shell.openPath(fsPath);
|
window.electronArduino.openPath(fsPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,2 +0,0 @@
|
|||||||
export const SHOW_PLOTTER_WINDOW = 'SHOW_PLOTTER_WINDOW';
|
|
||||||
export const CLOSE_PLOTTER_WINDOW = 'CLOSE_PLOTTER_WINDOW';
|
|
@ -1,5 +1,15 @@
|
|||||||
import { JsonRpcServer } from '@theia/core/lib/common/messaging/proxy-factory';
|
import { JsonRpcServer } from '@theia/core/lib/common/messaging/proxy-factory';
|
||||||
import { AuthOptions } from '../../node/auth/types';
|
export const authServerPort = 9876;
|
||||||
|
|
||||||
|
export interface AuthOptions {
|
||||||
|
redirectUri: string;
|
||||||
|
responseType: string;
|
||||||
|
clientID: string;
|
||||||
|
domain: string;
|
||||||
|
audience: string;
|
||||||
|
registerUri: string;
|
||||||
|
scopes: string[];
|
||||||
|
}
|
||||||
|
|
||||||
export interface AuthenticationSession {
|
export interface AuthenticationSession {
|
||||||
readonly id: string;
|
readonly id: string;
|
||||||
|
@ -1,8 +1,12 @@
|
|||||||
import * as semver from 'semver';
|
import type { MessageService } from '@theia/core/lib/common/message-service';
|
||||||
import { ExecuteWithProgress } from './progressible';
|
import {
|
||||||
|
coerce as coerceSemver,
|
||||||
|
compare as compareSemver,
|
||||||
|
parse as parseSemver,
|
||||||
|
} from 'semver';
|
||||||
import { naturalCompare } from '../utils';
|
import { naturalCompare } from '../utils';
|
||||||
import type { ArduinoComponent } from './arduino-component';
|
import type { ArduinoComponent } from './arduino-component';
|
||||||
import type { MessageService } from '@theia/core/lib/common/message-service';
|
import { ExecuteWithProgress } from './progressible';
|
||||||
import type { ResponseServiceClient } from './response-service';
|
import type { ResponseServiceClient } from './response-service';
|
||||||
|
|
||||||
export interface Installable<T extends ArduinoComponent> {
|
export interface Installable<T extends ArduinoComponent> {
|
||||||
@ -35,16 +39,16 @@ export namespace Installable {
|
|||||||
right: Version,
|
right: Version,
|
||||||
coerce = false
|
coerce = false
|
||||||
): number => {
|
): number => {
|
||||||
const validLeft = semver.parse(left);
|
const validLeft = parseSemver(left);
|
||||||
const validRight = semver.parse(right);
|
const validRight = parseSemver(right);
|
||||||
if (validLeft && validRight) {
|
if (validLeft && validRight) {
|
||||||
return semver.compare(validLeft, validRight);
|
return compareSemver(validLeft, validRight);
|
||||||
}
|
}
|
||||||
if (coerce) {
|
if (coerce) {
|
||||||
const coercedLeft = validLeft ?? semver.coerce(left);
|
const coercedLeft = validLeft ?? coerceSemver(left);
|
||||||
const coercedRight = validRight ?? semver.coerce(right);
|
const coercedRight = validRight ?? coerceSemver(right);
|
||||||
if (coercedLeft && coercedRight) {
|
if (coercedLeft && coercedRight) {
|
||||||
return semver.compare(coercedLeft, coercedRight);
|
return compareSemver(coercedLeft, coercedRight);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return naturalCompare(left, right);
|
return naturalCompare(left, right);
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
import { ApplicationError, Event, JsonRpcServer, nls } from '@theia/core';
|
import { ApplicationError, Event, JsonRpcServer, nls } from '@theia/core';
|
||||||
import {
|
|
||||||
PluggableMonitorSettings,
|
|
||||||
MonitorSettings,
|
|
||||||
} from '../../node/monitor-settings/monitor-settings-provider';
|
|
||||||
import { Board, Port } from './boards-service';
|
import { Board, Port } from './boards-service';
|
||||||
|
|
||||||
|
export type PluggableMonitorSettings = Record<string, PluggableMonitorSetting>;
|
||||||
|
export interface MonitorSettings {
|
||||||
|
pluggableMonitorSettings?: PluggableMonitorSettings;
|
||||||
|
monitorUISettings?: Partial<MonitorState>;
|
||||||
|
}
|
||||||
|
|
||||||
export const MonitorManagerProxyFactory = Symbol('MonitorManagerProxyFactory');
|
export const MonitorManagerProxyFactory = Symbol('MonitorManagerProxyFactory');
|
||||||
export type MonitorManagerProxyFactory = () => MonitorManagerProxy;
|
export type MonitorManagerProxyFactory = () => MonitorManagerProxy;
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { ApplicationError } from '@theia/core/lib/common/application-error';
|
import { ApplicationError } from '@theia/core/lib/common/application-error';
|
||||||
import { nls } from '@theia/core/lib/common/nls';
|
import { nls } from '@theia/core/lib/common/nls';
|
||||||
import URI from '@theia/core/lib/common/uri';
|
import URI from '@theia/core/lib/common/uri';
|
||||||
import * as dateFormat from 'dateformat';
|
import dateFormat from 'dateformat';
|
||||||
const filenameReservedRegex = require('filename-reserved-regex');
|
const filenameReservedRegex = require('filename-reserved-regex');
|
||||||
|
|
||||||
export namespace SketchesError {
|
export namespace SketchesError {
|
||||||
|
@ -0,0 +1,26 @@
|
|||||||
|
import type { Disposable } from '@theia/core/lib/common/disposable';
|
||||||
|
import { injectable } from '@theia/core/shared/inversify';
|
||||||
|
import type { AppService } from '../browser/app-service';
|
||||||
|
import type { Sketch } from '../common/protocol/sketches-service';
|
||||||
|
import type { StartupTasks } from '../electron-common/startup-task';
|
||||||
|
|
||||||
|
@injectable()
|
||||||
|
export class ElectronAppService implements AppService {
|
||||||
|
quit(): void {
|
||||||
|
window.electronArduino.quitApp();
|
||||||
|
}
|
||||||
|
|
||||||
|
version(): Promise<string> {
|
||||||
|
return window.electronArduino.appVersion();
|
||||||
|
}
|
||||||
|
|
||||||
|
registerStartupTasksHandler(
|
||||||
|
handler: (tasks: StartupTasks) => void
|
||||||
|
): Disposable {
|
||||||
|
return window.electronArduino.registerStartupTasksHandler(handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
scheduleDeletion(sketch: Sketch): void {
|
||||||
|
window.electronArduino.scheduleDeletion(sketch);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
import { ContainerModule } from '@theia/core/shared/inversify';
|
||||||
|
import { AppService } from '../browser/app-service';
|
||||||
|
import { DialogService } from '../browser/dialog-service';
|
||||||
|
import { ElectronAppService } from './electron-app-service';
|
||||||
|
import { ElectronDialogService } from './electron-dialog-service';
|
||||||
|
|
||||||
|
export default new ContainerModule((bind) => {
|
||||||
|
bind(ElectronAppService).toSelf().inSingletonScope();
|
||||||
|
bind(AppService).toService(ElectronAppService);
|
||||||
|
bind(ElectronDialogService).toSelf().inSingletonScope();
|
||||||
|
bind(DialogService).toService(ElectronDialogService);
|
||||||
|
});
|
@ -0,0 +1,25 @@
|
|||||||
|
import { injectable } from '@theia/core/shared/inversify';
|
||||||
|
import type { DialogService } from '../browser/dialog-service';
|
||||||
|
import type {
|
||||||
|
MessageBoxOptions,
|
||||||
|
MessageBoxReturnValue,
|
||||||
|
OpenDialogOptions,
|
||||||
|
OpenDialogReturnValue,
|
||||||
|
SaveDialogOptions,
|
||||||
|
SaveDialogReturnValue,
|
||||||
|
} from '../electron-common/electron-arduino';
|
||||||
|
|
||||||
|
@injectable()
|
||||||
|
export class ElectronDialogService implements DialogService {
|
||||||
|
showMessageBox(options: MessageBoxOptions): Promise<MessageBoxReturnValue> {
|
||||||
|
return window.electronArduino.showMessageBox(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
showOpenDialog(options: OpenDialogOptions): Promise<OpenDialogReturnValue> {
|
||||||
|
return window.electronArduino.showOpenDialog(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
showSaveDialog(options: SaveDialogOptions): Promise<SaveDialogReturnValue> {
|
||||||
|
return window.electronArduino.showSaveDialog(options);
|
||||||
|
}
|
||||||
|
}
|
131
arduino-ide-extension/src/electron-browser/preload.ts
Normal file
131
arduino-ide-extension/src/electron-browser/preload.ts
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
import {
|
||||||
|
contextBridge,
|
||||||
|
ipcRenderer,
|
||||||
|
} from '@theia/core/electron-shared/electron';
|
||||||
|
import { Disposable } from '@theia/core/lib/common/disposable';
|
||||||
|
import {
|
||||||
|
CHANNEL_REQUEST_RELOAD,
|
||||||
|
MenuDto,
|
||||||
|
} from '@theia/core/lib/electron-common/electron-api';
|
||||||
|
import { v4 } from 'uuid';
|
||||||
|
import type { Sketch } from '../common/protocol/sketches-service';
|
||||||
|
import {
|
||||||
|
CHANNEL_APP_VERSION,
|
||||||
|
CHANNEL_IS_FIRST_WINDOW,
|
||||||
|
CHANNEL_MAIN_MENU_ITEM_DID_CLICK,
|
||||||
|
CHANNEL_OPEN_PATH,
|
||||||
|
CHANNEL_PLOTTER_WINDOW_DID_CLOSE,
|
||||||
|
CHANNEL_QUIT_APP,
|
||||||
|
CHANNEL_SCHEDULE_DELETION,
|
||||||
|
CHANNEL_SEND_STARTUP_TASKS,
|
||||||
|
CHANNEL_SET_MENU_WITH_NODE_ID,
|
||||||
|
CHANNEL_SET_REPRESENTED_FILENAME,
|
||||||
|
CHANNEL_SHOW_MESSAGE_BOX,
|
||||||
|
CHANNEL_SHOW_OPEN_DIALOG,
|
||||||
|
CHANNEL_SHOW_PLOTTER_WINDOW,
|
||||||
|
CHANNEL_SHOW_SAVE_DIALOG,
|
||||||
|
ElectronArduino,
|
||||||
|
InternalMenuDto,
|
||||||
|
MessageBoxOptions,
|
||||||
|
OpenDialogOptions,
|
||||||
|
SaveDialogOptions,
|
||||||
|
} from '../electron-common/electron-arduino';
|
||||||
|
import { hasStartupTasks, StartupTasks } from '../electron-common/startup-task';
|
||||||
|
|
||||||
|
let mainMenuHandlers: Map<string, () => void> = new Map();
|
||||||
|
|
||||||
|
function convertMenu(
|
||||||
|
menu: MenuDto[] | undefined,
|
||||||
|
handlerMap: Map<string, () => void>
|
||||||
|
): InternalMenuDto[] | undefined {
|
||||||
|
if (!menu) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return menu.map((item) => {
|
||||||
|
let nodeId = v4();
|
||||||
|
if (item.execute) {
|
||||||
|
if (!item.id) {
|
||||||
|
throw new Error(
|
||||||
|
"A menu item having the 'execute' property must have an 'id' too."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
nodeId = item.id;
|
||||||
|
handlerMap.set(nodeId, item.execute);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: item.id,
|
||||||
|
submenu: convertMenu(item.submenu, handlerMap),
|
||||||
|
accelerator: item.accelerator,
|
||||||
|
label: item.label,
|
||||||
|
nodeId,
|
||||||
|
checked: item.checked,
|
||||||
|
enabled: item.enabled,
|
||||||
|
role: item.role,
|
||||||
|
type: item.type,
|
||||||
|
visible: item.visible,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const api: ElectronArduino = {
|
||||||
|
showMessageBox: (options: MessageBoxOptions) =>
|
||||||
|
ipcRenderer.invoke(CHANNEL_SHOW_MESSAGE_BOX, options),
|
||||||
|
showOpenDialog: (options: OpenDialogOptions) =>
|
||||||
|
ipcRenderer.invoke(CHANNEL_SHOW_OPEN_DIALOG, options),
|
||||||
|
showSaveDialog: (options: SaveDialogOptions) =>
|
||||||
|
ipcRenderer.invoke(CHANNEL_SHOW_SAVE_DIALOG, options),
|
||||||
|
appVersion: () => ipcRenderer.invoke(CHANNEL_APP_VERSION),
|
||||||
|
quitApp: () => ipcRenderer.send(CHANNEL_QUIT_APP),
|
||||||
|
isFirstWindow: () => ipcRenderer.invoke(CHANNEL_IS_FIRST_WINDOW),
|
||||||
|
requestReload: (options: StartupTasks) =>
|
||||||
|
ipcRenderer.send(CHANNEL_REQUEST_RELOAD, options),
|
||||||
|
registerStartupTasksHandler: (handler: (tasks: StartupTasks) => void) => {
|
||||||
|
const listener = (_: Electron.IpcRendererEvent, args: unknown) => {
|
||||||
|
if (hasStartupTasks(args)) {
|
||||||
|
handler(args);
|
||||||
|
} else {
|
||||||
|
console.warn(
|
||||||
|
`Events received on the ${CHANNEL_SEND_STARTUP_TASKS} channel expected to have a startup task argument, but it was: ${JSON.stringify(
|
||||||
|
args
|
||||||
|
)}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
ipcRenderer.on(CHANNEL_SEND_STARTUP_TASKS, listener);
|
||||||
|
return Disposable.create(() =>
|
||||||
|
ipcRenderer.removeListener(CHANNEL_SEND_STARTUP_TASKS, listener)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
scheduleDeletion: (sketch: Sketch) =>
|
||||||
|
ipcRenderer.send(CHANNEL_SCHEDULE_DELETION, sketch),
|
||||||
|
setRepresentedFilename: (fsPath: string) =>
|
||||||
|
ipcRenderer.send(CHANNEL_SET_REPRESENTED_FILENAME, fsPath),
|
||||||
|
showPlotterWindow: (params: { url: string; forceReload?: boolean }) =>
|
||||||
|
ipcRenderer.send(CHANNEL_SHOW_PLOTTER_WINDOW, params),
|
||||||
|
registerPlotterWindowCloseHandler: (handler: () => void) => {
|
||||||
|
const listener = () => handler();
|
||||||
|
ipcRenderer.on(CHANNEL_PLOTTER_WINDOW_DID_CLOSE, listener);
|
||||||
|
return Disposable.create(() =>
|
||||||
|
ipcRenderer.removeListener(CHANNEL_PLOTTER_WINDOW_DID_CLOSE, listener)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
openPath: (fsPath: string) => ipcRenderer.send(CHANNEL_OPEN_PATH, fsPath),
|
||||||
|
setMenu: (menu: MenuDto[] | undefined): void => {
|
||||||
|
mainMenuHandlers = new Map();
|
||||||
|
const internalMenu = convertMenu(menu, mainMenuHandlers);
|
||||||
|
ipcRenderer.send(CHANNEL_SET_MENU_WITH_NODE_ID, internalMenu);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export function preload(): void {
|
||||||
|
contextBridge.exposeInMainWorld('electronArduino', api);
|
||||||
|
ipcRenderer.on(CHANNEL_MAIN_MENU_ITEM_DID_CLICK, (_, nodeId: string) => {
|
||||||
|
const handler = mainMenuHandlers.get(nodeId);
|
||||||
|
if (handler) {
|
||||||
|
handler();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
console.log('Exposed Arduino IDE electron API');
|
||||||
|
}
|
@ -1,4 +1,3 @@
|
|||||||
import { webFrame } from '@theia/core/electron-shared/electron/';
|
|
||||||
import {
|
import {
|
||||||
ContextMenuAccess,
|
ContextMenuAccess,
|
||||||
coordinateFromAnchor,
|
coordinateFromAnchor,
|
||||||
@ -16,28 +15,24 @@ export class ElectronContextMenuRenderer extends TheiaElectronContextMenuRendere
|
|||||||
options: RenderContextMenuOptions
|
options: RenderContextMenuOptions
|
||||||
): ContextMenuAccess {
|
): ContextMenuAccess {
|
||||||
if (this.useNativeStyle) {
|
if (this.useNativeStyle) {
|
||||||
const { menuPath, anchor, args, onHide, context } = options;
|
const { menuPath, anchor, args, onHide, context, contextKeyService } =
|
||||||
|
options;
|
||||||
const menu = this['electronMenuFactory'].createElectronContextMenu(
|
const menu = this['electronMenuFactory'].createElectronContextMenu(
|
||||||
menuPath,
|
menuPath,
|
||||||
args,
|
args,
|
||||||
context,
|
context,
|
||||||
|
contextKeyService,
|
||||||
this.showDisabled(options)
|
this.showDisabled(options)
|
||||||
);
|
);
|
||||||
const { x, y } = coordinateFromAnchor(anchor);
|
const { x, y } = coordinateFromAnchor(anchor);
|
||||||
const zoom = webFrame.getZoomFactor();
|
const menuHandle = window.electronTheiaCore.popup(menu, x, y, () => {
|
||||||
// TODO: Remove the offset once Electron fixes https://github.com/electron/electron/issues/31641
|
if (onHide) {
|
||||||
const offset = process.platform === 'win32' ? 0 : 2;
|
onHide();
|
||||||
// x and y values must be Ints or else there is a conversion error
|
}
|
||||||
menu.popup({
|
|
||||||
x: Math.round(x * zoom) + offset,
|
|
||||||
y: Math.round(y * zoom) + offset,
|
|
||||||
});
|
});
|
||||||
// native context menu stops the event loop, so there is no keyboard events
|
// native context menu stops the event loop, so there is no keyboard events
|
||||||
this.context.resetAltPressed();
|
this.context.resetAltPressed();
|
||||||
if (onHide) {
|
return new ElectronContextMenuAccess(menuHandle);
|
||||||
menu.once('menu-will-close', () => onHide());
|
|
||||||
}
|
|
||||||
return new ElectronContextMenuAccess(menu);
|
|
||||||
} else {
|
} else {
|
||||||
return super.doRender(options);
|
return super.doRender(options);
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import * as remote from '@theia/core/electron-shared/@electron/remote';
|
import { ContextMatcher } from '@theia/core/lib/browser/context-key-service';
|
||||||
import { FrontendApplicationConfigProvider } from '@theia/core/lib/browser/frontend-application-config-provider';
|
import { FrontendApplicationConfigProvider } from '@theia/core/lib/browser/frontend-application-config-provider';
|
||||||
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
|
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
|
||||||
import {
|
import {
|
||||||
@ -11,15 +11,19 @@ import {
|
|||||||
} from '@theia/core/lib/common/menu';
|
} from '@theia/core/lib/common/menu';
|
||||||
import { isOSX } from '@theia/core/lib/common/os';
|
import { isOSX } from '@theia/core/lib/common/os';
|
||||||
import {
|
import {
|
||||||
ElectronMainMenuFactory as TheiaElectronMainMenuFactory,
|
|
||||||
ElectronMenuItemRole,
|
|
||||||
ElectronMenuOptions,
|
ElectronMenuOptions,
|
||||||
|
ElectronMainMenuFactory as TheiaElectronMainMenuFactory,
|
||||||
} from '@theia/core/lib/electron-browser/menu/electron-main-menu-factory';
|
} from '@theia/core/lib/electron-browser/menu/electron-main-menu-factory';
|
||||||
|
import type {
|
||||||
|
MenuDto,
|
||||||
|
MenuRole,
|
||||||
|
} from '@theia/core/lib/electron-common/electron-api';
|
||||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||||
import {
|
import {
|
||||||
ArduinoMenus,
|
ArduinoMenus,
|
||||||
PlaceholderMenuNode,
|
PlaceholderMenuNode,
|
||||||
} from '../../../browser/menu/arduino-menus';
|
} from '../../../browser/menu/arduino-menus';
|
||||||
|
import debounce from 'lodash.debounce';
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class ElectronMainMenuFactory extends TheiaElectronMainMenuFactory {
|
export class ElectronMainMenuFactory extends TheiaElectronMainMenuFactory {
|
||||||
@ -30,7 +34,27 @@ export class ElectronMainMenuFactory extends TheiaElectronMainMenuFactory {
|
|||||||
private updateWhenReady = false;
|
private updateWhenReady = false;
|
||||||
|
|
||||||
override postConstruct(): void {
|
override postConstruct(): void {
|
||||||
super.postConstruct();
|
// #region Theia `postConstruct` customizations with calling IDE2 `setMenu`
|
||||||
|
this.preferencesService.onPreferenceChanged(
|
||||||
|
debounce((e) => {
|
||||||
|
if (e.preferenceName === 'window.menuBarVisibility') {
|
||||||
|
this.setMenuBar();
|
||||||
|
}
|
||||||
|
if (this._menu) {
|
||||||
|
for (const cmd of this._toggledCommands) {
|
||||||
|
const menuItem = this.findMenuById(this._menu, cmd);
|
||||||
|
if (menuItem) {
|
||||||
|
menuItem.checked = this.commandRegistry.isToggled(cmd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
window.electronArduino.setMenu(this._menu); // calls the IDE2-specific implementation
|
||||||
|
}
|
||||||
|
}, 10)
|
||||||
|
);
|
||||||
|
this.keybindingRegistry.onKeybindingsChanged(() => {
|
||||||
|
this.setMenuBar();
|
||||||
|
});
|
||||||
|
// #endregion Theia `postConstruct`
|
||||||
this.appStateService.reachedState('ready').then(() => {
|
this.appStateService.reachedState('ready').then(() => {
|
||||||
this.appReady = true;
|
this.appReady = true;
|
||||||
if (this.updateWhenReady) {
|
if (this.updateWhenReady) {
|
||||||
@ -39,18 +63,18 @@ export class ElectronMainMenuFactory extends TheiaElectronMainMenuFactory {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
override createElectronMenuBar(): Electron.Menu {
|
override createElectronMenuBar(): MenuDto[] {
|
||||||
this._toggledCommands.clear(); // https://github.com/eclipse-theia/theia/issues/8977
|
this._toggledCommands.clear(); // https://github.com/eclipse-theia/theia/issues/8977
|
||||||
const menuModel = this.menuProvider.getMenu(MAIN_MENU_BAR);
|
const menuModel = this.menuProvider.getMenu(MAIN_MENU_BAR);
|
||||||
const template = this.fillMenuTemplate([], menuModel, [], {
|
const menu = this.fillMenuTemplate([], menuModel, [], {
|
||||||
rootMenuPath: MAIN_MENU_BAR,
|
rootMenuPath: MAIN_MENU_BAR,
|
||||||
});
|
});
|
||||||
if (isOSX) {
|
if (isOSX) {
|
||||||
template.unshift(this.createOSXMenu());
|
menu.unshift(this.createOSXMenu());
|
||||||
}
|
}
|
||||||
const menu = remote.Menu.buildFromTemplate(this.escapeAmpersand(template));
|
const escapedMenu = this.escapeAmpersand(menu);
|
||||||
this._menu = menu;
|
this._menu = escapedMenu;
|
||||||
return menu;
|
return escapedMenu;
|
||||||
}
|
}
|
||||||
|
|
||||||
override async setMenuBar(): Promise<void> {
|
override async setMenuBar(): Promise<void> {
|
||||||
@ -63,11 +87,7 @@ export class ElectronMainMenuFactory extends TheiaElectronMainMenuFactory {
|
|||||||
}
|
}
|
||||||
await this.preferencesService.ready;
|
await this.preferencesService.ready;
|
||||||
const createdMenuBar = this.createElectronMenuBar();
|
const createdMenuBar = this.createElectronMenuBar();
|
||||||
if (isOSX) {
|
window.electronArduino.setMenu(createdMenuBar);
|
||||||
remote.Menu.setApplicationMenu(createdMenuBar);
|
|
||||||
} else {
|
|
||||||
remote.getCurrentWindow().setMenu(createdMenuBar);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override createElectronContextMenu(
|
override createElectronContextMenu(
|
||||||
@ -75,35 +95,68 @@ export class ElectronMainMenuFactory extends TheiaElectronMainMenuFactory {
|
|||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
args?: any[],
|
args?: any[],
|
||||||
context?: HTMLElement,
|
context?: HTMLElement,
|
||||||
|
contextKeyService?: ContextMatcher,
|
||||||
showDisabled?: boolean
|
showDisabled?: boolean
|
||||||
): Electron.Menu {
|
): MenuDto[] {
|
||||||
const menuModel = this.menuProvider.getMenu(menuPath);
|
const menuModel = this.menuProvider.getMenu(menuPath);
|
||||||
const template = this.fillMenuTemplate([], menuModel, args, {
|
return this.fillMenuTemplate([], menuModel, args, {
|
||||||
showDisabled,
|
showDisabled,
|
||||||
context,
|
context,
|
||||||
rootMenuPath: menuPath,
|
rootMenuPath: menuPath,
|
||||||
|
contextKeyService,
|
||||||
});
|
});
|
||||||
return remote.Menu.buildFromTemplate(this.escapeAmpersand(template));
|
}
|
||||||
|
|
||||||
|
protected override async execute(
|
||||||
|
commandId: string,
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
args: any[],
|
||||||
|
menuPath: MenuPath
|
||||||
|
): Promise<void> {
|
||||||
|
try {
|
||||||
|
// This is workaround for https://github.com/eclipse-theia/theia/issues/446.
|
||||||
|
// Electron menus do not update based on the `isEnabled`, `isVisible` property of the command.
|
||||||
|
// We need to check if we can execute it.
|
||||||
|
if (this.menuCommandExecutor.isEnabled(menuPath, commandId, ...args)) {
|
||||||
|
await this.menuCommandExecutor.executeCommand(
|
||||||
|
menuPath,
|
||||||
|
commandId,
|
||||||
|
...args
|
||||||
|
);
|
||||||
|
if (
|
||||||
|
this._menu &&
|
||||||
|
this.menuCommandExecutor.isVisible(menuPath, commandId, ...args)
|
||||||
|
) {
|
||||||
|
const item = this.findMenuById(this._menu, commandId);
|
||||||
|
if (item) {
|
||||||
|
item.checked = this.menuCommandExecutor.isToggled(
|
||||||
|
menuPath,
|
||||||
|
commandId,
|
||||||
|
...args
|
||||||
|
);
|
||||||
|
window.electronArduino.setMenu(this._menu); // overridden to call the IDE2-specific implementation.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// no-op
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: remove after https://github.com/eclipse-theia/theia/pull/9231
|
// TODO: remove after https://github.com/eclipse-theia/theia/pull/9231
|
||||||
private escapeAmpersand(
|
private escapeAmpersand(template: MenuDto[]): MenuDto[] {
|
||||||
template: Electron.MenuItemConstructorOptions[]
|
|
||||||
): Electron.MenuItemConstructorOptions[] {
|
|
||||||
for (const option of template) {
|
for (const option of template) {
|
||||||
if (option.label) {
|
if (option.label) {
|
||||||
option.label = option.label.replace(/\&+/g, '&$&');
|
option.label = option.label.replace(/\&+/g, '&$&');
|
||||||
}
|
}
|
||||||
if (option.submenu) {
|
if (option.submenu) {
|
||||||
this.escapeAmpersand(
|
this.escapeAmpersand(option.submenu);
|
||||||
option.submenu as Electron.MenuItemConstructorOptions[]
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return template;
|
return template;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override createOSXMenu(): Electron.MenuItemConstructorOptions {
|
protected override createOSXMenu(): MenuDto {
|
||||||
const { submenu } = super.createOSXMenu();
|
const { submenu } = super.createOSXMenu();
|
||||||
const label = FrontendApplicationConfigProvider.get().applicationName;
|
const label = FrontendApplicationConfigProvider.get().applicationName;
|
||||||
if (!!submenu && Array.isArray(submenu)) {
|
if (!!submenu && Array.isArray(submenu)) {
|
||||||
@ -142,7 +195,7 @@ export class ElectronMainMenuFactory extends TheiaElectronMainMenuFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars, unused-imports/no-unused-vars
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars, unused-imports/no-unused-vars
|
||||||
protected override roleFor(id: string): ElectronMenuItemRole | undefined {
|
protected override roleFor(id: string): MenuRole | undefined {
|
||||||
// MenuItem `roles` are completely broken on macOS:
|
// MenuItem `roles` are completely broken on macOS:
|
||||||
// - https://github.com/eclipse-theia/theia/issues/11217,
|
// - https://github.com/eclipse-theia/theia/issues/11217,
|
||||||
// - https://github.com/arduino/arduino-ide/issues/969
|
// - https://github.com/arduino/arduino-ide/issues/969
|
||||||
@ -151,11 +204,11 @@ export class ElectronMainMenuFactory extends TheiaElectronMainMenuFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected override fillMenuTemplate(
|
protected override fillMenuTemplate(
|
||||||
parentItems: Electron.MenuItemConstructorOptions[],
|
parentItems: MenuDto[],
|
||||||
menuModel: MenuNode,
|
menuModel: MenuNode,
|
||||||
args: unknown[] | undefined,
|
args: unknown[] | undefined,
|
||||||
options: ElectronMenuOptions
|
options: ElectronMenuOptions
|
||||||
): Electron.MenuItemConstructorOptions[] {
|
): MenuDto[] {
|
||||||
if (menuModel instanceof PlaceholderMenuNode) {
|
if (menuModel instanceof PlaceholderMenuNode) {
|
||||||
parentItems.push({
|
parentItems.push({
|
||||||
label: menuModel.label,
|
label: menuModel.label,
|
||||||
@ -172,24 +225,28 @@ export class ElectronMainMenuFactory extends TheiaElectronMainMenuFactory {
|
|||||||
// Source: https://github.com/eclipse-theia/theia/blob/5e641750af83383f2ce0cb3432ec333df70778a8/packages/core/src/electron-browser/menu/electron-main-menu-factory.ts#L132-L203
|
// Source: https://github.com/eclipse-theia/theia/blob/5e641750af83383f2ce0cb3432ec333df70778a8/packages/core/src/electron-browser/menu/electron-main-menu-factory.ts#L132-L203
|
||||||
// See https://github.com/arduino/arduino-ide/issues/1533
|
// See https://github.com/arduino/arduino-ide/issues/1533
|
||||||
private superFillMenuTemplate(
|
private superFillMenuTemplate(
|
||||||
parentItems: Electron.MenuItemConstructorOptions[],
|
parentItems: MenuDto[],
|
||||||
menu: MenuNode,
|
menu: MenuNode,
|
||||||
args: unknown[] = [],
|
args: unknown[] = [],
|
||||||
options: ElectronMenuOptions
|
options: ElectronMenuOptions
|
||||||
): Electron.MenuItemConstructorOptions[] {
|
): MenuDto[] {
|
||||||
const showDisabled = options?.showDisabled !== false;
|
const showDisabled = options?.showDisabled !== false;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
CompoundMenuNode.is(menu) &&
|
CompoundMenuNode.is(menu) &&
|
||||||
this.visibleSubmenu(menu) && // customization for #569 and #655
|
this.visibleSubmenu(menu) && // customization for #569 and #655
|
||||||
this.undefinedOrMatch(menu.when, options.context)
|
this.undefinedOrMatch(
|
||||||
|
options.contextKeyService ?? this.contextKeyService,
|
||||||
|
menu.when,
|
||||||
|
options.context
|
||||||
|
)
|
||||||
) {
|
) {
|
||||||
const role = CompoundMenuNode.getRole(menu);
|
const role = CompoundMenuNode.getRole(menu);
|
||||||
if (role === CompoundMenuNodeRole.Group && menu.id === 'inline') {
|
if (role === CompoundMenuNodeRole.Group && menu.id === 'inline') {
|
||||||
return parentItems;
|
return parentItems;
|
||||||
}
|
}
|
||||||
const children = CompoundMenuNode.getFlatChildren(menu.children);
|
const children = CompoundMenuNode.getFlatChildren(menu.children);
|
||||||
const myItems: Electron.MenuItemConstructorOptions[] = [];
|
const myItems: MenuDto[] = [];
|
||||||
children.forEach((child) =>
|
children.forEach((child) =>
|
||||||
this.fillMenuTemplate(myItems, child, args, options)
|
this.fillMenuTemplate(myItems, child, args, options)
|
||||||
);
|
);
|
||||||
@ -236,7 +293,11 @@ export class ElectronMainMenuFactory extends TheiaElectronMainMenuFactory {
|
|||||||
commandId,
|
commandId,
|
||||||
...args
|
...args
|
||||||
) ||
|
) ||
|
||||||
!this.undefinedOrMatch(node.when, options.context)
|
!this.undefinedOrMatch(
|
||||||
|
options.contextKeyService ?? this.contextKeyService,
|
||||||
|
node.when,
|
||||||
|
options.context
|
||||||
|
)
|
||||||
) {
|
) {
|
||||||
return parentItems;
|
return parentItems;
|
||||||
}
|
}
|
||||||
@ -258,7 +319,7 @@ export class ElectronMainMenuFactory extends TheiaElectronMainMenuFactory {
|
|||||||
|
|
||||||
const accelerator = bindings[0] && this.acceleratorFor(bindings[0]);
|
const accelerator = bindings[0] && this.acceleratorFor(bindings[0]);
|
||||||
|
|
||||||
const menuItem: Electron.MenuItemConstructorOptions = {
|
const menuItem: MenuDto = {
|
||||||
id: node.id,
|
id: node.id,
|
||||||
label: node.label,
|
label: node.label,
|
||||||
type: this.commandRegistry.getToggledHandler(commandId, ...args)
|
type: this.commandRegistry.getToggledHandler(commandId, ...args)
|
||||||
@ -268,14 +329,14 @@ export class ElectronMainMenuFactory extends TheiaElectronMainMenuFactory {
|
|||||||
enabled: this.commandRegistry.isEnabled(commandId, ...args), // Unlike Theia https://github.com/eclipse-theia/theia/blob/v1.31.1/packages/core/src/electron-browser/menu/electron-main-menu-factory.ts#L183
|
enabled: this.commandRegistry.isEnabled(commandId, ...args), // Unlike Theia https://github.com/eclipse-theia/theia/blob/v1.31.1/packages/core/src/electron-browser/menu/electron-main-menu-factory.ts#L183
|
||||||
visible: true,
|
visible: true,
|
||||||
accelerator,
|
accelerator,
|
||||||
click: () => this.execute(commandId, args, options.rootMenuPath),
|
execute: () => this.execute(commandId, args, options.rootMenuPath),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (isOSX) {
|
if (isOSX) {
|
||||||
const role = this.roleFor(node.id);
|
const role = this.roleFor(node.id);
|
||||||
if (role) {
|
if (role) {
|
||||||
menuItem.role = role;
|
menuItem.role = role;
|
||||||
delete menuItem.click;
|
delete menuItem.execute;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
parentItems.push(menuItem);
|
parentItems.push(menuItem);
|
||||||
|
@ -1,57 +1,40 @@
|
|||||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
import type { FrontendApplication } from '@theia/core/lib/browser/frontend-application';
|
||||||
import { CommandRegistry } from '@theia/core/lib/common/command';
|
import type { KeybindingRegistry } from '@theia/core/lib/browser/keybinding';
|
||||||
import { MenuModelRegistry } from '@theia/core/lib/common/menu';
|
import type { CommandRegistry } from '@theia/core/lib/common/command';
|
||||||
import { KeybindingRegistry } from '@theia/core/lib/browser/keybinding';
|
import type { MenuModelRegistry } from '@theia/core/lib/common/menu';
|
||||||
|
import { isOSX } from '@theia/core/lib/common/os';
|
||||||
import {
|
import {
|
||||||
ElectronMenuContribution as TheiaElectronMenuContribution,
|
|
||||||
ElectronCommands,
|
ElectronCommands,
|
||||||
|
ElectronMenuContribution as TheiaElectronMenuContribution,
|
||||||
} from '@theia/core/lib/electron-browser/menu/electron-menu-contribution';
|
} from '@theia/core/lib/electron-browser/menu/electron-menu-contribution';
|
||||||
import { MainMenuManager } from '../../../common/main-menu-manager';
|
import type { MenuDto } from '@theia/core/lib/electron-common/electron-api';
|
||||||
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
|
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||||
import { FrontendApplication } from '@theia/core/lib/browser/frontend-application';
|
import type { MainMenuManager } from '../../../common/main-menu-manager';
|
||||||
import { ZoomLevel } from '@theia/core/lib/electron-browser/window/electron-window-preferences';
|
import { ElectronMainMenuFactory } from './electron-main-menu-factory';
|
||||||
import { PreferenceScope } from '@theia/core/lib/browser/preferences/preference-scope';
|
|
||||||
import {
|
|
||||||
getCurrentWindow,
|
|
||||||
getCurrentWebContents,
|
|
||||||
} from '@theia/core/electron-shared/@electron/remote';
|
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class ElectronMenuContribution
|
export class ElectronMenuUpdater implements MainMenuManager {
|
||||||
extends TheiaElectronMenuContribution
|
@inject(ElectronMainMenuFactory)
|
||||||
implements MainMenuManager
|
protected readonly factory: ElectronMainMenuFactory;
|
||||||
{
|
|
||||||
@inject(FrontendApplicationStateService)
|
|
||||||
private readonly appStateService: FrontendApplicationStateService;
|
|
||||||
|
|
||||||
// private appReady = false;
|
public update(): void {
|
||||||
// private updateWhenReady = false;
|
this.setMenu();
|
||||||
|
|
||||||
override onStart(app: FrontendApplication): void {
|
|
||||||
super.onStart(app);
|
|
||||||
this.appStateService.reachedState('ready').then(() => {
|
|
||||||
// this.appReady = true;
|
|
||||||
// if (this.updateWhenReady) {
|
|
||||||
// this.update();
|
|
||||||
// }
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private setMenu(): void {
|
||||||
|
window.electronArduino.setMenu(this.factory.createElectronMenuBar());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@injectable()
|
||||||
|
export class ElectronMenuContribution extends TheiaElectronMenuContribution {
|
||||||
protected override hideTopPanel(): void {
|
protected override hideTopPanel(): void {
|
||||||
// NOOP
|
// NOOP
|
||||||
// We reuse the `div` for the Arduino toolbar.
|
// We reuse the `div` for the Arduino toolbar.
|
||||||
}
|
}
|
||||||
|
|
||||||
update(): void {
|
|
||||||
// if (this.appReady) {
|
|
||||||
(this as any).setMenu();
|
|
||||||
// } else {
|
|
||||||
// this.updateWhenReady = true;
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
override registerCommands(registry: CommandRegistry): void {
|
override registerCommands(registry: CommandRegistry): void {
|
||||||
this.theiaRegisterCommands(registry);
|
super.registerCommands(registry);
|
||||||
registry.unregisterCommand(ElectronCommands.CLOSE_WINDOW);
|
registry.unregisterCommand(ElectronCommands.CLOSE_WINDOW);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,80 +50,17 @@ export class ElectronMenuContribution
|
|||||||
registry.unregisterKeybinding(ElectronCommands.ZOOM_OUT.id);
|
registry.unregisterKeybinding(ElectronCommands.ZOOM_OUT.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copied from Theia: https://github.com/eclipse-theia/theia/blob/9ec8835cf35d5a46101a62ae93285aeb37a2f382/packages/core/src/electron-browser/menu/electron-menu-contribution.ts#L260-L314
|
protected override setMenu(
|
||||||
// Unlike the Theia implementation, this does not require synchronously the browser window, but use a function only when the command handler executes.
|
app: FrontendApplication,
|
||||||
private theiaRegisterCommands(registry: CommandRegistry): void {
|
electronMenu: MenuDto[] | undefined = this.factory.createElectronMenuBar()
|
||||||
const currentWindow = () => getCurrentWindow();
|
): void {
|
||||||
|
if (!isOSX) {
|
||||||
registry.registerCommand(ElectronCommands.TOGGLE_DEVELOPER_TOOLS, {
|
this.hideTopPanel(); // no app args. the overridden method is noop in IDE2.
|
||||||
execute: () => {
|
if (this.titleBarStyle === 'custom' && !this.menuBar) {
|
||||||
const webContent = getCurrentWebContents();
|
this.createCustomTitleBar(app);
|
||||||
if (!webContent.isDevToolsOpened()) {
|
|
||||||
webContent.openDevTools();
|
|
||||||
} else {
|
|
||||||
webContent.closeDevTools();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
registry.registerCommand(ElectronCommands.RELOAD, {
|
|
||||||
execute: () => this.windowService.reload(),
|
|
||||||
});
|
|
||||||
registry.registerCommand(ElectronCommands.CLOSE_WINDOW, {
|
|
||||||
execute: () => currentWindow().close(),
|
|
||||||
});
|
|
||||||
|
|
||||||
registry.registerCommand(ElectronCommands.ZOOM_IN, {
|
|
||||||
execute: () => {
|
|
||||||
const webContents = currentWindow().webContents;
|
|
||||||
// When starting at a level that is not a multiple of 0.5, increment by at most 0.5 to reach the next highest multiple of 0.5.
|
|
||||||
let zoomLevel =
|
|
||||||
Math.floor(webContents.zoomLevel / ZoomLevel.VARIATION) *
|
|
||||||
ZoomLevel.VARIATION +
|
|
||||||
ZoomLevel.VARIATION;
|
|
||||||
if (zoomLevel > ZoomLevel.MAX) {
|
|
||||||
zoomLevel = ZoomLevel.MAX;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.preferenceService.set(
|
|
||||||
'window.zoomLevel',
|
|
||||||
zoomLevel,
|
|
||||||
PreferenceScope.User
|
|
||||||
);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
registry.registerCommand(ElectronCommands.ZOOM_OUT, {
|
|
||||||
execute: () => {
|
|
||||||
const webContents = currentWindow().webContents;
|
|
||||||
// When starting at a level that is not a multiple of 0.5, decrement by at most 0.5 to reach the next lowest multiple of 0.5.
|
|
||||||
let zoomLevel =
|
|
||||||
Math.ceil(webContents.zoomLevel / ZoomLevel.VARIATION) *
|
|
||||||
ZoomLevel.VARIATION -
|
|
||||||
ZoomLevel.VARIATION;
|
|
||||||
if (zoomLevel < ZoomLevel.MIN) {
|
|
||||||
zoomLevel = ZoomLevel.MIN;
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
this.preferenceService.set(
|
window.electronArduino.setMenu(electronMenu); // overridden to call the IDE20-specific implementation.
|
||||||
'window.zoomLevel',
|
|
||||||
zoomLevel,
|
|
||||||
PreferenceScope.User
|
|
||||||
);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
registry.registerCommand(ElectronCommands.RESET_ZOOM, {
|
|
||||||
execute: () =>
|
|
||||||
this.preferenceService.set(
|
|
||||||
'window.zoomLevel',
|
|
||||||
ZoomLevel.DEFAULT,
|
|
||||||
PreferenceScope.User
|
|
||||||
),
|
|
||||||
});
|
|
||||||
registry.registerCommand(ElectronCommands.TOGGLE_FULL_SCREEN, {
|
|
||||||
isEnabled: () => currentWindow().isFullScreenable(),
|
|
||||||
isVisible: () => currentWindow().isFullScreenable(),
|
|
||||||
execute: () =>
|
|
||||||
currentWindow().setFullScreen(!currentWindow().isFullScreen()),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,11 +5,15 @@ import { ContainerModule } from '@theia/core/shared/inversify';
|
|||||||
import { MainMenuManager } from '../../../common/main-menu-manager';
|
import { MainMenuManager } from '../../../common/main-menu-manager';
|
||||||
import { ElectronContextMenuRenderer } from './electron-context-menu-renderer';
|
import { ElectronContextMenuRenderer } from './electron-context-menu-renderer';
|
||||||
import { ElectronMainMenuFactory } from './electron-main-menu-factory';
|
import { ElectronMainMenuFactory } from './electron-main-menu-factory';
|
||||||
import { ElectronMenuContribution } from './electron-menu-contribution';
|
import {
|
||||||
|
ElectronMenuContribution,
|
||||||
|
ElectronMenuUpdater,
|
||||||
|
} from './electron-menu-contribution';
|
||||||
|
|
||||||
export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||||
bind(ElectronMenuContribution).toSelf().inSingletonScope();
|
bind(ElectronMenuContribution).toSelf().inSingletonScope();
|
||||||
bind(MainMenuManager).toService(ElectronMenuContribution);
|
bind(ElectronMenuUpdater).toSelf().inSingletonScope();
|
||||||
|
bind(MainMenuManager).toService(ElectronMenuUpdater);
|
||||||
bind(ElectronContextMenuRenderer).toSelf().inSingletonScope();
|
bind(ElectronContextMenuRenderer).toSelf().inSingletonScope();
|
||||||
rebind(ContextMenuRenderer).toService(ElectronContextMenuRenderer);
|
rebind(ContextMenuRenderer).toService(ElectronContextMenuRenderer);
|
||||||
rebind(TheiaElectronMenuContribution).toService(ElectronMenuContribution);
|
rebind(TheiaElectronMenuContribution).toService(ElectronMenuContribution);
|
||||||
|
@ -1,23 +1,10 @@
|
|||||||
import { WindowService } from '@theia/core/lib/browser/window/window-service';
|
import { WindowService } from '@theia/core/lib/browser/window/window-service';
|
||||||
import { ElectronIpcConnectionProvider } from '@theia/core/lib/electron-browser/messaging/electron-ipc-connection-provider';
|
|
||||||
import { ContainerModule } from '@theia/core/shared/inversify';
|
import { ContainerModule } from '@theia/core/shared/inversify';
|
||||||
import { WindowServiceExt } from '../../../browser/theia/core/window-service-ext';
|
import { WindowServiceExt } from '../../../browser/theia/core/window-service-ext';
|
||||||
import {
|
|
||||||
ElectronMainWindowServiceExt,
|
|
||||||
electronMainWindowServiceExtPath,
|
|
||||||
} from '../../../electron-common/electron-main-window-service-ext';
|
|
||||||
import { ElectronWindowService } from './electron-window-service';
|
import { ElectronWindowService } from './electron-window-service';
|
||||||
|
|
||||||
export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||||
bind(ElectronWindowService).toSelf().inSingletonScope();
|
bind(ElectronWindowService).toSelf().inSingletonScope();
|
||||||
rebind(WindowService).toService(ElectronWindowService);
|
rebind(WindowService).toService(ElectronWindowService);
|
||||||
bind(WindowServiceExt).toService(ElectronWindowService);
|
bind(WindowServiceExt).toService(ElectronWindowService);
|
||||||
bind(ElectronMainWindowServiceExt)
|
|
||||||
.toDynamicValue(({ container }) =>
|
|
||||||
ElectronIpcConnectionProvider.createProxy(
|
|
||||||
container,
|
|
||||||
electronMainWindowServiceExtPath
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.inSingletonScope();
|
|
||||||
});
|
});
|
||||||
|
@ -1,94 +1,52 @@
|
|||||||
import * as remote from '@theia/core/electron-shared/@electron/remote';
|
|
||||||
import { ipcRenderer } from '@theia/core/electron-shared/electron';
|
|
||||||
import {
|
|
||||||
ConnectionStatus,
|
|
||||||
ConnectionStatusService,
|
|
||||||
} from '@theia/core/lib/browser/connection-status-service';
|
|
||||||
import { nls } from '@theia/core/lib/common';
|
|
||||||
import { Deferred } from '@theia/core/lib/common/promise-util';
|
import { Deferred } from '@theia/core/lib/common/promise-util';
|
||||||
import { NewWindowOptions } from '@theia/core/lib/common/window';
|
import type { NewWindowOptions } from '@theia/core/lib/common/window';
|
||||||
import { ElectronWindowService as TheiaElectronWindowService } from '@theia/core/lib/electron-browser/window/electron-window-service';
|
import { ElectronWindowService as TheiaElectronWindowService } from '@theia/core/lib/electron-browser/window/electron-window-service';
|
||||||
import { RELOAD_REQUESTED_SIGNAL } from '@theia/core/lib/electron-common/messaging/electron-messages';
|
import { injectable, postConstruct } from '@theia/core/shared/inversify';
|
||||||
import {
|
|
||||||
inject,
|
|
||||||
injectable,
|
|
||||||
postConstruct,
|
|
||||||
} from '@theia/core/shared/inversify';
|
|
||||||
import { WindowServiceExt } from '../../../browser/theia/core/window-service-ext';
|
import { WindowServiceExt } from '../../../browser/theia/core/window-service-ext';
|
||||||
import { ElectronMainWindowServiceExt } from '../../../electron-common/electron-main-window-service-ext';
|
import {
|
||||||
import { StartupTask } from '../../../electron-common/startup-task';
|
hasStartupTasks,
|
||||||
|
StartupTasks,
|
||||||
|
} from '../../../electron-common/startup-task';
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class ElectronWindowService
|
export class ElectronWindowService
|
||||||
extends TheiaElectronWindowService
|
extends TheiaElectronWindowService
|
||||||
implements WindowServiceExt
|
implements WindowServiceExt
|
||||||
{
|
{
|
||||||
@inject(ConnectionStatusService)
|
private _isFirstWindow: Deferred<boolean> | undefined;
|
||||||
private readonly connectionStatusService: ConnectionStatusService;
|
|
||||||
|
|
||||||
@inject(ElectronMainWindowServiceExt)
|
|
||||||
private readonly mainWindowServiceExt: ElectronMainWindowServiceExt;
|
|
||||||
|
|
||||||
@postConstruct()
|
@postConstruct()
|
||||||
protected override init(): void {
|
protected override init(): void {
|
||||||
// NOOP
|
// NOOP
|
||||||
// Does not listen on Theia's `window.zoomLevel` changes.
|
// IDE2 listens on the zoom level changes in `ArduinoFrontendContribution#onStart`
|
||||||
// TODO: IDE2 must switch to the Theia preferences and drop the custom one.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected shouldUnload(): boolean {
|
|
||||||
const offline =
|
|
||||||
this.connectionStatusService.currentStatus === ConnectionStatus.OFFLINE;
|
|
||||||
const detail = offline
|
|
||||||
? nls.localize(
|
|
||||||
'arduino/electron/couldNotSave',
|
|
||||||
'Could not save the sketch. Please copy your unsaved work into your favorite text editor, and restart the IDE.'
|
|
||||||
)
|
|
||||||
: nls.localize(
|
|
||||||
'arduino/electron/unsavedChanges',
|
|
||||||
'Any unsaved changes will not be saved.'
|
|
||||||
);
|
|
||||||
const electronWindow = remote.getCurrentWindow();
|
|
||||||
const response = remote.dialog.showMessageBoxSync(electronWindow, {
|
|
||||||
type: 'question',
|
|
||||||
buttons: [
|
|
||||||
nls.localize('vscode/extensionsUtils/yes', 'Yes'),
|
|
||||||
nls.localize('vscode/extensionsUtils/no', 'No'),
|
|
||||||
],
|
|
||||||
title: nls.localize('vscode/Default/ConfirmTitle', 'Confirm'),
|
|
||||||
message: nls.localize(
|
|
||||||
'arduino/sketch/close',
|
|
||||||
'Are you sure you want to close the sketch?'
|
|
||||||
),
|
|
||||||
detail,
|
|
||||||
});
|
|
||||||
return response === 0; // 'Yes', close the window.
|
|
||||||
}
|
|
||||||
|
|
||||||
private _firstWindow: Deferred<boolean> | undefined;
|
|
||||||
async isFirstWindow(): Promise<boolean> {
|
async isFirstWindow(): Promise<boolean> {
|
||||||
if (this._firstWindow === undefined) {
|
if (!this._isFirstWindow) {
|
||||||
this._firstWindow = new Deferred<boolean>();
|
this._isFirstWindow = new Deferred();
|
||||||
const windowId = remote.getCurrentWindow().id; // This is expensive and synchronous so we check it once per FE.
|
window.electronArduino
|
||||||
this.mainWindowServiceExt
|
.isFirstWindow()
|
||||||
.isFirstWindow(windowId)
|
.then((isFirstWindow) => this._isFirstWindow?.resolve(isFirstWindow));
|
||||||
.then((firstWindow) => this._firstWindow?.resolve(firstWindow));
|
|
||||||
}
|
}
|
||||||
return this._firstWindow.promise;
|
return this._isFirstWindow.promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Overridden because the default Theia implementation destroys the additional properties of the `options` arg, such as `tasks`.
|
// Overridden because the default Theia implementation destructures the additional properties of the `options` arg, such as `tasks`.
|
||||||
|
// https://github.com/eclipse-theia/theia/blob/2deedbad70bd4b503bf9c7e733ab9603f492600f/packages/core/src/electron-browser/window/electron-window-service.ts#L43
|
||||||
override openNewWindow(url: string, options?: NewWindowOptions): undefined {
|
override openNewWindow(url: string, options?: NewWindowOptions): undefined {
|
||||||
return this.delegate.openNewWindow(url, options);
|
return this.delegate.openNewWindow(url, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Overridden to support optional task owner params and make `tsc` happy.
|
// Overridden to support optional task owner params and make `tsc` happy.
|
||||||
override reload(options?: StartupTask.Owner): void {
|
override reload(options?: StartupTasks): void {
|
||||||
if (options?.tasks && options.tasks.length) {
|
if (hasStartupTasks(options)) {
|
||||||
const { tasks } = options;
|
window.electronArduino.requestReload(options);
|
||||||
ipcRenderer.send(RELOAD_REQUESTED_SIGNAL, { tasks });
|
|
||||||
} else {
|
} else {
|
||||||
ipcRenderer.send(RELOAD_REQUESTED_SIGNAL);
|
window.electronTheiaCore.requestReload();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
close(): void {
|
||||||
|
window.electronTheiaCore.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,92 @@
|
|||||||
|
import type {
|
||||||
|
MessageBoxOptions as ElectronMessageBoxOptions,
|
||||||
|
MessageBoxReturnValue as ElectronMessageBoxReturnValue,
|
||||||
|
OpenDialogOptions as ElectronOpenDialogOptions,
|
||||||
|
OpenDialogReturnValue as ElectronOpenDialogReturnValue,
|
||||||
|
SaveDialogOptions as ElectronSaveDialogOptions,
|
||||||
|
SaveDialogReturnValue as ElectronSaveDialogReturnValue,
|
||||||
|
} from '@theia/core/electron-shared/electron';
|
||||||
|
import type { Disposable } from '@theia/core/lib/common/disposable';
|
||||||
|
import type {
|
||||||
|
InternalMenuDto as TheiaInternalMenuDto,
|
||||||
|
MenuDto,
|
||||||
|
} from '@theia/core/lib/electron-common/electron-api';
|
||||||
|
import type { Sketch } from '../common/protocol/sketches-service';
|
||||||
|
import type { StartupTasks } from './startup-task';
|
||||||
|
|
||||||
|
export interface InternalMenuDto
|
||||||
|
extends Omit<TheiaInternalMenuDto, 'handlerId'> {
|
||||||
|
// Theia handles the menus with a running-index handler ID. https://github.com/eclipse-theia/theia/issues/12493
|
||||||
|
// IDE2 keeps the menu `nodeId` instead of the running-index.
|
||||||
|
nodeId?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type MessageBoxOptions = Omit<
|
||||||
|
ElectronMessageBoxOptions,
|
||||||
|
'icon' | 'signal'
|
||||||
|
>;
|
||||||
|
export type MessageBoxReturnValue = ElectronMessageBoxReturnValue;
|
||||||
|
export type OpenDialogOptions = ElectronOpenDialogOptions;
|
||||||
|
export type OpenDialogReturnValue = ElectronOpenDialogReturnValue;
|
||||||
|
export type SaveDialogOptions = ElectronSaveDialogOptions;
|
||||||
|
export type SaveDialogReturnValue = ElectronSaveDialogReturnValue;
|
||||||
|
|
||||||
|
export interface ShowPlotterWindowParams {
|
||||||
|
readonly url: string;
|
||||||
|
readonly forceReload?: boolean;
|
||||||
|
}
|
||||||
|
export function isShowPlotterWindowParams(
|
||||||
|
arg: unknown
|
||||||
|
): arg is ShowPlotterWindowParams {
|
||||||
|
return (
|
||||||
|
typeof arg === 'object' &&
|
||||||
|
typeof (<ShowPlotterWindowParams>arg).url === 'string' &&
|
||||||
|
((<ShowPlotterWindowParams>arg).forceReload === undefined ||
|
||||||
|
typeof (<ShowPlotterWindowParams>arg).forceReload === 'boolean')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ElectronArduino {
|
||||||
|
showMessageBox(options: MessageBoxOptions): Promise<MessageBoxReturnValue>;
|
||||||
|
showOpenDialog(options: OpenDialogOptions): Promise<OpenDialogReturnValue>;
|
||||||
|
showSaveDialog(options: SaveDialogOptions): Promise<SaveDialogReturnValue>;
|
||||||
|
appVersion(): Promise<string>;
|
||||||
|
quitApp(): void;
|
||||||
|
isFirstWindow(): Promise<boolean>;
|
||||||
|
requestReload(tasks: StartupTasks): void;
|
||||||
|
registerStartupTasksHandler(
|
||||||
|
handler: (tasks: StartupTasks) => void
|
||||||
|
): Disposable;
|
||||||
|
scheduleDeletion(sketch: Sketch): void;
|
||||||
|
setRepresentedFilename(fsPath: string): void;
|
||||||
|
showPlotterWindow(params: { url: string; forceReload?: boolean }): void;
|
||||||
|
registerPlotterWindowCloseHandler(handler: () => void): Disposable;
|
||||||
|
openPath(fsPath: string): void;
|
||||||
|
// Unlike the Theia implementation, IDE2 uses the command IDs, and not the running-index handler IDs.
|
||||||
|
// https://github.com/eclipse-theia/theia/issues/12493
|
||||||
|
setMenu(menu: MenuDto[] | undefined): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface Window {
|
||||||
|
electronArduino: ElectronArduino;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// renderer to main
|
||||||
|
export const CHANNEL_SHOW_MESSAGE_BOX = 'Arduino:ShowMessageBox';
|
||||||
|
export const CHANNEL_SHOW_OPEN_DIALOG = 'Arduino:ShowOpenDialog';
|
||||||
|
export const CHANNEL_SHOW_SAVE_DIALOG = 'Arduino:ShowSaveDialog';
|
||||||
|
export const CHANNEL_APP_VERSION = 'Arduino:AppVersion';
|
||||||
|
export const CHANNEL_QUIT_APP = 'Arduino:QuitApp';
|
||||||
|
export const CHANNEL_IS_FIRST_WINDOW = 'Arduino:IsFirstWindow';
|
||||||
|
export const CHANNEL_SCHEDULE_DELETION = 'Arduino:ScheduleDeletion';
|
||||||
|
export const CHANNEL_SET_REPRESENTED_FILENAME =
|
||||||
|
'Arduino:SetRepresentedFilename';
|
||||||
|
export const CHANNEL_SHOW_PLOTTER_WINDOW = 'Arduino:ShowPlotterWindow';
|
||||||
|
export const CHANNEL_OPEN_PATH = 'Arduino:OpenPath';
|
||||||
|
export const CHANNEL_SET_MENU_WITH_NODE_ID = 'Arduino:SetMenuWithNodeId';
|
||||||
|
// main to renderer
|
||||||
|
export const CHANNEL_SEND_STARTUP_TASKS = 'Arduino:SendStartupTasks';
|
||||||
|
export const CHANNEL_PLOTTER_WINDOW_DID_CLOSE = 'Arduino:PlotterWindowDidClose';
|
||||||
|
export const CHANNEL_MAIN_MENU_ITEM_DID_CLICK = 'Arduino:MainMenuItemDidClick';
|
@ -1,7 +0,0 @@
|
|||||||
export const electronMainWindowServiceExtPath = '/services/electron-window-ext';
|
|
||||||
export const ElectronMainWindowServiceExt = Symbol(
|
|
||||||
'ElectronMainWindowServiceExt'
|
|
||||||
);
|
|
||||||
export interface ElectronMainWindowServiceExt {
|
|
||||||
isFirstWindow(windowId: number): Promise<boolean>;
|
|
||||||
}
|
|
@ -1 +0,0 @@
|
|||||||
export const SCHEDULE_DELETION_SIGNAL = 'arduino/scheduleDeletion';
|
|
@ -1,50 +1,46 @@
|
|||||||
|
export const StartupTaskProvider = Symbol('StartupTaskProvider');
|
||||||
|
export interface StartupTaskProvider {
|
||||||
|
tasks(): StartupTask[];
|
||||||
|
}
|
||||||
|
|
||||||
export interface StartupTask {
|
export interface StartupTask {
|
||||||
command: string;
|
readonly command: string;
|
||||||
/**
|
/**
|
||||||
* Must be JSON serializable.
|
* Must be JSON serializable.
|
||||||
* See the restrictions [here](https://www.electronjs.org/docs/latest/api/web-contents#contentssendchannel-args).
|
* See the restrictions [here](https://www.electronjs.org/docs/latest/api/web-contents#contentssendchannel-args).
|
||||||
*/
|
*/
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
args?: any[];
|
readonly args?: any[];
|
||||||
}
|
|
||||||
export namespace StartupTask {
|
|
||||||
export function is(arg: unknown): arg is StartupTask {
|
|
||||||
if (typeof arg === 'object') {
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
const object = arg as any;
|
|
||||||
return (
|
|
||||||
'command' in object &&
|
|
||||||
typeof object['command'] === 'string' &&
|
|
||||||
(!('args' in object) || Array.isArray(object['args']))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
export function has(arg: unknown): arg is unknown & Owner {
|
|
||||||
if (typeof arg === 'object') {
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
const object = arg as any;
|
|
||||||
return (
|
|
||||||
'tasks' in object &&
|
|
||||||
Array.isArray(object['tasks']) &&
|
|
||||||
object['tasks'].every(is)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
export namespace Messaging {
|
|
||||||
export const STARTUP_TASKS_SIGNAL = 'arduino/startupTasks';
|
|
||||||
export function APP_READY_SIGNAL(id: number): string {
|
|
||||||
return `arduino/appReady${id}`;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Owner {
|
export interface StartupTasks {
|
||||||
readonly tasks: StartupTask[];
|
readonly tasks: StartupTask[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isStartupTask(arg: unknown): arg is StartupTask {
|
||||||
|
if (typeof arg === 'object') {
|
||||||
|
if (
|
||||||
|
(<StartupTask>arg).command !== undefined &&
|
||||||
|
typeof (<StartupTask>arg).command === 'string'
|
||||||
|
) {
|
||||||
|
return (
|
||||||
|
(<StartupTask>arg).args === undefined ||
|
||||||
|
((<StartupTask>arg).args !== undefined &&
|
||||||
|
Array.isArray((<StartupTask>arg).args))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const StartupTaskProvider = Symbol('StartupTaskProvider');
|
export function hasStartupTasks(arg: unknown): arg is unknown & StartupTasks {
|
||||||
export interface StartupTaskProvider {
|
if (typeof arg === 'object') {
|
||||||
tasks(): StartupTask[];
|
return (
|
||||||
|
(<StartupTasks>arg).tasks !== undefined &&
|
||||||
|
Array.isArray((<StartupTasks>arg).tasks) &&
|
||||||
|
Boolean((<StartupTasks>arg).tasks.length) &&
|
||||||
|
(<StartupTasks>arg).tasks.every(isStartupTask)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,6 @@ import {
|
|||||||
ElectronMainApplication as TheiaElectronMainApplication,
|
ElectronMainApplication as TheiaElectronMainApplication,
|
||||||
ElectronMainApplicationContribution,
|
ElectronMainApplicationContribution,
|
||||||
} from '@theia/core/lib/electron-main/electron-main-application';
|
} from '@theia/core/lib/electron-main/electron-main-application';
|
||||||
import { ElectronMessagingContribution as TheiaElectronMessagingContribution } from '@theia/core/lib/electron-main/messaging/electron-messaging-contribution';
|
|
||||||
import { TheiaElectronWindow as DefaultTheiaElectronWindow } from '@theia/core/lib/electron-main/theia-electron-window';
|
import { TheiaElectronWindow as DefaultTheiaElectronWindow } from '@theia/core/lib/electron-main/theia-electron-window';
|
||||||
import { ContainerModule } from '@theia/core/shared/inversify';
|
import { ContainerModule } from '@theia/core/shared/inversify';
|
||||||
import {
|
import {
|
||||||
@ -13,12 +12,11 @@ import {
|
|||||||
IDEUpdaterClient,
|
IDEUpdaterClient,
|
||||||
IDEUpdaterPath,
|
IDEUpdaterPath,
|
||||||
} from '../common/protocol/ide-updater';
|
} from '../common/protocol/ide-updater';
|
||||||
import { electronMainWindowServiceExtPath } from '../electron-common/electron-main-window-service-ext';
|
|
||||||
import { IsTempSketch } from '../node/is-temp-sketch';
|
import { IsTempSketch } from '../node/is-temp-sketch';
|
||||||
|
import { ElectronArduino } from './electron-arduino';
|
||||||
import { IDEUpdaterImpl } from './ide-updater/ide-updater-impl';
|
import { IDEUpdaterImpl } from './ide-updater/ide-updater-impl';
|
||||||
import { ElectronMainApplication } from './theia/electron-main-application';
|
import { ElectronMainApplication } from './theia/electron-main-application';
|
||||||
import { ElectronMainWindowServiceImpl } from './theia/electron-main-window-service';
|
import { ElectronMainWindowServiceImpl } from './theia/electron-main-window-service';
|
||||||
import { ElectronMessagingContribution } from './theia/electron-messaging-contribution';
|
|
||||||
import { TheiaElectronWindow } from './theia/theia-electron-window';
|
import { TheiaElectronWindow } from './theia/theia-electron-window';
|
||||||
|
|
||||||
export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||||
@ -50,20 +48,8 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
|||||||
bind(TheiaElectronWindow).toSelf();
|
bind(TheiaElectronWindow).toSelf();
|
||||||
rebind(DefaultTheiaElectronWindow).toService(TheiaElectronWindow);
|
rebind(DefaultTheiaElectronWindow).toService(TheiaElectronWindow);
|
||||||
|
|
||||||
bind(ElectronConnectionHandler)
|
|
||||||
.toDynamicValue(
|
|
||||||
(context) =>
|
|
||||||
new JsonRpcConnectionHandler(electronMainWindowServiceExtPath, () =>
|
|
||||||
context.container.get(ElectronMainWindowServiceImpl)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.inSingletonScope();
|
|
||||||
|
|
||||||
bind(IsTempSketch).toSelf().inSingletonScope();
|
bind(IsTempSketch).toSelf().inSingletonScope();
|
||||||
|
|
||||||
// Fix for cannot reload window: https://github.com/eclipse-theia/theia/issues/11600
|
bind(ElectronArduino).toSelf().inSingletonScope();
|
||||||
bind(ElectronMessagingContribution).toSelf().inSingletonScope();
|
bind(ElectronMainApplicationContribution).toService(ElectronArduino);
|
||||||
rebind(TheiaElectronMessagingContribution).toService(
|
|
||||||
ElectronMessagingContribution
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user