Compare commits

...

32 Commits

Author SHA1 Message Date
Alberto Iannaccone
9cabd40429 2.0.0-rc9.2 (#1312)
* 2.0.0-rc9.2

* use arduino-cli version 0.26.0-rc1
2022-08-10 13:04:02 +02:00
Francesco Spissu
6e3681896c Add Auto Format item under the Edit menu (#1230) 2022-08-10 11:36:53 +02:00
Akos Kitta
8a1cabd2bc Defer notification area rendering until app ready.
Signed-off-by: Akos Kitta <a.kitta@arduino.cc>
2022-08-09 17:23:10 +02:00
Akos Kitta
7a3e6789d1 Defer settings/certificates load until app ready.
Signed-off-by: Akos Kitta <a.kitta@arduino.cc>
2022-08-09 17:23:10 +02:00
Akos Kitta
92bc5ecf7b Replaced the splash screen with a preload.
Added a bare minimum example.

Closes #193
Closes #324
Closes #327
Closes #717
Closes #851

Signed-off-by: Akos Kitta <a.kitta@arduino.cc>
2022-08-09 17:23:10 +02:00
Francesco Spissu
aebec0f942 Live change of theme from Preferences dropdown (#1296) 2022-08-09 14:40:56 +02:00
per1234
54db9bbce8 Sync sketch formatter configuration from source
The Arduino IDE's "Auto Format" feature is configured to produce the standard Arduino sketch formatting style, as
established by the Arduino IDE 1.x formatter.

The configuration is consumed by several other projects which require the configuration in a YAML file. In order to
provide all the consumers with a single canonical source and to locate the infrastructure and activity related to the
maintenance of the file in a more appropriate repository, it is now hosted in a permanent location in the
`arduino/tooling-project-assets` repository.

The following changes have been made to the source configuration:

- Move documentation comments to a dedicated file in the upstream repository
- Make additional non-functional changes to the configuration format to facilitate maintenance
- Update to use the configuration API of ClangFormat 14.0.0

This last item did result in some functional changes to the configuration which will result in minor differences in the
formatter output.

These are actually reversions of unwanted differences from the Arduino IDE 1.x formatter output, which were unavoidable
when using the 11.0.1 version of ClangFormat in use at the time of the configuration's creation. These changes will
provide greater consistency during the migration from Arduino IDE 1.x to 2.x. The default output of the Arduino IDE
1.x formatter will continue to be considered the "gold standard" until Arduino IDE 2.x graduates from "pre-release"
status.

The Arduino IDE 2.x formatter configuration is fully customizable according to the preferences of each user. Those
already using custom configurations will not be affected in any way (though they are encouraged to sync their
configuration files from the source to bring them into compliance with the configuration API of the ClangFormat version
currently in use by Arduino IDE 2.x).

See the documentation and commit history for the source file for details on the configuration changes:

https://github.com/arduino/tooling-project-assets/tree/main/other/clang-format-configuration
2022-08-08 12:48:41 -07:00
per1234
676eb2f588 Escape special characters in formatter configuration for Windows
The sketch code formatter configuration is passed to the ClangFormat tool as a string representing a JSON object via a
command line argument.

Previously, the contents of this string were not given any special treatment to ensure compatibility with the command
interpreter used on Windows machines. That did not result in problems only because the configuration didn't contain
problematic combinations of characters. This good fortune will not persist through updates to the configuration, so the
command must be properly processed.

The Windows command interpreter does not use the POSIX style backslash escaping. For this reason, escaped quotes in the
argument are recognized as normal quotes, meaning that the string alternates between quoted and unquoted states at
random. When a character with special significance to the Windows command interpreter happens to occur outside a quoted
section, an error results.

The solution is to use the Windows command interpreter's caret escaping on these characters. Since such an escaping
system is not recognized by POSIX shells, this is only done when the application is running on a Windows machine.

References:

- https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/echo#remarks
- https://en.wikipedia.org/wiki/Escape_character#Windows_Command_Prompt
2022-08-08 12:48:41 -07:00
per1234
ce273adf77 Correctly escape escaped content in formatter configuration
The sketch code formatter configuration is passed to the ClangFormat tool as a string representing a JSON object via a
command line argument.

The quotes in the JSON syntax are escaped in order to make them compatible with this usage. Previously, consideration
was not given to escaping of the content. For example, with the previous escaping code, this content: `\"` would be
converted to `\\"`, whereas the correct escaping would look like `\\\"`.

That did not result in problems only because the configuration didn't contain escaped content. This good fortune will
not persist through updates to the configuration so the command must be properly processed.

The content of the configuration will now be escaped in addition to the quotes of the JSON data format.
2022-08-08 12:48:41 -07:00
Akos Kitta
0b33b51700 Set XDG_CONFIG_HOME env on Linux when not set.
Otherwise, `node-log-rotate` creates a folder with `undefined` name.

Closes #394.

Signed-off-by: Akos Kitta <a.kitta@arduino.cc>
2022-08-04 16:29:27 +02:00
Akos Kitta
36ac47b975 Can check if the current window is the first one.
Closes #1070

Signed-off-by: Akos Kitta <a.kitta@arduino.cc>
2022-08-04 11:11:46 +02:00
Akos Kitta
bf193b1cac Pinned 2dd8976 CLI in the IDE2. (#1280)
Signed-off-by: Akos Kitta <a.kitta@arduino.cc>
2022-08-04 09:28:28 +02:00
InstantMuffin
879aedeaa3 Update BUILDING.md (#1281)
* Update BUILDING.md

Added "Notes for Linux contributors" based on my own building experience

* Update BUILDING.md

Removing the linux specific section and instead updating the Theia IDE prerequisites link to point to the mentioned file directly.
2022-08-03 16:43:01 +02:00
Akos Kitta
d556ee95c0 Use FQBN instead of Board for the monitor ID.
Closes #1278

Signed-off-by: Akos Kitta <a.kitta@arduino.cc>
2022-08-03 15:16:39 +02:00
Alberto Iannaccone
d93c9ba654 2.0.0-rc9.1 (#1272) 2022-08-02 15:29:15 +02:00
Francesco Spissu
8a0dc1be7e Custom colors clean up (#1252) 2022-08-02 15:24:54 +02:00
Alberto Iannaccone
564862e173 Prevent board selector item labels to overflow (#1216)
* prevent board selector item labels to overflow

* make board selector show ellipsis when the board name is too long
2022-08-02 11:11:38 +02:00
Francesco Spissu
d7f7010bb5 High Contrast theme update (#1265) 2022-08-01 15:24:52 +02:00
Akos Kitta
e156dcc213 Show 'progress' indicator during verify/upload.
Closes #575
Closes #1175

Signed-off-by: Akos Kitta <a.kitta@arduino.cc>
2022-08-01 15:07:14 +02:00
Akos Kitta
27a2a6ca03 #1191: resolve temp path if copying/cloning sketch
Signed-off-by: Akos Kitta <a.kitta@arduino.cc>
2022-08-01 10:11:14 +02:00
Akos Kitta
581379f86f #1191: fixed default sketchbook URI for _save as_
Signed-off-by: Akos Kitta <a.kitta@arduino.cc>
2022-08-01 10:11:14 +02:00
Akos Kitta
b62f3dec84 #714: UX improvements of the Arduino LS in IDE2
- Debounced the connectivity status update.
 - Silent the output channel for the Arduino LS.
 - Delay the problem markers update with 500ms.
 - Do not update the status bar on every `keypress` event.
 - Debounced the tab-bar toolbar updates when typing in editor.
 - Fixed electron menu contribution binding.
 - Aligned the editor widget factory's API to Theia.
 - Set the zoom level when the app is ready (Closes #1244)
 - Fixed event listener leak (Closes #1062)

Signed-off-by: Akos Kitta <a.kitta@arduino.cc>
2022-08-01 10:11:14 +02:00
Akos Kitta
90d2950bdd Use 0.25.1 CLI.
Signed-off-by: Akos Kitta <a.kitta@arduino.cc>
2022-08-01 09:12:43 +02:00
github-actions[bot]
5b7d64c1c1 Updated translation files (#1269)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2022-08-01 08:22:58 +02:00
Dave Simpson
55927ac3dd remove state from stepper input and simplify (#1264)
* remove state from stepper input and simplify

* get rid of lodash
2022-07-29 17:44:58 +02:00
github-actions[bot]
40c93bc19a Updated translation files (#1249)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2022-07-29 17:36:25 +02:00
Alberto Iannaccone
59b8a2d6bb Register custom themes after the monaco theme init (#1257)
Signed-off-by: Akos Kitta <a.kitta@arduino.cc>

Co-authored-by: Akos Kitta <a.kitta@arduino.cc>
2022-07-29 15:09:53 +02:00
Alberto Iannaccone
124738d810 wait for language packs to be deployed (#1261) 2022-07-29 15:08:07 +02:00
Dave Simpson
19c0334a91 use fixed footer and overflow: auto for content (#1256) 2022-07-28 17:38:47 +02:00
Dave Simpson
f22be3c587 #1223: use theme service on settings load (#1238)
* use theme service on settings load

* use window.matchMedia in loadSettings

* typo fix

* Patched app config to dispatch on OS' theme.

Signed-off-by: Akos Kitta <a.kitta@arduino.cc>

Co-authored-by: Akos Kitta <a.kitta@arduino.cc>
2022-07-27 11:06:48 +02:00
Dave Simpson
9373a0bcaf #374: ensure compile verbose pref is included on upload (#1237)
* ensure compile verbose pref is included on upload

* better verbose typings

Signed-off-by: Akos Kitta <a.kitta@arduino.cc>

Co-authored-by: Akos Kitta <a.kitta@arduino.cc>
2022-07-26 14:05:12 +02:00
Francesco Spissu
5087ff08f2 Primary action to the right of the notification box (#1234) 2022-07-20 16:49:30 +02:00
119 changed files with 2565 additions and 1615 deletions

1
.vscode/launch.json vendored
View File

@@ -20,7 +20,6 @@
"--no-app-auto-install",
"--plugins=local-dir:../plugins",
"--hosted-plugin-inspect=9339",
"--nosplash",
"--content-trace",
"--open-devtools"
],

View File

@@ -41,7 +41,7 @@ The _frontend_ is running as an Electron renderer process and can invoke service
If youre familiar with TypeScript, the [Theia IDE](https://theia-ide.org/), and if you want to contribute to the
project, you should be able to build the Arduino IDE locally.
Please refer to the [Theia IDE prerequisites](https://github.com/theia-ide/theia/blob/master/doc/) documentation for the setup instructions.
Please refer to the [Theia IDE prerequisites](https://github.com/eclipse-theia/theia/blob/master/doc/Developing.md#prerequisites) documentation for the setup instructions.
> **Note**: Node.js 14 must be used instead of the version 12 recommended at the link above.
Once you have all the tools installed, you can build the editor following these steps
@@ -89,7 +89,6 @@ This project is built on [GitHub Actions](https://github.com/arduino/arduino-ide
git push origin 1.2.3
```
## Notes for macOS contributors
Beginning in macOS 10.14.5, the software [must be notarized to run](https://developer.apple.com/documentation/xcode/notarizing_macos_software_before_distribution). The signing and notarization processes for the Arduino IDE are managed by our Continuous Integration (CI) workflows, implemented with GitHub Actions. On every push and pull request, the Arduino IDE is built and saved to a workflow artifact. These artifacts can be used by contributors and beta testers who don't want to set up a build system locally.
For security reasons, signing and notarization are disabled for workflow runs for pull requests from forks of this repository. This means that macOS will block you from running those artifacts.

View File

@@ -1,6 +1,6 @@
{
"name": "arduino-ide-extension",
"version": "2.0.0-rc9",
"version": "2.0.0-rc9.2",
"description": "An extension for Theia building the Arduino IDE",
"license": "AGPL-3.0-or-later",
"scripts": {
@@ -150,13 +150,17 @@
"frontend": "lib/browser/theia/core/browser-menu-module",
"frontendElectron": "lib/electron-browser/theia/core/electron-menu-module"
},
{
"frontend": "lib/browser/theia/core/browser-window-module",
"frontendElectron": "lib/electron-browser/theia/core/electron-window-module"
},
{
"electronMain": "lib/electron-main/arduino-electron-main-module"
}
],
"arduino": {
"cli": {
"version": "0.25.0"
"version": "0.26.0-rc.1"
},
"fwuploader": {
"version": "2.2.0"

View File

@@ -104,7 +104,7 @@ export class ArduinoFrontendContribution
}
}
});
this.appStateService.reachedState('initialized_layout').then(() =>
this.appStateService.reachedState('ready').then(() =>
this.arduinoPreferences.ready.then(() => {
const webContents = remote.getCurrentWebContents();
const zoomLevel = this.arduinoPreferences.get(
@@ -183,34 +183,6 @@ export class ArduinoFrontendContribution
registerColors(colors: ColorRegistry): void {
colors.register(
{
id: 'arduino.branding.primary',
defaults: {
dark: 'statusBar.background',
light: 'statusBar.background',
},
description:
'The primary branding color, such as dialog titles, library, and board manager list labels.',
},
{
id: 'arduino.branding.secondary',
defaults: {
dark: 'statusBar.background',
light: 'statusBar.background',
},
description:
'Secondary branding color for list selections, dropdowns, and widget borders.',
},
{
id: 'arduino.foreground',
defaults: {
dark: 'editorWidget.background',
light: 'editorWidget.background',
hc: 'editorWidget.background',
},
description:
'Color of the Arduino IDE foreground which is used for dialogs, such as the Select Board dialog.',
},
{
id: 'arduino.toolbar.button.background',
defaults: {
@@ -225,8 +197,8 @@ export class ArduinoFrontendContribution
id: 'arduino.toolbar.button.hoverBackground',
defaults: {
dark: 'button.hoverBackground',
light: 'button.foreground',
hc: 'textLink.foreground',
light: 'button.hoverBackground',
hc: 'button.background',
},
description:
'Background color of the toolbar items when hovering over them. Such as Upload, Verify, etc.',
@@ -261,24 +233,6 @@ export class ArduinoFrontendContribution
description:
'Toggle color of the toolbar items when they are currently toggled (the command is in progress)',
},
{
id: 'arduino.output.foreground',
defaults: {
dark: 'editor.foreground',
light: 'editor.foreground',
hc: 'editor.foreground',
},
description: 'Color of the text in the Output view.',
},
{
id: 'arduino.output.background',
defaults: {
dark: 'editor.background',
light: 'editor.background',
hc: 'editor.background',
},
description: 'Background color of the Output view.',
},
{
id: 'arduino.toolbar.dropdown.border',
defaults: {
@@ -303,8 +257,8 @@ export class ArduinoFrontendContribution
id: 'arduino.toolbar.dropdown.background',
defaults: {
dark: 'tab.unfocusedActiveBackground',
light: 'tab.unfocusedActiveBackground',
hc: 'tab.unfocusedActiveBackground',
light: 'dropdown.background',
hc: 'dropdown.background',
},
description: 'Background color of the Board Selector.',
},
@@ -312,18 +266,18 @@ export class ArduinoFrontendContribution
{
id: 'arduino.toolbar.dropdown.label',
defaults: {
dark: 'foreground',
light: 'foreground',
hc: 'foreground',
dark: 'dropdown.foreground',
light: 'dropdown.foreground',
hc: 'dropdown.foreground',
},
description: 'Font color of the Board Selector.',
},
{
id: 'arduino.toolbar.dropdown.iconSelected',
defaults: {
dark: 'statusBar.background',
light: 'statusBar.background',
hc: 'statusBar.background',
dark: 'list.activeSelectionIconForeground',
light: 'list.activeSelectionIconForeground',
hc: 'list.activeSelectionIconForeground',
},
description:
'Color of the selected protocol icon in the Board Selector.',
@@ -331,18 +285,18 @@ export class ArduinoFrontendContribution
{
id: 'arduino.toolbar.dropdown.option.backgroundHover',
defaults: {
dark: 'editor.background',
light: 'editor.background',
hc: 'editor.background',
dark: 'list.hoverBackground',
light: 'list.hoverBackground',
hc: 'list.hoverBackground',
},
description: 'Background color on hover of the Board Selector options.',
},
{
id: 'arduino.toolbar.dropdown.option.backgroundSelected',
defaults: {
dark: 'editor.background',
light: 'editor.background',
hc: 'editor.background',
dark: 'list.activeSelectionBackground',
light: 'list.activeSelectionBackground',
hc: 'list.activeSelectionBackground',
},
description:
'Background color of the selected board in the Board Selector.',

View File

@@ -50,13 +50,17 @@ import {
ApplicationShell as TheiaApplicationShell,
ShellLayoutRestorer as TheiaShellLayoutRestorer,
CommonFrontendContribution as TheiaCommonFrontendContribution,
DockPanelRenderer as TheiaDockPanelRenderer,
TabBarRendererFactory,
ContextMenuRenderer,
createTreeContainer,
TreeWidget,
} from '@theia/core/lib/browser';
import { MenuContribution } from '@theia/core/lib/common/menu';
import { ApplicationShell } from './theia/core/application-shell';
import {
ApplicationShell,
DockPanelRenderer,
} from './theia/core/application-shell';
import { FrontendApplication } from './theia/core/frontend-application';
import {
BoardsConfigDialog,
@@ -82,7 +86,10 @@ import { BoardsAutoInstaller } from './boards/boards-auto-installer';
import { ShellLayoutRestorer } from './theia/core/shell-layout-restorer';
import { ListItemRenderer } from './widgets/component-list/list-item-renderer';
import { ColorContribution } from '@theia/core/lib/browser/color-application-contribution';
import { MonacoThemingService } from '@theia/monaco/lib/browser/monaco-theming-service';
import {
MonacoThemeJson,
MonacoThemingService,
} from '@theia/monaco/lib/browser/monaco-theming-service';
import {
ArduinoDaemonPath,
ArduinoDaemon,
@@ -310,20 +317,36 @@ import { SelectedBoard } from './contributions/selected-board';
import { CheckForUpdates } from './contributions/check-for-updates';
import { OpenBoardsConfig } from './contributions/open-boards-config';
import { SketchFilesTracker } from './contributions/sketch-files-tracker';
import { MonacoThemeServiceIsReady } from './utils/window';
import { Deferred } from '@theia/core/lib/common/promise-util';
import { StatusBarImpl } from './theia/core/status-bar';
import { StatusBarImpl as TheiaStatusBarImpl } from '@theia/core/lib/browser';
MonacoThemingService.register({
id: 'arduino-theme',
label: 'Light (Arduino)',
uiTheme: 'vs',
json: require('../../src/browser/data/default.color-theme.json'),
});
MonacoThemingService.register({
id: 'arduino-theme-dark',
label: 'Dark (Arduino)',
uiTheme: 'vs-dark',
json: require('../../src/browser/data/dark.color-theme.json'),
});
const registerArduinoThemes = () => {
const themes: MonacoThemeJson[] = [
{
id: 'arduino-theme',
label: 'Light (Arduino)',
uiTheme: 'vs',
json: require('../../src/browser/data/default.color-theme.json'),
},
{
id: 'arduino-theme-dark',
label: 'Dark (Arduino)',
uiTheme: 'vs-dark',
json: require('../../src/browser/data/dark.color-theme.json'),
},
];
themes.forEach((theme) => MonacoThemingService.register(theme));
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const global = window as any;
const ready = global[MonacoThemeServiceIsReady] as Deferred;
if (ready) {
ready.promise.then(registerArduinoThemes);
} else {
registerArduinoThemes();
}
export default new ContainerModule((bind, unbind, isBound, rebind) => {
// Commands and toolbar items
@@ -566,7 +589,7 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
// Disabled reference counter in the editor manager to avoid opening the same editor (with different opener options) multiple times.
bind(EditorManager).toSelf().inSingletonScope();
rebind(TheiaEditorManager).to(EditorManager);
rebind(TheiaEditorManager).toService(EditorManager);
// replace search icon
rebind(TheiaSearchInWorkspaceFactory)
@@ -805,6 +828,14 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
bind(WidgetManager).toSelf().inSingletonScope();
rebind(TheiaWidgetManager).toService(WidgetManager);
// To avoid running a status bar update on every single `keypress` event from the editor.
bind(StatusBarImpl).toSelf().inSingletonScope();
rebind(TheiaStatusBarImpl).toService(StatusBarImpl);
// Debounced update for the tab-bar toolbar when typing in the editor.
bind(DockPanelRenderer).toSelf();
rebind(TheiaDockPanelRenderer).toService(DockPanelRenderer);
// Preferences
bindArduinoPreferences(bind);

View File

@@ -237,6 +237,16 @@ export class BoardsAutoInstaller implements FrontendApplicationContribution {
);
const actions: AutoInstallPromptActions = [
{
key: manualInstall,
handler: () => {
this.boardsManagerFrontendContribution
.openView({ reveal: true })
.then((widget) =>
widget.refresh(candidate.name.toLocaleLowerCase())
);
},
},
{
isAcceptance: true,
key: yes,
@@ -250,16 +260,6 @@ export class BoardsAutoInstaller implements FrontendApplicationContribution {
});
},
},
{
key: manualInstall,
handler: () => {
this.boardsManagerFrontendContribution
.openView({ reveal: true })
.then((widget) =>
widget.refresh(candidate.name.toLocaleLowerCase())
);
},
},
];
return actions;

View File

@@ -130,11 +130,14 @@ export class BoardsDropDown extends React.Component<BoardsDropDown.Props> {
protocolIcon
)}
/>
<div className="arduino-boards-dropdown-item--label">
<div className="arduino-boards-dropdown-item--board-label">
<div
className="arduino-boards-dropdown-item--label"
title={`${boardLabel}\n${port.address}`}
>
<div className="arduino-boards-dropdown-item--board-label noWrapInfo noselect">
{boardLabel}
</div>
<div className="arduino-boards-dropdown-item--port-label">
<div className="arduino-boards-dropdown-item--port-label noWrapInfo noselect">
{port.address}
</div>
</div>
@@ -229,7 +232,8 @@ export class BoardsToolBarItem extends React.Component<
<div
className={classNames(
'arduino-boards-toolbar-item--label',
'noWrapInfo noselect',
'noWrapInfo',
'noselect',
{ 'arduino-boards-toolbar-item--label-connected': isConnected }
)}
>

View File

@@ -4,11 +4,8 @@ import URI from '@theia/core/lib/common/uri';
import { ConfirmDialog } from '@theia/core/lib/browser/dialogs';
import { EnvVariablesServer } from '@theia/core/lib/common/env-variables';
import { ArduinoMenus } from '../menu/arduino-menus';
import {
Installable,
LibraryService,
ResponseServiceClient,
} from '../../common/protocol';
import { LibraryService, ResponseServiceClient } from '../../common/protocol';
import { ExecuteWithProgress } from '../../common/protocol/progressible';
import {
SketchContribution,
Command,
@@ -88,7 +85,7 @@ export class AddZipLibrary extends SketchContribution {
private async doInstall(zipUri: string, overwrite?: boolean): Promise<void> {
try {
await Installable.doWithProgress({
await ExecuteWithProgress.doWithProgress({
messageService: this.messageService,
progressText:
nls.localize('arduino/common/processing', 'Processing') +

View File

@@ -1,23 +1,16 @@
import { inject, injectable } from '@theia/core/shared/inversify';
import { nls } from '@theia/core/lib/common';
import { injectable } from '@theia/core/shared/inversify';
import { CoreService } from '../../common/protocol';
import { ArduinoMenus } from '../menu/arduino-menus';
import { BoardsDataStore } from '../boards/boards-data-store';
import { BoardsServiceProvider } from '../boards/boards-service-provider';
import {
CoreServiceContribution,
Command,
CommandRegistry,
CoreServiceContribution,
MenuModelRegistry,
} from './contribution';
import { nls } from '@theia/core/lib/common';
@injectable()
export class BurnBootloader extends CoreServiceContribution {
@inject(BoardsDataStore)
protected readonly boardsDataStore: BoardsDataStore;
@inject(BoardsServiceProvider)
protected readonly boardsServiceClientImpl: BoardsServiceProvider;
override registerCommands(registry: CommandRegistry): void {
registry.registerCommand(BurnBootloader.Commands.BURN_BOOTLOADER, {
execute: () => this.burnBootloader(),
@@ -35,32 +28,19 @@ export class BurnBootloader extends CoreServiceContribution {
});
}
async burnBootloader(): Promise<void> {
private async burnBootloader(): Promise<void> {
const options = await this.options();
try {
const { boardsConfig } = this.boardsServiceClientImpl;
const port = boardsConfig.selectedPort;
const [fqbn, { selectedProgrammer: programmer }, verify, verbose] =
await Promise.all([
this.boardsDataStore.appendConfigToFqbn(
boardsConfig.selectedBoard?.fqbn
),
this.boardsDataStore.getData(boardsConfig.selectedBoard?.fqbn),
this.preferences.get('arduino.upload.verify'),
this.preferences.get('arduino.upload.verbose'),
]);
const board = {
...boardsConfig.selectedBoard,
name: boardsConfig.selectedBoard?.name || '',
fqbn,
};
this.outputChannelManager.getChannel('Arduino').clear();
await this.coreService.burnBootloader({
board,
programmer,
port,
verify,
verbose,
await this.doWithProgress({
progressText: nls.localize(
'arduino/bootloader/burningBootloader',
'Burning bootloader...'
),
task: (progressId, coreService) =>
coreService.burnBootloader({
...options,
progressId,
}),
});
this.messageService.info(
nls.localize(
@@ -75,6 +55,27 @@ export class BurnBootloader extends CoreServiceContribution {
this.handleError(e);
}
}
private async options(): Promise<CoreService.Options.Bootloader> {
const { boardsConfig } = this.boardsServiceProvider;
const port = boardsConfig.selectedPort;
const [fqbn, { selectedProgrammer: programmer }, verify, verbose] =
await Promise.all([
this.boardsDataStore.appendConfigToFqbn(
boardsConfig.selectedBoard?.fqbn
),
this.boardsDataStore.getData(boardsConfig.selectedBoard?.fqbn),
this.preferences.get('arduino.upload.verify'),
this.preferences.get('arduino.upload.verbose'),
]);
return {
fqbn,
programmer,
port,
verify,
verbose,
};
}
}
export namespace BurnBootloader {

View File

@@ -49,13 +49,16 @@ import {
Sketch,
CoreService,
CoreError,
ResponseServiceClient,
} from '../../common/protocol';
import { ArduinoPreferences } from '../arduino-preferences';
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
import { CoreErrorHandler } from './core-error-handler';
import { nls } from '@theia/core';
import { OutputChannelManager } from '../theia/output/output-channel';
import { ClipboardService } from '@theia/core/lib/browser/clipboard-service';
import { ExecuteWithProgress } from '../../common/protocol/progressible';
import { BoardsServiceProvider } from '../boards/boards-service-provider';
import { BoardsDataStore } from '../boards/boards-data-store';
export {
Command,
@@ -167,18 +170,23 @@ export abstract class SketchContribution extends Contribution {
}
@injectable()
export class CoreServiceContribution extends SketchContribution {
@inject(CoreService)
protected readonly coreService: CoreService;
export abstract class CoreServiceContribution extends SketchContribution {
@inject(BoardsDataStore)
protected readonly boardsDataStore: BoardsDataStore;
@inject(CoreErrorHandler)
protected readonly coreErrorHandler: CoreErrorHandler;
@inject(BoardsServiceProvider)
protected readonly boardsServiceProvider: BoardsServiceProvider;
@inject(CoreService)
private readonly coreService: CoreService;
@inject(ClipboardService)
private readonly clipboardService: ClipboardService;
@inject(ResponseServiceClient)
private readonly responseService: ResponseServiceClient;
protected handleError(error: unknown): void {
this.coreErrorHandler.tryHandle(error);
this.tryToastErrorMessage(error);
}
@@ -214,6 +222,25 @@ export class CoreServiceContribution extends SketchContribution {
throw error;
}
}
protected async doWithProgress<T>(options: {
progressText: string;
keepOutput?: boolean;
task: (progressId: string, coreService: CoreService) => Promise<T>;
}): Promise<T> {
const { progressText, keepOutput, task } = options;
this.outputChannelManager
.getChannel('Arduino')
.show({ preserveFocus: true });
const result = await ExecuteWithProgress.doWithProgress({
messageService: this.messageService,
responseService: this.responseService,
progressText,
run: ({ progressId }) => task(progressId, this.coreService),
keepOutput,
});
return result;
}
}
export namespace Contribution {

View File

@@ -141,6 +141,11 @@ ${value}
label: nls.localize('arduino/editor/decreaseIndent', 'Decrease Indent'),
order: '2',
});
registry.registerMenuAction(ArduinoMenus.EDIT__CODE_CONTROL_GROUP, {
commandId: EditContributions.Commands.AUTO_FORMAT.id,
label: nls.localize('arduino/editor/autoFormat', 'Auto Format'),
order: '3',
});
registry.registerMenuAction(ArduinoMenus.EDIT__FONT_CONTROL_GROUP, {
commandId: EditContributions.Commands.INCREASE_FONT_SIZE.id,
@@ -248,10 +253,13 @@ ${value}
});
}
protected async current(): Promise<ICodeEditor | StandaloneCodeEditor | undefined> {
protected async current(): Promise<
ICodeEditor | StandaloneCodeEditor | undefined
> {
return (
this.codeEditorService.getFocusedCodeEditor() ||
this.codeEditorService.getActiveCodeEditor() || undefined
this.codeEditorService.getActiveCodeEditor() ||
undefined
);
}

View File

@@ -43,7 +43,7 @@ export class FirstStartupInstaller extends Contribution {
// If arduino:avr installation fails because it's already installed we don't want to retry on next start-up
console.error(e);
} else {
// But if there is any other error (e.g.: no interntet cconnection), we want to retry next time
// But if there is any other error (e.g.: no Internet connection), we want to retry next time
avrPackageError = e;
}
}
@@ -64,7 +64,7 @@ export class FirstStartupInstaller extends Contribution {
// If Arduino_BuiltIn installation fails because it's already installed we don't want to retry on next start-up
console.log('error installing core', e);
} else {
// But if there is any other error (e.g.: no interntet cconnection), we want to retry next time
// But if there is any other error (e.g.: no Internet connection), we want to retry next time
builtInLibraryError = e;
}
}

View File

@@ -145,6 +145,7 @@ export class InoLanguage extends SketchContribution {
name: name ? `"${name}"` : undefined,
},
realTimeDiagnostics,
silentOutput: true,
}
),
]);

View File

@@ -77,15 +77,11 @@ export class SaveAsSketch extends SketchContribution {
const exists = await this.fileService.exists(
sketchDirUri.resolve(sketch.name)
);
const defaultUri = exists
? sketchDirUri.resolve(
sketchDirUri
.resolve(
`${sketch.name}_copy_${dateFormat(new Date(), 'yyyymmddHHMMss')}`
)
.toString()
)
: sketchDirUri.resolve(sketch.name);
const defaultUri = sketchDirUri.resolve(
exists
? `${sketch.name}_copy_${dateFormat(new Date(), 'yyyymmddHHMMss')}`
: sketch.name
);
const defaultPath = await this.fileService.fsPath(defaultUri);
const { filePath, canceled } = await remote.dialog.showSaveDialog({
title: nls.localize(

View File

@@ -3,56 +3,47 @@ import { Emitter } from '@theia/core/lib/common/event';
import { BoardUserField, CoreService } from '../../common/protocol';
import { ArduinoMenus, PlaceholderMenuNode } from '../menu/arduino-menus';
import { ArduinoToolbar } from '../toolbar/arduino-toolbar';
import { BoardsDataStore } from '../boards/boards-data-store';
import { BoardsServiceProvider } from '../boards/boards-service-provider';
import {
CoreServiceContribution,
Command,
CommandRegistry,
MenuModelRegistry,
KeybindingRegistry,
TabBarToolbarRegistry,
CoreServiceContribution,
} from './contribution';
import { UserFieldsDialog } from '../dialogs/user-fields/user-fields-dialog';
import { DisposableCollection, nls } from '@theia/core/lib/common';
import { CurrentSketch } from '../../common/protocol/sketches-service-client-impl';
import type { VerifySketchParams } from './verify-sketch';
@injectable()
export class UploadSketch extends CoreServiceContribution {
@inject(MenuModelRegistry)
protected readonly menuRegistry: MenuModelRegistry;
@inject(BoardsDataStore)
protected readonly boardsDataStore: BoardsDataStore;
@inject(BoardsServiceProvider)
protected readonly boardsServiceClientImpl: BoardsServiceProvider;
private readonly menuRegistry: MenuModelRegistry;
@inject(UserFieldsDialog)
protected readonly userFieldsDialog: UserFieldsDialog;
private readonly userFieldsDialog: UserFieldsDialog;
protected cachedUserFields: Map<string, BoardUserField[]> = new Map();
private boardRequiresUserFields = false;
private readonly cachedUserFields: Map<string, BoardUserField[]> = new Map();
private readonly menuActionsDisposables = new DisposableCollection();
protected readonly onDidChangeEmitter = new Emitter<Readonly<void>>();
readonly onDidChange = this.onDidChangeEmitter.event;
protected uploadInProgress = false;
protected boardRequiresUserFields = false;
protected readonly menuActionsDisposables = new DisposableCollection();
private readonly onDidChangeEmitter = new Emitter<void>();
private readonly onDidChange = this.onDidChangeEmitter.event;
private uploadInProgress = false;
protected override init(): void {
super.init();
this.boardsServiceClientImpl.onBoardsConfigChanged(async () => {
this.boardsServiceProvider.onBoardsConfigChanged(async () => {
const userFields =
await this.boardsServiceClientImpl.selectedBoardUserFields();
await this.boardsServiceProvider.selectedBoardUserFields();
this.boardRequiresUserFields = userFields.length > 0;
this.registerMenus(this.menuRegistry);
});
}
private selectedFqbnAddress(): string {
const { boardsConfig } = this.boardsServiceClientImpl;
const { boardsConfig } = this.boardsServiceProvider;
const fqbn = boardsConfig.selectedBoard?.fqbn;
if (!fqbn) {
return '';
@@ -76,7 +67,7 @@ export class UploadSketch extends CoreServiceContribution {
if (this.boardRequiresUserFields && !this.cachedUserFields.has(key)) {
// Deep clone the array of board fields to avoid editing the cached ones
this.userFieldsDialog.value = (
await this.boardsServiceClientImpl.selectedBoardUserFields()
await this.boardsServiceProvider.selectedBoardUserFields()
).map((f) => ({ ...f }));
const result = await this.userFieldsDialog.open();
if (!result) {
@@ -98,8 +89,7 @@ export class UploadSketch extends CoreServiceContribution {
const cached = this.cachedUserFields.get(key);
// Deep clone the array of board fields to avoid editing the cached ones
this.userFieldsDialog.value = (
cached ??
(await this.boardsServiceClientImpl.selectedBoardUserFields())
cached ?? (await this.boardsServiceProvider.selectedBoardUserFields())
).map((f) => ({ ...f }));
const result = await this.userFieldsDialog.open();
@@ -130,7 +120,6 @@ export class UploadSketch extends CoreServiceContribution {
override registerMenus(registry: MenuModelRegistry): void {
this.menuActionsDisposables.dispose();
this.menuActionsDisposables.push(
registry.registerMenuAction(ArduinoMenus.SKETCH__MAIN_GROUP, {
commandId: UploadSketch.Commands.UPLOAD_SKETCH.id,
@@ -153,7 +142,7 @@ export class UploadSketch extends CoreServiceContribution {
new PlaceholderMenuNode(
ArduinoMenus.SKETCH__MAIN_GROUP,
// commandId: UploadSketch.Commands.UPLOAD_WITH_CONFIGURATION.id,
UploadSketch.Commands.UPLOAD_WITH_CONFIGURATION.label!,
UploadSketch.Commands.UPLOAD_WITH_CONFIGURATION.label,
{ order: '2' }
)
)
@@ -193,54 +182,42 @@ export class UploadSketch extends CoreServiceContribution {
}
async uploadSketch(usingProgrammer = false): Promise<void> {
// even with buttons disabled, better to double check if an upload is already in progress
if (this.uploadInProgress) {
return;
}
const sketch = await this.sketchServiceClient.currentSketch();
if (!CurrentSketch.isValid(sketch)) {
return;
}
try {
// toggle the toolbar button and menu item state.
// uploadInProgress will be set to false whether the upload fails or not
this.uploadInProgress = true;
this.coreErrorHandler.reset();
this.onDidChangeEmitter.fire();
const { boardsConfig } = this.boardsServiceClientImpl;
const [
fqbn,
{ selectedProgrammer },
verify,
verbose,
sourceOverride,
optimizeForDebug,
] = await Promise.all([
this.boardsDataStore.appendConfigToFqbn(
boardsConfig.selectedBoard?.fqbn
),
this.boardsDataStore.getData(boardsConfig.selectedBoard?.fqbn),
this.preferences.get('arduino.upload.verify'),
this.preferences.get('arduino.upload.verbose'),
this.sourceOverride(),
this.commandService.executeCommand<boolean>(
'arduino-is-optimize-for-debug'
),
]);
const board = {
...boardsConfig.selectedBoard,
name: boardsConfig.selectedBoard?.name || '',
fqbn,
};
let options: CoreService.Upload.Options | undefined = undefined;
const { selectedPort } = boardsConfig;
const port = selectedPort;
const userFields =
this.cachedUserFields.get(this.selectedFqbnAddress()) ?? [];
if (userFields.length === 0 && this.boardRequiresUserFields) {
const verifyOptions =
await this.commandService.executeCommand<CoreService.Options.Compile>(
'arduino-verify-sketch',
<VerifySketchParams>{
exportBinaries: false,
silent: true,
}
);
if (!verifyOptions) {
return;
}
const uploadOptions = await this.uploadOptions(
usingProgrammer,
verifyOptions
);
if (!uploadOptions) {
return;
}
// TODO: This does not belong here.
// IDE2 should not do any preliminary checks but let the CLI fail and then toast a user consumable error message.
if (
uploadOptions.userFields.length === 0 &&
this.boardRequiresUserFields
) {
this.messageService.error(
nls.localize(
'arduino/sketch/userFieldsNotFoundError',
@@ -250,37 +227,13 @@ export class UploadSketch extends CoreServiceContribution {
return;
}
if (usingProgrammer) {
const programmer = selectedProgrammer;
options = {
sketch,
board,
optimizeForDebug: Boolean(optimizeForDebug),
programmer,
port,
verbose,
verify,
sourceOverride,
userFields,
};
} else {
options = {
sketch,
board,
optimizeForDebug: Boolean(optimizeForDebug),
port,
verbose,
verify,
sourceOverride,
userFields,
};
}
this.outputChannelManager.getChannel('Arduino').clear();
if (usingProgrammer) {
await this.coreService.uploadUsingProgrammer(options);
} else {
await this.coreService.upload(options);
}
await this.doWithProgress({
progressText: nls.localize('arduino/sketch/uploading', 'Uploading...'),
task: (progressId, coreService) =>
coreService.upload({ ...uploadOptions, progressId }),
keepOutput: true,
});
this.messageService.info(
nls.localize('arduino/sketch/doneUploading', 'Done uploading.'),
{ timeout: 3000 }
@@ -292,6 +245,52 @@ export class UploadSketch extends CoreServiceContribution {
this.onDidChangeEmitter.fire();
}
}
private async uploadOptions(
usingProgrammer: boolean,
verifyOptions: CoreService.Options.Compile
): Promise<CoreService.Options.Upload | undefined> {
const sketch = await this.sketchServiceClient.currentSketch();
if (!CurrentSketch.isValid(sketch)) {
return undefined;
}
const userFields = this.userFields();
const { boardsConfig } = this.boardsServiceProvider;
const [fqbn, { selectedProgrammer: programmer }, verify, verbose] =
await Promise.all([
verifyOptions.fqbn, // already decorated FQBN
this.boardsDataStore.getData(this.sanitizeFqbn(verifyOptions.fqbn)),
this.preferences.get('arduino.upload.verify'),
this.preferences.get('arduino.upload.verbose'),
]);
const port = boardsConfig.selectedPort;
return {
sketch,
fqbn,
...(usingProgrammer && { programmer }),
port,
verbose,
verify,
userFields,
};
}
private userFields() {
return this.cachedUserFields.get(this.selectedFqbnAddress()) ?? [];
}
/**
* Converts the `VENDOR:ARCHITECTURE:BOARD_ID[:MENU_ID=OPTION_ID[,MENU2_ID=OPTION_ID ...]]` FQBN to
* `VENDOR:ARCHITECTURE:BOARD_ID` format.
* See the details of the `{build.fqbn}` entry in the [specs](https://arduino.github.io/arduino-cli/latest/platform-specification/#global-predefined-properties).
*/
private sanitizeFqbn(fqbn: string | undefined): string | undefined {
if (!fqbn) {
return undefined;
}
const [vendor, arch, id] = fqbn.split(':');
return `${vendor}:${arch}:${id}`;
}
}
export namespace UploadSketch {
@@ -299,7 +298,7 @@ export namespace UploadSketch {
export const UPLOAD_SKETCH: Command = {
id: 'arduino-upload-sketch',
};
export const UPLOAD_WITH_CONFIGURATION: Command = {
export const UPLOAD_WITH_CONFIGURATION: Command & { label: string } = {
id: 'arduino-upload-with-configuration-sketch',
label: nls.localize(
'arduino/sketch/configureAndUpload',

View File

@@ -2,8 +2,6 @@ import { inject, injectable } from '@theia/core/shared/inversify';
import { Emitter } from '@theia/core/lib/common/event';
import { ArduinoMenus } from '../menu/arduino-menus';
import { ArduinoToolbar } from '../toolbar/arduino-toolbar';
import { BoardsDataStore } from '../boards/boards-data-store';
import { BoardsServiceProvider } from '../boards/boards-service-provider';
import {
CoreServiceContribution,
Command,
@@ -14,27 +12,36 @@ import {
} from './contribution';
import { nls } from '@theia/core/lib/common';
import { CurrentSketch } from '../../common/protocol/sketches-service-client-impl';
import { CoreService } from '../../common/protocol';
import { CoreErrorHandler } from './core-error-handler';
export interface VerifySketchParams {
/**
* Same as `CoreService.Options.Compile#exportBinaries`
*/
readonly exportBinaries?: boolean;
/**
* If `true`, there won't be any UI indication of the verify command. It's `false` by default.
*/
readonly silent?: boolean;
}
@injectable()
export class VerifySketch extends CoreServiceContribution {
@inject(BoardsDataStore)
protected readonly boardsDataStore: BoardsDataStore;
@inject(CoreErrorHandler)
private readonly coreErrorHandler: CoreErrorHandler;
@inject(BoardsServiceProvider)
protected readonly boardsServiceClientImpl: BoardsServiceProvider;
protected readonly onDidChangeEmitter = new Emitter<Readonly<void>>();
readonly onDidChange = this.onDidChangeEmitter.event;
protected verifyInProgress = false;
private readonly onDidChangeEmitter = new Emitter<void>();
private readonly onDidChange = this.onDidChangeEmitter.event;
private verifyInProgress = false;
override registerCommands(registry: CommandRegistry): void {
registry.registerCommand(VerifySketch.Commands.VERIFY_SKETCH, {
execute: () => this.verifySketch(),
execute: (params?: VerifySketchParams) => this.verifySketch(params),
isEnabled: () => !this.verifyInProgress,
});
registry.registerCommand(VerifySketch.Commands.EXPORT_BINARIES, {
execute: () => this.verifySketch(true),
execute: () => this.verifySketch({ exportBinaries: true }),
isEnabled: () => !this.verifyInProgress,
});
registry.registerCommand(VerifySketch.Commands.VERIFY_SKETCH_TOOLBAR, {
@@ -84,61 +91,87 @@ export class VerifySketch extends CoreServiceContribution {
});
}
async verifySketch(exportBinaries?: boolean): Promise<void> {
// even with buttons disabled, better to double check if a verify is already in progress
protected override handleError(error: unknown): void {
this.coreErrorHandler.tryHandle(error);
super.handleError(error);
}
private async verifySketch(
params?: VerifySketchParams
): Promise<CoreService.Options.Compile | undefined> {
if (this.verifyInProgress) {
return;
return undefined;
}
// toggle the toolbar button and menu item state.
// verifyInProgress will be set to false whether the compilation fails or not
const sketch = await this.sketchServiceClient.currentSketch();
if (!CurrentSketch.isValid(sketch)) {
return;
}
try {
this.verifyInProgress = true;
if (!params?.silent) {
this.verifyInProgress = true;
this.onDidChangeEmitter.fire();
}
this.coreErrorHandler.reset();
this.onDidChangeEmitter.fire();
const { boardsConfig } = this.boardsServiceClientImpl;
const [fqbn, sourceOverride] = await Promise.all([
this.boardsDataStore.appendConfigToFqbn(
boardsConfig.selectedBoard?.fqbn
const options = await this.options(params?.exportBinaries);
if (!options) {
return undefined;
}
await this.doWithProgress({
progressText: nls.localize(
'arduino/sketch/compile',
'Compiling sketch...'
),
this.sourceOverride(),
]);
const board = {
...boardsConfig.selectedBoard,
name: boardsConfig.selectedBoard?.name || '',
fqbn,
};
const verbose = this.preferences.get('arduino.compile.verbose');
const compilerWarnings = this.preferences.get('arduino.compile.warnings');
const optimizeForDebug =
await this.commandService.executeCommand<boolean>(
'arduino-is-optimize-for-debug'
);
this.outputChannelManager.getChannel('Arduino').clear();
await this.coreService.compile({
sketch,
board,
optimizeForDebug: Boolean(optimizeForDebug),
verbose,
exportBinaries,
sourceOverride,
compilerWarnings,
task: (progressId, coreService) =>
coreService.compile({
...options,
progressId,
}),
});
this.messageService.info(
nls.localize('arduino/sketch/doneCompiling', 'Done compiling.'),
{ timeout: 3000 }
);
// Returns with the used options for the compilation
// so that follow-up tasks (such as upload) can reuse the compiled code.
// Note that the `fqbn` is already decorated with the board settings, if any.
return options;
} catch (e) {
this.handleError(e);
return undefined;
} finally {
this.verifyInProgress = false;
this.onDidChangeEmitter.fire();
if (!params?.silent) {
this.onDidChangeEmitter.fire();
}
}
}
private async options(
exportBinaries?: boolean
): Promise<CoreService.Options.Compile | undefined> {
const sketch = await this.sketchServiceClient.currentSketch();
if (!CurrentSketch.isValid(sketch)) {
return undefined;
}
const { boardsConfig } = this.boardsServiceProvider;
const [fqbn, sourceOverride, optimizeForDebug] = await Promise.all([
this.boardsDataStore.appendConfigToFqbn(boardsConfig.selectedBoard?.fqbn),
this.sourceOverride(),
this.commandService.executeCommand<boolean>(
'arduino-is-optimize-for-debug'
),
]);
const verbose = this.preferences.get('arduino.compile.verbose');
const compilerWarnings = this.preferences.get('arduino.compile.warnings');
return {
sketch,
fqbn,
optimizeForDebug: Boolean(optimizeForDebug),
verbose,
exportBinaries,
sourceOverride,
compilerWarnings,
};
}
}
export namespace VerifySketch {

View File

@@ -8,6 +8,7 @@
"list.inactiveSelectionForeground": "#dae3e3",
"list.inactiveSelectionBackground": "#434f54",
"list.hoverBackground": "#1f272a",
"list.activeSelectionIconForeground": "#0ca1a6",
"progressBar.background": "#005c5f",
"editor.background": "#1f272a",
"editor.foreground": "#dae3e3",
@@ -16,6 +17,7 @@
"editorCursor.foreground": "#434f54",
"editorWhitespace.foreground": "#bfbfbf",
"editorWidget.background": "#171e21",
"editorWidget.foreground": "#dae3e3",
"focusBorder": "#dae3e3",
"menubar.selectionBackground": "#ffffff",
"menubar.selectionForeground": "#212121",
@@ -28,7 +30,7 @@
"titleBar.activeBackground": "#171e21",
"titleBar.activeForeground": "#dae3e3",
"terminal.background": "#000000",
"terminal.foreground": "#e0e0e0",
"terminal.foreground": "#ffffff",
"dropdown.border": "#7fcbcd",
"dropdown.background": "#2c353a",
"dropdown.foreground": "#dae3e3",
@@ -64,7 +66,8 @@
"settings.headerForeground": "#dae3e3",
"tree.indentGuidesStroke": "#374146",
"tab.unfocusedActiveForeground": "#dae3e3",
"tab.inactiveBackground": "#171e21"
"tab.inactiveBackground": "#171e21",
"textLink.foreground": "#0ca1a6"
},
"tokenColors": [
{

View File

@@ -8,6 +8,7 @@
"list.inactiveSelectionForeground": "#4e5b61",
"list.inactiveSelectionBackground": "#dae3e3",
"list.hoverBackground": "#ecf1f1",
"list.activeSelectionIconForeground": "#008184",
"progressBar.background": "#005c5f",
"editor.background": "#ffffff",
"editor.foreground": "#4e5b61",
@@ -16,6 +17,7 @@
"editorCursor.foreground": "#434f54",
"editorWhitespace.foreground": "#bfbfbf",
"editorWidget.background": "#f7f9f9",
"editorWidget.foreground": "#4e5b61",
"focusBorder": "#7fcbcd",
"menubar.selectionBackground": "#ffffff",
"menubar.selectionForeground": "#212121",
@@ -28,7 +30,7 @@
"titleBar.activeBackground": "#006d70",
"titleBar.activeForeground": "#f7f9f9",
"terminal.background": "#000000",
"terminal.foreground": "#e0e0e0",
"terminal.foreground": "#ffffff",
"dropdown.border": "#dae3e3",
"dropdown.background": "#ffffff",
"dropdown.foreground": "#4e5b61",
@@ -64,7 +66,8 @@
"settings.headerForeground": "#4e5b61",
"tree.indentGuidesStroke": "#dae3e3",
"tab.unfocusedActiveForeground": "#4e5b61",
"tab.inactiveBackground": "#ecf1f1"
"tab.inactiveBackground": "#ecf1f1",
"textLink.foreground": "#008184"
},
"tokenColors": [
{

View File

@@ -19,6 +19,7 @@ import { CommandRegistry } from '@theia/core/lib/common/command';
import { certificateList, sanifyCertString } from './utils';
import { ArduinoFirmwareUploader } from '../../../common/protocol/arduino-firmware-uploader';
import { nls } from '@theia/core/lib/common';
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
@injectable()
export class UploadCertificateDialogWidget extends ReactWidget {
@@ -37,6 +38,9 @@ export class UploadCertificateDialogWidget extends ReactWidget {
@inject(ArduinoFirmwareUploader)
protected readonly arduinoFirmwareUploader: ArduinoFirmwareUploader;
@inject(FrontendApplicationStateService)
private readonly appStateService: FrontendApplicationStateService;
protected certificates: string[] = [];
protected updatableFqbns: string[] = [];
protected availableBoards: AvailableBoard[] = [];
@@ -66,10 +70,12 @@ export class UploadCertificateDialogWidget extends ReactWidget {
}
});
this.arduinoFirmwareUploader.updatableBoards().then((fqbns) => {
this.updatableFqbns = fqbns;
this.update();
});
this.appStateService.reachedState('ready').then(() =>
this.arduinoFirmwareUploader.updatableBoards().then((fqbns) => {
this.updatableFqbns = fqbns;
this.update();
})
);
this.boardsServiceClient.onAvailableBoardsChanged((availableBoards) => {
this.availableBoards = availableBoards;

View File

@@ -201,12 +201,7 @@ export class SettingsComponent extends React.Component<
<div className="flex-line">
<select
className="theia-select"
value={
ThemeService.get()
.getThemes()
.find(({ id }) => id === this.state.themeId)?.label ||
nls.localize('arduino/common/unknown', 'Unknown')
}
value={ThemeService.get().getCurrentTheme().label}
onChange={this.themeDidChange}
>
{ThemeService.get()
@@ -591,6 +586,9 @@ export class SettingsComponent extends React.Component<
const theme = ThemeService.get().getThemes()[selectedIndex];
if (theme) {
this.setState({ themeId: theme.id });
if (ThemeService.get().getCurrentTheme().id !== theme.id) {
ThemeService.get().setCurrentTheme(theme.id);
}
}
};

View File

@@ -16,6 +16,7 @@ import { SettingsComponent } from './settings-component';
import { AsyncLocalizationProvider } from '@theia/core/lib/common/i18n/localization';
import { AdditionalUrls } from '../../../common/protocol';
import { AbstractDialog } from '../../theia/dialogs/dialogs';
import { ThemeService } from '@theia/core/lib/browser/theming';
@injectable()
export class SettingsWidget extends ReactWidget {
@@ -118,6 +119,17 @@ export class SettingsDialog extends AbstractDialog<Promise<Settings>> {
this.widget.activate();
}
override async open(): Promise<Promise<Settings> | undefined> {
const themeIdBeforeOpen = ThemeService.get().getCurrentTheme().id;
const result = await super.open();
if (!result) {
if (ThemeService.get().getCurrentTheme().id !== themeIdBeforeOpen) {
ThemeService.get().setCurrentTheme(themeIdBeforeOpen);
}
}
return result;
}
}
export class AdditionalUrlsDialog extends AbstractDialog<string[]> {

View File

@@ -16,64 +16,30 @@ const SettingsStepInput: React.FC<SettingsStepInputProps> = (
const { value, setSettingsStateValue, step, maxValue, minValue, classNames } =
props;
const [stepUpDisabled, setStepUpDisabled] = React.useState(false);
const [stepDownDisabled, setStepDownDisabled] = React.useState(false);
const clamp = (value: number, min: number, max: number): number => {
return Math.min(Math.max(value, min), max);
};
const onStepUp = (): void => {
const valueRoundedToScale = Math.ceil(value / step) * step;
const onStep = (
roundingOperation: 'ceil' | 'floor',
stepOperation: (a: number, b: number) => number
): void => {
const valueRoundedToScale = Math[roundingOperation](value / step) * step;
const calculatedValue =
valueRoundedToScale === value ? value + step : valueRoundedToScale;
const newValue = limitValueByCondition(
calculatedValue,
maxValue,
calculatedValue >= maxValue,
disableStepUp
);
valueRoundedToScale === value
? stepOperation(value, step)
: valueRoundedToScale;
const newValue = clamp(calculatedValue, minValue, maxValue);
setSettingsStateValue(newValue);
};
const onStepUp = (): void => {
onStep('ceil', (a: number, b: number) => a + b);
};
const onStepDown = (): void => {
const valueRoundedToScale = Math.floor(value / step) * step;
const calculatedValue =
valueRoundedToScale === value ? value - step : valueRoundedToScale;
const newValue = limitValueByCondition(
calculatedValue,
minValue,
calculatedValue <= minValue,
disableStepDown
);
setSettingsStateValue(newValue);
};
const limitValueByCondition = (
calculatedValue: number,
limitedValue: number,
condition: boolean,
onConditionTrue: () => void,
onConditionFalse = enableButtons
): number => {
if (condition) {
onConditionTrue();
return limitedValue;
} else {
onConditionFalse();
return calculatedValue;
}
};
const enableButtons = (): void => {
setStepUpDisabled(false);
setStepDownDisabled(false);
};
const disableStepUp = (): void => {
setStepUpDisabled(true);
};
const disableStepDown = (): void => {
setStepDownDisabled(true);
onStep('floor', (a: number, b: number) => a - b);
};
const onUserInput = (event: React.ChangeEvent<HTMLInputElement>): void => {
@@ -86,34 +52,14 @@ const SettingsStepInput: React.FC<SettingsStepInputProps> = (
const number = Number(eventValue);
if (!isNaN(number) && number !== value) {
let newValue;
if (number > value) {
newValue = limitValueByCondition(
number,
maxValue,
number >= maxValue,
disableStepUp
);
} else {
newValue = limitValueByCondition(
number,
minValue,
number <= minValue,
disableStepDown
);
}
const newValue = clamp(number, minValue, maxValue);
setSettingsStateValue(newValue);
}
};
// the component does not unmount when we close the settings dialog
// in theia which necessitates the below useEffect
React.useEffect(() => {
if (value > minValue && value < maxValue) {
enableButtons();
}
}, [value, minValue, maxValue]);
const upDisabled = value >= maxValue;
const downDisabled = value <= minValue;
return (
<div className="settings-step-input-container">
@@ -127,14 +73,14 @@ const SettingsStepInput: React.FC<SettingsStepInputProps> = (
<div className="settings-step-input-buttons-container">
<button
className="settings-step-input-button settings-step-input-up-button"
disabled={stepUpDisabled}
disabled={upDisabled}
onClick={onStepUp}
>
&#9662;
</button>
<button
className="settings-step-input-button"
disabled={stepDownDisabled}
disabled={downDisabled}
onClick={onStepDown}
>
&#9662;

View File

@@ -111,9 +111,11 @@ export class SettingsService {
@postConstruct()
protected async init(): Promise<void> {
const settings = await this.loadSettings();
this._settings = deepClone(settings);
this.ready.resolve();
this.appStateService.reachedState('ready').then(async () => {
const settings = await this.loadSettings();
this._settings = deepClone(settings);
this.ready.resolve();
});
}
protected async loadSettings(): Promise<Settings> {
@@ -139,7 +141,10 @@ export class SettingsService {
this.preferenceService.get<number>(FONT_SIZE_SETTING, 12),
this.preferenceService.get<string>(
'workbench.colorTheme',
'arduino-theme'
window.matchMedia &&
window.matchMedia('(prefers-color-scheme: dark)').matches
? 'arduino-theme-dark'
: 'arduino-theme'
),
this.preferenceService.get<Settings.AutoSave>(
AUTO_SAVE_SETTING,

View File

@@ -49,3 +49,14 @@
padding-top: 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);
}

View File

@@ -15,7 +15,7 @@ div.dialogContent.select-board-dialog > div.head .title {
font-weight: 400;
letter-spacing: 0.02em;
font-size: 1.2em;
color: var(--theia-arduino-branding-primary);
color: var(--theia-editorWidget-foreground);
margin-bottom: 10px;
}
@@ -24,7 +24,7 @@ div#select-board-dialog .selectBoardContainer .body .list .item.selected {
}
div#select-board-dialog .selectBoardContainer .body .list .item.selected i {
color: var(--theia-arduino-branding-primary);
color: var(--theia-list-activeSelectionIconForeground);
}
#select-board-dialog .selectBoardContainer .search,
@@ -43,7 +43,7 @@ div#select-board-dialog .selectBoardContainer .body .list .item.selected i {
margin: 0;
vertical-align: top;
display: flex;
color: var(--theia-editor-foreground);
color: var(--theia-input-foreground);
}
#select-board-dialog .selectBoardContainer .body .search input:focus {
@@ -66,7 +66,7 @@ div#select-board-dialog .selectBoardContainer .body .list .item.selected i {
}
#select-board-dialog .selectBoardContainer .body .container .content .title {
color: #7f8c8d;
color: var(--theia-editorWidget-foreground);
padding: 0px 0px 10px 0px;
text-transform: uppercase;
}
@@ -77,7 +77,7 @@ div#select-board-dialog .selectBoardContainer .body .list .item.selected i {
#select-board-dialog .selectBoardContainer .body .container .content .loading {
font-size: var(--theia-ui-font-size1);
color: var(--theia-arduino-branding-secondary);
color: var(--theia-editorWidget-foreground);
padding: 10px 5px 10px 10px;
text-transform: uppercase;
/* The max, min-height comes from `.body .list` 200px + 47px top padding - 2 * 10px top padding */
@@ -148,6 +148,7 @@ div#select-board-dialog .selectBoardContainer .body .list .item.selected i {
background: var(--theia-arduino-toolbar-dropdown-background);
border-radius: 1px;
color: var(--theia-arduino-toolbar-dropdown-label);
border: 1px solid var(--theia-arduino-toolbar-dropdown-border);
display: flex;
gap: 10px;
height: 28px;
@@ -164,10 +165,7 @@ div#select-board-dialog .selectBoardContainer .body .list .item.selected i {
font-size: 16px;
}
.arduino-boards-toolbar-item--protocol {
color: var(--theia-arduino-toolbar-dropdown-label);
}
.arduino-boards-toolbar-item--protocol ,
.arduino-boards-dropdown-item--protocol {
color: var(--theia-arduino-toolbar-dropdown-label);
}
@@ -180,9 +178,6 @@ div#select-board-dialog .selectBoardContainer .body .list .item.selected i {
}
.arduino-boards-toolbar-item--label {
height: 100%;
display: flex;
align-items: center;
width: 100%;
}
@@ -208,6 +203,7 @@ div#select-board-dialog .selectBoardContainer .body .list .item.selected i {
.arduino-boards-dropdown-list--items-container {
overflow: auto;
max-height: 404px;
background: var(--theia-arduino-toolbar-dropdown-background);
}
.arduino-boards-dropdown-list--items-container::-webkit-scrollbar {
@@ -226,6 +222,7 @@ div#select-board-dialog .selectBoardContainer .body .list .item.selected i {
}
.arduino-boards-dropdown-item--label {
overflow: hidden;
flex: 1;
}
@@ -248,8 +245,8 @@ div#select-board-dialog .selectBoardContainer .body .list .item.selected i {
}
.arduino-boards-dropdown-item--selected
.arduino-boards-dropdown-item--port-label {
color: var(--theia-arduino-toolbar-dropdown-label);
.arduino-boards-dropdown-item--port-label {
color: var(--theia-arduino-toolbar-dropdown-label);
}
.arduino-boards-dropdown-item--selected .fa {
@@ -261,6 +258,16 @@ div#select-board-dialog .selectBoardContainer .body .list .item.selected i {
}
.arduino-board-dropdown-footer {
color: var(--theia-arduino-branding-primary);
border-top: 1px solid var(--theia-arduino-toolbar-dropdown-border);
color: var(--theia-secondaryButton-foreground);
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 .body .list .item:hover {
outline: 1px dashed var(--theia-focusBorder);
}
.hc-black.hc-theia.theia-hc div#select-board-dialog .selectBoardContainer .body .list .item.selected {
outline: 1px solid var(--theia-focusBorder);
}

View File

@@ -7,7 +7,7 @@
}
.certificate-uploader-dialog .arduino-select__control {
height: 31px;
background: var(--theia-menubar-selectionBackground) !important;
background: var(--theia-dropdown-background) !important;
}
.certificate-uploader-dialog .dialogRow > button{
@@ -15,9 +15,10 @@
}
.certificate-uploader-dialog .certificate-list {
border: 1px solid #BDC7C7;
border: 1px solid var(--theia-editorWidget-border);
border-radius: 2px;;
background: var(--theia-menubar-selectionBackground) !important;
color: var(--theia-editor-foreground);
background-color: var(--theia-editor-background);
overflow: auto;
height: 120px;
flex: 1;
@@ -60,9 +61,10 @@
.certificate-add {
padding: 16px;
background-color: var(--theia-list-hoverBackground);
border-radius: 3px;
border: 1px solid #BDC7C7;
border: 1px solid var(--theia-editorWidget-border);
color: var(--theia-editorWidget-foreground);
background-color: var(--theia-editorWidget-background);
}
.certificate-add input {
@@ -71,4 +73,4 @@
width: 100%;
box-sizing: border-box;
}
}

View File

@@ -96,7 +96,7 @@
.cloud-sketchbook-welcome > .item .link {
cursor: pointer;
color: var(--theia-arduino-branding-primary);
color: var(--theia-textLink-foreground);
}
.pull-sketch-icon {

View File

@@ -17,7 +17,7 @@
font-weight: 500;
background-color: transparent;
font-size: var(--theia-ui-font-size2);
color: var(--theia-list-inactiveSelectionForeground);
color: var(--theia-editorWidget-foreground);
min-height: 0;
}

View File

@@ -6,7 +6,7 @@
}
.monaco-list-row.show-file-icons.focused {
background-color: #d6ebff;
background-color: var(--theia-quickInputList-focusBackground);
}
.monaco-editor .view-overlays .compiler-error {

View File

@@ -27,9 +27,9 @@
}
.ide-updater-dialog .changelog-container {
color: var(--theia-dropdown-foreground);
background-color: var(--theia-dropdown-background);
border: 1px solid var(--theia-tree-indentGuidesStroke);
color: var(--theia-editor-foreground);
background-color: var(--theia-editor-background);
border: 1px solid var(--theia-editorWidget-border);
border-radius: 2px;
font-size: 12px;
height: 180px;
@@ -39,7 +39,7 @@
}
.ide-updater-dialog .changelog-container a {
color: #018184;
color: var(--theia-textLink-foreground);
}
.ide-updater-dialog .changelog-container a:hover {
@@ -48,13 +48,13 @@
}
.ide-updater-dialog .changelog-container code {
background: #ecf1f1;
background: var(--theia-textBlockQuote-background);
border-radius: 2px;
padding: 0 2px;
}
.ide-updater-dialog .changelog-container a code {
color: #018184;
color: var(--theia-textLink-foreground);
}
.ide-updater-dialog .buttons-container {

View File

@@ -135,3 +135,23 @@ button.secondary[disabled], .theia-button.secondary[disabled] {
.fa-reload {
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;
}

View File

@@ -145,3 +145,14 @@ https://github.com/arduino/arduino-pro-ide/issues/82 */
.component-list-item a:hover {
text-decoration: underline;
}
/* 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:hover:before {
background-color: transparent;
outline: 1px dashed var(--theia-focusBorder);
}
.hc-black.hc-theia.theia-hc .component-list-item .header .installed:before {
border: 1px solid var(--theia-button-border);
}

View File

@@ -173,18 +173,73 @@
/* Output */
.theia-output .editor-container {
background-color: var(--theia-arduino-output-background);
background-color: var(--theia-terminal-background);
}
.theia-output .monaco-editor .lines-content.monaco-editor-background {
background-color: var(--theia-arduino-output-background);
background-color: var(--theia-terminal-background);
}
.theia-output .monaco-editor .lines-content.monaco-editor-background .view-lines .view-line .mtk1:not(.theia-output-error):not(.theia-output-warning) {
color: var(--theia-arduino-output-foreground);
color: var(--theia-terminal-foreground);
}
.theia-output .monaco-editor .margin {
border-right: none;
background-color: var(--theia-arduino-output-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;
}

View File

@@ -3,7 +3,7 @@
}
.progress-bar--outer {
background: #e5e5e5;
background: var(--theia-editorWidget-background);
border-radius: 11px;
height: 6px;
position: relative;
@@ -13,7 +13,7 @@
.progress-bar--inner {
transition: width 1s;
height: 100%;
background: #008184;
background: var(--theia-progressBar-background);
border-radius: 11px;
}

View File

@@ -6,6 +6,11 @@
#arduino-settings-dialog-container > .dialogBlock > .dialogContent {
justify-content: flex-start;
overflow: auto;
}
#arduino-settings-dialog-container > .dialogBlock > .dialogControl {
padding: 16px 0 26px;
}
.arduino-settings-dialog .content {
@@ -61,8 +66,19 @@
color: var(--theia-textLink-activeForeground);
}
.arduino-settings-dialog .react-tabs__tab--selected {
background: var(--theia-editorWidget-background);
border-color: var(--theia-tab-activeBorder);
color: var(--theia-tab-activeForeground);
border-radius: 5px 5px 0 0;
}
.arduino-settings-dialog .react-tabs__tab-list {
border-color: var(--theia-tab-activeBorder);
}
.arduino-settings-dialog .react-tabs__tab-panel {
padding-bottom: 25px;
padding-bottom: 8px;
}
.arduino-settings-dialog .react-tabs__tab-list {

View File

@@ -45,7 +45,7 @@
.active-sketch {
font-weight: 500;
background-color: var(--theia-list-activeSelectionBackground) !important;
color: var(--theia-list-inactiveSelectionForeground) !important;
color: var(--theia-list-activeSelectionForeground) !important;
}
@@ -69,4 +69,21 @@
.theia-Tree:focus .theia-TreeNode.theia-mod-selected,
.theia-Tree .ReactVirtualized__List:focus .theia-TreeNode.theia-mod-selected {
background: var(--theia-list-inactiveSelectionBackground);
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);
}

View File

@@ -14,7 +14,7 @@
}
.user-fields-dialog-content .field-label {
color: #2c353a;
color: var(--theia-editorWidget-foreground);
font-size: 14px;
font-style: normal;
font-weight: 400;
@@ -29,4 +29,4 @@
.user-fields-dialog-content .button-container {
justify-content: flex-end;
}
}

View File

@@ -10,28 +10,35 @@ import {
import {
ApplicationShell as TheiaApplicationShell,
DockPanel,
DockPanelRenderer as TheiaDockPanelRenderer,
Panel,
TabBar,
Widget,
SHELL_TABBAR_CONTEXT_MENU,
} from '@theia/core/lib/browser';
import { Sketch } from '../../../common/protocol';
import { SaveAsSketch } from '../../contributions/save-as-sketch';
import { CurrentSketch, SketchesServiceClientImpl } from '../../../common/protocol/sketches-service-client-impl';
import {
CurrentSketch,
SketchesServiceClientImpl,
} from '../../../common/protocol/sketches-service-client-impl';
import { nls } from '@theia/core/lib/common';
import URI from '@theia/core/lib/common/uri';
import { ToolbarAwareTabBar } from './tab-bars';
@injectable()
export class ApplicationShell extends TheiaApplicationShell {
@inject(CommandService)
protected readonly commandService: CommandService;
private readonly commandService: CommandService;
@inject(MessageService)
protected readonly messageService: MessageService;
private readonly messageService: MessageService;
@inject(SketchesServiceClientImpl)
protected readonly sketchesServiceClient: SketchesServiceClientImpl;
private readonly sketchesServiceClient: SketchesServiceClientImpl;
@inject(ConnectionStatusService)
protected readonly connectionStatusService: ConnectionStatusService;
private readonly connectionStatusService: ConnectionStatusService;
protected override track(widget: Widget): void {
super.track(widget);
@@ -43,7 +50,7 @@ export class ApplicationShell extends TheiaApplicationShell {
this.sketchesServiceClient.currentSketch().then((sketch) => {
if (CurrentSketch.isValid(sketch)) {
if (!this.isSketchFile(widget.editor.uri, sketch.uri)) {
return;
return;
}
if (Sketch.isInSketch(widget.editor.uri, sketch)) {
widget.title.closable = false;
@@ -54,11 +61,11 @@ export class ApplicationShell extends TheiaApplicationShell {
}
private isSketchFile(uri: URI, sketchUriString: string): boolean {
const sketchUri = new URI(sketchUriString);
if (uri.parent.isEqual(sketchUri)) {
return true;
}
return false;
const sketchUri = new URI(sketchUriString);
if (uri.parent.isEqual(sketchUri)) {
return true;
}
return false;
}
override async addWidget(
@@ -120,15 +127,41 @@ export class ApplicationShell extends TheiaApplicationShell {
}
}
export class DockPanelRenderer extends TheiaDockPanelRenderer {
override createTabBar(): TabBar<Widget> {
const renderer = this.tabBarRendererFactory();
// `ToolbarAwareTabBar` is from IDE2 and not from Theia. Check the imports.
const tabBar = new ToolbarAwareTabBar(
this.tabBarToolbarRegistry,
this.tabBarToolbarFactory,
this.breadcrumbsRendererFactory,
{
renderer,
// Scroll bar options
handlers: ['drag-thumb', 'keyboard', 'wheel', 'touch'],
useBothWheelAxes: true,
scrollXMarginOffset: 4,
suppressScrollY: true,
}
);
this.tabBarClasses.forEach((c) => tabBar.addClass(c));
renderer.tabBar = tabBar;
tabBar.disposed.connect(() => renderer.dispose());
renderer.contextMenuPath = SHELL_TABBAR_CONTEXT_MENU;
tabBar.currentChanged.connect(this.onCurrentTabChanged, this);
return tabBar;
}
}
const originalHandleEvent = DockPanel.prototype.handleEvent;
DockPanel.prototype.handleEvent = function (event) {
switch (event.type) {
case 'p-dragenter':
case 'p-dragleave':
case 'p-dragover':
case 'p-drop':
return;
case 'p-dragenter':
case 'p-dragleave':
case 'p-dragover':
case 'p-drop':
return;
}
originalHandleEvent.bind(this)(event);
};

View File

@@ -0,0 +1,10 @@
import { DefaultWindowService as TheiaDefaultWindowService } from '@theia/core/lib/browser/window/default-window-service';
import { ContainerModule } from '@theia/core/shared/inversify';
import { DefaultWindowService } from './default-window-service';
import { WindowServiceExt } from './window-service-ext';
export default new ContainerModule((bind, unbind, isBound, rebind) => {
bind(DefaultWindowService).toSelf().inSingletonScope();
rebind(TheiaDefaultWindowService).toService(DefaultWindowService);
bind(WindowServiceExt).toService(DefaultWindowService);
});

View File

@@ -13,6 +13,7 @@ import {
import { ArduinoDaemon } from '../../../common/protocol';
import { NotificationCenter } from '../../notification-center';
import { nls } from '@theia/core/lib/common';
import debounce = require('lodash.debounce');
@injectable()
export class FrontendConnectionStatusService extends TheiaFrontendConnectionStatusService {
@@ -36,10 +37,11 @@ export class FrontendConnectionStatusService extends TheiaFrontendConnectionStat
this.notificationCenter.onDaemonDidStop(
() => (this.connectedPort = undefined)
);
this.wsConnectionProvider.onIncomingMessageActivity(() => {
const refresh = debounce(() => {
this.updateStatus(!!this.connectedPort);
this.schedulePing();
});
}, this.options.offlineTimeout - 10);
this.wsConnectionProvider.onIncomingMessageActivity(() => refresh());
}
}

View File

@@ -0,0 +1,17 @@
import { DefaultWindowService as TheiaDefaultWindowService } from '@theia/core/lib/browser/window/default-window-service';
import { injectable } from '@theia/core/shared/inversify';
import { WindowServiceExt } from './window-service-ext';
@injectable()
export class DefaultWindowService
extends TheiaDefaultWindowService
implements WindowServiceExt
{
/**
* The default implementation always resolves to `true`.
* IDE2 does not use it. It's currently an electron-only app.
*/
async isFirstWindow(): Promise<boolean> {
return true;
}
}

View File

@@ -0,0 +1,13 @@
import { injectable } from '@theia/core/shared/inversify';
import { StatusBarImpl as TheiaStatusBarImpl } from '@theia/core/lib/browser';
@injectable()
export class StatusBarImpl extends TheiaStatusBarImpl {
override async removeElement(id: string): Promise<void> {
await this.ready;
if (this.entries.delete(id)) {
// Unlike Theia, IDE2 updates the status bar only if the element to remove was among the entries. Otherwise, it's a NOOP.
this.update();
}
}
}

View File

@@ -1,8 +1,13 @@
import { TabBar } from '@theia/core/shared/@phosphor/widgets';
import type { TabBar } from '@theia/core/shared/@phosphor/widgets';
import { Saveable } from '@theia/core/lib/browser/saveable';
import { TabBarRenderer as TheiaTabBarRenderer } from '@theia/core/lib/browser/shell/tab-bars';
import {
TabBarRenderer as TheiaTabBarRenderer,
ToolbarAwareTabBar as TheiaToolbarAwareTabBar,
} from '@theia/core/lib/browser/shell/tab-bars';
import debounce = require('lodash.debounce');
export class TabBarRenderer extends TheiaTabBarRenderer {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
override createTabClass(data: TabBar.IRenderData<any>): string {
let className = super.createTabClass(data);
if (!data.title.closable && Saveable.isDirty(data.title.owner)) {
@@ -16,3 +21,16 @@ export class TabBarRenderer extends TheiaTabBarRenderer {
// Context menus are empty, so they have been removed
};
}
export class ToolbarAwareTabBar extends TheiaToolbarAwareTabBar {
protected override async updateBreadcrumbs(): Promise<void> {
// NOOP
// IDE2 does not use breadcrumbs.
}
private readonly doUpdateToolbar = debounce(() => super.updateToolbar(), 500);
protected override updateToolbar(): void {
// Unlike Theia, IDE2 debounces the toolbar updates with 500ms
this.doUpdateToolbar();
}
}

View File

@@ -0,0 +1,7 @@
export const WindowServiceExt = Symbol('WindowServiceExt');
export interface WindowServiceExt {
/**
* Returns with a promise that resolves to `true` if the current window is the first window.
*/
isFirstWindow(): Promise<boolean>;
}

View File

@@ -1,7 +1,7 @@
import { inject, injectable } from '@theia/core/shared/inversify';
import URI from '@theia/core/lib/common/uri';
import { EditorWidget } from '@theia/editor/lib/browser';
import { LabelProvider } from '@theia/core/lib/browser';
import type { NavigatableWidgetOptions } from '@theia/core/lib/browser';
import { EditorWidgetFactory as TheiaEditorWidgetFactory } from '@theia/editor/lib/browser/editor-widget-factory';
import {
CurrentSketch,
@@ -13,16 +13,16 @@ import { nls } from '@theia/core/lib/common';
@injectable()
export class EditorWidgetFactory extends TheiaEditorWidgetFactory {
@inject(SketchesService)
protected readonly sketchesService: SketchesService;
private readonly sketchesService: SketchesService;
@inject(SketchesServiceClientImpl)
protected readonly sketchesServiceClient: SketchesServiceClientImpl;
private readonly sketchesServiceClient: SketchesServiceClientImpl;
@inject(LabelProvider)
protected override readonly labelProvider: LabelProvider;
protected override async createEditor(uri: URI): Promise<EditorWidget> {
const widget = await super.createEditor(uri);
protected override async createEditor(
uri: URI,
options: NavigatableWidgetOptions
): Promise<EditorWidget> {
const widget = await super.createEditor(uri, options);
return this.maybeUpdateCaption(widget);
}

View File

@@ -1,10 +1,15 @@
import { inject, injectable, postConstruct } from '@theia/core/shared/inversify';
import {
inject,
injectable,
postConstruct,
} from '@theia/core/shared/inversify';
import { Diagnostic } from 'vscode-languageserver-types';
import URI from '@theia/core/lib/common/uri';
import { ILogger } from '@theia/core';
import { Marker } from '@theia/markers/lib/common/marker';
import { ProblemManager as TheiaProblemManager } from '@theia/markers/lib/browser/problem/problem-manager';
import { ConfigService } from '../../../common/protocol/config-service';
import debounce = require('lodash.debounce');
@injectable()
export class ProblemManager extends TheiaProblemManager {
@@ -37,4 +42,12 @@ export class ProblemManager extends TheiaProblemManager {
}
return super.setMarkers(uri, owner, data);
}
private readonly debouncedFireOnDidChangeMakers = debounce(
(uri: URI) => this.onDidChangeMarkersEmitter.fire(uri),
500
);
protected override fireOnDidChangeMarkers(uri: URI): void {
this.debouncedFireOnDidChangeMakers(uri);
}
}

View File

@@ -1,12 +1,29 @@
import * as React from '@theia/core/shared/react';
import * as ReactDOM from '@theia/core/shared/react-dom';
import { injectable } from '@theia/core/shared/inversify';
import {
inject,
injectable,
postConstruct,
} from '@theia/core/shared/inversify';
import { NotificationCenterComponent } from './notification-center-component';
import { NotificationToastsComponent } from './notification-toasts-component';
import { NotificationsRenderer as TheiaNotificationsRenderer } from '@theia/messages/lib/browser/notifications-renderer';
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
@injectable()
export class NotificationsRenderer extends TheiaNotificationsRenderer {
@inject(FrontendApplicationStateService)
private readonly appStateService: FrontendApplicationStateService;
@postConstruct()
protected override init(): void {
// Unlike Theia, IDE2 renders the notification area only when the app is ready.
this.appStateService.reachedState('ready').then(() => {
this.createOverlayContainer();
this.render();
});
}
protected override render(): void {
ReactDOM.render(
<div>

View File

@@ -5,3 +5,11 @@
export function setURL(url: URL, data: any = {}): void {
history.pushState(data, '', url);
}
/**
* If available from the `window` object, then it means, the IDE2 has successfully patched the `MonacoThemingService#init` static method,
* and can wait the custom theme registration.
*/
export const MonacoThemeServiceIsReady = Symbol(
'@arduino-ide#monaco-theme-service-is-ready'
);

View File

@@ -5,6 +5,7 @@ import { CommandService } from '@theia/core/lib/common/command';
import { MessageService } from '@theia/core/lib/common/message-service';
import { ConfirmDialog } from '@theia/core/lib/browser/dialogs';
import { Searchable } from '../../../common/protocol/searchable';
import { ExecuteWithProgress } from '../../../common/protocol/progressible';
import { Installable } from '../../../common/protocol/installable';
import { ArduinoComponent } from '../../../common/protocol/arduino-component';
import { SearchBar } from './search-bar';
@@ -111,7 +112,7 @@ export class FilterableListContainer<
version: Installable.Version
): Promise<void> {
const { install, searchable } = this.props;
await Installable.doWithProgress({
await ExecuteWithProgress.doWithProgress({
...this.props,
progressText:
nls.localize('arduino/common/processing', 'Processing') +
@@ -137,7 +138,7 @@ export class FilterableListContainer<
return;
}
const { uninstall, searchable } = this.props;
await Installable.doWithProgress({
await ExecuteWithProgress.doWithProgress({
...this.props,
progressText:
nls.localize('arduino/common/processing', 'Processing') +

View File

@@ -1,7 +1,6 @@
import { ApplicationError } from '@theia/core/lib/common/application-error';
import type { Location } from '@theia/core/shared/vscode-languageserver-protocol';
import type {
Board,
BoardUserField,
Port,
} from '../../common/protocol/boards-service';
@@ -60,45 +59,39 @@ export namespace CoreError {
export const CoreServicePath = '/services/core-service';
export const CoreService = Symbol('CoreService');
export interface CoreService {
compile(
options: CoreService.Compile.Options &
Readonly<{
exportBinaries?: boolean;
compilerWarnings?: CompilerWarnings;
}>
): Promise<void>;
upload(options: CoreService.Upload.Options): Promise<void>;
uploadUsingProgrammer(options: CoreService.Upload.Options): Promise<void>;
burnBootloader(options: CoreService.Bootloader.Options): Promise<void>;
compile(options: CoreService.Options.Compile): Promise<void>;
upload(options: CoreService.Options.Upload): Promise<void>;
burnBootloader(options: CoreService.Options.Bootloader): Promise<void>;
}
export namespace CoreService {
export namespace Compile {
export interface Options {
export namespace Options {
export interface Base {
readonly fqbn?: string | undefined;
readonly verbose: boolean; // TODO: (API) why not optional with a default false?
readonly progressId?: string;
}
export interface SketchBased {
readonly sketch: Sketch;
readonly board?: Board;
readonly optimizeForDebug: boolean;
readonly verbose: boolean;
readonly sourceOverride: Record<string, string>;
}
}
export namespace Upload {
export interface Options extends Compile.Options {
export interface BoardBased {
readonly port?: Port;
readonly programmer?: Programmer | undefined;
readonly verify: boolean;
/**
* For the _Verify after upload_ setting.
*/
readonly verify: boolean; // TODO: (API) why not optional with false as the default value?
}
export interface Compile extends Base, SketchBased {
readonly optimizeForDebug: boolean; // TODO: (API) make this optional
readonly sourceOverride: Record<string, string>; // TODO: (API) make this optional
readonly exportBinaries?: boolean;
readonly compilerWarnings?: CompilerWarnings;
}
export interface Upload extends Base, SketchBased, BoardBased {
readonly userFields: BoardUserField[];
readonly usingProgrammer?: boolean;
}
}
export namespace Bootloader {
export interface Options {
readonly board?: Board;
readonly port?: Port;
readonly programmer?: Programmer | undefined;
readonly verbose: boolean;
readonly verify: boolean;
}
export interface Bootloader extends Base, BoardBased {}
}
}

View File

@@ -1,10 +1,6 @@
import * as semver from 'semver';
import type { Progress } from '@theia/core/lib/common/message-service-protocol';
import {
CancellationToken,
CancellationTokenSource,
} from '@theia/core/lib/common/cancellation';
import { naturalCompare } from './../utils';
import { ExecuteWithProgress } from './progressible';
import { naturalCompare } from '../utils';
import type { ArduinoComponent } from './arduino-component';
import type { MessageService } from '@theia/core/lib/common/message-service';
import type { ResponseServiceClient } from './response-service';
@@ -32,7 +28,7 @@ export namespace Installable {
/**
* Most recent version comes first, then the previous versions. (`1.8.1`, `1.6.3`, `1.6.2`, `1.6.1` and so on.)
*/
export const COMPARATOR = (left: Version, right: Version) => {
export const COMPARATOR = (left: Version, right: Version): number => {
if (semver.valid(left) && semver.valid(right)) {
return semver.compare(left, right);
}
@@ -50,7 +46,7 @@ export namespace Installable {
version: Installable.Version;
}): Promise<void> {
const { item, version } = options;
return doWithProgress({
return ExecuteWithProgress.doWithProgress({
...options,
progressText: `Processing ${item.name}:${version}`,
run: ({ progressId }) =>
@@ -71,7 +67,7 @@ export namespace Installable {
item: T;
}): Promise<void> {
const { item } = options;
return doWithProgress({
return ExecuteWithProgress.doWithProgress({
...options,
progressText: `Processing ${item.name}${
item.installedVersion ? `:${item.installedVersion}` : ''
@@ -83,51 +79,4 @@ export namespace Installable {
}),
});
}
export async function doWithProgress(options: {
run: ({ progressId }: { progressId: string }) => Promise<void>;
messageService: MessageService;
responseService: ResponseServiceClient;
progressText: string;
}): Promise<void> {
return withProgress(
options.progressText,
options.messageService,
async (progress, _) => {
const progressId = progress.id;
const toDispose = options.responseService.onProgressDidChange(
(progressMessage) => {
if (progressId === progressMessage.progressId) {
const { message, work } = progressMessage;
progress.report({ message, work });
}
}
);
try {
options.responseService.clearOutput();
await options.run({ progressId });
} finally {
toDispose.dispose();
}
}
);
}
async function withProgress(
text: string,
messageService: MessageService,
cb: (progress: Progress, token: CancellationToken) => Promise<void>
): Promise<void> {
const cancellationSource = new CancellationTokenSource();
const { token } = cancellationSource;
const progress = await messageService.showProgress(
{ text, options: { cancelable: false } },
() => cancellationSource.cancel()
);
try {
await cb(progress, token);
} finally {
progress.cancel();
}
}
}

View File

@@ -0,0 +1,60 @@
import type { CancellationToken } from '@theia/core/lib/common/cancellation';
import { CancellationTokenSource } from '@theia/core/lib/common/cancellation';
import type { MessageService } from '@theia/core/lib/common/message-service';
import type { Progress } from '@theia/core/lib/common/message-service-protocol';
import type { ResponseServiceClient } from './response-service';
export namespace ExecuteWithProgress {
export async function doWithProgress<T>(options: {
run: ({ progressId }: { progressId: string }) => Promise<T>;
messageService: MessageService;
responseService: ResponseServiceClient;
progressText: string;
keepOutput?: boolean;
}): Promise<T> {
return withProgress(
options.progressText,
options.messageService,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
async (progress, _token) => {
const progressId = progress.id;
const toDispose = options.responseService.onProgressDidChange(
(progressMessage) => {
if (progressId === progressMessage.progressId) {
const { message, work } = progressMessage;
progress.report({ message, work });
}
}
);
try {
if (!options.keepOutput) {
options.responseService.clearOutput();
}
const result = await options.run({ progressId });
return result;
} finally {
toDispose.dispose();
}
}
);
}
async function withProgress<T>(
text: string,
messageService: MessageService,
cb: (progress: Progress, token: CancellationToken) => Promise<T>
): Promise<T> {
const cancellationSource = new CancellationTokenSource();
const { token } = cancellationSource;
const progress = await messageService.showProgress(
{ text, options: { cancelable: false } },
() => cancellationSource.cancel()
);
try {
const result = await cb(progress, token);
return result;
} finally {
progress.cancel();
}
}
}

View File

@@ -46,5 +46,5 @@ export interface ResponseService {
export const ResponseServiceClient = Symbol('ResponseServiceClient');
export interface ResponseServiceClient extends ResponseService {
onProgressDidChange: Event<ProgressMessage>;
clearOutput: () => void;
clearOutput: () => void; // TODO: this should not belong here.
}

View File

@@ -1,14 +1,7 @@
import { ContainerModule } from '@theia/core/shared/inversify';
import { WindowService } from '@theia/core/lib/browser/window/window-service';
import { ElectronMainMenuFactory as TheiaElectronMainMenuFactory } from '@theia/core/lib/electron-browser/menu/electron-main-menu-factory';
import { ElectronMenuContribution as TheiaElectronMenuContribution } from '@theia/core/lib/electron-browser/menu/electron-menu-contribution';
import { ElectronIpcConnectionProvider } from '@theia/core/lib/electron-browser/messaging/electron-ipc-connection-provider';
import {
SplashService,
splashServicePath,
} from '../../../electron-common/splash-service';
import { MainMenuManager } from '../../../common/main-menu-manager';
import { ElectronWindowService } from '../../electron-window-service';
import { ElectronMainMenuFactory } from './electron-main-menu-factory';
import { ElectronMenuContribution } from './electron-menu-contribution';
import { nls } from '@theia/core/lib/common/nls';
@@ -16,39 +9,31 @@ import { nls } from '@theia/core/lib/common/nls';
import * as remote from '@theia/core/electron-shared/@electron/remote';
import * as dialogs from '@theia/core/lib/browser/dialogs';
Object.assign(dialogs, {
confirmExit: async () => {
const messageBoxResult = await remote.dialog.showMessageBox(
remote.getCurrentWindow(),
{
message: nls.localize('theia/core/quitMessage', 'Any unsaved changes will not be saved.'),
title: nls.localize('theia/core/quitTitle', 'Are you sure you want to quit?'),
message: nls.localize(
'theia/core/quitMessage',
'Any unsaved changes will not be saved.'
),
title: nls.localize(
'theia/core/quitTitle',
'Are you sure you want to quit?'
),
type: 'question',
buttons: [
dialogs.Dialog.CANCEL,
dialogs.Dialog.YES,
],
buttons: [dialogs.Dialog.CANCEL, dialogs.Dialog.YES],
}
)
);
return messageBoxResult.response === 1;
}
},
});
export default new ContainerModule((bind, unbind, isBound, rebind) => {
bind(ElectronMenuContribution).toSelf().inSingletonScope();
bind(MainMenuManager).toService(ElectronMenuContribution);
rebind(TheiaElectronMenuContribution).to(ElectronMenuContribution);
rebind(TheiaElectronMenuContribution).toService(ElectronMenuContribution);
bind(ElectronMainMenuFactory).toSelf().inSingletonScope();
rebind(TheiaElectronMainMenuFactory).toService(ElectronMainMenuFactory);
bind(ElectronWindowService).toSelf().inSingletonScope();
rebind(WindowService).toService(ElectronWindowService);
bind(SplashService)
.toDynamicValue((context) =>
ElectronIpcConnectionProvider.createProxy(
context.container,
splashServicePath
)
)
.inSingletonScope();
});

View File

@@ -0,0 +1,23 @@
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 { 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';
export default new ContainerModule((bind, unbind, isBound, rebind) => {
bind(ElectronWindowService).toSelf().inSingletonScope();
rebind(WindowService).toService(ElectronWindowService);
bind(WindowServiceExt).toService(ElectronWindowService);
bind(ElectronMainWindowServiceExt)
.toDynamicValue(({ container }) =>
ElectronIpcConnectionProvider.createProxy(
container,
electronMainWindowServiceExtPath
)
)
.inSingletonScope();
});

View File

@@ -1,30 +1,34 @@
import { inject, injectable, postConstruct } from '@theia/core/shared/inversify';
import * as remote from '@theia/core/electron-shared/@electron/remote';
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
import {
ConnectionStatus,
ConnectionStatusService,
} from '@theia/core/lib/browser/connection-status-service';
import { ElectronWindowService as TheiaElectronWindowService } from '@theia/core/lib/electron-browser/window/electron-window-service';
import { SplashService } from '../electron-common/splash-service';
import { nls } from '@theia/core/lib/common';
import { ElectronWindowService as TheiaElectronWindowService } from '@theia/core/lib/electron-browser/window/electron-window-service';
import {
inject,
injectable,
postConstruct,
} from '@theia/core/shared/inversify';
import { WindowServiceExt } from '../../../browser/theia/core/window-service-ext';
import { ElectronMainWindowServiceExt } from '../../../electron-common/electron-main-window-service-ext';
@injectable()
export class ElectronWindowService extends TheiaElectronWindowService {
export class ElectronWindowService
extends TheiaElectronWindowService
implements WindowServiceExt
{
@inject(ConnectionStatusService)
protected readonly connectionStatusService: ConnectionStatusService;
private readonly connectionStatusService: ConnectionStatusService;
@inject(SplashService)
protected readonly splashService: SplashService;
@inject(FrontendApplicationStateService)
protected readonly appStateService: FrontendApplicationStateService;
@inject(ElectronMainWindowServiceExt)
private readonly mainWindowServiceExt: ElectronMainWindowServiceExt;
@postConstruct()
protected override init(): void {
this.appStateService
.reachedAnyState('initialized_layout')
.then(() => this.splashService.requestClose());
// NOOP
// Does not listen on Theia's `window.zoomLevel` changes.
// TODO: IDE2 must switch to the Theia preferences and drop the custom one.
}
protected shouldUnload(): boolean {
@@ -55,4 +59,15 @@ export class ElectronWindowService extends TheiaElectronWindowService {
});
return response === 0; // 'Yes', close the window.
}
private _firstWindow: boolean | undefined;
async isFirstWindow(): Promise<boolean> {
if (this._firstWindow === undefined) {
const windowId = remote.getCurrentWindow().id; // This is expensive and synchronous so we check it once per FE.
this._firstWindow = await this.mainWindowServiceExt.isFirstWindow(
windowId
);
}
return this._firstWindow;
}
}

View File

@@ -0,0 +1,7 @@
export const electronMainWindowServiceExtPath = '/services/electron-window-ext';
export const ElectronMainWindowServiceExt = Symbol(
'ElectronMainWindowServiceExt'
);
export interface ElectronMainWindowServiceExt {
isFirstWindow(windowId: number): Promise<boolean>;
}

View File

@@ -1,5 +0,0 @@
export const splashServicePath = '/services/splash-service';
export const SplashService = Symbol('SplashService');
export interface SplashService {
requestClose(): Promise<void>;
}

View File

@@ -1,31 +1,26 @@
import { ContainerModule } from '@theia/core/shared/inversify';
import { JsonRpcConnectionHandler } from '@theia/core/lib/common/messaging/proxy-factory';
import { ElectronConnectionHandler } from '@theia/core/lib/electron-common/messaging/electron-connection-handler';
import { ElectronMainWindowService } from '@theia/core/lib/electron-common/electron-main-window-service';
import { ElectronConnectionHandler } from '@theia/core/lib/electron-common/messaging/electron-connection-handler';
import {
ElectronMainApplication as TheiaElectronMainApplication,
ElectronMainApplicationContribution,
} from '@theia/core/lib/electron-main/electron-main-application';
import {
SplashService,
splashServicePath,
} from '../electron-common/splash-service';
import { SplashServiceImpl } from './splash/splash-service-impl';
import { ElectronMainApplication } from './theia/electron-main-application';
import { ElectronMainWindowServiceImpl } from './theia/electron-main-window-service';
import { TheiaElectronWindow as DefaultTheiaElectronWindow } from '@theia/core/lib/electron-main/theia-electron-window';
import { ContainerModule } from '@theia/core/shared/inversify';
import {
IDEUpdater,
IDEUpdaterClient,
IDEUpdaterPath,
} from '../common/protocol/ide-updater';
import { IDEUpdaterImpl } from './ide-updater/ide-updater-impl';
import { TheiaElectronWindow } from './theia/theia-electron-window';
import { TheiaElectronWindow as DefaultTheiaElectronWindow } from '@theia/core/lib/electron-main/theia-electron-window';
import { SurveyNotificationServiceImpl } from '../node/survey-service-impl';
import {
SurveyNotificationService,
SurveyNotificationServicePath,
} from '../common/protocol/survey-service';
ElectronMainWindowServiceExt,
electronMainWindowServiceExtPath,
} from '../electron-common/electron-main-window-service-ext';
import { ElectronMainWindowServiceExtImpl } from './electron-main-window-service-ext-impl';
import { IDEUpdaterImpl } from './ide-updater/ide-updater-impl';
import { ElectronMainApplication } from './theia/electron-main-application';
import { ElectronMainWindowServiceImpl } from './theia/electron-main-window-service';
import { TheiaElectronWindow } from './theia/theia-electron-window';
export default new ContainerModule((bind, unbind, isBound, rebind) => {
bind(ElectronMainApplication).toSelf().inSingletonScope();
@@ -34,17 +29,6 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
bind(ElectronMainWindowServiceImpl).toSelf().inSingletonScope();
rebind(ElectronMainWindowService).toService(ElectronMainWindowServiceImpl);
bind(SplashServiceImpl).toSelf().inSingletonScope();
bind(SplashService).toService(SplashServiceImpl);
bind(ElectronConnectionHandler)
.toDynamicValue(
(context) =>
new JsonRpcConnectionHandler(splashServicePath, () =>
context.container.get(SplashService)
)
)
.inSingletonScope();
// IDE updater bindings
bind(IDEUpdaterImpl).toSelf().inSingletonScope();
bind(IDEUpdater).toService(IDEUpdaterImpl);
@@ -67,19 +51,14 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
bind(TheiaElectronWindow).toSelf();
rebind(DefaultTheiaElectronWindow).toService(TheiaElectronWindow);
// Survey notification bindings
bind(SurveyNotificationServiceImpl).toSelf().inSingletonScope();
bind(SurveyNotificationService).toService(SurveyNotificationServiceImpl);
bind(ElectronMainApplicationContribution).toService(
SurveyNotificationService
);
bind(ElectronMainWindowServiceExt)
.to(ElectronMainWindowServiceExtImpl)
.inSingletonScope();
bind(ElectronConnectionHandler)
.toDynamicValue(
(context) =>
new JsonRpcConnectionHandler(SurveyNotificationServicePath, () =>
context.container.get<SurveyNotificationService>(
SurveyNotificationService
)
new JsonRpcConnectionHandler(electronMainWindowServiceExtPath, () =>
context.container.get(ElectronMainWindowServiceExt)
)
)
.inSingletonScope();

View File

@@ -0,0 +1,15 @@
import { inject, injectable } from '@theia/core/shared/inversify';
import { ElectronMainWindowServiceExt } from '../electron-common/electron-main-window-service-ext';
import { ElectronMainApplication } from './theia/electron-main-application';
@injectable()
export class ElectronMainWindowServiceExtImpl
implements ElectronMainWindowServiceExt
{
@inject(ElectronMainApplication)
private readonly app: ElectronMainApplication;
async isFirstWindow(windowId: number): Promise<boolean> {
return this.app.firstWindowId === windowId;
}
}

View File

@@ -1,158 +0,0 @@
/*
MIT License
Copyright (c) 2017 Troy McKinnon
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
// Copied from https://raw.githubusercontent.com/trodi/electron-splashscreen/2f5052a133be021cbf9a438d0ef4719cd1796b75/index.ts
/**
* Module handles configurable splashscreen to show while app is loading.
*/
import { Event } from '@theia/core/lib/common/event';
import { BrowserWindow } from 'electron';
/**
* When splashscreen was shown.
* @ignore
*/
let splashScreenTimestamp = 0;
/**
* Splashscreen is loaded and ready to show.
* @ignore
*/
let splashScreenReady = false;
/**
* Main window has been loading for a min amount of time.
* @ignore
*/
let slowStartup = false;
/**
* True when expected work is complete and we've closed splashscreen, else user prematurely closed splashscreen.
* @ignore
*/
let done = false;
/**
* Show splashscreen if criteria are met.
* @ignore
*/
const showSplash = () => {
if (splashScreen && splashScreenReady && slowStartup) {
splashScreen.show();
splashScreenTimestamp = Date.now();
}
};
/**
* Close splashscreen / show main screen. Ensure screen is visible for a min amount of time.
* @ignore
*/
const closeSplashScreen = (main: Electron.BrowserWindow, min: number): void => {
if (splashScreen) {
const timeout = min - (Date.now() - splashScreenTimestamp);
setTimeout(() => {
done = true;
if (splashScreen) {
splashScreen.isDestroyed() || splashScreen.close(); // Avoid `Error: Object has been destroyed` (#19)
splashScreen = null;
}
if (!main.isDestroyed()) {
main.show();
}
}, timeout);
}
};
/** `electron-splashscreen` config object. */
export interface Config {
/** Options for the window that is loading and having a splashscreen tied to. */
windowOpts: Electron.BrowserWindowConstructorOptions;
/**
* URL to the splashscreen template. This is the path to an `HTML` or `SVG` file.
* If you want to simply show a `PNG`, wrap it in an `HTML` file.
*/
templateUrl: string;
/**
* Full set of browser window options for the splashscreen. We override key attributes to
* make it look & feel like a splashscreen; the rest is up to you!
*/
splashScreenOpts: Electron.BrowserWindowConstructorOptions;
/** Number of ms the window will load before splashscreen appears (default: 500ms). */
delay?: number;
/** Minimum ms the splashscreen will be visible (default: 500ms). */
minVisible?: number;
/** Close window that is loading if splashscreen is closed by user (default: true). */
closeWindow?: boolean;
}
/**
* The actual splashscreen browser window.
* @ignore
*/
let splashScreen: Electron.BrowserWindow | null;
/**
* Initializes a splashscreen that will show/hide smartly (and handle show/hiding of main window).
* @param config - Configures splashscreen
* @returns {BrowserWindow} the main browser window ready for loading
*/
export const initSplashScreen = async (
config: Config,
windowFactory: (
options: Electron.BrowserViewConstructorOptions
) => Promise<BrowserWindow>,
onCloseRequested?: Event<void>
): Promise<BrowserWindow> => {
const xConfig: Required<Config> = {
windowOpts: config.windowOpts,
templateUrl: config.templateUrl,
splashScreenOpts: config.splashScreenOpts,
delay: config.delay ?? 500,
minVisible: config.minVisible ?? 500,
closeWindow: config.closeWindow ?? true,
};
xConfig.splashScreenOpts.center = true;
xConfig.splashScreenOpts.frame = false;
xConfig.windowOpts.show = false;
const window = await windowFactory(xConfig.windowOpts);
splashScreen = new BrowserWindow(xConfig.splashScreenOpts);
splashScreen.loadURL(`file://${xConfig.templateUrl}`);
xConfig.closeWindow &&
splashScreen.on('close', () => {
done || window.close();
});
// Splashscreen is fully loaded and ready to view.
splashScreen.webContents.on('did-finish-load', () => {
splashScreenReady = true;
showSplash();
});
// Startup is taking enough time to show a splashscreen.
setTimeout(() => {
slowStartup = true;
showSplash();
}, xConfig.delay);
if (onCloseRequested) {
onCloseRequested(() => closeSplashScreen(window, xConfig.minVisible));
} else {
window.webContents.on('did-finish-load', () => {
closeSplashScreen(window, xConfig.minVisible);
});
}
window.on('closed', () => closeSplashScreen(window, 0)); // XXX: close splash when main window is closed
return window;
};

View File

@@ -1,20 +0,0 @@
import { injectable } from '@theia/core/shared/inversify';
import { Event, Emitter } from '@theia/core/lib/common/event';
import { SplashService } from '../../electron-common/splash-service';
@injectable()
export class SplashServiceImpl implements SplashService {
protected requested = false;
protected readonly onCloseRequestedEmitter = new Emitter<void>();
get onCloseRequested(): Event<void> {
return this.onCloseRequestedEmitter.event;
}
async requestClose(): Promise<void> {
if (!this.requested) {
this.requested = true;
this.onCloseRequestedEmitter.fire();
}
}
}

View File

@@ -1,25 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<style>
.container {
width: auto;
text-align: center;
padding: 0px;
}
img {
max-width: 95%;
height: auto;
}
</style>
</head>
<body>
<div class="container">
<p><img src="splash.png"></p>
</div>
</body>
</html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 103 KiB

View File

@@ -1,18 +1,15 @@
import { inject, injectable } from '@theia/core/shared/inversify';
import { injectable } from '@theia/core/shared/inversify';
import {
app,
BrowserWindow,
BrowserWindowConstructorOptions,
contentTracing,
ipcMain,
screen,
Event as ElectronEvent,
} from '@theia/core/electron-shared/electron';
import { fork } from 'child_process';
import { AddressInfo } from 'net';
import { join, dirname } from 'path';
import * as fs from 'fs-extra';
import { initSplashScreen } from '../splash/splash-screen';
import { MaybePromise } from '@theia/core/lib/common/types';
import { ElectronSecurityToken } from '@theia/core/lib/electron-common/electron-token';
import { FrontendApplicationConfig } from '@theia/application-package/lib/application-props';
@@ -20,7 +17,6 @@ import {
ElectronMainApplication as TheiaElectronMainApplication,
ElectronMainExecutionParams,
} from '@theia/core/lib/electron-main/electron-main-application';
import { SplashServiceImpl } from '../splash/splash-service-impl';
import { URI } from '@theia/core/shared/vscode-uri';
import { Deferred } from '@theia/core/lib/common/promise-util';
import * as os from '@theia/core/lib/common/os';
@@ -42,14 +38,6 @@ interface WorkspaceOptions {
const WORKSPACES = 'workspaces';
/**
* Purely a dev thing. If you start the app with the `--nosplash` argument,
* then you won't have the splash screen (which is always on top :confused:) and can debug the app at startup.
* Note: if you start the app from VS Code with the `App (Electron)` config, the splash screen will be disabled.
*/
const APP_STARTED_WITH_NOSPLASH =
typeof process !== 'undefined' && process.argv.indexOf('--nosplash') !== -1;
/**
* If the app is started with `--open-devtools` argument, the `Dev Tools` will be opened.
*/
@@ -66,11 +54,9 @@ const APP_STARTED_WITH_CONTENT_TRACE =
@injectable()
export class ElectronMainApplication extends TheiaElectronMainApplication {
protected startup = false;
protected openFilePromise = new Deferred();
@inject(SplashServiceImpl)
protected readonly splashService: SplashServiceImpl;
private startup = false;
private _firstWindowId: number | undefined;
private openFilePromise = new Deferred();
override async start(config: FrontendApplicationConfig): Promise<void> {
// Explicitly set the app name to have better menu items on macOS. ("About", "Hide", and "Quit")
@@ -142,7 +128,7 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
})();
}
attachFileAssociations() {
private attachFileAssociations(): void {
// OSX: register open-file event
if (os.isOSX) {
app.on('open-file', async (event, uri) => {
@@ -158,7 +144,7 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
}
}
protected async isValidSketchPath(uri: string): Promise<boolean | undefined> {
private async isValidSketchPath(uri: string): Promise<boolean | undefined> {
return typeof uri === 'string' && (await fs.pathExists(uri));
}
@@ -201,7 +187,7 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
}
}
protected async launchFromArgs(
private async launchFromArgs(
params: ElectronMainExecutionParams
): Promise<boolean> {
// Copy to prevent manipulation of original array
@@ -223,7 +209,7 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
return false;
}
protected async openSketch(
private async openSketch(
workspace: WorkspaceOptions | string
): Promise<BrowserWindow> {
const options = await this.getLastWindowOptions();
@@ -257,7 +243,8 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
}
protected override getTitleBarStyle(
config: FrontendApplicationConfig
// eslint-disable-next-line @typescript-eslint/no-unused-vars
_config: FrontendApplicationConfig
): 'native' | 'custom' {
return 'native';
}
@@ -287,73 +274,17 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
super.onSecondInstance(event, argv, cwd);
}
/**
* Use this rather than creating `BrowserWindow` instances from scratch, since some security parameters need to be set, this method will do it.
*
* @param options
*/
override async createWindow(
asyncOptions: MaybePromise<TheiaBrowserWindowOptions> = this.getDefaultTheiaWindowOptions()
): Promise<BrowserWindow> {
let options = await asyncOptions;
options = this.avoidOverlap(options);
let electronWindow: BrowserWindow | undefined;
if (this.windows.size || APP_STARTED_WITH_NOSPLASH) {
electronWindow = await this.doCreateWindow(options);
} else {
const { bounds } = screen.getDisplayNearestPoint(
screen.getCursorScreenPoint()
);
const splashHeight = 450;
const splashWidth = 600;
const splashY = Math.floor(bounds.y + (bounds.height - splashHeight) / 2);
const splashX = Math.floor(bounds.x + (bounds.width - splashWidth) / 2);
const splashScreenOpts: BrowserWindowConstructorOptions = {
height: splashHeight,
width: splashWidth,
x: splashX,
y: splashY,
transparent: true,
alwaysOnTop: true,
focusable: false,
minimizable: false,
maximizable: false,
hasShadow: false,
resizable: false,
};
electronWindow = await initSplashScreen(
{
windowOpts: options,
templateUrl: join(
__dirname,
'..',
'..',
'..',
'src',
'electron-main',
'splash',
'static',
'splash.html'
),
delay: 0,
minVisible: 2000,
splashScreenOpts,
},
(windowOptions) => this.doCreateWindow(windowOptions),
this.splashService.onCloseRequested
);
}
return electronWindow;
}
private async doCreateWindow(
options: TheiaBrowserWindowOptions
): Promise<BrowserWindow> {
const electronWindow = await super.createWindow(options);
const electronWindow = await super.createWindow(asyncOptions);
if (APP_STARTED_WITH_DEV_TOOLS) {
electronWindow.webContents.openDevTools();
}
this.attachListenersToWindow(electronWindow);
if (this._firstWindowId === undefined) {
this._firstWindowId = electronWindow.id;
}
return electronWindow;
}
@@ -389,7 +320,7 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
});
event.newGuest = new BrowserWindow(options);
event.newGuest.setMenu(null);
event.newGuest?.on('closed', (e: any) => {
event.newGuest?.on('closed', () => {
electronWindow?.webContents.send('CLOSE_CHILD_WINDOW');
});
event.newGuest?.loadURL(url);
@@ -462,9 +393,9 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
}
}
protected closedWorkspaces: WorkspaceOptions[] = [];
private closedWorkspaces: WorkspaceOptions[] = [];
protected attachClosedWorkspace(window: BrowserWindow): void {
private attachClosedWorkspace(window: BrowserWindow): void {
// Since the `before-quit` event is only fired when closing the *last* window
// We need to keep track of recently closed windows/workspaces manually
window.on('close', () => {
@@ -522,4 +453,8 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
get browserWindows(): BrowserWindow[] {
return Array.from(this.windows.values()).map(({ window }) => window);
}
get firstWindowId(): number | undefined {
return this._firstWindowId;
}
}

View File

@@ -76,7 +76,7 @@ export class ArduinoFirmwareUploaderImpl implements ArduinoFirmwareUploader {
fqbn: firmware.board_fqbn,
};
try {
await this.monitorManager.notifyUploadStarted(board, port);
await this.monitorManager.notifyUploadStarted(board.fqbn, port);
output = await this.runCommand([
'firmware',
'flash',
@@ -90,7 +90,7 @@ export class ArduinoFirmwareUploaderImpl implements ArduinoFirmwareUploader {
} catch (e) {
throw e;
} finally {
await this.monitorManager.notifyUploadFinished(board, port);
await this.monitorManager.notifyUploadFinished(board.fqbn, port);
return output;
}
}

View File

@@ -82,7 +82,7 @@ import {
} from '../common/protocol/authentication-service';
import { ArduinoFirmwareUploaderImpl } from './arduino-firmware-uploader-impl';
import { PlotterBackendContribution } from './plotter/plotter-backend-contribution';
import { ArduinoLocalizationContribution } from './arduino-localization-contribution';
import { ArduinoLocalizationContribution } from './i18n/arduino-localization-contribution';
import { LocalizationContribution } from '@theia/core/lib/node/i18n/localization-contribution';
import { MonitorManagerProxyImpl } from './monitor-manager-proxy-impl';
import { MonitorManager, MonitorManagerName } from './monitor-manager';
@@ -102,6 +102,13 @@ import WebSocketProviderImpl from './web-socket/web-socket-provider-impl';
import { WebSocketProvider } from './web-socket/web-socket-provider';
import { ClangFormatter } from './clang-formatter';
import { FormatterPath } from '../common/protocol/formatter';
import { LocalizationBackendContribution } from './i18n/localization-backend-contribution';
import { LocalizationBackendContribution as TheiaLocalizationBackendContribution } from '@theia/core/lib/node/i18n/localization-backend-contribution';
import { SurveyNotificationServiceImpl } from './survey-service-impl';
import {
SurveyNotificationService,
SurveyNotificationServicePath,
} from '../common/protocol/survey-service';
export default new ContainerModule((bind, unbind, isBound, rebind) => {
bind(BackendApplication).toSelf().inSingletonScope();
@@ -395,4 +402,21 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
bind(BackendApplicationContribution).toService(PlotterBackendContribution);
bind(ArduinoLocalizationContribution).toSelf().inSingletonScope();
bind(LocalizationContribution).toService(ArduinoLocalizationContribution);
bind(LocalizationBackendContribution).toSelf().inSingletonScope();
rebind(TheiaLocalizationBackendContribution).toService(
LocalizationBackendContribution
);
// Survey notification bindings
// It's currently unused. https://github.com/arduino/arduino-ide/pull/1150
bind(SurveyNotificationServiceImpl).toSelf().inSingletonScope();
bind(SurveyNotificationService).toService(SurveyNotificationServiceImpl);
bind(ConnectionHandler)
.toDynamicValue(
({ container }) =>
new JsonRpcConnectionHandler(SurveyNotificationServicePath, () =>
container.get<SurveyNotificationService>(SurveyNotificationService)
)
)
.inSingletonScope();
});

View File

@@ -1,3 +1,4 @@
import * as os from 'os';
import { EnvVariablesServer } from '@theia/core/lib/common/env-variables';
import { MaybePromise } from '@theia/core/lib/common/types';
import { FileUri } from '@theia/core/lib/node/file-uri';
@@ -121,25 +122,39 @@ function toClangOptions(
return { UseTab: 'Never', TabWidth: 2 };
}
// See: https://releases.llvm.org/11.0.1/tools/clang/docs/ClangFormatStyleOptions.html
export function style({ TabWidth, UseTab }: ClangFormatOptions): string {
return JSON.stringify(styleJson({ TabWidth, UseTab })).replace(/\"/g, '\\"');
let styleArgument = JSON.stringify(styleJson({ TabWidth, UseTab })).replace(
/[\\"]/g,
'\\$&'
);
if (os.platform() === 'win32') {
// Windows command interpreter does not use backslash escapes. This causes the argument to have alternate quoted and
// unquoted sections.
// Special characters in the unquoted sections must be caret escaped.
const styleArgumentSplit = styleArgument.split('"');
for (let i = 1; i < styleArgumentSplit.length; i += 2) {
styleArgumentSplit[i] = styleArgumentSplit[i].replace(/[<>^|]/g, '^$&');
}
styleArgument = styleArgumentSplit.join('"');
}
return styleArgument;
}
function styleJson({
TabWidth,
UseTab,
}: ClangFormatOptions): Record<string, unknown> {
// Source: https://github.com/arduino/tooling-project-assets/tree/main/other/clang-format-configuration
return {
Language: 'Cpp',
// # LLVM is the default style setting, used when a configuration option is not set here
BasedOnStyle: 'LLVM',
AccessModifierOffset: -2,
AlignAfterOpenBracket: 'Align',
AlignConsecutiveAssignments: false,
AlignConsecutiveBitFields: false,
AlignConsecutiveDeclarations: false,
AlignConsecutiveMacros: false,
AlignArrayOfStructures: 'None',
AlignConsecutiveAssignments: 'None',
AlignConsecutiveBitFields: 'None',
AlignConsecutiveDeclarations: 'None',
AlignConsecutiveMacros: 'None',
AlignEscapedNewlines: 'DontAlign',
AlignOperands: 'Align',
AlignTrailingComments: true,
@@ -150,16 +165,18 @@ function styleJson({
AllowShortCaseLabelsOnASingleLine: true,
AllowShortEnumsOnASingleLine: true,
AllowShortFunctionsOnASingleLine: 'Empty',
AllowShortIfStatementsOnASingleLine: 'Always',
AllowShortIfStatementsOnASingleLine: 'AllIfsAndElse',
AllowShortLambdasOnASingleLine: 'Empty',
AllowShortLoopsOnASingleLine: true,
AlwaysBreakAfterDefinitionReturnType: 'None',
AlwaysBreakAfterReturnType: 'None',
AlwaysBreakBeforeMultilineStrings: false,
AlwaysBreakTemplateDeclarations: 'No',
AttributeMacros: ['__capability'],
BasedOnStyle: 'LLVM',
BinPackArguments: true,
BinPackParameters: true,
// # Only used when "BreakBeforeBraces" set to "Custom"
BitFieldColonSpacing: 'Both',
BraceWrapping: {
AfterCaseLabel: false,
AfterClass: false,
@@ -167,7 +184,7 @@ function styleJson({
AfterEnum: false,
AfterFunction: false,
AfterNamespace: false,
// #AfterObjCDeclaration:
AfterObjCDeclaration: false,
AfterStruct: false,
AfterUnion: false,
AfterExternBlock: false,
@@ -176,104 +193,143 @@ function styleJson({
BeforeLambdaBody: false,
BeforeWhile: false,
IndentBraces: false,
SplitEmptyFunction: false,
SplitEmptyRecord: false,
SplitEmptyNamespace: false,
SplitEmptyFunction: true,
SplitEmptyRecord: true,
SplitEmptyNamespace: true,
},
// # Java-specific
// #BreakAfterJavaFieldAnnotations:
BreakAfterJavaFieldAnnotations: false,
BreakBeforeBinaryOperators: 'NonAssignment',
BreakBeforeBraces: 'Attach',
BreakBeforeConceptDeclarations: false,
BreakBeforeInheritanceComma: false,
BreakBeforeTernaryOperators: true,
BreakConstructorInitializers: 'BeforeColon',
BreakConstructorInitializersBeforeComma: false,
BreakInheritanceList: 'BeforeColon',
BreakStringLiterals: false,
ColumnLimit: 0,
// # "" matches none
CommentPragmas: '',
CompactNamespaces: false,
ConstructorInitializerAllOnOneLineOrOnePerLine: true,
ConstructorInitializerAllOnOneLineOrOnePerLine: false,
ConstructorInitializerIndentWidth: 2,
ContinuationIndentWidth: 2,
Cpp11BracedListStyle: false,
DeriveLineEnding: true,
DerivePointerAlignment: true,
DisableFormat: false,
// # Docs say "Do not use this in config files". The default (LLVM 11.0.1) is "false".
// #ExperimentalAutoDetectBinPacking:
EmptyLineAfterAccessModifier: 'Leave',
EmptyLineBeforeAccessModifier: 'Leave',
ExperimentalAutoDetectBinPacking: false,
FixNamespaceComments: false,
ForEachMacros: [],
ForEachMacros: ['foreach', 'Q_FOREACH', 'BOOST_FOREACH'],
IfMacros: ['KJ_IF_MAYBE'],
IncludeBlocks: 'Preserve',
IncludeCategories: [],
// # "" matches none
IncludeCategories: [
{
Regex: '^"(llvm|llvm-c|clang|clang-c)/',
Priority: 2,
SortPriority: 0,
CaseSensitive: false,
},
{
Regex: '^(<|"(gtest|gmock|isl|json)/)',
Priority: 3,
SortPriority: 0,
CaseSensitive: false,
},
{ Regex: '.*', Priority: 1, SortPriority: 0, CaseSensitive: false },
],
IncludeIsMainRegex: '',
IncludeIsMainSourceRegex: '',
IndentAccessModifiers: false,
IndentCaseBlocks: true,
IndentCaseLabels: true,
IndentExternBlock: 'Indent',
IndentGotoLabels: false,
IndentPPDirectives: 'None',
IndentRequires: true,
IndentWidth: 2,
IndentWrappedFunctionNames: false,
InsertTrailingCommas: 'None',
// # Java-specific
// #JavaImportGroups:
// # JavaScript-specific
// #JavaScriptQuotes:
// #JavaScriptWrapImports
JavaScriptQuotes: 'Leave',
JavaScriptWrapImports: true,
KeepEmptyLinesAtTheStartOfBlocks: true,
LambdaBodyIndentation: 'Signature',
Language: 'Cpp',
MacroBlockBegin: '',
MacroBlockEnd: '',
// # Set to a large number to effectively disable
MaxEmptyLinesToKeep: 100000,
NamespaceIndentation: 'None',
NamespaceMacros: [],
// # Objective C-specific
// #ObjCBinPackProtocolList:
// #ObjCBlockIndentWidth:
// #ObjCBreakBeforeNestedBlockParam:
// #ObjCSpaceAfterProperty:
// #ObjCSpaceBeforeProtocolList
ObjCBinPackProtocolList: 'Auto',
ObjCBlockIndentWidth: 2,
ObjCBreakBeforeNestedBlockParam: true,
ObjCSpaceAfterProperty: false,
ObjCSpaceBeforeProtocolList: true,
PPIndentWidth: -1,
PackConstructorInitializers: 'BinPack',
PenaltyBreakAssignment: 1,
PenaltyBreakBeforeFirstCallParameter: 1,
PenaltyBreakComment: 1,
PenaltyBreakFirstLessLess: 1,
PenaltyBreakOpenParenthesis: 1,
PenaltyBreakString: 1,
PenaltyBreakTemplateDeclaration: 1,
PenaltyExcessCharacter: 1,
PenaltyIndentedWhitespace: 1,
PenaltyReturnTypeOnItsOwnLine: 1,
// # Used as a fallback if alignment style can't be detected from code (DerivePointerAlignment: true)
PointerAlignment: 'Right',
RawStringFormats: [],
QualifierAlignment: 'Leave',
ReferenceAlignment: 'Pointer',
ReflowComments: false,
SortIncludes: false,
RemoveBracesLLVM: false,
SeparateDefinitionBlocks: 'Leave',
ShortNamespaceLines: 0,
SortIncludes: 'Never',
SortJavaStaticImport: 'Before',
SortUsingDeclarations: false,
SpaceAfterCStyleCast: false,
SpaceAfterLogicalNot: false,
SpaceAfterTemplateKeyword: false,
SpaceAroundPointerQualifiers: 'Default',
SpaceBeforeAssignmentOperators: true,
SpaceBeforeCaseColon: false,
SpaceBeforeCpp11BracedList: false,
SpaceBeforeCtorInitializerColon: true,
SpaceBeforeInheritanceColon: true,
SpaceBeforeParens: 'ControlStatements',
SpaceBeforeParensOptions: {
AfterControlStatements: true,
AfterForeachMacros: true,
AfterFunctionDefinitionName: false,
AfterFunctionDeclarationName: false,
AfterIfMacros: true,
AfterOverloadedOperator: false,
BeforeNonEmptyParentheses: false,
},
SpaceBeforeRangeBasedForLoopColon: true,
SpaceBeforeSquareBrackets: false,
SpaceInEmptyBlock: false,
SpaceInEmptyParentheses: false,
SpacesBeforeTrailingComments: 2,
SpacesInAngles: false,
SpacesInAngles: 'Leave',
SpacesInCStyleCastParentheses: false,
SpacesInConditionalStatement: false,
SpacesInContainerLiterals: false,
SpacesInLineCommentPrefix: { Minimum: 0, Maximum: -1 },
SpacesInParentheses: false,
SpacesInSquareBrackets: false,
Standard: 'Auto',
StatementMacros: [],
StatementAttributeLikeMacros: ['Q_EMIT'],
StatementMacros: ['Q_UNUSED', 'QT_REQUIRE_VERSION'],
TabWidth,
TypenameMacros: [],
// # Default to LF if line endings can't be detected from the content (DeriveLineEnding).
UseCRLF: false,
UseTab,
WhitespaceSensitiveMacros: [],
WhitespaceSensitiveMacros: [
'STRINGIZE',
'PP_STRINGIZE',
'BOOST_PP_STRINGIZE',
'NS_SWIFT_NAME',
'CF_SWIFT_NAME',
],
};
}

View File

@@ -23,7 +23,7 @@ import {
UploadUsingProgrammerResponse,
} from './cli-protocol/cc/arduino/cli/commands/v1/upload_pb';
import { ResponseService } from '../common/protocol/response-service';
import { Board, OutputMessage, Port, Status } from '../common/protocol';
import { OutputMessage, Port, Status } from '../common/protocol';
import { ArduinoCoreServiceClient } from './cli-protocol/cc/arduino/cli/commands/v1/commands_grpc_pb';
import { Port as GrpcPort } from './cli-protocol/cc/arduino/cli/commands/v1/port_pb';
import { ApplicationError, CommandService, Disposable, nls } from '@theia/core';
@@ -33,6 +33,12 @@ import { tryParseError } from './cli-error-parser';
import { Instance } from './cli-protocol/cc/arduino/cli/commands/v1/common_pb';
import { firstToUpperCase, notEmpty } from '../common/utils';
import { ServiceError } from './service-error';
import { ExecuteWithProgress, ProgressResponse } from './grpc-progressible';
namespace Uploadable {
export type Request = UploadRequest | UploadUsingProgrammerRequest;
export type Response = UploadResponse | UploadUsingProgrammerResponse;
}
@injectable()
export class CoreServiceImpl extends CoreClientAware implements CoreService {
@@ -45,27 +51,27 @@ export class CoreServiceImpl extends CoreClientAware implements CoreService {
@inject(CommandService)
private readonly commandService: CommandService;
async compile(
options: CoreService.Compile.Options & {
exportBinaries?: boolean;
compilerWarnings?: CompilerWarnings;
}
): Promise<void> {
async compile(options: CoreService.Options.Compile): Promise<void> {
const coreClient = await this.coreClient;
const { client, instance } = coreClient;
let buildPath: string | undefined = undefined;
const handler = this.createOnDataHandler<CompileResponse>((response) => {
const progressHandler = this.createProgressHandler(options);
const buildPathHandler = (response: CompileResponse) => {
const currentBuildPath = response.getBuildPath();
if (!buildPath && currentBuildPath) {
if (currentBuildPath) {
buildPath = currentBuildPath;
} else {
if (!!currentBuildPath && currentBuildPath !== buildPath) {
if (!!buildPath && currentBuildPath !== buildPath) {
throw new Error(
`The CLI has already provided a build path: <${buildPath}>, and there is a new build path value: <${currentBuildPath}>.`
`The CLI has already provided a build path: <${buildPath}>, and IDE2 received a new build path value: <${currentBuildPath}>.`
);
}
}
});
};
const handler = this.createOnDataHandler<CompileResponse>(
progressHandler,
buildPathHandler
);
const request = this.compileRequest(options, instance);
return new Promise<void>((resolve, reject) => {
client
@@ -132,20 +138,20 @@ export class CoreServiceImpl extends CoreClientAware implements CoreService {
}
private compileRequest(
options: CoreService.Compile.Options & {
options: CoreService.Options.Compile & {
exportBinaries?: boolean;
compilerWarnings?: CompilerWarnings;
},
instance: Instance
): CompileRequest {
const { sketch, board, compilerWarnings } = options;
const { sketch, fqbn, compilerWarnings } = options;
const sketchUri = sketch.uri;
const sketchPath = FileUri.fsPath(sketchUri);
const request = new CompileRequest();
request.setInstance(instance);
request.setSketchPath(sketchPath);
if (board?.fqbn) {
request.setFqbn(board.fqbn);
if (fqbn) {
request.setFqbn(fqbn);
}
if (compilerWarnings) {
request.setWarnings(compilerWarnings.toLowerCase());
@@ -163,56 +169,44 @@ export class CoreServiceImpl extends CoreClientAware implements CoreService {
return request;
}
upload(options: CoreService.Upload.Options): Promise<void> {
upload(options: CoreService.Options.Upload): Promise<void> {
const { usingProgrammer } = options;
return this.doUpload(
options,
() => new UploadRequest(),
(client, req) => client.upload(req),
(message: string, locations: CoreError.ErrorLocation[]) =>
CoreError.UploadFailed(message, locations),
'upload'
usingProgrammer
? new UploadUsingProgrammerRequest()
: new UploadRequest(),
(client) =>
(usingProgrammer ? client.uploadUsingProgrammer : client.upload).bind(
client
),
usingProgrammer
? CoreError.UploadUsingProgrammerFailed
: CoreError.UploadFailed,
`upload${usingProgrammer ? ' using programmer' : ''}`
);
}
async uploadUsingProgrammer(
options: CoreService.Upload.Options
): Promise<void> {
return this.doUpload(
options,
() => new UploadUsingProgrammerRequest(),
(client, req) => client.uploadUsingProgrammer(req),
(message: string, locations: CoreError.ErrorLocation[]) =>
CoreError.UploadUsingProgrammerFailed(message, locations),
'upload using programmer'
);
}
protected async doUpload(
options: CoreService.Upload.Options,
requestFactory: () => UploadRequest | UploadUsingProgrammerRequest,
responseHandler: (
client: ArduinoCoreServiceClient,
request: UploadRequest | UploadUsingProgrammerRequest
) => ClientReadableStream<UploadResponse | UploadUsingProgrammerResponse>,
errorHandler: (
message: string,
locations: CoreError.ErrorLocation[]
) => ApplicationError<number, CoreError.ErrorLocation[]>,
protected async doUpload<
REQ extends Uploadable.Request,
RESP extends Uploadable.Response
>(
options: CoreService.Options.Upload,
request: REQ,
responseFactory: (
client: ArduinoCoreServiceClient
) => (request: REQ) => ClientReadableStream<RESP>,
errorCtor: ApplicationError.Constructor<number, CoreError.ErrorLocation[]>,
task: string
): Promise<void> {
await this.compile(Object.assign(options, { exportBinaries: false }));
const coreClient = await this.coreClient;
const { client, instance } = coreClient;
const request = this.uploadOrUploadUsingProgrammerRequest(
options,
instance,
requestFactory
);
const handler = this.createOnDataHandler();
const progressHandler = this.createProgressHandler(options);
const handler = this.createOnDataHandler(progressHandler);
const grpcCall = responseFactory(client);
return this.notifyUploadWillStart(options).then(() =>
new Promise<void>((resolve, reject) => {
responseHandler(client, request)
grpcCall(this.initUploadRequest(request, options, instance))
.on('data', handler.onData)
.on('error', (error) => {
if (!ServiceError.is(error)) {
@@ -227,7 +221,7 @@ export class CoreServiceImpl extends CoreClientAware implements CoreService {
);
this.sendResponse(error.details, OutputMessage.Severity.Error);
reject(
errorHandler(
errorCtor(
message,
tryParseError({
content: handler.stderr,
@@ -245,18 +239,17 @@ export class CoreServiceImpl extends CoreClientAware implements CoreService {
);
}
private uploadOrUploadUsingProgrammerRequest(
options: CoreService.Upload.Options,
instance: Instance,
requestFactory: () => UploadRequest | UploadUsingProgrammerRequest
): UploadRequest | UploadUsingProgrammerRequest {
const { sketch, board, port, programmer } = options;
private initUploadRequest<REQ extends Uploadable.Request>(
request: REQ,
options: CoreService.Options.Upload,
instance: Instance
): REQ {
const { sketch, fqbn, port, programmer } = options;
const sketchPath = FileUri.fsPath(sketch.uri);
const request = requestFactory();
request.setInstance(instance);
request.setSketchPath(sketchPath);
if (board?.fqbn) {
request.setFqbn(board.fqbn);
if (fqbn) {
request.setFqbn(fqbn);
}
request.setPort(this.createPort(port));
if (programmer) {
@@ -271,10 +264,11 @@ export class CoreServiceImpl extends CoreClientAware implements CoreService {
return request;
}
async burnBootloader(options: CoreService.Bootloader.Options): Promise<void> {
async burnBootloader(options: CoreService.Options.Bootloader): Promise<void> {
const coreClient = await this.coreClient;
const { client, instance } = coreClient;
const handler = this.createOnDataHandler();
const progressHandler = this.createProgressHandler(options);
const handler = this.createOnDataHandler(progressHandler);
const request = this.burnBootloaderRequest(options, instance);
return this.notifyUploadWillStart(options).then(() =>
new Promise<void>((resolve, reject) => {
@@ -311,14 +305,14 @@ export class CoreServiceImpl extends CoreClientAware implements CoreService {
}
private burnBootloaderRequest(
options: CoreService.Bootloader.Options,
options: CoreService.Options.Bootloader,
instance: Instance
): BurnBootloaderRequest {
const { board, port, programmer } = options;
const { fqbn, port, programmer } = options;
const request = new BurnBootloaderRequest();
request.setInstance(instance);
if (board?.fqbn) {
request.setFqbn(board.fqbn);
if (fqbn) {
request.setFqbn(fqbn);
}
request.setPort(this.createPort(port));
if (programmer) {
@@ -329,8 +323,24 @@ export class CoreServiceImpl extends CoreClientAware implements CoreService {
return request;
}
private createProgressHandler<R extends ProgressResponse>(
options: CoreService.Options.Base
): (response: R) => void {
// If client did not provide the progress ID, do nothing.
if (!options.progressId) {
return () => {
/* NOOP */
};
}
return ExecuteWithProgress.createDataCallback<R>({
progressId: options.progressId,
responseService: this.responseService,
});
}
private createOnDataHandler<R extends StreamingResponse>(
onResponse?: (response: R) => void
// TODO: why not creating a composite handler with progress, `build_path`, and out/err stream handlers?
...handlers: ((response: R) => void)[]
): Disposable & {
stderr: Buffer[];
onData: (response: R) => void;
@@ -343,14 +353,14 @@ export class CoreServiceImpl extends CoreClientAware implements CoreService {
}
});
});
const onData = StreamingResponse.createOnDataHandler(
const onData = StreamingResponse.createOnDataHandler({
stderr,
(out, err) => {
onData: (out, err) => {
buffer.addChunk(out);
buffer.addChunk(err, OutputMessage.Severity.Error);
},
onResponse
);
handlers,
});
return {
dispose: () => buffer.dispose(),
stderr,
@@ -366,28 +376,28 @@ export class CoreServiceImpl extends CoreClientAware implements CoreService {
}
private async notifyUploadWillStart({
board,
fqbn,
port,
}: {
board?: Board | undefined;
fqbn?: string | undefined;
port?: Port | undefined;
}): Promise<void> {
return this.monitorManager.notifyUploadStarted(board, port);
return this.monitorManager.notifyUploadStarted(fqbn, port);
}
private async notifyUploadDidFinish({
board,
fqbn,
port,
}: {
board?: Board | undefined;
fqbn?: string | undefined;
port?: Port | undefined;
}): Promise<Status> {
return this.monitorManager.notifyUploadFinished(board, port);
return this.monitorManager.notifyUploadFinished(fqbn, port);
}
private mergeSourceOverrides(
req: { getSourceOverrideMap(): jspb.Map<string, string> },
options: CoreService.Compile.Options
options: CoreService.Options.Compile
): void {
const sketchPath = FileUri.fsPath(options.sketch.uri);
for (const uri of Object.keys(options.sourceOverride)) {
@@ -418,18 +428,24 @@ type StreamingResponse =
namespace StreamingResponse {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function createOnDataHandler<R extends StreamingResponse>(
stderr: Uint8Array[],
onData: (out: Uint8Array, err: Uint8Array) => void,
onResponse?: (response: R) => void
options: StreamingResponse.Options<R>
): (response: R) => void {
return (response: R) => {
const out = response.getOutStream_asU8();
const err = response.getErrStream_asU8();
stderr.push(err);
onData(out, err);
if (onResponse) {
onResponse(response);
}
options.stderr.push(err);
options.onData(out, err);
options.handlers?.forEach((handler) => handler(response));
};
}
export interface Options<R extends StreamingResponse> {
readonly stderr: Uint8Array[];
readonly onData: (out: Uint8Array, err: Uint8Array) => void;
/**
* Additional request handlers.
* For example, when tracing the progress of a task and
* collecting the output (out, err) and the `build_path` from the CLI.
*/
readonly handlers?: ((response: R) => void)[];
}
}

View File

@@ -12,6 +12,7 @@ import {
DownloadProgress,
TaskProgress,
} from './cli-protocol/cc/arduino/cli/commands/v1/common_pb';
import { CompileResponse } from './cli-protocol/cc/arduino/cli/commands/v1/compile_pb';
import {
PlatformInstallResponse,
PlatformUninstallResponse,
@@ -21,6 +22,11 @@ import {
LibraryUninstallResponse,
ZipLibraryInstallResponse,
} from './cli-protocol/cc/arduino/cli/commands/v1/lib_pb';
import {
BurnBootloaderResponse,
UploadResponse,
UploadUsingProgrammerResponse,
} from './cli-protocol/cc/arduino/cli/commands/v1/upload_pb';
type LibraryProgressResponse =
| LibraryInstallResponse
@@ -78,15 +84,62 @@ namespace IndexProgressResponse {
return { download: response.getDownloadProgress() };
}
}
/**
* These responses have neither `task` nor `progress` property but for the sake of completeness
* on typings (from the gRPC API) and UX, these responses represent an indefinite progress.
*/
type IndefiniteProgressResponse =
| UploadResponse
| UploadUsingProgrammerResponse
| BurnBootloaderResponse;
namespace IndefiniteProgressResponse {
export function is(
response: unknown
): response is IndefiniteProgressResponse {
return (
response instanceof UploadResponse ||
response instanceof UploadUsingProgrammerResponse ||
response instanceof BurnBootloaderResponse
);
}
}
type DefiniteProgressResponse = CompileResponse;
namespace DefiniteProgressResponse {
export function is(response: unknown): response is DefiniteProgressResponse {
return response instanceof CompileResponse;
}
}
type CoreProgressResponse =
| DefiniteProgressResponse
| IndefiniteProgressResponse;
namespace CoreProgressResponse {
export function is(response: unknown): response is CoreProgressResponse {
return (
DefiniteProgressResponse.is(response) ||
IndefiniteProgressResponse.is(response)
);
}
export function workUnit(response: CoreProgressResponse): UnitOfWork {
if (DefiniteProgressResponse.is(response)) {
return { task: response.getProgress() };
}
return UnitOfWork.Unknown;
}
}
export type ProgressResponse =
| LibraryProgressResponse
| PlatformProgressResponse
| IndexProgressResponse;
| IndexProgressResponse
| CoreProgressResponse;
interface UnitOfWork {
task?: TaskProgress;
download?: DownloadProgress;
}
namespace UnitOfWork {
export const Unknown: UnitOfWork = {};
}
/**
* It's solely a dev thing. Flip it to `true` if you want to debug the progress from the CLI responses.
@@ -115,14 +168,28 @@ export namespace ExecuteWithProgress {
console.log(`Progress response [${uuid}]: ${json}`);
}
}
const { task, download } = resolve(response);
const unitOfWork = resolve(response);
const { task, download } = unitOfWork;
if (!download && !task) {
console.warn(
"Implementation error. Neither 'download' nor 'task' is available."
);
// This is still an API error from the CLI, but IDE2 ignores it.
// Technically, it does not cause an error, but could mess up the progress reporting.
// See an example of an empty object `{}` repose here: https://github.com/arduino/arduino-ide/issues/906#issuecomment-1171145630.
// report a fake unknown progress.
if (unitOfWork === UnitOfWork.Unknown && progressId) {
if (progressId) {
responseService.reportProgress?.({
progressId,
message: '',
work: { done: Number.NaN, total: Number.NaN },
});
}
return;
}
if (DEBUG) {
// This is still an API error from the CLI, but IDE2 ignores it.
// Technically, it does not cause an error, but could mess up the progress reporting.
// See an example of an empty object `{}` repose here: https://github.com/arduino/arduino-ide/issues/906#issuecomment-1171145630.
console.warn(
"Implementation error. Neither 'download' nor 'task' is available."
);
}
return;
}
if (task && download) {
@@ -132,6 +199,7 @@ export namespace ExecuteWithProgress {
}
if (task) {
const message = task.getName() || task.getMessage();
const percent = task.getPercent();
if (message) {
if (progressId) {
responseService.reportProgress?.({
@@ -141,6 +209,14 @@ export namespace ExecuteWithProgress {
});
}
responseService.appendToOutput?.({ chunk: `${message}\n` });
} else if (percent) {
if (progressId) {
responseService.reportProgress?.({
progressId,
message,
work: { done: percent, total: 100 },
});
}
}
} else if (download) {
if (download.getFile() && !localFile) {
@@ -191,38 +267,38 @@ export namespace ExecuteWithProgress {
return PlatformProgressResponse.workUnit(response);
} else if (IndexProgressResponse.is(response)) {
return IndexProgressResponse.workUnit(response);
} else if (CoreProgressResponse.is(response)) {
return CoreProgressResponse.workUnit(response);
}
console.warn('Unhandled gRPC response', response);
return {};
}
function toJson(response: ProgressResponse): string | undefined {
let object: Record<string, unknown> | undefined = undefined;
if (response instanceof LibraryInstallResponse) {
return JSON.stringify(LibraryInstallResponse.toObject(false, response));
object = LibraryInstallResponse.toObject(false, response);
} else if (response instanceof LibraryUninstallResponse) {
return JSON.stringify(LibraryUninstallResponse.toObject(false, response));
object = LibraryUninstallResponse.toObject(false, response);
} else if (response instanceof ZipLibraryInstallResponse) {
return JSON.stringify(
ZipLibraryInstallResponse.toObject(false, response)
);
object = ZipLibraryInstallResponse.toObject(false, response);
} else if (response instanceof PlatformInstallResponse) {
return JSON.stringify(PlatformInstallResponse.toObject(false, response));
object = PlatformInstallResponse.toObject(false, response);
} else if (response instanceof PlatformUninstallResponse) {
return JSON.stringify(
PlatformUninstallResponse.toObject(false, response)
);
object = PlatformUninstallResponse.toObject(false, response);
} else if (response instanceof UpdateIndexResponse) {
return JSON.stringify(UpdateIndexResponse.toObject(false, response));
object = UpdateIndexResponse.toObject(false, response);
} else if (response instanceof UpdateLibrariesIndexResponse) {
return JSON.stringify(
UpdateLibrariesIndexResponse.toObject(false, response)
);
object = UpdateLibrariesIndexResponse.toObject(false, response);
} else if (response instanceof UpdateCoreLibrariesIndexResponse) {
return JSON.stringify(
UpdateCoreLibrariesIndexResponse.toObject(false, response)
);
object = UpdateCoreLibrariesIndexResponse.toObject(false, response);
} else if (response instanceof CompileResponse) {
object = CompileResponse.toObject(false, response);
}
console.warn('Unhandled gRPC response', response);
return undefined;
if (!object) {
console.warn('Unhandled gRPC response', response);
return undefined;
}
return JSON.stringify(object);
}
}

View File

@@ -11,142 +11,142 @@ export class ArduinoLocalizationContribution
async registerLocalizations(registry: LocalizationRegistry): Promise<void> {
registry.registerLocalizationFromRequire(
'af',
require('../../build/i18n/af.json')
require('../../../build/i18n/af.json')
);
registry.registerLocalizationFromRequire(
'en',
require('../../build/i18n/en.json')
require('../../../build/i18n/en.json')
);
registry.registerLocalizationFromRequire(
'fr',
require('../../build/i18n/fr.json')
require('../../../build/i18n/fr.json')
);
registry.registerLocalizationFromRequire(
'ko',
require('../../build/i18n/ko.json')
require('../../../build/i18n/ko.json')
);
registry.registerLocalizationFromRequire(
'pt-br',
require('../../build/i18n/pt.json')
require('../../../build/i18n/pt.json')
);
registry.registerLocalizationFromRequire(
'uk_UA',
require('../../build/i18n/uk_UA.json')
require('../../../build/i18n/uk_UA.json')
);
registry.registerLocalizationFromRequire(
'ar',
require('../../build/i18n/ar.json')
require('../../../build/i18n/ar.json')
);
registry.registerLocalizationFromRequire(
'es',
require('../../build/i18n/es.json')
require('../../../build/i18n/es.json')
);
registry.registerLocalizationFromRequire(
'he',
require('../../build/i18n/he.json')
require('../../../build/i18n/he.json')
);
registry.registerLocalizationFromRequire(
'my_MM',
require('../../build/i18n/my_MM.json')
require('../../../build/i18n/my_MM.json')
);
registry.registerLocalizationFromRequire(
'ro',
require('../../build/i18n/ro.json')
require('../../../build/i18n/ro.json')
);
registry.registerLocalizationFromRequire(
'zh-cn',
require('../../build/i18n/zh.json')
require('../../../build/i18n/zh.json')
);
registry.registerLocalizationFromRequire(
'bg',
require('../../build/i18n/bg.json')
require('../../../build/i18n/bg.json')
);
registry.registerLocalizationFromRequire(
'eu',
require('../../build/i18n/eu.json')
require('../../../build/i18n/eu.json')
);
registry.registerLocalizationFromRequire(
'hu',
require('../../build/i18n/hu.json')
require('../../../build/i18n/hu.json')
);
registry.registerLocalizationFromRequire(
'ne',
require('../../build/i18n/ne.json')
require('../../../build/i18n/ne.json')
);
registry.registerLocalizationFromRequire(
'ru',
require('../../build/i18n/ru.json')
require('../../../build/i18n/ru.json')
);
registry.registerLocalizationFromRequire(
'zh_TW',
require('../../build/i18n/zh_TW.json')
require('../../../build/i18n/zh_TW.json')
);
registry.registerLocalizationFromRequire(
'de',
require('../../build/i18n/de.json')
require('../../../build/i18n/de.json')
);
registry.registerLocalizationFromRequire(
'fa',
require('../../build/i18n/fa.json')
require('../../../build/i18n/fa.json')
);
registry.registerLocalizationFromRequire(
'it',
require('../../build/i18n/it.json')
require('../../../build/i18n/it.json')
);
registry.registerLocalizationFromRequire(
'nl',
require('../../build/i18n/nl.json')
require('../../../build/i18n/nl.json')
);
registry.registerLocalizationFromRequire(
'sv_SE',
require('../../build/i18n/sv_SE.json')
require('../../../build/i18n/sv_SE.json')
);
registry.registerLocalizationFromRequire(
'el',
require('../../build/i18n/el.json')
require('../../../build/i18n/el.json')
);
registry.registerLocalizationFromRequire(
'fil',
require('../../build/i18n/fil.json')
require('../../../build/i18n/fil.json')
);
registry.registerLocalizationFromRequire(
'ja',
require('../../build/i18n/ja.json')
require('../../../build/i18n/ja.json')
);
registry.registerLocalizationFromRequire(
'pl',
require('../../build/i18n/pl.json')
require('../../../build/i18n/pl.json')
);
registry.registerLocalizationFromRequire(
'tr',
require('../../build/i18n/tr.json')
require('../../../build/i18n/tr.json')
);
}
}

View File

@@ -0,0 +1,45 @@
import * as express from 'express';
import { inject, injectable } from '@theia/core/shared/inversify';
import { LocalizationBackendContribution as TheiaLocalizationBackendContribution } from '@theia/core/lib/node/i18n/localization-backend-contribution';
import { PluginDeployer } from '@theia/plugin-ext/lib/common/plugin-protocol';
import { PluginDeployerImpl } from '@theia/plugin-ext/lib/main/node/plugin-deployer-impl';
import { Deferred } from '@theia/core/lib/common/promise-util';
@injectable()
export class LocalizationBackendContribution extends TheiaLocalizationBackendContribution {
@inject(PluginDeployer)
private readonly pluginDeployer: PluginDeployerImpl;
private readonly initialized = new Deferred<void>();
override async initialize(): Promise<void> {
this.pluginDeployer.onDidDeploy(() => {
this.initialized.resolve();
});
return super.initialize();
}
override configure(app: express.Application): void {
app.get('/i18n/:locale', async (req, res) => {
let locale = req.params.locale;
/*
Waiting for the deploy of the language plugins is neecessary to avoid checking the available
languages before they're finished to be loaded: https://github.com/eclipse-theia/theia/issues/11471
*/
const start = performance.now();
await this.initialized.promise;
console.info(
'Waiting for the deploy of the language plugins took: ' +
(performance.now() - start) +
' ms.'
);
locale = this.localizationProvider
.getAvailableLanguages()
.some((e) => e.languageId === locale)
? locale
: 'en';
this.localizationProvider.setCurrentLanguage(locale);
res.send(this.localizationProvider.loadLocalization(locale));
});
}
}

View File

@@ -58,7 +58,7 @@ export class MonitorManager extends CoreClientAware {
* combination specified, false in all other cases.
*/
isStarted(board: Board, port: Port): boolean {
const monitorID = this.monitorID(board, port);
const monitorID = this.monitorID(board.fqbn, port);
const monitor = this.monitorServices.get(monitorID);
if (monitor) {
return monitor.isStarted();
@@ -106,7 +106,7 @@ export class MonitorManager extends CoreClientAware {
port: Port,
connectToClient: (status: Status) => void
): Promise<void> {
const monitorID = this.monitorID(board, port);
const monitorID = this.monitorID(board.fqbn, port);
let monitor = this.monitorServices.get(monitorID);
if (!monitor) {
@@ -138,7 +138,7 @@ export class MonitorManager extends CoreClientAware {
* @param port port monitored
*/
async stopMonitor(board: Board, port: Port): Promise<void> {
const monitorID = this.monitorID(board, port);
const monitorID = this.monitorID(board.fqbn, port);
const monitor = this.monitorServices.get(monitorID);
if (!monitor) {
// There's no monitor to stop, bail
@@ -155,7 +155,7 @@ export class MonitorManager extends CoreClientAware {
* @returns port of the MonitorService's WebSocket
*/
getWebsocketAddressPort(board: Board, port: Port): number {
const monitorID = this.monitorID(board, port);
const monitorID = this.monitorID(board.fqbn, port);
const monitor = this.monitorServices.get(monitorID);
if (!monitor) {
return -1;
@@ -168,17 +168,17 @@ export class MonitorManager extends CoreClientAware {
* that an upload process started on that exact board/port combination.
* This must be done so that we can stop the monitor for the time being
* until the upload process finished.
* @param board board connected to port
* @param fqbn the FQBN of the board connected to port
* @param port port to monitor
*/
async notifyUploadStarted(board?: Board, port?: Port): Promise<void> {
if (!board || !port) {
async notifyUploadStarted(fqbn?: string, port?: Port): Promise<void> {
if (!fqbn || !port) {
// We have no way of knowing which monitor
// to retrieve if we don't have this information.
return;
}
const monitorID = this.monitorID(board, port);
const monitorID = this.monitorID(fqbn, port);
this.addToMonitorIDsByUploadState('uploadInProgress', monitorID);
const monitor = this.monitorServices.get(monitorID);
@@ -194,19 +194,22 @@ export class MonitorManager extends CoreClientAware {
/**
* Notifies the monitor service of that board/port combination
* that an upload process started on that exact board/port combination.
* @param board board connected to port
* @param fqbn the FQBN of the board connected to port
* @param port port to monitor
* @returns a Status object to know if the process has been
* started or if there have been errors.
*/
async notifyUploadFinished(board?: Board, port?: Port): Promise<Status> {
async notifyUploadFinished(
fqbn?: string | undefined,
port?: Port
): Promise<Status> {
let status: Status = Status.NOT_CONNECTED;
let portDidChangeOnUpload = false;
// We have no way of knowing which monitor
// to retrieve if we don't have this information.
if (board && port) {
const monitorID = this.monitorID(board, port);
if (fqbn && port) {
const monitorID = this.monitorID(fqbn, port);
this.removeFromMonitorIDsByUploadState('uploadInProgress', monitorID);
const monitor = this.monitorServices.get(monitorID);
@@ -277,7 +280,7 @@ export class MonitorManager extends CoreClientAware {
port: Port,
settings: PluggableMonitorSettings
) {
const monitorID = this.monitorID(board, port);
const monitorID = this.monitorID(board.fqbn, port);
let monitor = this.monitorServices.get(monitorID);
if (!monitor) {
monitor = this.createMonitor(board, port);
@@ -296,7 +299,7 @@ export class MonitorManager extends CoreClientAware {
board: Board,
port: Port
): Promise<MonitorSettings> {
const monitorID = this.monitorID(board, port);
const monitorID = this.monitorID(board.fqbn, port);
const monitor = this.monitorServices.get(monitorID);
if (!monitor) {
return {};
@@ -312,7 +315,7 @@ export class MonitorManager extends CoreClientAware {
* @returns a new instance of MonitorService ready to use.
*/
private createMonitor(board: Board, port: Port): MonitorService {
const monitorID = this.monitorID(board, port);
const monitorID = this.monitorID(board.fqbn, port);
const monitor = this.monitorServiceFactory({
board,
port,
@@ -341,12 +344,12 @@ export class MonitorManager extends CoreClientAware {
/**
* Utility function to create a unique ID for a monitor service.
* @param board
* @param fqbn
* @param port
* @returns a unique monitor ID
*/
private monitorID(board: Board, port: Port): MonitorID {
const splitFqbn = board?.fqbn?.split(':') || [];
private monitorID(fqbn: string | undefined, port: Port): MonitorID {
const splitFqbn = fqbn?.split(':') || [];
const shortenedFqbn = splitFqbn.slice(0, 3).join(':') || '';
return `${shortenedFqbn}-${port.address}-${port.protocol}`;
}

View File

@@ -341,15 +341,7 @@ export class SketchesServiceImpl
async cloneExample(uri: string): Promise<Sketch> {
const sketch = await this.loadSketch(uri);
const parentPath = await new Promise<string>((resolve, reject) => {
temp.mkdir({ prefix }, (err, dirPath) => {
if (err) {
reject(err);
return;
}
resolve(dirPath);
});
});
const parentPath = await this.createTempFolder();
const destinationUri = FileUri.create(
path.join(parentPath, sketch.name)
).toString();
@@ -373,21 +365,7 @@ export class SketchesServiceImpl
'dec',
];
const today = new Date();
const parentPath = await new Promise<string>((resolve, reject) => {
temp.mkdir({ prefix }, (createError, dirPath) => {
if (createError) {
reject(createError);
return;
}
fs.realpath.native(dirPath, (resolveError, resolvedDirPath) => {
if (resolveError) {
reject(resolveError);
return;
}
resolve(resolvedDirPath);
});
});
});
const parentPath = await this.createTempFolder();
const sketchBaseName = `sketch_${
monthNames[today.getMonth()]
}${today.getDate()}`;
@@ -438,6 +416,30 @@ void loop() {
return this.loadSketch(FileUri.create(sketchDir).toString());
}
/**
* Creates a temp folder and returns with a promise that resolves with the canonicalized absolute pathname of the newly created temp folder.
* This method ensures that the file-system path pointing to the new temp directory is fully resolved.
* For example, on Windows, instead of getting an [8.3 filename](https://en.wikipedia.org/wiki/8.3_filename), callers will get a fully resolved path.
* `C:\\Users\\KITTAA~1\\AppData\\Local\\Temp\\.arduinoIDE-unsaved2022615-21100-iahybb.yyvh\\sketch_jul15a` will be `C:\\Users\\kittaakos\\AppData\\Local\\Temp\\.arduinoIDE-unsaved2022615-21100-iahybb.yyvh\\sketch_jul15a`
*/
private createTempFolder(): Promise<string> {
return new Promise<string>((resolve, reject) => {
temp.mkdir({ prefix }, (createError, dirPath) => {
if (createError) {
reject(createError);
return;
}
fs.realpath.native(dirPath, (resolveError, resolvedDirPath) => {
if (resolveError) {
reject(resolveError);
return;
}
resolve(resolvedDirPath);
});
});
});
}
async getSketchFolder(uri: string): Promise<Sketch | undefined> {
if (!uri) {
return undefined;
@@ -534,15 +536,7 @@ void loop() {
// `ncp` makes a recursion and copies the folders over and over again. In such cases, we copy the source into a temp folder,
// then move it to the desired destination.
const destination = FileUri.fsPath(destinationUri);
let tempDestination = await new Promise<string>((resolve, reject) => {
temp.track().mkdir({ prefix }, async (err, dirPath) => {
if (err) {
reject(err);
return;
}
resolve(dirPath);
});
});
let tempDestination = await this.createTempFolder();
tempDestination = path.join(tempDestination, sketch.name);
await fs.promises.mkdir(tempDestination, { recursive: true });
await copy(source, tempDestination);

View File

@@ -1,7 +1,7 @@
{
"private": true,
"name": "browser-app",
"version": "2.0.0-rc9",
"version": "2.0.0-rc9.2",
"license": "AGPL-3.0-or-later",
"dependencies": {
"@theia/core": "1.25.0",
@@ -19,7 +19,7 @@
"@theia/process": "1.25.0",
"@theia/terminal": "1.25.0",
"@theia/workspace": "1.25.0",
"arduino-ide-extension": "2.0.0-rc9"
"arduino-ide-extension": "2.0.0-rc9.2"
},
"devDependencies": {
"@theia/cli": "1.25.0"

View File

@@ -1,7 +1,7 @@
{
"private": true,
"name": "electron-app",
"version": "2.0.0-rc9",
"version": "2.0.0-rc9.2",
"license": "AGPL-3.0-or-later",
"main": "src-gen/frontend/electron-main.js",
"dependencies": {
@@ -21,7 +21,7 @@
"@theia/process": "1.25.0",
"@theia/terminal": "1.25.0",
"@theia/workspace": "1.25.0",
"arduino-ide-extension": "2.0.0-rc9"
"arduino-ide-extension": "2.0.0-rc9.2"
},
"devDependencies": {
"@theia/cli": "1.25.0",
@@ -61,7 +61,7 @@
},
"generator": {
"config": {
"preloadTemplate": "<div class='theia-preload' style='background-color: rgb(237, 241, 242);'></div>"
"preloadTemplate": "./resources/preload.html"
}
}
}

View File

@@ -0,0 +1,100 @@
// Patch for the startup theme. Customizes the `ThemeService.get().defaultTheme();` to dispatch the default IDE2 theme based on the OS' theme.
// For all subsequent starts of the IDE the theme applied will be the last one set by the user.
// With the current version of Theia adopted (1.25) it is not possible to extend the `ThemeService`, it will be possible starting from Theia 1.27.
// Once the version of Theia is updated, this patch will be removed and this functionality will be implemented via dependency injection.
// Ideally, we should open a PR in Theia and add support for `light` and `dark` default themes in the app config.
const {
ThemeService,
ThemeServiceSymbol,
BuiltinThemeProvider,
} = require('@theia/core/lib/browser/theming');
const {
ApplicationProps,
} = require('@theia/application-package/lib/application-props');
const {
FrontendApplicationConfigProvider,
} = require('@theia/core/lib/browser/frontend-application-config-provider');
// It is a mighty hack to support theme updates in the bundled IDE2.
// If the custom theme registration happens before the restoration of the existing monaco themes, then any custom theme changes will be ignored.
// This patch introduces a static deferred promise in the monaco-theming service that will be resolved when the restoration is ready.
// IDE2 cannot require the monaco theme service on the outer module level, as it requires the application config provider to be initialized,
// but the initialization happens only in the generated `index.js`.
// This patch customizes the monaco theme service behavior before loading the DI containers via the preload.
// The preload is called only once before the app loads. The Theia extensions are not loaded at that point, but the app config provider is ready.
const preloader = require('@theia/core/lib/browser/preloader');
const originalPreload = preloader.preload;
preloader.preload = async function () {
const { MonacoThemingService } = require('@theia/monaco/lib/browser/monaco-theming-service');
const { MonacoThemeServiceIsReady } = require('arduino-ide-extension/lib/browser/utils/window');
const { Deferred } = require('@theia/core/lib/common/promise-util');
const ready = new Deferred();
if (!window[MonacoThemeServiceIsReady]) {
window[MonacoThemeServiceIsReady] = ready;
console.log('Registered a custom monaco-theme service initialization signal on the window object.');
}
// Here, it is safe to patch the theme service, app config provider is ready.
MonacoThemingService.init = async function () {
this.updateBodyUiTheme();
ThemeService.get().onDidColorThemeChange(() => this.updateBodyUiTheme());
await this.restore();
ready.resolve();
}.bind(MonacoThemingService);
return originalPreload();
}.bind(preloader);
const lightTheme = 'arduino-theme';
const darkTheme = 'arduino-theme-dark';
const defaultTheme =
window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches
? darkTheme
: lightTheme;
const originalGet = FrontendApplicationConfigProvider.get;
FrontendApplicationConfigProvider.get = function () {
const originalProps = originalGet.bind(FrontendApplicationConfigProvider)();
return { ...originalProps, defaultTheme };
}.bind(FrontendApplicationConfigProvider);
const arduinoDarkTheme = {
id: 'arduino-theme-dark',
type: 'dark',
label: 'Dark (Arduino)',
editorTheme: 'arduino-theme-dark',
activate() {},
deactivate() {},
};
const arduinoLightTheme = {
id: 'arduino-theme',
type: 'light',
label: 'Light (Arduino)',
editorTheme: 'arduino-theme',
activate() {},
deactivate() {},
};
if (!window[ThemeServiceSymbol]) {
const themeService = new ThemeService();
Object.defineProperty(themeService, 'defaultTheme', {
get: function () {
return (
this.themes[defaultTheme] ||
this.themes[ApplicationProps.DEFAULT.frontend.config.defaultTheme]
);
},
});
themeService.register(
...BuiltinThemeProvider.themes,
arduinoDarkTheme,
arduinoLightTheme
);
themeService.startupTheme();
themeService.setCurrentTheme(defaultTheme);
window[ThemeServiceSymbol] = themeService;
}
// Require the original, generated `index.js` for `webpack` as the next entry for the `bundle.js`.
require('../../src-gen/frontend/index');

View File

@@ -0,0 +1,114 @@
<head>
<style>
/* The colors are hard-coded and based on the `editor.background` and `editor.foreground` values from `./arduino-ide-extension/src/browser/data/default.color-theme.json` */
@media (prefers-color-scheme: light) {
html {
background: #ffffff;
}
}
/* The colors are hard-coded and based on the `editor.background` and `editor.foreground` values from `./arduino-ide-extension/src/browser/data/dark.color-theme.json` */
@media (prefers-color-scheme: dark) {
html {
background: #1f272a;
}
}
.theia-preload {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
/* Above styles copied from https://github.com/eclipse-theia/theia/blob/5aeef6c0c683b4e91713ab736957e6655b486adc/packages/core/src/browser/style/index.css#L147-L161 */
/* Otherwise, there is a flickering when Theia's CSS loads. */
background-image: none;
}
.theia-preload::after {
/* remove default loading animation */
content: none;
}
.spinner-container {
display: flex;
flex-direction: center;
align-self: center;
justify-content: center;
height: 100vh;
width: 100vw;
}
.custom-spinner {
align-self: center;
}
.custom-spinner svg {
width: 16vw;
height: 16vh;
animation-delay: 0;
animation-duration: 2s;
animation-iteration-count: infinite;
animation-name: preload-spinner;
animation-timing-function: ease;
}
@keyframes preload-spinner {
0% {
transform: scale(1.0);
}
50% {
transform: scale(0.8);
}
100% {
transform: scale(1.0);
}
}
</style>
</head>
<body>
<div class='spinner-container'>
<div class='custom-spinner'>
<svg id="spinner" xmlns="http://www.w3.org/2000/svg" width="2499" height="2500"
viewBox="0 0 1372.201 1372.684">
<path fill="#00979D" stroke="#81C9CB" stroke-width=".932" stroke-miterlimit="10"
d="M1371.701 686.024c0 378.658-306.972 685.605-685.549 685.605C307.451 1371.629.5 1064.682.5 686.024.5 307.455 307.451.483 686.152.483c378.594.001 685.549 306.972 685.549 685.541z" />
<linearGradient id="a" gradientUnits="userSpaceOnUse" x1="-16.3" y1="16.071" x2="1354.901" y2="16.071"
gradientTransform="matrix(1 0 0 -1 16.8 702.696)">
<stop offset=".117" stop-color="#fff" stop-opacity="0" />
<stop offset=".252" stop-color="#c0d1d3" stop-opacity=".153" />
<stop offset=".387" stop-color="#91b3b7" stop-opacity=".306" />
<stop offset=".52" stop-color="#6d9fa3" stop-opacity=".457" />
<stop offset=".65" stop-color="#4d9195" stop-opacity=".604" />
<stop offset=".776" stop-color="#30888b" stop-opacity=".746" />
<stop offset=".895" stop-color="#148386" stop-opacity=".881" />
<stop offset="1" stop-color="#008184" />
</linearGradient>
<linearGradient id="b" gradientUnits="userSpaceOnUse" x1="-16.8" y1="16.071" x2="1355.401" y2="16.071"
gradientTransform="matrix(1 0 0 -1 16.8 702.696)">
<stop offset="0" stop-color="#fff" stop-opacity="0" />
<stop offset=".153" stop-color="#c0d1d3" stop-opacity=".153" />
<stop offset=".306" stop-color="#91b3b7" stop-opacity=".306" />
<stop offset=".457" stop-color="#6d9fa3" stop-opacity=".457" />
<stop offset=".604" stop-color="#4d9195" stop-opacity=".604" />
<stop offset=".746" stop-color="#30888b" stop-opacity=".746" />
<stop offset=".881" stop-color="#148386" stop-opacity=".881" />
<stop offset="1" stop-color="#008184" />
</linearGradient>
<path opacity=".5" fill="url(#a)" stroke="url(#b)" stroke-miterlimit="10"
d="M1371.701 686.595c0 378.65-306.972 685.606-685.549 685.606C307.451 1372.201.5 1065.23.5 686.595.5 308.019 307.451 1.048 686.152 1.048c378.594.016 685.549 306.97 685.549 685.547z" />
<g fill="#FFF">
<path
d="M947.959 931.196c-12.909 0-26.127-.929-39.127-2.864-108.978-15.554-181.848-93.822-222.665-153.989-40.946 60.166-113.811 138.512-222.74 154.045a275.864 275.864 0 0 1-39.133 2.785c-67.753 0-131.358-25.217-179.201-71.003-48.299-46.165-74.951-108.114-74.951-174.171 0-66.14 26.651-128.004 75.021-174.253 47.797-45.793 111.449-70.936 179.231-70.936 12.918 0 26.067.928 39.023 2.783 108.932 15.535 181.794 93.813 222.743 153.99 40.825-60.177 113.689-138.432 222.658-153.99 13-1.863 26.148-2.783 39.066-2.783 67.753 0 131.401 25.208 179.197 70.936 48.345 46.249 74.937 108.113 74.937 174.253 0 66.057-26.524 128.006-74.868 174.171-47.881 45.785-111.434 71.026-179.191 71.026M734.42 686.024c21.283 40.534 84.067 141.676 186.692 156.375 8.984 1.236 18.028 1.923 26.839 1.923 92.185 0 167.225-71.002 167.225-158.322s-75.023-158.321-167.291-158.321c-8.812 0-17.853.629-26.753 1.921-102.644 14.664-165.428 115.806-186.712 156.424M424.393 527.702c-92.308 0-167.36 70.998-167.36 158.321 0 87.305 75.021 158.322 167.245 158.322 8.852 0 17.897-.688 26.879-1.922 102.629-14.697 165.394-115.783 186.689-156.375-21.237-40.535-84.061-141.761-186.689-156.376-8.877-1.341-17.945-1.97-26.764-1.97" />
<path
d="M354.37 662.051h152.625v49.181H354.37zM1016.484 662.051h-51.671v-51.747h-49.348v51.747h-51.648v49.181h51.648v51.737h49.348v-51.737h51.671z" />
</g>
</svg>
</div>
</div>
</body>

View File

@@ -17,4 +17,10 @@ config.module.rules.push({
loader: require.resolve('@theia/application-manager/lib/expose-loader')
}); */
// Load the patched `index.js` that sets the desired theme in IDE2 based on the OS' theme.
// The `patch/frontend/index.js` will require the original, generated `index.js`.
// See: https://github.com/arduino/arduino-ide/pull/1160.
config.entry.bundle = require('path').resolve(__dirname, 'patch/frontend/index.js');
module.exports = config;

6
electron/.gitignore vendored
View File

@@ -15,4 +15,8 @@ dist/
electron-builder.env
# The generated `package.json` under the `build` folder.
build/package.json
build/package.json
# Resources the packager copies from dev to prod
build/resources/preload.html
build/patch/frontend/index.js

View File

@@ -1,4 +1,18 @@
// @ts-check
// Patch for on Linux when `XDG_CONFIG_HOME` is not available, `node-log-rotate` creates the folder with `undefined` name.
// See https://github.com/lemon-sour/node-log-rotate/issues/23 and https://github.com/arduino/arduino-ide/issues/394.
// If the IDE2 is running on Linux, and the `XDG_CONFIG_HOME` variable is not available, set it to avoid the `undefined` folder.
// From the specs: https://specifications.freedesktop.org/basedir-spec/latest/ar01s03.html
// "If $XDG_CONFIG_HOME is either not set or empty, a default equal to $HOME/.config should be used."
const os = require('os');
if (os.platform() === 'linux' && !process.env['XDG_CONFIG_HOME']) {
const { join } = require('path');
const home = process.env['HOME'];
const xdgConfigHome = home ? join(home, '.config') : join(os.homedir(), '.config');
process.env['XDG_CONFIG_HOME'] = xdgConfigHome;
}
const { setup, log } = require('node-log-rotate');
setup({
appName: 'Arduino IDE',

View File

@@ -1,59 +0,0 @@
// Patch for the startup theme. Customizes the `ThemeService.get().defaultTheme();` to dispatch the default IDE2 theme based on the OS' theme.
// For all subsequent starts of the IDE the theme applied will be the last one set by the user.
// With the current version of Theia adopted (1.25) it is not possible to extend the `ThemeService`, it will be possible starting from Theia 1.27.
// Once the version of Theia is updated, this patch will be removed and this functionality will be implemented via dependency injection.
// Ideally, we should open a PR in Theia and add support for `light` and `dark` default themes in the app config.
const {
ThemeService,
ThemeServiceSymbol,
BuiltinThemeProvider,
} = require('@theia/core/lib/browser/theming');
const {
ApplicationProps,
} = require('@theia/application-package/lib/application-props');
const lightTheme = 'arduino-theme';
const darkTheme = 'arduino-theme-dark';
const defaultTheme =
window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches
? darkTheme
: lightTheme;
const arduinoDarkTheme = {
id: 'arduino-theme-dark',
type: 'dark',
label: 'Dark (Arduino)',
editorTheme: 'arduino-theme-dark',
activate() { },
deactivate() { }
};
const arduinoLightTheme = {
id: 'arduino-theme',
type: 'light',
label: 'Light (Arduino)',
editorTheme: 'arduino-theme',
activate() { },
deactivate() { }
};
if (!window[ThemeServiceSymbol]) {
const themeService = new ThemeService();
Object.defineProperty(themeService, 'defaultTheme', {
get: function () {
return (
this.themes[defaultTheme] ||
this.themes[ApplicationProps.DEFAULT.frontend.config.defaultTheme]
);
},
});
themeService.register(...BuiltinThemeProvider.themes, arduinoDarkTheme, arduinoLightTheme);
themeService.startupTheme();
themeService.setCurrentTheme(defaultTheme);
window[ThemeServiceSymbol] = themeService;
}
// Require the original, generated `index.js` for `webpack` as the next entry for the `bundle.js`.
require('../../src-gen/frontend/index');

View File

@@ -141,7 +141,7 @@
"theiaPluginsDir": "plugins",
"theiaPlugins": {
"vscode-builtin-cpp": "https://open-vsx.org/api/vscode/cpp/1.52.1/file/vscode.cpp-1.52.1.vsix",
"vscode-arduino-tools": "https://downloads.arduino.cc/vscode-arduino-tools/vscode-arduino-tools-0.0.2-beta.4.vsix",
"vscode-arduino-tools": "https://downloads.arduino.cc/vscode-arduino-tools/vscode-arduino-tools-0.0.2-beta.5.vsix",
"vscode-builtin-json": "https://open-vsx.org/api/vscode/json/1.46.1/file/vscode.json-1.46.1.vsix",
"vscode-builtin-json-language-features": "https://open-vsx.org/api/vscode/json-language-features/1.46.1/file/vscode.json-language-features-1.46.1.vsix",
"cortex-debug": "https://open-vsx.org/api/marus25/cortex-debug/0.3.10/file/marus25.cortex-debug-0.3.10.vsix",

View File

@@ -7,6 +7,8 @@
const glob = require('glob');
const isCI = require('is-ci');
shell.env.THEIA_ELECTRON_SKIP_REPLACE_FFMPEG = '1'; // Do not run the ffmpeg validation for the packager.
// Note, this will crash on PI if the available memory is less than desired heap size.
// https://github.com/shelljs/shelljs/issues/1024#issuecomment-1001552543
shell.env.NODE_OPTIONS = '--max_old_space_size=4096'; // Increase heap size for the CI
shell.env.PUPPETEER_SKIP_CHROMIUM_DOWNLOAD = 'true'; // Skip download and avoid `ERROR: Failed to download Chromium`.
const template = require('./config').generateTemplate(
@@ -14,7 +16,7 @@
);
const utils = require('./utils');
const merge = require('deepmerge');
const { isRelease, isElectronPublish, getChannelFile } = utils;
const { isRelease, getChannelFile } = utils;
const { version } = template;
const { productName } = template.build;
@@ -58,6 +60,11 @@
.filter((filename) => resourcesToKeep.indexOf(filename) === -1)
.forEach((filename) => rm('-rf', path('..', 'build', filename)));
// Clean up the `./electron/build/patch` and `./electron/build/resources` folder with Git.
// To avoid file duplication between bundled app and dev mode, some files are copied from `./electron-app` to `./electron/build` folder.
const foldersToSyncFromDev = ['patch', 'resources'];
foldersToSyncFromDev.forEach(filename => shell.exec(`git -C ${path('..', 'build', filename)} clean -ffxdq`, { async: false }));
const extensions = require('./extensions.json');
echo(
`Building the application with the following extensions:\n${extensions
@@ -70,20 +77,28 @@
// Copy the following items into the `working-copy` folder. Make sure to reuse the `yarn.lock`. |
//----------------------------------------------------------------------------------------------+
mkdir('-p', path('..', workingCopy));
for (const name of [
for (const filename of [
...allDependencies,
'yarn.lock',
'package.json',
'lerna.json',
'i18n'
]) {
cp('-rf', path(rootPath, name), path('..', workingCopy));
cp('-rf', path(rootPath, filename), path('..', workingCopy));
}
//---------------------------------------------------------------------------------------------+
// Copy the patched `index.js` for the frontend, the Theia preload, etc. from `./electron-app` |
//---------------------------------------------------------------------------------------------+
for (const filename of foldersToSyncFromDev) {
cp('-rf', path('..', workingCopy, 'electron-app', filename), path('..', 'build'));
}
//----------------------------------------------+
// Sanity check: all versions must be the same. |
//----------------------------------------------+
verifyVersions(allDependencies);
//----------------------------------------------------------------------+
// Use the nightly patch version if not a release but requires publish. |
//----------------------------------------------------------------------+
@@ -438,6 +453,12 @@ ${fs.readFileSync(path('..', 'build', 'package.json')).toString()}
}
}
/**
* @param {import('fs').PathLike} file
* @param {string|undefined} [algorithm="sha512"]
* @param {BufferEncoding|undefined} [encoding="base64"]
* @param {object|undefined} [options]
*/
async function hashFile(
file,
algorithm = 'sha512',

View File

@@ -192,6 +192,7 @@
"visit": "Visit Arduino.cc"
},
"ide-updater": {
"checkForUpdates": "Check for Arduino IDE updates",
"closeAndInstallButton": "Close and Install",
"closeToInstallNotice": "Close the software and install the update on your machine.",
"downloadButton": "Aflaai",
@@ -253,7 +254,7 @@
"cloud.pull.warn": "True if users should be warned before pulling a cloud sketch. Defaults to true.",
"cloud.push.warn": "True if users should be warned before pushing a cloud sketch. Defaults to true.",
"cloud.pushpublic.warn": "True if users should be warned before pushing a public sketch to the cloud. Defaults to true.",
"cloud.sketchSyncEnpoint": "The endpoint used to push and pull sketches from a backend. By default it points to Arduino Cloud API.",
"cloud.sketchSyncEndpoint": "The endpoint used to push and pull sketches from a backend. By default it points to Arduino Cloud API.",
"compile": "saamstel",
"compile.experimental": "True if the IDE should handle multiple compiler errors. False by default",
"compile.revealRange": "Adjusts how compiler errors are revealed in the editor after a failed verify/upload. Possible values: 'auto': Scroll vertically as necessary and reveal a line. 'center': Scroll vertically as necessary and reveal a line centered vertically. 'top': Scroll vertically as necessary and reveal a line close to the top of the viewport, optimized for viewing a code definition. 'centerIfOutsideViewport': Scroll vertically as necessary and reveal a line centered vertically only if it lies outside the viewport. The default value is '{0}'.",
@@ -271,6 +272,7 @@
"invalid.sketchbook.location": "Invalid sketchbook location: {0}",
"invalid.theme": "Invalid theme.",
"language.log": "True if the Arduino Language Server should generate log files into the sketch folder. Otherwise, false. It's false by default.",
"language.realTimeDiagnostics": "If true, the language server provides real-time diagnostics when typing in the editor. It's false by default.",
"manualProxy": "Manual proxy configuration",
"network": "Netwerk",
"newSketchbookLocation": "Select new sketchbook location",
@@ -296,6 +298,7 @@
"newLineCarriageReturn": "Beide NL & CR",
"noLineEndings": "No Line Ending",
"notConnected": "Not connected. Select a board and a port to connect automatically.",
"openSerialPlotter": "Serial Plotter",
"timestamp": "Tydstempel",
"toggleTimestamp": "Toggle Timestamp"
},

View File

@@ -192,6 +192,7 @@
"visit": "زيارة Arduino.cc"
},
"ide-updater": {
"checkForUpdates": "Check for Arduino IDE updates",
"closeAndInstallButton": "قم بالاغلاق و التحديث",
"closeToInstallNotice": "اغلق البرمجية و حّدث الجهاز الخاص بك ",
"downloadButton": "حمّل",
@@ -253,7 +254,7 @@
"cloud.pull.warn": "True اذا كان يجب تحذير المستخدمين قبل سحب مشروع من السحابة . True افتراضيا",
"cloud.push.warn": "True اذا كان يجب تحذير المستخدمين قبل دفع مشروع الى السحابة . True افتراضيا",
"cloud.pushpublic.warn": "True اذا كان يجب تحذير المستخدمين قبل دفع مشروع عام الى السحابة . True افتراضيا",
"cloud.sketchSyncEnpoint": "الوجهة المستخدمة لدفع و سحب المشاريع من الخلفية . تشير افتراضيا الى Arduino Cloud API.",
"cloud.sketchSyncEndpoint": "الوجهة المستخدمة لدفع و سحب المشاريع من الخلفية . تشير افتراضيا الى Arduino Cloud API.",
"compile": "الترجمة",
"compile.experimental": "True if the IDE should handle multiple compiler errors. False by default",
"compile.revealRange": "Adjusts how compiler errors are revealed in the editor after a failed verify/upload. Possible values: 'auto': Scroll vertically as necessary and reveal a line. 'center': Scroll vertically as necessary and reveal a line centered vertically. 'top': Scroll vertically as necessary and reveal a line close to the top of the viewport, optimized for viewing a code definition. 'centerIfOutsideViewport': Scroll vertically as necessary and reveal a line centered vertically only if it lies outside the viewport. The default value is '{0}'.",
@@ -271,6 +272,7 @@
"invalid.sketchbook.location": "موقع ملف المشروع غير صالح : {0}",
"invalid.theme": "سمة غير صالحة",
"language.log": "\"True\" اذا كان مخدم اللغات الخاص بArduino يستطيع توليد سجلات الى ملف المشروع , و الا \"False\", و هي كذلك بشكل افتراضي.",
"language.realTimeDiagnostics": "If true, the language server provides real-time diagnostics when typing in the editor. It's false by default.",
"manualProxy": "اعدادات الوكيل يدوياً",
"network": "شبكة",
"newSketchbookLocation": "اختر مكان المشروع الجديد",
@@ -296,6 +298,7 @@
"newLineCarriageReturn": " NL & CR معاً",
"noLineEndings": "نهاية السطر غير موجودة",
"notConnected": "غير متصل . اختر لوحة و منفذ للاتصال تلقائيا",
"openSerialPlotter": "Serial Plotter",
"timestamp": "الطابع الزمني",
"toggleTimestamp": "تبديل الطابع الزمني"
},

View File

@@ -192,6 +192,7 @@
"visit": "Ardunio cc-yi Ziyarət Edin"
},
"ide-updater": {
"checkForUpdates": "Check for Arduino IDE updates",
"closeAndInstallButton": "Bağla Və Yüklə",
"closeToInstallNotice": "Close the software and install the update on your machine.",
"downloadButton": "Download",
@@ -253,7 +254,7 @@
"cloud.pull.warn": "True if users should be warned before pulling a cloud sketch. Defaults to true.",
"cloud.push.warn": "True if users should be warned before pushing a cloud sketch. Defaults to true.",
"cloud.pushpublic.warn": "True if users should be warned before pushing a public sketch to the cloud. Defaults to true.",
"cloud.sketchSyncEnpoint": "The endpoint used to push and pull sketches from a backend. By default it points to Arduino Cloud API.",
"cloud.sketchSyncEndpoint": "The endpoint used to push and pull sketches from a backend. By default it points to Arduino Cloud API.",
"compile": "compile",
"compile.experimental": "True if the IDE should handle multiple compiler errors. False by default",
"compile.revealRange": "Adjusts how compiler errors are revealed in the editor after a failed verify/upload. Possible values: 'auto': Scroll vertically as necessary and reveal a line. 'center': Scroll vertically as necessary and reveal a line centered vertically. 'top': Scroll vertically as necessary and reveal a line close to the top of the viewport, optimized for viewing a code definition. 'centerIfOutsideViewport': Scroll vertically as necessary and reveal a line centered vertically only if it lies outside the viewport. The default value is '{0}'.",
@@ -271,6 +272,7 @@
"invalid.sketchbook.location": "Invalid sketchbook location: {0}",
"invalid.theme": "Invalid theme.",
"language.log": "True if the Arduino Language Server should generate log files into the sketch folder. Otherwise, false. It's false by default.",
"language.realTimeDiagnostics": "If true, the language server provides real-time diagnostics when typing in the editor. It's false by default.",
"manualProxy": "Manual proxy configuration",
"network": "Network",
"newSketchbookLocation": "Select new sketchbook location",
@@ -296,6 +298,7 @@
"newLineCarriageReturn": "Both NL & CR",
"noLineEndings": "No Line Ending",
"notConnected": "Not connected. Select a board and a port to connect automatically.",
"openSerialPlotter": "Serial Plotter",
"timestamp": "Timestamp",
"toggleTimestamp": "Toggle Timestamp"
},

View File

@@ -192,6 +192,7 @@
"visit": "Посетете Arduino.cc"
},
"ide-updater": {
"checkForUpdates": "Check for Arduino IDE updates",
"closeAndInstallButton": "Close and Install",
"closeToInstallNotice": "Close the software and install the update on your machine.",
"downloadButton": "Download",
@@ -253,7 +254,7 @@
"cloud.pull.warn": "True е, ако потребителите трябва да бъдат предупредени, преди да се изтегли скица от облака. По подразбиране е true.",
"cloud.push.warn": "True, ако потребителите трябва да бъдат предупредени, преди да бъде пусната скица в облака. По подразбиране е true.",
"cloud.pushpublic.warn": "True, ако потребителите трябва да бъдат предупредени, преди да бъде изпратена публична скица в облака. По подразбиране е true.",
"cloud.sketchSyncEnpoint": "Крайната точка, използвана за изпращане и изтегляне на скици от бекенда. По подразбиране той сочи към Arduino Cloud API.",
"cloud.sketchSyncEndpoint": "Крайната точка, използвана за изпращане и изтегляне на скици от бекенда. По подразбиране той сочи към Arduino Cloud API.",
"compile": "компилиране",
"compile.experimental": "True if the IDE should handle multiple compiler errors. False by default",
"compile.revealRange": "Adjusts how compiler errors are revealed in the editor after a failed verify/upload. Possible values: 'auto': Scroll vertically as necessary and reveal a line. 'center': Scroll vertically as necessary and reveal a line centered vertically. 'top': Scroll vertically as necessary and reveal a line close to the top of the viewport, optimized for viewing a code definition. 'centerIfOutsideViewport': Scroll vertically as necessary and reveal a line centered vertically only if it lies outside the viewport. The default value is '{0}'.",
@@ -271,6 +272,7 @@
"invalid.sketchbook.location": "Невалидно местоположение на скицника: {0}",
"invalid.theme": "Невалидна тема.",
"language.log": "True, ако езиковият сървър на Arduino трябва да генерира лог файлове в папката за скици. В противен случай false. По подразбиране е false.",
"language.realTimeDiagnostics": "If true, the language server provides real-time diagnostics when typing in the editor. It's false by default.",
"manualProxy": "Ръчна конфигурация на прокси",
"network": "Мрежа",
"newSketchbookLocation": "Изберете местоположение за новата скицниката",
@@ -296,6 +298,7 @@
"newLineCarriageReturn": "Както NL, така и CR",
"noLineEndings": "Без край на реда",
"notConnected": "Няма връзка. Изберете платка и порт за автоматично свързване.",
"openSerialPlotter": "Serial Plotter",
"timestamp": "Печат за време",
"toggleTimestamp": "Превключване на клеймото за време"
},

View File

@@ -192,6 +192,7 @@
"visit": "Visiteu Arduino.cc"
},
"ide-updater": {
"checkForUpdates": "Check for Arduino IDE updates",
"closeAndInstallButton": "Tanca i instal·la",
"closeToInstallNotice": "Tanqueu el programari i instal·leu l'actualització a la vostra màquina.",
"downloadButton": "Descarregar",
@@ -253,7 +254,7 @@
"cloud.pull.warn": "És cert si s'ha d'avisar als usuaris abans de baixar un programa del núvol. El valor predeterminat és true.",
"cloud.push.warn": "És cert si s'ha d'avisar als usuaris abans d'enviar un programa del núvol. El valor predeterminat és true.",
"cloud.pushpublic.warn": "És cert si s'ha d'avisar als usuaris abans d'enviar un programa públic al núvol. El valor predeterminat és true.",
"cloud.sketchSyncEnpoint": "El punt final s'utilitza per a enviar i descarregar programes des d'un backend. Per defecte apunta a l'API d'Arduino Cloud.",
"cloud.sketchSyncEndpoint": "El punt final s'utilitza per a enviar i descarregar programes des d'un backend. Per defecte apunta a l'API d'Arduino Cloud.",
"compile": "compilar",
"compile.experimental": "True if the IDE should handle multiple compiler errors. False by default",
"compile.revealRange": "Adjusts how compiler errors are revealed in the editor after a failed verify/upload. Possible values: 'auto': Scroll vertically as necessary and reveal a line. 'center': Scroll vertically as necessary and reveal a line centered vertically. 'top': Scroll vertically as necessary and reveal a line close to the top of the viewport, optimized for viewing a code definition. 'centerIfOutsideViewport': Scroll vertically as necessary and reveal a line centered vertically only if it lies outside the viewport. The default value is '{0}'.",
@@ -271,6 +272,7 @@
"invalid.sketchbook.location": "La ubicació del quadern de programes no és vàlida: {0}",
"invalid.theme": "Tema no vàlid.",
"language.log": "És cert si el servidor d'idiomes Arduino hauria de generar fitxers de registre a la carpeta de programes. En cas contrari, fals. És fals per defecte.",
"language.realTimeDiagnostics": "If true, the language server provides real-time diagnostics when typing in the editor. It's false by default.",
"manualProxy": "Configuració manual del proxy",
"network": "Xarxa",
"newSketchbookLocation": "Seleccioneu una ubicació nova del quadern de programes",
@@ -296,6 +298,7 @@
"newLineCarriageReturn": "Ambdós NL & CR",
"noLineEndings": "Sense final de línia",
"notConnected": "No connectat. Seleccioneu una tarja i un port per connectar-vos automàticament.",
"openSerialPlotter": "Serial Plotter",
"timestamp": "Marca de temps",
"toggleTimestamp": "Activa o desactiva la marca de temps"
},

View File

@@ -192,6 +192,7 @@
"visit": "Navštívit Arduino.cc"
},
"ide-updater": {
"checkForUpdates": "Check for Arduino IDE updates",
"closeAndInstallButton": "Zavřít a nainstalovat",
"closeToInstallNotice": "Vypněte program a nainstalujte update na Váš stroj. ",
"downloadButton": "Stáhnout",
@@ -253,7 +254,7 @@
"cloud.pull.warn": "Ano pokud by měl být uživatel varován před stahováním cloud sketche. Ano je výchozí hodnota. ",
"cloud.push.warn": "Ano pokud by měl být uživatel varován před odesláním cloud sketche. Ano je výchozí hodnota. ",
"cloud.pushpublic.warn": "Ano pokud by měl být uživatel varován před odesláním veřejné sketche do cloudu. Ano je výchozí hodnota. ",
"cloud.sketchSyncEnpoint": "Endpoint použitý pro stahování a odesílání sketchí z backendu. Ve výchozím stavu je toto směrováno na Arduino Cloud API.",
"cloud.sketchSyncEndpoint": "Endpoint použitý pro stahování a odesílání sketchí z backendu. Ve výchozím stavu je toto směrováno na Arduino Cloud API.",
"compile": "kompilovat",
"compile.experimental": "True if the IDE should handle multiple compiler errors. False by default",
"compile.revealRange": "Adjusts how compiler errors are revealed in the editor after a failed verify/upload. Possible values: 'auto': Scroll vertically as necessary and reveal a line. 'center': Scroll vertically as necessary and reveal a line centered vertically. 'top': Scroll vertically as necessary and reveal a line close to the top of the viewport, optimized for viewing a code definition. 'centerIfOutsideViewport': Scroll vertically as necessary and reveal a line centered vertically only if it lies outside the viewport. The default value is '{0}'.",
@@ -271,6 +272,7 @@
"invalid.sketchbook.location": "Neplatné umístění projektů:{0}",
"invalid.theme": "Neplatné téma.",
"language.log": "Ano pokud by jazykový server pro Arduino měl generovat logovací soubory do složky se sketchi, jinak ne. Ne je výchozí hodnota.",
"language.realTimeDiagnostics": "If true, the language server provides real-time diagnostics when typing in the editor. It's false by default.",
"manualProxy": "Ruční nastavení proxy",
"network": "Síť",
"newSketchbookLocation": "Zvolit nové umístění projektů",
@@ -296,6 +298,7 @@
"newLineCarriageReturn": "Oba NL & CR",
"noLineEndings": "Bez konce řádku",
"notConnected": "Nepřipojen. Zvolte desku a port pro automatické připojení.",
"openSerialPlotter": "Serial Plotter",
"timestamp": "Časová značka",
"toggleTimestamp": "Přepnout časovou značku"
},

View File

@@ -192,6 +192,7 @@
"visit": "Besuche Arduino.cc"
},
"ide-updater": {
"checkForUpdates": "Check for Arduino IDE updates",
"closeAndInstallButton": "Schließen und Installieren",
"closeToInstallNotice": "Schließe die Software und installiere das Update auf deinem Computer",
"downloadButton": "Download",
@@ -253,7 +254,7 @@
"cloud.pull.warn": "Wahr, wenn Benutzer vor dem Herunterladen eines Sketches aus der Cloud gewarnt werden sollen. Standardmäßig Wahr.",
"cloud.push.warn": "Wahr, wenn Benutzer vor dem Hochladen eines Cloud-Sketches gewarnt werden sollen. Standardmäßig Wahr.",
"cloud.pushpublic.warn": "Wahr, wenn Benutzer vor dem Hochladen eines öffentlichen Sketches in die Cloud gewarnt werden sollen. Standardmäßig Wahr.",
"cloud.sketchSyncEnpoint": "Der Endpunkt, um Sketches zu/von einem Backend zu laden. Standardeinstellung ist die Arduino Cloud API.",
"cloud.sketchSyncEndpoint": "Der Endpunkt, um Sketches zu/von einem Backend zu laden. Standardeinstellung ist die Arduino Cloud API.",
"compile": "Kompilieren",
"compile.experimental": "True if the IDE should handle multiple compiler errors. False by default",
"compile.revealRange": "Adjusts how compiler errors are revealed in the editor after a failed verify/upload. Possible values: 'auto': Scroll vertically as necessary and reveal a line. 'center': Scroll vertically as necessary and reveal a line centered vertically. 'top': Scroll vertically as necessary and reveal a line close to the top of the viewport, optimized for viewing a code definition. 'centerIfOutsideViewport': Scroll vertically as necessary and reveal a line centered vertically only if it lies outside the viewport. The default value is '{0}'.",
@@ -271,6 +272,7 @@
"invalid.sketchbook.location": "Ungültiger Sketchbook Speicherort: {0}",
"invalid.theme": "Ungültiges Erscheinungsbild",
"language.log": "Wahr, wenn der Arduino Language Server Logfiles in den Sketch-Ordner schreiben soll. Sonst falsch. Standardeinstellung ist falsch.\n ",
"language.realTimeDiagnostics": "If true, the language server provides real-time diagnostics when typing in the editor. It's false by default.",
"manualProxy": "Manuelle Proxy Einstellung",
"network": "Netzwerk",
"newSketchbookLocation": "Wähle einen neuen Ort für das Sketchbook ",
@@ -296,6 +298,7 @@
"newLineCarriageReturn": "Sowohl NL als auch CR",
"noLineEndings": "Kein Zeilenende",
"notConnected": "Nicht verbunden. Wählen Sie ein Board und einen Port, um automatisch zu verbinden.",
"openSerialPlotter": "Serial Plotter",
"timestamp": "Zeitstempel",
"toggleTimestamp": "Zeitstempel an/aus"
},

View File

@@ -192,6 +192,7 @@
"visit": "Επίσκεψη Arduino.cc"
},
"ide-updater": {
"checkForUpdates": "Check for Arduino IDE updates",
"closeAndInstallButton": "Close and Install",
"closeToInstallNotice": "Close the software and install the update on your machine.",
"downloadButton": "Download",
@@ -253,7 +254,7 @@
"cloud.pull.warn": "Αληθές αν οι χρήστες πρέπει προειδοποιηθούν πριν τραβηχτεί ενα σχέδιο σύννεφου. Προεπιλογή ως αληθές.",
"cloud.push.warn": "Αληθές αν οι χρήστες πρέπει προειδοποιηθούν πριν σπρωχθεί ενα σχέδιο σύννεφου. Προεπιλογή ως αληθές. ",
"cloud.pushpublic.warn": "Αληθές αν οι χρήστες πρέπει προειδοποιηθούν πριν σπρωχθεί ενα δημόσιο σχέδιο σύννεφου. Προεπιλογή ως αληθές. ",
"cloud.sketchSyncEnpoint": "The endpoint used to push and pull sketches from a backend. By default it points to Arduino Cloud API.",
"cloud.sketchSyncEndpoint": "The endpoint used to push and pull sketches from a backend. By default it points to Arduino Cloud API.",
"compile": "μεταγλώττιση",
"compile.experimental": "True if the IDE should handle multiple compiler errors. False by default",
"compile.revealRange": "Adjusts how compiler errors are revealed in the editor after a failed verify/upload. Possible values: 'auto': Scroll vertically as necessary and reveal a line. 'center': Scroll vertically as necessary and reveal a line centered vertically. 'top': Scroll vertically as necessary and reveal a line close to the top of the viewport, optimized for viewing a code definition. 'centerIfOutsideViewport': Scroll vertically as necessary and reveal a line centered vertically only if it lies outside the viewport. The default value is '{0}'.",
@@ -271,6 +272,7 @@
"invalid.sketchbook.location": "Μη-έγκυρη τοποθεσία σχεδίων: {0}",
"invalid.theme": "Μη-έγκυρο θέμα.",
"language.log": "Αληθές αν ο Arduino Language Server πρέπει να παράξει αρχεία κατάστασης στον φάκελο σχεδίου. Διαφορετικά, ψευδές. Είναι ψευδές απο προεπιλογή.",
"language.realTimeDiagnostics": "If true, the language server provides real-time diagnostics when typing in the editor. It's false by default.",
"manualProxy": "Manual proxy configuration",
"network": "Δίκτυο",
"newSketchbookLocation": "Επιλογή νέας τοποθεσίας σχεδίων",
@@ -296,6 +298,7 @@
"newLineCarriageReturn": "Both NL & CR",
"noLineEndings": "No Line Ending",
"notConnected": "Not connected. Select a board and a port to connect automatically.",
"openSerialPlotter": "Serial Plotter",
"timestamp": "Timestamp",
"toggleTimestamp": "Toggle Timestamp"
},

View File

@@ -36,6 +36,7 @@
"boardsManager": "Boards Manager",
"bootloader": {
"burnBootloader": "Burn Bootloader",
"burningBootloader": "Burning bootloader...",
"doneBurningBootloader": "Done burning bootloader."
},
"burnBootloader": {
@@ -306,6 +307,7 @@
"archiveSketch": "Archive Sketch",
"cantOpen": "A folder named \"{0}\" already exists. Can't open sketch.",
"close": "Are you sure you want to close the sketch?",
"compile": "Compiling sketch...",
"configureAndUpload": "Configure And Upload",
"createdArchive": "Created archive '{0}'.",
"doneCompiling": "Done compiling.",
@@ -327,6 +329,7 @@
"titleSketchbook": "Sketchbook",
"upload": "Upload",
"uploadUsingProgrammer": "Upload Using Programmer",
"uploading": "Uploading...",
"userFieldsNotFoundError": "Can't find user fields for connected board",
"verify": "Verify",
"verifyOrCompile": "Verify/Compile"

View File

@@ -11,7 +11,7 @@
"configDialog2": "Si seleccionas solo una placa podrás compilar, pero no cargar tu sketch.",
"configDialogTitle": "Seleccione otra placa y puerto",
"couldNotFindPreviouslySelected": "No se ha podido encontrar la placa previamente seleccionada '{0}' en la plataforma instalada '{1}'. Por favor, vuelve a seleccionar manualmente la placa que quieres utilizar. ¿Quieres volver a seleccionarla ahora?",
"disconnected": "Disconnected",
"disconnected": "Desconectado",
"getBoardInfo": "Obtener información de la placa",
"inSketchbook": " (en el Sketchbook)",
"installManually": "Instalar manualmente",
@@ -23,7 +23,7 @@
"platformMissing": "La plataforma seleccionada para la placa '{0}' no está instalada.",
"pleasePickBoard": "Por favor, elija una placa conectada al puerto que haya seleccionado.",
"port": "Puerto {0}",
"portLabel": "Port: {0}",
"portLabel": "Puerto: {0}",
"programmer": "Programador",
"reselectLater": "Vuelve a seleccionar más tarde",
"selectBoard": "Seleccionar Placa",
@@ -59,11 +59,11 @@
"uploadingCertificates": "Cargando certificados."
},
"cli-error-parser": {
"keyboardError": "'Keyboard' not found. Does your sketch include the line '#include <Keyboard.h>'?",
"mouseError": "'Mouse' not found. Does your sketch include the line '#include <Mouse.h>'?"
"keyboardError": "'Keyboard' no encontrado. ¿Tiene tu proyecto incluida la linea '#include <Keyboard.h>'?",
"mouseError": "'Mouse' no encontrado. ¿Tiene tu proyecto incluida la linea '#include <Mouse.h>'?"
},
"cloud": {
"account": "Account",
"account": "Cuenta",
"chooseSketchVisibility": "Elige la visibilidad de tu Sketch:",
"connected": "Conectado",
"continue": "Continuar",
@@ -88,14 +88,14 @@
"pushSketch": "Pulsa para listar las URLs de la tarjetas no oficiales",
"pushSketchMsg": "Este es un Sketch público. Antes de enviarlo, asegúrate de que cualquier información sensible está definida en los archivos arduino_secrets.h. Puedes hacer que un Sketch sea privado desde el panel Compartir.",
"remote": "Remoto",
"remoteSketchbook": "Remote Sketchbook",
"remoteSketchbook": "Libreria de proyectos remota",
"share": "Compartir...",
"shareSketch": "Compartir Sketch",
"showHideRemoveSketchbook": "Mostrar/Ocultar Sketchbook Remoto",
"signIn": "Iniciar sesión",
"signInToCloud": "Iniciar sesión en Arduino Cloud",
"signOut": "Cerrar sesión",
"sync": "Sync",
"sync": "Sincronizar",
"syncEditSketches": "Sincroniza y edita tus Arduino Cloud Sketches",
"visitArduinoCloud": "Visita Arduino Cloud para crear Cloud Sketches. "
},
@@ -129,12 +129,12 @@
"replaceTitle": "Reemplazar"
},
"coreContribution": {
"copyError": "Copy error messages"
"copyError": "Copiar mensajes de error"
},
"daemon": {
"restart": "Restart Daemon",
"start": "Start Daemon",
"stop": "Stop Daemon"
"restart": "Reiniciar Daemon",
"start": "Iniciar Daemon",
"stop": "Parar Daemon"
},
"debug": {
"debugWithMessage": "Debug - {0}",
@@ -153,8 +153,8 @@
"decreaseIndent": "Disminuir sangría",
"increaseFontSize": "Aumentar tamaño de fuente",
"increaseIndent": "Aumentar sangría",
"nextError": "Next Error",
"previousError": "Previous Error"
"nextError": "Siguiente Error",
"previousError": "Error Anterior"
},
"electron": {
"couldNotSave": "No se ha podido guardar el sketch. Por favor, copia tu trabajo no guardado en tu editor de texto favorito y reinicia el IDE.",
@@ -192,6 +192,7 @@
"visit": "Visitar Arduino.cc"
},
"ide-updater": {
"checkForUpdates": "Buscar actualizaciones para Arduino IDE",
"closeAndInstallButton": "Cerrar e instalar",
"closeToInstallNotice": "Cierra el programa e instala la actualización en tu máquina.",
"downloadButton": "Descargar",
@@ -230,7 +231,7 @@
"zipLibrary": "Biblioteca"
},
"menu": {
"advanced": "Advanced",
"advanced": "Avanzado",
"sketch": "Sketch",
"tools": "Herramientas"
},
@@ -253,10 +254,10 @@
"cloud.pull.warn": "Verdadero si se debe advertir a los usuarios antes de sacar un boceto de la nube. El valor predeterminado es verdadero.",
"cloud.push.warn": "Verdadero si se debe advertir a los usuarios antes de enviar un boceto a la nube. El valor predeterminado es verdadero.",
"cloud.pushpublic.warn": "Verdadero si se debe advertir a los usuarios antes de enviar un boceto público a la nube. El valor predeterminado es verdadero.",
"cloud.sketchSyncEnpoint": "El punto final utilizado para empujar y extraer bocetos de un backend. Por defecto, apunta a la API de Arduino Cloud.",
"cloud.sketchSyncEndpoint": "El punto final utilizado para empujar y extraer bocetos de un backend. Por defecto, apunta a la API de Arduino Cloud.",
"compile": "Compliar",
"compile.experimental": "True if the IDE should handle multiple compiler errors. False by default",
"compile.revealRange": "Adjusts how compiler errors are revealed in the editor after a failed verify/upload. Possible values: 'auto': Scroll vertically as necessary and reveal a line. 'center': Scroll vertically as necessary and reveal a line centered vertically. 'top': Scroll vertically as necessary and reveal a line close to the top of the viewport, optimized for viewing a code definition. 'centerIfOutsideViewport': Scroll vertically as necessary and reveal a line centered vertically only if it lies outside the viewport. The default value is '{0}'.",
"compile.experimental": "True si el IDE debe manejar multiples errores del compilador. False por defecto",
"compile.revealRange": "Ajusta cómo se revelan los errores del compilador en el editor después de una verificación/carga fallida. Valores posibles: 'auto': Desplázate verticalmente según sea necesario y revela una línea. 'centrar': Desplázate verticalmente según sea necesario y revela una línea centrada verticalmente. \"superior\": Desplázate verticalmente según sea necesario y revela una línea cerca de la parte superior de la ventana gráfica, optimizada para ver una definición de código. 'centerIfOutsideViewport': Desplázate verticalmente según sea necesario y revela una línea centrada verticalmente sólo si se encuentra fuera de la ventana gráfica. El valor por defecto es '{0}'.",
"compile.verbose": "Verdadero para compilación detallada. Falso por defecto",
"compile.warnings": "Indica a gcc qué nivel de advertencia usar. Por defecto es \"Ninguno\"",
"compilerWarnings": "alertas de compilación",
@@ -271,6 +272,7 @@
"invalid.sketchbook.location": "Ruta del sketchbook no válida: {0}",
"invalid.theme": "Tema no válido.",
"language.log": "Verdadero si el Servidor de Lenguaje Arduino debe generar archivos de registro en la carpeta del sketch. En caso contrario, falso. Por defecto es falso.",
"language.realTimeDiagnostics": "Si está habilitado, el lenguaje proveerá diagnoticos en tiepo real meintras se teclea en el editor. Deshabilitado por defecto.",
"manualProxy": "Configuración manual del proxy",
"network": "Red",
"newSketchbookLocation": "Selecciona la nueva ruta del sketchbook",
@@ -296,6 +298,7 @@
"newLineCarriageReturn": "Ambos NL & CR",
"noLineEndings": "Sin ajuste de línea",
"notConnected": "No conectado. Selecciona una placa y un puerto para conectarte automáticamente.",
"openSerialPlotter": "Plotter Serie",
"timestamp": "Marca de tiempo",
"toggleTimestamp": "Alternar la marca de tiempo"
},

View File

@@ -192,6 +192,7 @@
"visit": "Bisitatu Arduino.cc"
},
"ide-updater": {
"checkForUpdates": "Check for Arduino IDE updates",
"closeAndInstallButton": "Itxi eta instalatu",
"closeToInstallNotice": "Itxi softwarea eta instalatu eguneratzea zure makinan.",
"downloadButton": "Software eguneratzea",
@@ -253,7 +254,7 @@
"cloud.pull.warn": "Egia bada erabiltzaileek abisua jasoko dute programa bat hodeitik kargatu aurretik. Lehenetsia egia da.",
"cloud.push.warn": "Egia bada erabiltzaileek abisua jasoko dute programa bat hodeian gorde aurretik. Lehenetsia egia da.",
"cloud.pushpublic.warn": "Egia bada erabiltzaileek abisua jasoko dute programa publiko bat hodeian gorde aurretik. Lehenetsia egia da.",
"cloud.sketchSyncEnpoint": "Zerbitzari batean programak gorde eta kargatzeko amaiera-puntua. Lehenetsia Arduino Cloud API da.",
"cloud.sketchSyncEndpoint": "Zerbitzari batean programak gorde eta kargatzeko amaiera-puntua. Lehenetsia Arduino Cloud API da.",
"compile": "konpilazioa",
"compile.experimental": "True if the IDE should handle multiple compiler errors. False by default",
"compile.revealRange": "Adjusts how compiler errors are revealed in the editor after a failed verify/upload. Possible values: 'auto': Scroll vertically as necessary and reveal a line. 'center': Scroll vertically as necessary and reveal a line centered vertically. 'top': Scroll vertically as necessary and reveal a line close to the top of the viewport, optimized for viewing a code definition. 'centerIfOutsideViewport': Scroll vertically as necessary and reveal a line centered vertically only if it lies outside the viewport. The default value is '{0}'.",
@@ -271,6 +272,7 @@
"invalid.sketchbook.location": "Programa bildumaren kokaleku baliogabea: {0}",
"invalid.theme": "Itxura baliogabea.",
"language.log": "Egia Arduino Language Server-ek egunkari-fitxategiak sortu behar baditu programaren karpetan. Bestela, gezurra. Lehenetsia gezurra da.",
"language.realTimeDiagnostics": "If true, the language server provides real-time diagnostics when typing in the editor. It's false by default.",
"manualProxy": "Proxyaren eskuzko konfigurazioa",
"network": "Sarea",
"newSketchbookLocation": "Hautatu programa bilduma berriaren kokalekua",
@@ -296,6 +298,7 @@
"newLineCarriageReturn": "NL & CR biak",
"noLineEndings": "Lerro amaierarik ez",
"notConnected": "Ez dago konektatuta. Hautatu plaka eta ataka automatikoki konektatzeko.",
"openSerialPlotter": "Serial Plotter",
"timestamp": "Denbora-zigilua",
"toggleTimestamp": "Txandakatu denbora-zigilua"
},

View File

@@ -192,6 +192,7 @@
"visit": "بازدید از Arduino.cc"
},
"ide-updater": {
"checkForUpdates": "Check for Arduino IDE updates",
"closeAndInstallButton": "بستن و نصب",
"closeToInstallNotice": "نرم افزار را ببندید و به روز رسانی را روی دستگاه خود نصب کنید.",
"downloadButton": "دانلود",
@@ -253,7 +254,7 @@
"cloud.pull.warn": "اگر هشدار دادن به کاربران قبل از کشیدن یک طرح ابری درست می باشد ، پیش فرض ها صحیح است.",
"cloud.push.warn": "اگر هشدار دادن به کاربران قبل از ارسال یک طرح ابری درست است پیش فرض ها درست می باشد",
"cloud.pushpublic.warn": "اگر هشدار دادن به کاربران قبل از ارسال یک طرح عمومی به فضای درست می باشد پیش فرض ها درست است.",
"cloud.sketchSyncEnpoint": "نقطه ای برای ارسال و دریافت طرح ها استفاده می شود . به طور پیش فرض به رابط ابر آردوینو استفاده می کند.",
"cloud.sketchSyncEndpoint": "نقطه ای برای ارسال و دریافت طرح ها استفاده می شود . به طور پیش فرض به رابط ابر آردوینو استفاده می کند.",
"compile": "کامپایل",
"compile.experimental": "True if the IDE should handle multiple compiler errors. False by default",
"compile.revealRange": "Adjusts how compiler errors are revealed in the editor after a failed verify/upload. Possible values: 'auto': Scroll vertically as necessary and reveal a line. 'center': Scroll vertically as necessary and reveal a line centered vertically. 'top': Scroll vertically as necessary and reveal a line close to the top of the viewport, optimized for viewing a code definition. 'centerIfOutsideViewport': Scroll vertically as necessary and reveal a line centered vertically only if it lies outside the viewport. The default value is '{0}'.",
@@ -271,6 +272,7 @@
"invalid.sketchbook.location": "مکان نامعتبر منبع طرح ها: {0}",
"invalid.theme": "طرح زمینه موجود نیست",
"language.log": "اگر سرور زبان آردوینو باید فایل های گزارش را در پوشه طرح ایجاد کند درست می باشد. در غیر این صورت، نادرست است. به طور پیش فرض نادرست است.",
"language.realTimeDiagnostics": "If true, the language server provides real-time diagnostics when typing in the editor. It's false by default.",
"manualProxy": "پیکربندی دستی پروکسی",
"network": "نتورک",
"newSketchbookLocation": "مکان جدید منبع طرح ها را مشخص کنید",
@@ -296,6 +298,7 @@
"newLineCarriageReturn": "هم NL و هم CR",
"noLineEndings": "بدون پایان خط",
"notConnected": "متصل نشد. برد و پورت را انتخاب کنید تا بطور خودکار متصل شود.",
"openSerialPlotter": "Serial Plotter",
"timestamp": "برچسب زمانی",
"toggleTimestamp": "اتصال برچسب زمان"
},

View File

@@ -192,6 +192,7 @@
"visit": "Visit Arduino.cc"
},
"ide-updater": {
"checkForUpdates": "Check for Arduino IDE updates",
"closeAndInstallButton": "Close and Install",
"closeToInstallNotice": "Close the software and install the update on your machine.",
"downloadButton": "Download",
@@ -253,7 +254,7 @@
"cloud.pull.warn": "True if users should be warned before pulling a cloud sketch. Defaults to true.",
"cloud.push.warn": "True if users should be warned before pushing a cloud sketch. Defaults to true.",
"cloud.pushpublic.warn": "True if users should be warned before pushing a public sketch to the cloud. Defaults to true.",
"cloud.sketchSyncEnpoint": "The endpoint used to push and pull sketches from a backend. By default it points to Arduino Cloud API.",
"cloud.sketchSyncEndpoint": "The endpoint used to push and pull sketches from a backend. By default it points to Arduino Cloud API.",
"compile": "compile",
"compile.experimental": "True if the IDE should handle multiple compiler errors. False by default",
"compile.revealRange": "Adjusts how compiler errors are revealed in the editor after a failed verify/upload. Possible values: 'auto': Scroll vertically as necessary and reveal a line. 'center': Scroll vertically as necessary and reveal a line centered vertically. 'top': Scroll vertically as necessary and reveal a line close to the top of the viewport, optimized for viewing a code definition. 'centerIfOutsideViewport': Scroll vertically as necessary and reveal a line centered vertically only if it lies outside the viewport. The default value is '{0}'.",
@@ -271,6 +272,7 @@
"invalid.sketchbook.location": "Invalid sketchbook location: {0}",
"invalid.theme": "Invalid theme.",
"language.log": "True, kung dapat na gumawa ng log files ang Arduino Language Server sa mismong sketch folder. False naman kung hindi. Ito ay false, by default. ",
"language.realTimeDiagnostics": "If true, the language server provides real-time diagnostics when typing in the editor. It's false by default.",
"manualProxy": "Manual proxy configuration",
"network": "Network",
"newSketchbookLocation": "Select new sketchbook location",
@@ -296,6 +298,7 @@
"newLineCarriageReturn": "Both NL & CR",
"noLineEndings": "No Line Ending",
"notConnected": "Not connected. Select a board and a port to connect automatically.",
"openSerialPlotter": "Serial Plotter",
"timestamp": "Timestamp",
"toggleTimestamp": "Toggle Timestamp"
},

View File

@@ -192,6 +192,7 @@
"visit": "Visitez Arduino.cc"
},
"ide-updater": {
"checkForUpdates": "Check for Arduino IDE updates",
"closeAndInstallButton": "Fermer et installer",
"closeToInstallNotice": "Fermez le logiciel et installez la mise à jour sur votre machine.",
"downloadButton": "Télécharger",
@@ -253,7 +254,7 @@
"cloud.pull.warn": "Vrai si les utilisateurs devrait être averti avant de pull un croquis sur le cloud. Par défaut, la valeur est vrai.",
"cloud.push.warn": "Vrai, si les utilisateurs devrait être averti avant de push un croquis sur le cloud. Par défaut, la valeur est vrai.",
"cloud.pushpublic.warn": "Vrai si les utilisateurs devrait être avertit avant de publier un croquis public sur le cloud. Vrai par défaut.",
"cloud.sketchSyncEnpoint": "L'endpoint est utilisé pour pousser et tirer des croquis à partir du backend. Par défault, il pointe vers l'Arduino Cloud API.",
"cloud.sketchSyncEndpoint": "L'endpoint est utilisé pour pousser et tirer des croquis à partir du backend. Par défault, il pointe vers l'Arduino Cloud API.",
"compile": "compiler",
"compile.experimental": "True if the IDE should handle multiple compiler errors. False by default",
"compile.revealRange": "Adjusts how compiler errors are revealed in the editor after a failed verify/upload. Possible values: 'auto': Scroll vertically as necessary and reveal a line. 'center': Scroll vertically as necessary and reveal a line centered vertically. 'top': Scroll vertically as necessary and reveal a line close to the top of the viewport, optimized for viewing a code definition. 'centerIfOutsideViewport': Scroll vertically as necessary and reveal a line centered vertically only if it lies outside the viewport. The default value is '{0}'.",
@@ -271,6 +272,7 @@
"invalid.sketchbook.location": "Localisation invalide du croquis : {0}",
"invalid.theme": "Thème invalide.",
"language.log": "Vrai si le serveur de langage Arduino devrait générer des fichiers logs dans le dossier du croquis. Si non faux. La valeur par défaut est faux.",
"language.realTimeDiagnostics": "If true, the language server provides real-time diagnostics when typing in the editor. It's false by default.",
"manualProxy": "Configuration manuelle du proxy",
"network": "Réseau",
"newSketchbookLocation": "Sélectionner la localisation du nouveau croquis.",
@@ -296,6 +298,7 @@
"newLineCarriageReturn": "Les deux, NL et CR",
"noLineEndings": "Pas de fin de ligne",
"notConnected": "Déconnecté. Sélectionnez une carte ainsi qu'un port pour vous connecter automatiquement.",
"openSerialPlotter": "Serial Plotter",
"timestamp": "horodatage",
"toggleTimestamp": "Activer l'horodatage"
},

View File

@@ -192,6 +192,7 @@
"visit": "בקר ב Arduino.cc"
},
"ide-updater": {
"checkForUpdates": "Check for Arduino IDE updates",
"closeAndInstallButton": "סגור והתקן",
"closeToInstallNotice": "סגור את התוכנה והתקן את העדכון על המכונה שלך.",
"downloadButton": "הורד",
@@ -253,7 +254,7 @@
"cloud.pull.warn": "True if users should be warned before pulling a cloud sketch. Defaults to true.",
"cloud.push.warn": "True if users should be warned before pushing a cloud sketch. Defaults to true.",
"cloud.pushpublic.warn": "True if users should be warned before pushing a public sketch to the cloud. Defaults to true.",
"cloud.sketchSyncEnpoint": "The endpoint used to push and pull sketches from a backend. By default it points to Arduino Cloud API.",
"cloud.sketchSyncEndpoint": "The endpoint used to push and pull sketches from a backend. By default it points to Arduino Cloud API.",
"compile": "compile",
"compile.experimental": "True if the IDE should handle multiple compiler errors. False by default",
"compile.revealRange": "Adjusts how compiler errors are revealed in the editor after a failed verify/upload. Possible values: 'auto': Scroll vertically as necessary and reveal a line. 'center': Scroll vertically as necessary and reveal a line centered vertically. 'top': Scroll vertically as necessary and reveal a line close to the top of the viewport, optimized for viewing a code definition. 'centerIfOutsideViewport': Scroll vertically as necessary and reveal a line centered vertically only if it lies outside the viewport. The default value is '{0}'.",
@@ -271,6 +272,7 @@
"invalid.sketchbook.location": "Invalid sketchbook location: {0}",
"invalid.theme": "ערכת נושא לא חוקית.",
"language.log": "True if the Arduino Language Server should generate log files into the sketch folder. Otherwise, false. It's false by default.",
"language.realTimeDiagnostics": "If true, the language server provides real-time diagnostics when typing in the editor. It's false by default.",
"manualProxy": "Manual proxy configuration",
"network": "רשת",
"newSketchbookLocation": "Select new sketchbook location",
@@ -296,6 +298,7 @@
"newLineCarriageReturn": "Both NL & CR",
"noLineEndings": "ללא סיום שורה",
"notConnected": "Not connected. Select a board and a port to connect automatically.",
"openSerialPlotter": "Serial Plotter",
"timestamp": "חותמת זמן",
"toggleTimestamp": "Toggle Timestamp"
},

View File

@@ -192,6 +192,7 @@
"visit": "Visit Arduino.cc"
},
"ide-updater": {
"checkForUpdates": "Check for Arduino IDE updates",
"closeAndInstallButton": "Close and Install",
"closeToInstallNotice": "Close the software and install the update on your machine.",
"downloadButton": "Download",
@@ -253,7 +254,7 @@
"cloud.pull.warn": "True if users should be warned before pulling a cloud sketch. Defaults to true.",
"cloud.push.warn": "True if users should be warned before pushing a cloud sketch. Defaults to true.",
"cloud.pushpublic.warn": "True if users should be warned before pushing a public sketch to the cloud. Defaults to true.",
"cloud.sketchSyncEnpoint": "The endpoint used to push and pull sketches from a backend. By default it points to Arduino Cloud API.",
"cloud.sketchSyncEndpoint": "The endpoint used to push and pull sketches from a backend. By default it points to Arduino Cloud API.",
"compile": "compile",
"compile.experimental": "True if the IDE should handle multiple compiler errors. False by default",
"compile.revealRange": "Adjusts how compiler errors are revealed in the editor after a failed verify/upload. Possible values: 'auto': Scroll vertically as necessary and reveal a line. 'center': Scroll vertically as necessary and reveal a line centered vertically. 'top': Scroll vertically as necessary and reveal a line close to the top of the viewport, optimized for viewing a code definition. 'centerIfOutsideViewport': Scroll vertically as necessary and reveal a line centered vertically only if it lies outside the viewport. The default value is '{0}'.",
@@ -271,6 +272,7 @@
"invalid.sketchbook.location": "Invalid sketchbook location: {0}",
"invalid.theme": "Invalid theme.",
"language.log": "True if the Arduino Language Server should generate log files into the sketch folder. Otherwise, false. It's false by default.",
"language.realTimeDiagnostics": "If true, the language server provides real-time diagnostics when typing in the editor. It's false by default.",
"manualProxy": "Manual proxy configuration",
"network": "Network",
"newSketchbookLocation": "Select new sketchbook location",
@@ -296,6 +298,7 @@
"newLineCarriageReturn": "Both NL & CR",
"noLineEndings": "No Line Ending",
"notConnected": "Not connected. Select a board and a port to connect automatically.",
"openSerialPlotter": "Serial Plotter",
"timestamp": "Timestamp",
"toggleTimestamp": "Toggle Timestamp"
},

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