Compare commits

..

43 Commits

Author SHA1 Message Date
Giacomo Cusinato
de1e2f06af chore: remove unused dompurify package 2025-11-05 17:40:12 +01:00
Giacomo Cusinato
100f08f9b3 chore: remove donation dialog and links 2025-11-05 17:39:50 +01:00
502E532E
93d27ea72a feat: add a copy button to serial monitor (#2718)
* Add a copy output button to serial monitor

If the arduino collects some data that you want to store on your
computer, a rather simple way is to write it to the serial monitor and
copy it to the clipboard. This commit introduces a button that copies
the whole content of the serial monitor to the clipboard to make this
rather simple. It is a new component added to the menu, and does not
change the behaviour of other compontents.

* Test merging lines to str in serial monitor utils

Adds a test for merging one or more lines to a single string. It is
supposed to just concatenate the content of the lines, without doing
anything else. This method is used when copying the serial monitor content to
the clipboard.

* Add copy output translation key

This serves as an addition to the previous commits. It is the result of
running `yarn i18n:generate` on the state after adding the copy output
button to the serial monitor (see 2df3f465). I hope that this will
resolve the current Github action failure.

* Improve readability for serial monitor utils

Replace return statement in inline method by direct statement, some
minor formatting changes. Does not affect the functionality.

* Rename linesToMergedStr in monitor-utils

Renames the method linesToMergedStr to joinLines in the serial monitor
utils. This brings the name more in line with truncateLines. No
functionality changes.

* Move label and icon registration for copy serial

Moves the registration of the label and icon for the copy output
button of the serial monitor to the toolbar item registration. Before,
it happened at the command registration, but is not necessary at this
level, as the icon and label are meant for the toolbar button only.

* Do not update widget when copying output

No longer updates the serial monitor output after its content is copied.
Copying the content does not change anything for the view, so there is
no need to update.
2025-11-05 16:56:04 +01:00
per1234
155f0aebaf Remove donation links from contributor guide
The Arduino company is no longer soliciting monetary donations. Community members who wish to contribute to the project
still have opportunities to do so via the other options listed here.
2025-10-10 21:56:12 -07:00
dependabot[bot]
3d8f3fa3e3 build(deps): Bump actions/checkout from 4 to 5
Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-11 19:07:28 -07:00
dependabot[bot]
c1e5fbc8a5 build(deps): Bump actions/download-artifact from 4 to 5
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 4 to 5.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-11 00:47:17 -07:00
Per Tillisch
ee4f74d566 Document localization capabilities in Translator Guide
The system used for localization of this project leverages the infrastructure and data that already exists for VS Code.
These assets are applicable to Arduino IDE 2.x due to the project being built on the Eclipse Theia IDE framework. In
this way, the Arduino IDE developers and community are only responsible for internationalization and localization of the
Arduino-specific strings (e.g., "Sketchbook") used in the IDE's UI.

The unfortunate downside to this approach is that there is a hard technical limit on localization of the project to
languages for which a VS Code "language pack" is available.

It will be helpful to clearly communicate the set of languages for which we are able to ship contributions from
translators.

Likewise, it will be useful to also communicate that, we are able to ship contributions of translations of Arduino CLI
user interface strings for any language. This is due to the fact that the Arduino CLI codebase uses a completely
different internationalization system that does not impose any technical limits on the scope of localization.
2025-07-15 19:32:12 -07:00
Giacomo Cusinato
0f9f0d07b7 chore: switch to version 2.3.7 after the release (#2701) 2025-04-09 19:48:50 +07:00
Giacomo Cusinato
2f0414a5a1 fix: use ElectronConnectionHandler to connect ide updater services (#2697) 2025-04-09 13:58:47 +07:00
github-actions[bot]
e3319dab1a Updated translation files (#2692)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-04-08 16:14:03 +07:00
Giacomo Cusinato
a669a43449 fix: wait for theia initalWindow to be set before opening sketch through open-file event (#2693)
* chore: use actual app name as `uriScheme`

* fix: wait for `initialWindow` to be set before opening sketch from file
2025-04-07 19:15:59 +02:00
Giacomo Cusinato
56ab874177 fix: propagate electron params in second instance startup (#2686) 2025-04-05 19:56:00 +09:00
Giacomo Cusinato
e36f393682 fix: prevent OutputWidget to gain focus when updated (#2681) 2025-04-03 17:51:30 +02:00
Giacomo Cusinato
4d52bb2843 chore: switch to version 2.3.6 after the release (#2682) 2025-04-03 17:51:12 +02:00
Giacomo Cusinato
39c8db8e90 fix: add missing linux dependencies for create-changelog job (#2677) 2025-04-02 23:24:35 +09:00
Giacomo Cusinato
8aa3c28c50 fix: allow write permission on release job (#2676)
Permission were previously changed here https://github.com/arduino/arduino-ide/pull/2651
Repo write permission is needed to allow creating the github release and publishing files
2025-04-02 21:51:35 +09:00
Giacomo Cusinato
d293595b89 fix: add missing linux dependencies (#2675) 2025-04-02 09:23:57 +09:00
Giacomo Cusinato
4b0982ccb3 fix: safer electron version parsing for electron-builder command (#2673) 2025-04-01 18:42:04 +09:00
Per Tillisch
9b15695c60 Give build workflow step access to required deployment environment (#2672)
* Trim trailing whitespace in build workflow

* Give build workflow step access to required deployment environment

Certain operations in the "Arduino IDE" GitHub Actions workflow use GitHub Actions "secrets" which are defined in the
repository's administrative settings.

These secrets will typically not be defined when the workflow is run in a fork. However, the workflow's base
functionality, the automated building of the application, does not require secrets. Since that base functionality alone
is very useful to contributors (either to validate relevant changes to the application and infrastructure, or to
generate tester builds) who are performing development work in a fork. For this reason, the workflow is configured to
only perform the secret-dependent operations when the required secrets have been defined in the repository settings.

One such operation is publishing the generated builds to Amazon S3, which Arduino uses to host files for distribution.
This operation depends on the "AWS_ROLE_ARN" secret. As a security measure, this secret is defined inside a deployment
environment (named "production"). GitHub Actions workflow jobs can only use secrets from deployment environments which
they have been explicitly configured to have access to.

At the time the workflow was originally developed, GitHub did not have the deployment environment feature, and so the
workflow was not configured to use environments. The switch to using a deployment environment for this secret was made
only recently, and when that was done, the workflow job that checks whether the secret is defined was not configured to
have access to the "production" environment. This caused the workflow to think it was running in a context where that
secret is not defined even when the secret is in fact defined. The bug caused the workflow to always spuriously skip the
"publish" job which publishes nightly builds of Arduino IDE, and the "publish release" step which publishes production
releases.

The bug is fixed by configuring the "build-type-determination" job so that it has access to the "production"
environment.
2025-04-01 16:27:06 +09:00
github-actions[bot]
0dff87e29c Updated translation files (#2597)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-03-31 19:42:48 +09:00
Giacomo Cusinato
7dafe7b0d3 feat: use Arduino CLI 1.2.0 (#2645) 2025-03-28 17:53:46 +01:00
Giacomo Cusinato
859d29d41a feat: use theia@1.57.0 (#2654) 2025-03-29 01:33:25 +09:00
Christian Sarnataro
d298b3ffc9 fix: sanitize message in notification component (#2664)
fix: sanitize messages in notification component
2025-03-24 12:42:48 +01:00
Giacomo Cusinato
9ab87bf8b5 chore: use AWS OpenID Connect for S3 publish 2025-03-11 21:42:12 +07:00
dankeboy36
5ec1915000 fix(plugin): decouple state update from the LS (#2643)
* fix(plugin): decouple state update from the LS

To enhance the reliability of Arduino IDE extensions, the update
process for `ArduinoState` has been modified to ensure independence
from the language server's availability. This change addresses issues
caused by `compileSummary` being `undefined` due to potential startup
failures of the Arduino Language Server, as noted in
https://github.com/dankeboy36/esp-exception-decoder/issues/28#issuecomment-2681800772.

The `compile` command now resolves with a `CompileSummary` rather than
`void`, facilitating a more reliable way for extensions to access
necessary data. Furthermore, the command has been adjusted to allow
resolution with `undefined` when the compiled data is partial.

By transitioning to direct usage of the resolved compile value for
state updates, the reliance on executed commands for extensions is
eliminated. This update also moves the VSIX command execution to the
frontend without altering existing IDE behavior.

Closes arduino/arduino-ide#2642

Signed-off-by: dankeboy36 <dankeboy36@gmail.com>

* fix: install missing libx11-dev and libxkbfile-dev

Signed-off-by: dankeboy36 <dankeboy36@gmail.com>

* fix: pick better GH step name

Signed-off-by: dankeboy36 <dankeboy36@gmail.com>

* fix: install the required dependencies on Linux

Signed-off-by: dankeboy36 <dankeboy36@gmail.com>

* fix(revert): do not manually install deps on Linux

Signed-off-by: dankeboy36 <dankeboy36@gmail.com>

* chore: pin `ubuntu-22.04` for linux actions

* fix: restore accidentally removed dispose on finally

Signed-off-by: dankeboy36 <dankeboy36@gmail.com>

* fix(test): align mock naming 💄

Signed-off-by: dankeboy36 <dankeboy36@gmail.com>

* fix: let the ino contribution notify the LS

+ event emitter dispatches the new state.

Signed-off-by: dankeboy36 <dankeboy36@gmail.com>

* fix(test): emit the new compiler summary state

Signed-off-by: dankeboy36 <dankeboy36@gmail.com>

* chore(revert): unpin linux version, use latest

revert of b11bde1c47

Signed-off-by: dankeboy36 <dankeboy36@gmail.com>

---------

Signed-off-by: dankeboy36 <dankeboy36@gmail.com>
Co-authored-by: Giacomo Cusinato <7659518+giacomocusinato@users.noreply.github.com>
2025-03-10 15:20:22 +07:00
Per Tillisch
6d96e227eb Bump built-in example sketches version to 1.10.2
The Arduino IDE installation includes a collection of example sketches demonstrating fundamental concepts.

These examples are hosted in a dedicated repository, which is a dependency of this project. A new release has been made
in that `arduino/arduino-examples` repository.

The infrastructure for downloading the examples during the Arduino IDE build is hereby updated to use the latest release
of the `arduino/arduino-examples` repository.
2025-02-28 06:56:33 -08:00
dankeboy36
1712f9ea9d fix: install missing linux dependencies
Install `libx11-dev`, `libxkbfile-dev`, `libsecret-1-dev` libraries as the most recent update to ubuntu-latest does not include them
Signed-off-by: dankeboy36 <dankeboy36@gmail.com>
2025-02-28 19:15:04 +07:00
Giacomo Cusinato
6eef09efd8 chore: switch to version 2.3.5 after the release (#2587)
To produce a correctly versioned nightly build.
See the [docs](1b9c7e93e0/docs/internal/release-procedure.md (7-%EF%B8%8F-bump-version-metadata-of-packages)) for more details.
2024-12-03 13:21:25 +01:00
github-actions[bot]
1112057979 Updated translation files (#2523)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-12-02 16:02:58 +01:00
Giacomo Cusinato
8e18c47d30 feat: use dompurify to sanitize translations
Pin same version of `dompurify` used in Theia
2024-12-02 15:21:22 +01:00
Giacomo Cusinato
4788bfbc3f feat: introduce VersionWelcomeDialog
Show donate dialog after the first time a first IDE version is loaded
2024-12-02 15:21:22 +01:00
Giacomo Cusinato
71b11ed829 feat: add donate footer to updater dialog 2024-12-02 15:21:22 +01:00
dependabot[bot]
3aedafa306 build(deps): Bump docker/build-push-action from 5 to 6
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 5 to 6.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v5...v6)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-29 01:38:27 -08:00
dependabot[bot]
284dd83d7d build(deps): Bump peter-evans/create-pull-request from 5 to 7
Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 5 to 7.
- [Release notes](https://github.com/peter-evans/create-pull-request/releases)
- [Commits](https://github.com/peter-evans/create-pull-request/compare/v5...v7)

---
updated-dependencies:
- dependency-name: peter-evans/create-pull-request
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-29 00:05:37 -08:00
per1234
c09b5f718a Use Ubuntu 18.10 in Linux build container
Background
==========

Shared Library Dependencies
---------------------------

The Linux build of Arduino IDE has dynamic linkage against the libstdc++ and glibc shared libraries. This results in
it having a dependency on the version of the libraries that happens to be present in the environment it is built in.

Although newer versions of the shared libraries are compatible with executables linked against an older version, the
reverse is not true. This means that building Arduino IDE on a Linux machine with a recent distro version installed
causes the IDE to error on startup for users who have a distro with older versions of the dependencies.

For example, if Arduino IDE were built on a machine with version 3.4.33 of libstdc++, then attempting to run it on a
machine with an older version of libstdc++ would fail with an error like:

```
Error: /usr/lib/x86_64-linux-gnu/libstdc++.so.6: version `GLIBCXX_3.4.33' not found (required by /home/foo/arduino-ide/resources/app/lib/backend/native/nsfw.node)
```

Likewise,  if Arduino IDE were built on a machine with version 2.39 of glibc, then attempting to run it on a machine
with an older version of glibc would fail with an error like:

```
Error: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.39' not found (required by /home/foo/arduino-ide/resources/app/node_modules/nsfw/build/Release/nsfw.node)
```

Build Machine Requirements
--------------------------

The IDE builds distributed by Arduino should be compatible with a reasonable range of Linux distribution versions. In
order to achieve this, the builds must be performed in a machine with an older version of the shared libraries. The
shared libraries are part of the Linux distro, and installing a different version is not feasible. So this imposes a
maximum limit on the build machine's distro version.

The distributed builds are generated via a GitHub Actions workflow. The most simple approach is to run the build in the
machine of the GitHub-hosted runners provided for each operating system. However, GitHub provides a limited range of
operating system versions in their runners, and removes the older versions as newer versions are added. This means that
building in the GitHub-hosted runner machine would not allow for the desired range of Linux distro version
compatibility. For this reason, the Linux build is performed in a Docker container that provides an older version of
Ubuntu.

The same situation of incompatibility with Linux distro versions that have a version of the shared library dependencies
older than the version present on the build machine occurs for several of the tools and frameworks used by the build
process (e.g., Node.js, Python). In this case, the tables are turned as we are now the user rather than the distributor
and so are at the mercy of the Linux distro version compatibility range provided by the distributor. So this imposes a
minimum limit on the build machine's distro version.

Although several of the dependencies used by the standard build system have dependencies on versions of glibc higher
than the version 2.27 present in Ubuntu 18.04, it was possible to use this distro version in the Linux build container
by using alternative distributions and/or versions of these dependencies.

Workflow Artifacts
------------------

The build workflow uses GitHub actions workflow artifacts to transfer the files generated by the build job to subsequent
jobs in the workflow. The "actions/upload-artifact" action is used for this purpose.

Problem
=======

GitHub is dropping support for the workflow artifacts produced by the version 3.x of the "actions/upload-artifact"
action that was previously used by the build job. So the action version used in the build workflow was updated to the
current version 4.x. This version of the action uses a newer version of the Node.js runtime (20). Unfortunately the the
Node.js 20 runtime used by the action has a dependency on glibc version 2.28, which causes the Linux build job to fail
after the update of the "actions/upload-artifact" action:

```
Run actions/upload-artifact@v4
/__e/node20/bin/node: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.28' not found (required by /__e/node20/bin/node)
```

Unlike the other dependencies of the build process, it is no longer possible to work around this incompatibility by
continuing to use the older compatible version of the "actions/upload-artifact" action. It is also impossible to replace
the incompatible Node.js 20.x distribution used by the action, since it comes from the read-only file system of the
runner image. Likewise, it is not possible to configure or force the action to use a Node.js installation at a different
path on the runner machine.

Resolution
==========

Compatibility with the new version of the "actions/upload-artifact" action is attained by updating the version of Linux
in the build container to 18.10, which is the oldest version that has glibc 2.28. The presence of a newer glibc version
in the container also makes it compatible with several other dependencies of the build process, meaning the code in the
Dockerfile and workflow for working around the incompatibilities of Ubuntu 18.04 can be removed.

Consequences
============

Unfortunately this means the loss of compatibility of the Linux Arduino IDE builds with distros that use glibc 2.27
(e.g., Ubuntu 18.04). User of those distros will now find that Arduino IDE fails to start with an error like:

```
Error: node-loader:
Error: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.28' not found (required by /home/foo/arduino-ide/resources/app/lib/backend/native/pty.node)
    at 85467 (/home/foo/arduino-ide/resources/app/lib/backend/main.js:2:2766)
    at __webpack_require__ (/home/foo/arduino-ide/resources/app/lib/backend/main.js:2:6663105)
    at 23571 (/home/foo/arduino-ide/resources/app/lib/backend/main.js:2:3374073)
    at __webpack_require__ (/home/foo/arduino-ide/resources/app/lib/backend/main.js:2:6663105)
    at 55444 (/home/foo/arduino-ide/resources/app/lib/backend/main.js:2:3369761)
    at __webpack_require__ (/home/foo/arduino-ide/resources/app/lib/backend/main.js:2:6663105)
    at 24290 (/home/foo/arduino-ide/resources/app/lib/backend/main.js:2:1780542)
    at __webpack_require__ (/home/foo/arduino-ide/resources/app/lib/backend/main.js:2:6663105)
    at 43416 (/home/foo/arduino-ide/resources/app/lib/backend/main.js:2:1770138)
    at __webpack_require__ (/home/foo/arduino-ide/resources/app/lib/backend/main.js:2:6663105)
```
2024-11-27 06:58:10 -08:00
per1234
dba57b312c Don't upload multiple times to same artifact in build workflow
The build workflow produces binaries for a range of target hosts. This is done by using a job matrix in the GitHub
Actions workflow that produces each build in a parallel job. GitHub Actions workflow artifacts are used to transfer the
generated files between sequential jobs in the workflow. The "actions/upload-artifact" action is used for this purpose.

Previously, a single artifact was used for this purpose, with each of the parallel jobs uploading its own generated
files to that artifact. However, support for uploading multiple times to a single artifact was dropped in version 4.0.0
of the "actions/upload-artifact" action. So it is now necessary to use a dedicated artifact for each of the builds.
These can be downloaded in aggregate by using the artifact name globbing and merging features which were introduced in
version 4.1.0 of the "actions/download-artifact" action.
2024-11-27 06:58:10 -08:00
per1234
90d3d77ca4 Don't upload multiple times to same artifact in label sync workflow
The "Sync Labels" GitHub Actions workflow is configured to allow the use of multiple shared label configuration files.
This is done by using a job matrix in the GitHub Actions workflow to download each of the files from the source
repository in a parallel GitHub Actions workflow job. A GitHub Actions workflow artifact was used to transfer the
generated files between sequential jobs in the workflow. The "actions/upload-artifact" and "actions/download-artifact"
actions are used for this purpose.

Previously, a single artifact was used for the transfer of all the shared label configuration files, with each of the
parallel jobs uploading its own generated files to that artifact. However, support for uploading multiple times to a
single artifact was dropped in version 4.0.0 of the "actions/upload-artifact" action. So it is now necessary to use a
dedicated artifact for each of the builds. These can be downloaded in aggregate by using the artifact name globbing and
merging features which were introduced in version 4.1.0 of the "actions/download-artifact" action.
2024-11-27 06:58:10 -08:00
dependabot[bot]
0aec778e84 build(deps): Bump geekyeggo/delete-artifact from 2 to 5
Bumps [geekyeggo/delete-artifact](https://github.com/geekyeggo/delete-artifact) from 2 to 5.
- [Release notes](https://github.com/geekyeggo/delete-artifact/releases)
- [Changelog](https://github.com/GeekyEggo/delete-artifact/blob/main/CHANGELOG.md)
- [Commits](https://github.com/geekyeggo/delete-artifact/compare/v2...v5)

---
updated-dependencies:
- dependency-name: geekyeggo/delete-artifact
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-27 06:58:10 -08:00
dependabot[bot]
84d2dfd13e build(deps): Bump actions/download-artifact from 3 to 4
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 3 to 4.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-27 06:58:10 -08:00
dependabot[bot]
86c7fd7b59 build(deps): Bump actions/upload-artifact from 3 to 4
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 3 to 4.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-27 06:58:10 -08:00
Giacomo Cusinato
de265694ee feat: use Arduino CLI v1.1.1 2024-11-27 14:05:16 +01:00
Giacomo Cusinato
8462d8a391 refactor: generate-protocol now fetch proto files from arduino_cli_{version}_proto.zip
- Use the CLI release proto.zip to get proto files for production versions of CLI
- Extract the proto files from repo if CLI version is declared as `commitsh` or version is 1.1.0

See https://github.com/arduino/arduino-cli/pull/2761
2024-11-27 14:05:16 +01:00
dankeboy36
48d6d37539 feat: can skip verify before upload
Adds a new preference to control whether the
verify command should automatically run before the
upload. If the `arduino.upload.autoVerify` setting
value is `false`, IDE does not recompile the
sketch code before uploading it to the board.

Signed-off-by: dankeboy36 <dankeboy36@gmail.com>
2024-11-26 21:41:48 +01:00
158 changed files with 6817 additions and 6301 deletions

View File

@@ -1,43 +1,28 @@
# The Arduino IDE Linux build workflow job runs in this container.
# syntax=docker/dockerfile:1
FROM ubuntu:18.04
# See: https://hub.docker.com/_/ubuntu/tags
FROM ubuntu:18.10
# See: https://unofficial-builds.nodejs.org/download/release/
ARG node_version="18.17.1"
# This is required in order to use the Ubuntu package repositories for EOL Ubuntu versions:
# https://help.ubuntu.com/community/EOLUpgrades#Update_sources.list
RUN \
sed \
--in-place \
--regexp-extended \
--expression='s/([a-z]{2}\.)?archive.ubuntu.com|security.ubuntu.com/old-releases.ubuntu.com/g' \
"/etc/apt/sources.list"
RUN \
apt-get \
--yes \
update
# This is required to get add-apt-repository
RUN \
apt-get \
--yes \
install \
"software-properties-common=0.96.24.32.22"
# Install Git
# The PPA is required to get a modern version of Git. The version in the Ubuntu 18.04 package repository is 2.17.1,
# while action/checkout@v3 requires 2.18 or higher.
RUN \
add-apt-repository \
--yes \
"ppa:git-core/ppa" && \
apt-get \
--yes \
update && \
\
apt-get \
--yes \
install \
"git" && \
\
apt-get \
--yes \
purge \
"software-properties-common"
"git"
# The repository path must be added to safe.directory, otherwise any Git operations on it would fail with a
# "dubious ownership" error. actions/checkout configures this, but it is not applied to containers.
@@ -51,18 +36,12 @@ ENV \
# Install Python
# The Python installed by actions/setup-python has dependency on a higher version of glibc than available in the
# ubuntu:18.04 container.
# container.
RUN \
apt-get \
--yes \
install \
"python3.8-minimal=3.8.0-3ubuntu1~18.04.2" && \
\
ln \
--symbolic \
--force \
"$(which python3.8)" \
"/usr/bin/python3"
"python3.7-minimal=3.7.3-2~18.10"
# Install Theia's package dependencies
# These are pre-installed in the GitHub Actions hosted runner machines.
@@ -70,43 +49,15 @@ RUN \
apt-get \
--yes \
install \
"libsecret-1-dev=0.18.6-1" \
"libx11-dev=2:1.6.4-3ubuntu0.4" \
"libsecret-1-dev=0.18.6-3" \
"libx11-dev=2:1.6.7-1" \
"libxkbfile-dev=1:1.0.9-2"
# Install Node.js
# It is necessary to use the "unofficial" linux-x64-glibc-217 build because the official Node.js 18.x is dynamically
# linked against glibc 2.28, while Ubuntu 18.04 has glibc 2.27.
ARG node_installation_path="/tmp/node-installation"
ARG artifact_name="node-v${node_version}-linux-x64-glibc-217"
# Target python3 symlink to Python 3.7 installation. It would otherwise target version 3.6 due to the installation of
# the `python3` package as a transitive dependency.
RUN \
mkdir "$node_installation_path" && \
cd "$node_installation_path" && \
\
apt-get \
--yes \
install \
"wget=1.19.4-1ubuntu2.2" && \
\
archive_name="${artifact_name}.tar.xz" && \
wget \
"https://unofficial-builds.nodejs.org/download/release/v${node_version}/${archive_name}" && \
\
apt-get \
--yes \
purge \
"wget" && \
\
tar \
--file="$archive_name" \
--extract && \
rm "$archive_name"
ENV PATH="${PATH}:${node_installation_path}/${artifact_name}/bin"
# Install Yarn
# Yarn is pre-installed in the GitHub Actions hosted runner machines.
RUN \
npm \
install \
--global \
"yarn@1.22.19"
ln \
--symbolic \
--force \
"$(which python3.7)" \
"/usr/bin/python3"

View File

@@ -48,9 +48,10 @@ env:
GO_VERSION: '1.21'
# See: https://github.com/actions/setup-node/#readme
NODE_VERSION: '18.17'
JOB_TRANSFER_ARTIFACT: build-artifacts
YARN_VERSION: '1.22'
JOB_TRANSFER_ARTIFACT_PREFIX: build-artifacts-
CHANGELOG_ARTIFACTS: changelog
STAGED_CHANNEL_FILES_ARTIFACT: staged-channel-files
STAGED_CHANNEL_FILE_ARTIFACT_PREFIX: staged-channel-file-
BASE_BUILD_DATA: |
- config:
# Human identifier for the job.
@@ -68,6 +69,8 @@ env:
certificate-extension: pfx
# Container for windows cert signing
certificate-container: INSTALLER_CERT_WINDOWS_CONTAINER
# Arbitrary identifier used to give the workflow artifact uploaded by each "build" matrix job a unique name.
job-transfer-artifact-suffix: Windows_64bit
# Quoting on the value is required here to allow the same comparison expression syntax to be used for this
# and the companion needs.select-targets.outputs.merge-channel-files property (output values always have string
# type).
@@ -91,6 +94,7 @@ env:
{
\"image\": \"ghcr.io/arduino/arduino-ide/linux:main\"
}
job-transfer-artifact-suffix: Linux_64bit
mergeable-channel-file: 'false'
artifacts:
- path: '*Linux_64bit.zip'
@@ -107,6 +111,7 @@ env:
certificate-secret: APPLE_SIGNING_CERTIFICATE_P12
certificate-password-secret: KEYCHAIN_PASSWORD
certificate-extension: p12
job-transfer-artifact-suffix: macOS_64bit
mergeable-channel-file: 'true'
artifacts:
- path: '*macOS_64bit.dmg'
@@ -121,6 +126,7 @@ env:
certificate-secret: APPLE_SIGNING_CERTIFICATE_P12
certificate-password-secret: KEYCHAIN_PASSWORD
certificate-extension: p12
job-transfer-artifact-suffix: macOS_arm64
mergeable-channel-file: 'true'
artifacts:
- path: '*macOS_arm64.dmg'
@@ -166,6 +172,7 @@ jobs:
is-nightly: ${{ steps.determination.outputs.is-nightly }}
channel-name: ${{ steps.determination.outputs.channel-name }}
publish-to-s3: ${{ steps.determination.outputs.publish-to-s3 }}
environment: production
permissions: {}
steps:
- name: Determine the type of build
@@ -197,7 +204,7 @@ jobs:
echo "is-nightly=$is_nightly" >> $GITHUB_OUTPUT
echo "channel-name=$channel_name" >> $GITHUB_OUTPUT
# Only attempt upload to Amazon S3 if the credentials are available.
echo "publish-to-s3=${{ secrets.AWS_SECRET_ACCESS_KEY != '' }}" >> $GITHUB_OUTPUT
echo "publish-to-s3=${{ secrets.AWS_ROLE_ARN != '' }}" >> $GITHUB_OUTPUT
select-targets:
needs: build-type-determination
@@ -233,7 +240,7 @@ jobs:
) | \
yq \
--output-format json \
'[.[].artifacts.[]]'
'map(.artifacts[] + (.config | pick(["job-transfer-artifact-suffix"])))'
)"
# The build matrix produces two macOS jobs (x86 and ARM) so the "channel update info files"
@@ -252,7 +259,7 @@ jobs:
echo "${{ env.BASE_BUILD_DATA }}" | \
yq \
--output-format json \
'[.[].artifacts.[]]'
'map(.artifacts[] + (.config | pick(["job-transfer-artifact-suffix"])))'
)"
merge_channel_files="false"
@@ -278,8 +285,6 @@ jobs:
- build-type-determination
- select-targets
env:
# https://github.blog/changelog/2024-03-07-github-actions-all-actions-will-run-on-node20-instead-of-node16-by-default/
ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION: true
# Location of artifacts generated by build.
BUILD_ARTIFACTS_PATH: electron-app/dist/build-artifacts
# to skip passing signing credentials to electron-builder
@@ -290,6 +295,7 @@ jobs:
SIGNTOOL_PATH: "C:/Program Files (x86)/Windows Kits/10/bin/10.0.19041.0/x86/signtool.exe"
WIN_CERT_PASSWORD: ${{ secrets[matrix.config.certificate-password-secret] }}
WIN_CERT_CONTAINER_NAME: ${{ secrets[matrix.config.certificate-container] }}
PUPPETEER_SKIP_DOWNLOAD: true
strategy:
matrix:
@@ -311,21 +317,26 @@ jobs:
if not exist "${{ matrix.config.working-directory }}" mklink /d "${{ matrix.config.working-directory }}" "C:\actions-runner\_work\arduino-ide\arduino-ide"
- name: Checkout
if: fromJSON(matrix.config.container) == null
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Checkout
# actions/checkout@v4 has dependency on a higher version of glibc than available in the Linux container.
if: fromJSON(matrix.config.container) != null
uses: actions/checkout@v3
- name: Install Node.js
if: fromJSON(matrix.config.container) == null && runner.name != 'WINDOWS-SIGN-PC'
if: runner.name != 'WINDOWS-SIGN-PC'
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
registry-url: 'https://registry.npmjs.org'
cache: 'yarn'
# Yarn is a prerequisite for the action's cache feature, so caching should be disabled when running in the
# container where Yarn is not pre-installed.
cache: ${{ fromJSON(matrix.config.container) == null && 'yarn' || null }}
- name: Install Yarn
if: runner.name != 'WINDOWS-SIGN-PC'
run: |
npm \
install \
--global \
"yarn@${{ env.YARN_VERSION }}"
- name: Install Python 3.x
if: fromJSON(matrix.config.container) == null && runner.name != 'WINDOWS-SIGN-PC'
@@ -334,41 +345,24 @@ jobs:
python-version: '3.11.x'
- name: Install Go
if: fromJSON(matrix.config.container) == null && runner.name != 'WINDOWS-SIGN-PC'
if: runner.name != 'WINDOWS-SIGN-PC'
uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
- name: Install Go
# actions/setup-go@v5 has dependency on a higher version of glibc than available in the Linux container.
if: fromJSON(matrix.config.container) != null && runner.name != 'WINDOWS-SIGN-PC'
uses: actions/setup-go@v4
with:
go-version: ${{ env.GO_VERSION }}
- name: Install Taskfile
if: fromJSON(matrix.config.container) == null && runner.name != 'WINDOWS-SIGN-PC'
if: runner.name != 'WINDOWS-SIGN-PC'
uses: arduino/setup-task@v2
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
version: 3.x
- name: Install Taskfile
# actions/setup-task@v2 has dependency on a higher version of glibc than available in the Linux container.
if: fromJSON(matrix.config.container) != null && runner.name != 'WINDOWS-SIGN-PC'
uses: arduino/setup-task@v1
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
version: 3.x
- name: Package
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
AC_USERNAME: ${{ secrets.AC_USERNAME }}
AC_PASSWORD: ${{ secrets.AC_PASSWORD }}
AC_TEAM_ID: ${{ secrets.AC_TEAM_ID }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
IS_NIGHTLY: ${{ needs.build-type-determination.outputs.is-nightly }}
IS_RELEASE: ${{ needs.build-type-determination.outputs.is-release }}
CAN_SIGN: ${{ secrets[matrix.config.certificate-secret] != '' }}
@@ -411,19 +405,19 @@ jobs:
echo "STAGED_CHANNEL_FILES_PATH=$staged_channel_files_path" >> "$GITHUB_ENV"
- name: Upload staged-for-merge channel file artifact
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
if: >
needs.select-targets.outputs.merge-channel-files == 'true' &&
matrix.config.mergeable-channel-file == 'true'
with:
if-no-files-found: error
name: ${{ env.STAGED_CHANNEL_FILES_ARTIFACT }}
name: ${{ env.STAGED_CHANNEL_FILE_ARTIFACT_PREFIX }}${{ matrix.config.job-transfer-artifact-suffix }}
path: ${{ matrix.config.working-directory && format('{0}/{1}', matrix.config.working-directory, env.STAGED_CHANNEL_FILES_PATH) || env.STAGED_CHANNEL_FILES_PATH }}
- name: Upload [GitHub Actions]
uses: actions/upload-artifact@v3
- name: Upload builds to job transfer artifact
uses: actions/upload-artifact@v4
with:
name: ${{ env.JOB_TRANSFER_ARTIFACT }}
name: ${{ env.JOB_TRANSFER_ARTIFACT_PREFIX }}${{ matrix.config.job-transfer-artifact-suffix }}
path: ${{ matrix.config.working-directory && format('{0}/{1}', matrix.config.working-directory, env.BUILD_ARTIFACTS_PATH) || env.BUILD_ARTIFACTS_PATH }}
- name: Manual Clean up for self-hosted runners
@@ -447,18 +441,19 @@ jobs:
echo "CHANNEL_FILES_PATH=${{ runner.temp }}/channel-files" >> "$GITHUB_ENV"
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Download staged-for-merge channel files artifact
uses: actions/download-artifact@v3
- name: Download staged-for-merge channel file artifacts
uses: actions/download-artifact@v5
with:
name: ${{ env.STAGED_CHANNEL_FILES_ARTIFACT }}
merge-multiple: true
path: ${{ env.CHANNEL_FILES_PATH }}
pattern: ${{ env.STAGED_CHANNEL_FILE_ARTIFACT_PREFIX }}*
- name: Remove no longer needed artifact
uses: geekyeggo/delete-artifact@v2
- name: Remove no longer needed artifacts
uses: geekyeggo/delete-artifact@v5
with:
name: ${{ env.STAGED_CHANNEL_FILES_ARTIFACT }}
name: ${{ env.STAGED_CHANNEL_FILE_ARTIFACT_PREFIX }}*
- name: Install Node.js
uses: actions/setup-node@v4
@@ -478,6 +473,12 @@ jobs:
repo-token: ${{ secrets.GITHUB_TOKEN }}
version: 3.x
- name: Install dependencies (Linux only)
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y libx11-dev libxkbfile-dev libsecret-1-dev
- name: Install dependencies
run: yarn
@@ -488,11 +489,11 @@ jobs:
--channel "${{ needs.build-type-determination.outputs.channel-name }}" \
--input "${{ env.CHANNEL_FILES_PATH }}"
- name: Upload merged channel files to job transfer artifact
uses: actions/upload-artifact@v3
- name: Upload merged channel files job transfer artifact
uses: actions/upload-artifact@v4
with:
if-no-files-found: error
name: ${{ env.JOB_TRANSFER_ARTIFACT }}
name: ${{ env.JOB_TRANSFER_ARTIFACT_PREFIX }}channel-files
path: ${{ env.CHANNEL_FILES_PATH }}
artifacts:
@@ -503,22 +504,25 @@ jobs:
if: always() && needs.build.result != 'skipped'
runs-on: ubuntu-latest
env:
BUILD_ARTIFACTS_FOLDER: build-artifacts
strategy:
matrix:
artifact: ${{ fromJson(needs.select-targets.outputs.artifact-matrix) }}
steps:
- name: Download job transfer artifact
uses: actions/download-artifact@v3
- name: Download job transfer artifact that contains ${{ matrix.artifact.name }} tester build
uses: actions/download-artifact@v5
with:
name: ${{ env.JOB_TRANSFER_ARTIFACT }}
path: ${{ env.JOB_TRANSFER_ARTIFACT }}
name: ${{ env.JOB_TRANSFER_ARTIFACT_PREFIX }}${{ matrix.artifact.job-transfer-artifact-suffix }}
path: ${{ env.BUILD_ARTIFACTS_FOLDER }}
- name: Upload tester build artifact
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.artifact.name }}
path: ${{ env.JOB_TRANSFER_ARTIFACT }}/${{ matrix.artifact.path }}
path: ${{ env.BUILD_ARTIFACTS_FOLDER }}/${{ matrix.artifact.path }}
changelog:
needs:
@@ -529,7 +533,7 @@ jobs:
BODY: ${{ steps.changelog.outputs.BODY }}
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
fetch-depth: 0 # To fetch all history for all branches and tags.
@@ -561,11 +565,11 @@ jobs:
echo "$BODY" > CHANGELOG.txt
- name: Upload Changelog [GitHub Actions]
- name: Upload changelog job transfer artifact
if: needs.build-type-determination.outputs.is-nightly == 'true'
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: ${{ env.JOB_TRANSFER_ARTIFACT }}
name: ${{ env.JOB_TRANSFER_ARTIFACT_PREFIX }}changelog
path: CHANGELOG.txt
publish:
@@ -584,22 +588,33 @@ jobs:
needs.build-type-determination.outputs.publish-to-s3 == 'true' &&
needs.build-type-determination.outputs.is-nightly == 'true'
runs-on: ubuntu-latest
env:
ARTIFACTS_FOLDER: build-artifacts
environment: production
permissions:
id-token: write
contents: read
steps:
- name: Download [GitHub Actions]
uses: actions/download-artifact@v3
- name: Download all job transfer artifacts
uses: actions/download-artifact@v5
with:
name: ${{ env.JOB_TRANSFER_ARTIFACT }}
path: ${{ env.JOB_TRANSFER_ARTIFACT }}
merge-multiple: true
path: ${{ env.ARTIFACTS_FOLDER }}
pattern: ${{ env.JOB_TRANSFER_ARTIFACT_PREFIX }}*
- name: Configure AWS Credentials for Nightly [S3]
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: us-east-1
- name: Publish Nightly [S3]
uses: docker://plugins/s3
env:
PLUGIN_SOURCE: '${{ env.JOB_TRANSFER_ARTIFACT }}/*'
PLUGIN_STRIP_PREFIX: '${{ env.JOB_TRANSFER_ARTIFACT }}/'
PLUGIN_TARGET: '/arduino-ide/nightly'
PLUGIN_BUCKET: ${{ secrets.DOWNLOADS_BUCKET }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
run: |
aws s3 sync ${{ env.ARTIFACTS_FOLDER }} s3://${{ secrets.DOWNLOADS_BUCKET }}/arduino-ide/nightly
release:
needs:
@@ -616,12 +631,23 @@ jobs:
needs.changelog.result == 'success' &&
needs.build-type-determination.outputs.is-release == 'true'
runs-on: ubuntu-latest
env:
ARTIFACTS_FOLDER: build-artifacts
environment: production
permissions:
id-token: write
contents: write
steps:
- name: Download [GitHub Actions]
uses: actions/download-artifact@v3
- name: Download all job transfer artifacts
uses: actions/download-artifact@v5
with:
name: ${{ env.JOB_TRANSFER_ARTIFACT }}
path: ${{ env.JOB_TRANSFER_ARTIFACT }}
merge-multiple: true
path: ${{ env.ARTIFACTS_FOLDER }}
pattern: ${{ env.JOB_TRANSFER_ARTIFACT_PREFIX }}*
- name: Get Tag
id: tag_name
@@ -633,21 +659,22 @@ jobs:
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
release_name: ${{ steps.tag_name.outputs.TAG_NAME }}
file: ${{ env.JOB_TRANSFER_ARTIFACT }}/*
file: ${{ env.ARTIFACTS_FOLDER }}/*
tag: ${{ github.ref }}
file_glob: true
body: ${{ needs.changelog.outputs.BODY }}
- name: Configure AWS Credentials for Release [S3]
if: needs.build-type-determination.outputs.publish-to-s3 == 'true'
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: us-east-1
- name: Publish Release [S3]
if: needs.build-type-determination.outputs.publish-to-s3 == 'true'
uses: docker://plugins/s3
env:
PLUGIN_SOURCE: '${{ env.JOB_TRANSFER_ARTIFACT }}/*'
PLUGIN_STRIP_PREFIX: '${{ env.JOB_TRANSFER_ARTIFACT }}/'
PLUGIN_TARGET: '/arduino-ide'
PLUGIN_BUCKET: ${{ secrets.DOWNLOADS_BUCKET }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
run: |
aws s3 sync ${{ env.ARTIFACTS_FOLDER }} s3://${{ secrets.DOWNLOADS_BUCKET }}/arduino-ide
clean:
# This job must run after all jobs that use the transfer artifact.
@@ -661,7 +688,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Remove unneeded job transfer artifact
uses: geekyeggo/delete-artifact@v2
- name: Remove unneeded job transfer artifacts
uses: geekyeggo/delete-artifact@v5
with:
name: ${{ env.JOB_TRANSFER_ARTIFACT }}
name: ${{ env.JOB_TRANSFER_ARTIFACT_PREFIX }}*

View File

@@ -40,10 +40,10 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Build and push to local registry
uses: docker/build-push-action@v5
uses: docker/build-push-action@v6
with:
context: .
file: ${{ matrix.image.path }}

View File

@@ -56,7 +56,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Install Node.js 18.17
uses: actions/setup-node@v4
@@ -76,6 +76,12 @@ jobs:
repo-token: ${{ secrets.GITHUB_TOKEN }}
version: 3.x
- name: Install dependencies (Linux only)
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y libx11-dev libxkbfile-dev libsecret-1-dev
- name: Install dependencies
run: yarn install --immutable
env:

View File

@@ -65,7 +65,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Setup Node.js
uses: actions/setup-node@v4
@@ -73,6 +73,12 @@ jobs:
cache: yarn
node-version: ${{ env.NODE_VERSION }}
- name: Install Dependencies (Linux only)
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y libx11-dev libxkbfile-dev libsecret-1-dev
- name: Install npm package dependencies
env:
# Avoid failure of @vscode/ripgrep installation due to GitHub API rate limiting:

View File

@@ -64,7 +64,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Setup Node.js
uses: actions/setup-node@v4
@@ -72,6 +72,12 @@ jobs:
cache: yarn
node-version: ${{ env.NODE_VERSION }}
- name: Install Dependencies (Linux only)
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y libx11-dev libxkbfile-dev libsecret-1-dev
- name: Install npm package dependencies
env:
# Avoid failure of @vscode/ripgrep installation due to GitHub API rate limiting:

View File

@@ -14,9 +14,14 @@ jobs:
create-changelog:
if: github.repository == 'arduino/arduino-ide'
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
environment: production
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Install Node.js
uses: actions/setup-node@v4
@@ -24,6 +29,12 @@ jobs:
node-version: ${{ env.NODE_VERSION }}
registry-url: 'https://registry.npmjs.org'
- name: Install Dependencies (Linux only)
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y libx11-dev libxkbfile-dev libsecret-1-dev
- name: Get Tag
id: tag_name
run: |
@@ -44,12 +55,12 @@ jobs:
# Compose changelog
yarn run compose-changelog "${{ github.workspace }}/${{ env.CHANGELOG_ARTIFACTS }}/$CHANGELOG_FILE_NAME"
- name: Configure AWS Credentials for Changelog [S3]
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: us-east-1
- name: Publish Changelog [S3]
uses: docker://plugins/s3
env:
PLUGIN_SOURCE: '${{ env.CHANGELOG_ARTIFACTS }}/*'
PLUGIN_STRIP_PREFIX: '${{ env.CHANGELOG_ARTIFACTS }}/'
PLUGIN_TARGET: '/arduino-ide/changelog'
PLUGIN_BUCKET: ${{ secrets.DOWNLOADS_BUCKET }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
run: |
aws s3 sync ${{ env.CHANGELOG_ARTIFACTS }} s3://${{ secrets.DOWNLOADS_BUCKET }}/arduino-ide/changelog

View File

@@ -14,7 +14,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Install Node.js 18.17
uses: actions/setup-node@v4
@@ -34,6 +34,12 @@ jobs:
repo-token: ${{ secrets.GITHUB_TOKEN }}
version: 3.x
- name: Install dependencies (Linux only)
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y libx11-dev libxkbfile-dev libsecret-1-dev
- name: Install dependencies
run: yarn install --immutable

View File

@@ -14,7 +14,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Install Node.js 18.17
uses: actions/setup-node@v4
@@ -34,6 +34,12 @@ jobs:
repo-token: ${{ secrets.GITHUB_TOKEN }}
version: 3.x
- name: Install dependencies (Linux only)
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y libx11-dev libxkbfile-dev libsecret-1-dev
- name: Install dependencies
run: yarn install --immutable
@@ -46,7 +52,7 @@ jobs:
TRANSIFEX_API_KEY: ${{ secrets.TRANSIFEX_API_KEY }}
- name: Create Pull Request
uses: peter-evans/create-pull-request@v5
uses: peter-evans/create-pull-request@v7
with:
commit-message: Updated translation files
title: Update translation files

View File

@@ -43,7 +43,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Log in to the Container registry
uses: docker/login-action@v3
@@ -59,7 +59,7 @@ jobs:
images: ${{ matrix.image.registry }}/${{ matrix.image.name }}
- name: Build and push image
uses: docker/build-push-action@v5
uses: docker/build-push-action@v6
with:
context: .
file: ${{ matrix.image.path }}

View File

@@ -19,7 +19,7 @@ on:
env:
CONFIGURATIONS_FOLDER: .github/label-configuration-files
CONFIGURATIONS_ARTIFACT: label-configuration-files
CONFIGURATIONS_ARTIFACT_PREFIX: label-configuration-file-
jobs:
check:
@@ -27,7 +27,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Download JSON schema for labels configuration file
id: download-schema
@@ -71,13 +71,13 @@ jobs:
file-url: https://raw.githubusercontent.com/arduino/tooling-project-assets/main/workflow-templates/assets/sync-labels/${{ matrix.filename }}
- name: Pass configuration files to next job via workflow artifact
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
path: |
*.yaml
*.yml
if-no-files-found: error
name: ${{ env.CONFIGURATIONS_ARTIFACT }}
name: ${{ env.CONFIGURATIONS_ARTIFACT_PREFIX }}${{ matrix.filename }}
sync:
needs: download
@@ -106,18 +106,19 @@ jobs:
echo "flag=--dry-run" >> $GITHUB_OUTPUT
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Download configuration files artifact
uses: actions/download-artifact@v3
- name: Download configuration file artifacts
uses: actions/download-artifact@v5
with:
name: ${{ env.CONFIGURATIONS_ARTIFACT }}
merge-multiple: true
pattern: ${{ env.CONFIGURATIONS_ARTIFACT_PREFIX }}*
path: ${{ env.CONFIGURATIONS_FOLDER }}
- name: Remove unneeded artifact
uses: geekyeggo/delete-artifact@v2
- name: Remove unneeded artifacts
uses: geekyeggo/delete-artifact@v5
with:
name: ${{ env.CONFIGURATIONS_ARTIFACT }}
name: ${{ env.CONFIGURATIONS_ARTIFACT_PREFIX }}*
- name: Merge label configuration files
run: |

View File

@@ -82,7 +82,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Setup Node.js
uses: actions/setup-node@v4
@@ -107,6 +107,12 @@ jobs:
repo-token: ${{ secrets.GITHUB_TOKEN }}
version: 3.x
- name: Install Dependencies (Linux only)
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y libx11-dev libxkbfile-dev libsecret-1-dev
- name: Install npm package dependencies
env:
# Avoid failure of @vscode/ripgrep installation due to GitHub API rate limiting:

View File

@@ -16,7 +16,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Install Node.js
uses: actions/setup-node@v4
@@ -36,6 +36,12 @@ jobs:
repo-token: ${{ secrets.GITHUB_TOKEN }}
version: 3.x
- name: Install dependencies (Linux only)
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y libx11-dev libxkbfile-dev libsecret-1-dev
- name: Install dependencies
run: yarn install --immutable
@@ -55,7 +61,7 @@ jobs:
run: yarn run themes:generate
- name: Create Pull Request
uses: peter-evans/create-pull-request@v5
uses: peter-evans/create-pull-request@v7
with:
commit-message: Updated themes
title: Update themes

View File

@@ -46,9 +46,9 @@ See [**the contributor guide**](docs/CONTRIBUTING.md#contributor-guide) for more
See the [**development guide**](docs/development.md) for a technical overview of the application and instructions for building the code.
## Donations
### Support the project
This open source code was written by the Arduino team and is maintained on a daily basis with the help of the community. We invest a considerable amount of time in development, testing and optimization. Please consider [donating](https://www.arduino.cc/en/donate/) or [sponsoring](https://github.com/sponsors/arduino) to support our work, as well as [buying original Arduino boards](https://store.arduino.cc/) which is the best way to make sure our effort can continue in the long term.
This open source code was written by the Arduino team and is maintained on a daily basis with the help of the community. We invest a considerable amount of time in development, testing and optimization. Please consider [buying original Arduino boards](https://store.arduino.cc/) to support our work on the project.
## License

View File

@@ -1,6 +1,6 @@
{
"name": "arduino-ide-extension",
"version": "2.3.4",
"version": "2.3.7",
"description": "An extension for Theia building the Arduino IDE",
"license": "AGPL-3.0-or-later",
"scripts": {
@@ -24,28 +24,29 @@
},
"dependencies": {
"@grpc/grpc-js": "^1.8.14",
"@theia/application-package": "1.41.0",
"@theia/core": "1.41.0",
"@theia/debug": "1.41.0",
"@theia/editor": "1.41.0",
"@theia/electron": "1.41.0",
"@theia/filesystem": "1.41.0",
"@theia/keymaps": "1.41.0",
"@theia/markers": "1.41.0",
"@theia/messages": "1.41.0",
"@theia/monaco": "1.41.0",
"@theia/monaco-editor-core": "1.72.3",
"@theia/navigator": "1.41.0",
"@theia/outline-view": "1.41.0",
"@theia/output": "1.41.0",
"@theia/plugin-ext": "1.41.0",
"@theia/plugin-ext-vscode": "1.41.0",
"@theia/preferences": "1.41.0",
"@theia/scm": "1.41.0",
"@theia/search-in-workspace": "1.41.0",
"@theia/terminal": "1.41.0",
"@theia/typehierarchy": "1.41.0",
"@theia/workspace": "1.41.0",
"@theia/application-package": "1.57.0",
"@theia/core": "1.57.0",
"@theia/debug": "1.57.0",
"@theia/editor": "1.57.0",
"@theia/electron": "1.57.0",
"@theia/filesystem": "1.57.0",
"@theia/keymaps": "1.57.0",
"@theia/markers": "1.57.0",
"@theia/messages": "1.57.0",
"@theia/monaco": "1.57.0",
"@theia/monaco-editor-core": "1.83.101",
"@theia/navigator": "1.57.0",
"@theia/outline-view": "1.57.0",
"@theia/output": "1.57.0",
"@theia/plugin-ext": "1.57.0",
"@theia/plugin-ext-vscode": "1.57.0",
"@theia/preferences": "1.57.0",
"@theia/scm": "1.57.0",
"@theia/search-in-workspace": "1.57.0",
"@theia/terminal": "1.57.0",
"@theia/test": "1.57.0",
"@theia/typehierarchy": "1.57.0",
"@theia/workspace": "1.57.0",
"@tippyjs/react": "^4.2.5",
"@types/auth0-js": "^9.21.3",
"@types/btoa": "^1.2.3",
@@ -57,7 +58,6 @@
"@types/node-fetch": "^2.5.7",
"@types/p-queue": "^2.3.1",
"@types/ps-tree": "^1.1.0",
"@types/react-tabs": "^2.3.2",
"@types/temp": "^0.8.34",
"arduino-serial-plotter-webapp": "0.2.0",
"async-mutex": "^0.3.0",
@@ -98,7 +98,7 @@
"react-markdown": "^8.0.0",
"react-perfect-scrollbar": "^1.5.8",
"react-select": "^5.6.0",
"react-tabs": "^3.1.2",
"react-tabs": "^6.1.0",
"react-window": "^1.8.6",
"semver": "^7.3.2",
"string-natural-compare": "^2.0.3",
@@ -125,7 +125,7 @@
"mockdate": "^3.0.5",
"moment": "^2.24.0",
"ncp": "^2.0.0",
"rimraf": "^2.6.1"
"rimraf": "^5.0.0"
},
"optionalDependencies": {
"@pingghost/protoc": "^1.0.2",
@@ -171,7 +171,7 @@
],
"arduino": {
"arduino-cli": {
"version": "1.1.0"
"version": "1.2.0"
},
"arduino-fwuploader": {
"version": "2.4.1"

View File

@@ -1,7 +1,7 @@
// @ts-check
// The version to use.
const version = '1.10.1';
const version = '1.10.2';
(async () => {
const os = require('node:os');

View File

@@ -3,10 +3,12 @@
(async () => {
const os = require('node:os');
const path = require('node:path');
const { mkdirSync, promises: fs, rmSync } = require('node:fs');
const decompress = require('decompress');
const unzip = require('decompress-unzip');
const { mkdirSync, promises: fs, rmSync, existsSync } = require('node:fs');
const { exec } = require('./utils');
const { glob } = require('glob');
const { SemVer, gte, valid: validSemVer, gt } = require('semver');
const { SemVer, gte, valid: validSemVer, eq } = require('semver');
// Use a node-protoc fork until apple arm32 is supported
// https://github.com/YePpHa/node-protoc/pull/10
const protoc = path.dirname(require('@pingghost/protoc/protoc'));
@@ -90,152 +92,198 @@
}
*/
const versionObject = JSON.parse(versionJson);
const version = versionObject.VersionString;
// Clone the repository and check out the tagged version
// Return folder with proto files
async function getProtoPath(forceCliVersion) {
const repository = await fs.mkdtemp(path.join(os.tmpdir(), 'arduino-cli-'));
async function globProtos(folder, pattern = '**/*.proto') {
let protos = [];
try {
const matches = await glob(pattern, { cwd: folder });
protos = matches.map((filename) => path.join(folder, filename));
} catch (error) {
console.log(error.stack ?? error.message);
}
return protos;
}
async function getProtosFromRepo(
commitish = '',
version = '',
owner = 'arduino',
repo = 'arduino-cli'
) {
const repoFolder = await fs.mkdtemp(path.join(os.tmpdir(), 'arduino-cli-'));
const url = `https://github.com/${owner}/${repo}.git`;
console.log(`>>> Cloning repository from '${url}'...`);
exec('git', ['clone', url, repository], { logStdout: true });
exec('git', ['clone', url, repoFolder], { logStdout: true });
console.log(`<<< Repository cloned.`);
let cliVersion = forceCliVersion || version;
if (validSemVer(cliVersion)) {
if (validSemVer(version)) {
let versionTag = version;
// https://github.com/arduino/arduino-cli/pull/2374
if (
gte(new SemVer(version, { loose: true }), new SemVer('0.35.0-rc.1'))
) {
cliVersion = `v${cliVersion}`;
versionTag = `v${version}`;
}
console.log(`>>> Checking out tagged version: '${cliVersion}'...`);
exec('git', ['-C', repository, 'fetch', '--all', '--tags'], {
console.log(`>>> Checking out tagged version: '${versionTag}'...`);
exec('git', ['-C', repoFolder, 'fetch', '--all', '--tags'], {
logStdout: true,
});
exec(
'git',
['-C', repository, 'checkout', `tags/${cliVersion}`, '-b', cliVersion],
['-C', repoFolder, 'checkout', `tags/${versionTag}`, '-b', versionTag],
{ logStdout: true }
);
console.log(`<<< Checked out tagged version: '${cliVersion}'.`);
} else if (forceCliVersion) {
console.log(`WARN: invalid semver: '${forceCliVersion}'.`);
// If the forced version is invalid, do not proceed with fallbacks.
return undefined;
console.log(`<<< Checked out tagged version: '${versionTag}'.`);
} else if (commitish) {
console.log(
`>>> Checking out commitish from 'package.json': '${commitish}'...`
);
exec('git', ['-C', repository, 'checkout', commitish], {
console.log(`>>> Checking out commitish: '${commitish}'...`);
exec('git', ['-C', repoFolder, 'checkout', commitish], {
logStdout: true,
});
console.log(
`<<< Checked out commitish from 'package.json': '${commitish}'.`
);
} else if (versionObject.Commit) {
console.log(
`>>> Checking out commitish from the CLI: '${versionObject.Commit}'...`
);
exec('git', ['-C', repository, 'checkout', versionObject.Commit], {
logStdout: true,
});
console.log(
`<<< Checked out commitish from the CLI: '${versionObject.Commit}'.`
);
console.log(`<<< Checked out commitish: '${commitish}'.`);
} else {
console.log(
`WARN: no 'git checkout'. Generating from the HEAD revision.`
);
}
return path.join(repository, 'rpc');
const rpcFolder = await fs.mkdtemp(
path.join(os.tmpdir(), 'arduino-cli-rpc')
);
// Copy the the repository rpc folder so we can remove the repository
await fs.cp(path.join(repoFolder, 'rpc'), path.join(rpcFolder), {
recursive: true,
});
rmSync(repoFolder, { recursive: true, maxRetries: 5, force: true });
// Patch for https://github.com/arduino/arduino-cli/issues/2755
// Google proto files are removed from source since v1.1.0
if (!existsSync(path.join(rpcFolder, 'google'))) {
// Include packaged google proto files from v1.1.1
// See https://github.com/arduino/arduino-cli/pull/2761
console.log(`>>> Missing google proto files. Including from v1.1.1...`);
const v111ProtoFolder = await getProtosFromZip('1.1.1');
// Create an return a folder name google in rpcFolder
const googleFolder = path.join(rpcFolder, 'google');
await fs.cp(path.join(v111ProtoFolder, 'google'), googleFolder, {
recursive: true,
});
console.log(`<<< Included google proto files from v1.1.1.`);
}
return rpcFolder;
}
const protoPath = await getProtoPath();
async function getProtosFromZip(version) {
if (!version) {
console.log(`Could not download proto files: CLI version not provided.`);
process.exit(1);
}
console.log(`>>> Downloading proto files from zip for ${version}.`);
if (!protoPath) {
console.log(`Could not find the proto files folder.`);
const url = `https://downloads.arduino.cc/arduino-cli/arduino-cli_${version}_proto.zip`;
const protos = await fs.mkdtemp(
path.join(os.tmpdir(), 'arduino-cli-proto')
);
const { default: download } = await import('@xhmikosr/downloader');
/** @type {import('node:buffer').Buffer} */
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
const data = await download(url);
await decompress(data, protos, {
plugins: [unzip()],
filter: (file) => file.path.endsWith('.proto'),
});
console.log(
`<<< Finished downloading and extracting proto files for ${version}.`
);
return protos;
}
let protosFolder;
if (commitish) {
protosFolder = await getProtosFromRepo(commitish, undefined, owner, repo);
} else if (
versionObject.VersionString &&
validSemVer(versionObject.VersionString)
) {
const version = versionObject.VersionString;
// v1.1.0 does not contains google proto files in zip
// See https://github.com/arduino/arduino-cli/issues/2755
const isV110 = eq(new SemVer(version, { loose: true }), '1.1.0');
protosFolder = isV110
? await getProtosFromRepo(undefined, version)
: await getProtosFromZip(version);
} else if (versionObject.Commit) {
protosFolder = await getProtosFromRepo(versionObject.Commit);
}
if (!protosFolder) {
console.log(`Could not get proto files: missing commitish or version.`);
process.exit(1);
}
const protos = await globProtos(protosFolder);
if (!protos || protos.length === 0) {
rmSync(protosFolder, { recursive: true, maxRetries: 5, force: true });
console.log(`Could not find any .proto files under ${protosFolder}.`);
process.exit(1);
}
console.log('>>> Generating TS/JS API from:');
exec('git', ['-C', protoPath, 'rev-parse', '--abbrev-ref', 'HEAD'], {
logStdout: true,
});
const out = path.join(__dirname, '..', 'src', 'node', 'cli-protocol');
// Must wipe the gen output folder. Otherwise, dangling service implementation remain in IDE2 code,
// although it has been removed from the proto file.
// For example, https://github.com/arduino/arduino-cli/commit/50a8bf5c3e61d5b661ccfcd6a055e82eeb510859.
rmSync(out, { recursive: true, maxRetries: 5, force: true });
// rmSync(out, { recursive: true, maxRetries: 5, force: true });
mkdirSync(out, { recursive: true });
if (gt(new SemVer(version, { loose: true }), new SemVer('1.0.4'))) {
// Patch for https://github.com/arduino/arduino-cli/issues/2755
// Credit https://github.com/dankeboy36/ardunno-cli-gen/pull/9/commits/64a5ac89aae605249261c8ceff7255655ecfafca
// Download the 1.0.4 version and use the missing google/rpc/status.proto file.
console.log('<<< Generating missing google proto files');
const v104ProtoPath = await getProtoPath('1.0.4');
if (!v104ProtoPath) {
console.log(`Could not find the proto files folder for version 1.0.4.`);
process.exit(1);
}
await fs.cp(
path.join(v104ProtoPath, 'google'),
path.join(protoPath, 'google'),
{
recursive: true,
}
);
console.log(`>>> Generated missing google file`);
}
let protos = [];
try {
const matches = await glob('**/*.proto', { cwd: protoPath });
protos = matches.map((filename) => path.join(protoPath, filename));
// Generate JS code from the `.proto` files.
exec(
'grpc_tools_node_protoc',
[
`--js_out=import_style=commonjs,binary:${out}`,
`--grpc_out=generate_package_definition:${out}`,
'-I',
protosFolder,
...protos,
],
{ logStdout: true }
);
// Generate the `.d.ts` files for JS.
exec(
path.join(protoc, `protoc${platform === 'win32' ? '.exe' : ''}`),
[
`--plugin=protoc-gen-ts=${path.resolve(
__dirname,
'..',
'node_modules',
'.bin',
`protoc-gen-ts${platform === 'win32' ? '.cmd' : ''}`
)}`,
`--ts_out=generate_package_definition:${out}`,
'-I',
protosFolder,
...protos,
],
{ logStdout: true }
);
} catch (error) {
console.log(error.stack ?? error.message);
console.log(error);
} finally {
rmSync(protosFolder, { recursive: true, maxRetries: 5, force: true });
}
if (!protos || protos.length === 0) {
console.log(`Could not find any .proto files under ${protoPath}.`);
process.exit(1);
}
// Generate JS code from the `.proto` files.
exec(
'grpc_tools_node_protoc',
[
`--js_out=import_style=commonjs,binary:${out}`,
`--grpc_out=generate_package_definition:${out}`,
'-I',
protoPath,
...protos,
],
{ logStdout: true }
);
// Generate the `.d.ts` files for JS.
exec(
path.join(protoc, `protoc${platform === 'win32' ? '.exe' : ''}`),
[
`--plugin=protoc-gen-ts=${path.resolve(
__dirname,
'..',
'node_modules',
'.bin',
`protoc-gen-ts${platform === 'win32' ? '.cmd' : ''}`
)}`,
`--ts_out=generate_package_definition:${out}`,
'-I',
protoPath,
...protos,
],
{ logStdout: true }
);
console.log('<<< Generation was successful.');
})();

View File

@@ -1,7 +1,7 @@
import { ColorContribution } from '@theia/core/lib/browser/color-application-contribution';
import { ColorRegistry } from '@theia/core/lib/browser/color-registry';
import { CommonMenus } from '@theia/core/lib/browser/common-frontend-contribution';
import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application';
import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application-contribution';
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
import {
TabBarToolbarContribution,

View File

@@ -1,18 +1,12 @@
import '../../src/browser/style/index.css';
import {
Container,
ContainerModule,
interfaces,
} from '@theia/core/shared/inversify';
import { Container, ContainerModule } from '@theia/core/shared/inversify';
import { WidgetFactory } from '@theia/core/lib/browser/widget-manager';
import { CommandContribution } from '@theia/core/lib/common/command';
import { bindViewContribution } from '@theia/core/lib/browser/shell/view-contribution';
import { TabBarToolbarContribution } from '@theia/core/lib/browser/shell/tab-bar-toolbar';
import { WebSocketConnectionProvider } from '@theia/core/lib/browser/messaging/ws-connection-provider';
import {
FrontendApplicationContribution,
FrontendApplication as TheiaFrontendApplication,
} from '@theia/core/lib/browser/frontend-application';
import { FrontendApplication as TheiaFrontendApplication } from '@theia/core/lib/browser/frontend-application';
import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application-contribution';
import { LibraryListWidget } from './library/library-list-widget';
import { ArduinoFrontendContribution } from './arduino-frontend-contribution';
import {
@@ -57,8 +51,6 @@ import {
DockPanelRenderer as TheiaDockPanelRenderer,
TabBarRendererFactory,
ContextMenuRenderer,
createTreeContainer,
TreeWidget,
} from '@theia/core/lib/browser';
import { MenuContribution } from '@theia/core/lib/common/menu';
import {
@@ -97,7 +89,6 @@ import {
ArduinoDaemonPath,
ArduinoDaemon,
} from '../common/protocol/arduino-daemon';
import { EditorCommandContribution as TheiaEditorCommandContribution } from '@theia/editor/lib/browser';
import {
FrontendConnectionStatusService,
ApplicationConnectionStatusContribution,
@@ -131,7 +122,10 @@ import { OpenSketch } from './contributions/open-sketch';
import { Close } from './contributions/close';
import { SaveAsSketch } from './contributions/save-as-sketch';
import { SaveSketch } from './contributions/save-sketch';
import { VerifySketch } from './contributions/verify-sketch';
import {
CompileSummaryProvider,
VerifySketch,
} from './contributions/verify-sketch';
import { UploadSketch } from './contributions/upload-sketch';
import { CommonFrontendContribution } from './theia/core/common-frontend-contribution';
import { EditContributions } from './contributions/edit-contributions';
@@ -183,7 +177,6 @@ import {
import { About } from './contributions/about';
import { IconThemeService } from '@theia/core/lib/browser/icon-theme-service';
import { TabBarRenderer } from './theia/core/tab-bars';
import { EditorCommandContribution } from './theia/editor/editor-command';
import { NavigatorTabBarDecorator as TheiaNavigatorTabBarDecorator } from '@theia/navigator/lib/browser/navigator-tab-bar-decorator';
import { NavigatorTabBarDecorator } from './theia/navigator/navigator-tab-bar-decorator';
import { Debug, DebugDisabledStatusMessageSource } from './contributions/debug';
@@ -272,7 +265,7 @@ import {
IDEUpdaterDialog,
IDEUpdaterDialogProps,
} from './dialogs/ide-updater/ide-updater-dialog';
import { ElectronIpcConnectionProvider } from '@theia/core/lib/electron-browser/messaging/electron-ipc-connection-provider';
import { ElectronIpcConnectionProvider } from '@theia/core/lib/electron-browser/messaging/electron-ipc-connection-source';
import { MonitorModel } from './monitor-model';
import { MonitorManagerProxyClientImpl } from './monitor-manager-proxy-client-impl';
import { EditorManager as TheiaEditorManager } from '@theia/editor/lib/browser/editor-manager';
@@ -292,10 +285,6 @@ import { PreferenceTreeGenerator } from './theia/preferences/preference-tree-gen
import { PreferenceTreeGenerator as TheiaPreferenceTreeGenerator } from '@theia/preferences/lib/browser/util/preference-tree-generator';
import { AboutDialog } from './theia/core/about-dialog';
import { AboutDialog as TheiaAboutDialog } from '@theia/core/lib/browser/about-dialog';
import {
SurveyNotificationService,
SurveyNotificationServicePath,
} from '../common/protocol/survey-service';
import { WindowContribution } from './theia/core/window-contribution';
import { WindowContribution as TheiaWindowContribution } from '@theia/core/lib/browser/window-contribution';
import { CoreErrorHandler } from './contributions/core-error-handler';
@@ -378,15 +367,9 @@ import { DebugSessionWidget } from '@theia/debug/lib/browser/view/debug-session-
import { DebugConfigurationWidget } from './theia/debug/debug-configuration-widget';
import { DebugConfigurationWidget as TheiaDebugConfigurationWidget } from '@theia/debug/lib/browser/view/debug-configuration-widget';
import { DebugToolBar } from '@theia/debug/lib/browser/view/debug-toolbar-widget';
import {
PluginTree,
PluginTreeModel,
TreeViewWidgetOptions,
VIEW_ITEM_CONTEXT_MENU,
} from '@theia/plugin-ext/lib/main/browser/view/tree-view-widget';
import { TreeViewDecoratorService } from '@theia/plugin-ext/lib/main/browser/view/tree-view-decorator-service';
import { PLUGIN_VIEW_DATA_FACTORY_ID } from '@theia/plugin-ext/lib/main/browser/view/plugin-view-registry';
import { TreeViewWidget } from './theia/plugin-ext/tree-view-widget';
import { TestViewContribution as TheiaTestViewContribution } from '@theia/test/lib/browser/view/test-view-contribution';
import { TestViewContribution } from './theia/test/test-view-contribution';
// Hack to fix copy/cut/paste issue after electron version update in Theia.
// https://github.com/eclipse-theia/theia/issues/12487
@@ -567,15 +550,6 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
WorkspaceVariableContribution
);
bind(SurveyNotificationService)
.toDynamicValue((context) => {
return ElectronIpcConnectionProvider.createProxy(
context.container,
SurveyNotificationServicePath
);
})
.inSingletonScope();
// Layout and shell customizations.
rebind(TheiaOutlineViewContribution)
.to(OutlineViewContribution)
@@ -784,6 +758,8 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
Contribution.configure(bind, BoardsDataMenuUpdater);
Contribution.configure(bind, AutoSelectProgrammer);
bind(CompileSummaryProvider).toService(VerifySketch);
bindContributionProvider(bind, StartupTaskProvider);
bind(StartupTaskProvider).toService(BoardsServiceProvider); // to reuse the boards config in another window
@@ -847,13 +823,6 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
);
});
// Workaround for https://github.com/eclipse-theia/theia/issues/8722
// Do not trigger a save on IDE startup if `"editor.autoSave": "on"` was set as a preference.
// Note: `"editor.autoSave" was renamed to `"files.autoSave" and `"on"` was replaced with three
// different cases, but we treat `!== 'off'` as auto save enabled. (https://github.com/eclipse-theia/theia/issues/10812)
bind(EditorCommandContribution).toSelf().inSingletonScope();
rebind(TheiaEditorCommandContribution).toService(EditorCommandContribution);
// Silent the badge decoration in the Explorer view.
bind(NavigatorTabBarDecorator).toSelf().inSingletonScope();
rebind(TheiaNavigatorTabBarDecorator).toService(NavigatorTabBarDecorator);
@@ -1098,42 +1067,7 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
TerminalFrontendContribution
);
bindViewsWelcome_TheiaGH14309({ bind, widget: TreeViewWidget });
// Hides the Test Explorer from the side-bar
bind(TestViewContribution).toSelf().inSingletonScope();
rebind(TheiaTestViewContribution).toService(TestViewContribution);
});
// Align the viewsWelcome rendering with VS Code (https://github.com/eclipse-theia/theia/issues/14309)
// Copied from Theia code but with customized TreeViewWidget with the customized viewsWelcome rendering
// https://github.com/eclipse-theia/theia/blob/0c5f69455d9ee355b1a7ca510ffa63d2b20f0c77/packages/plugin-ext/src/main/browser/plugin-ext-frontend-module.ts#L159-L181
function bindViewsWelcome_TheiaGH14309({
bind,
widget,
}: {
bind: interfaces.Bind;
widget: interfaces.Newable<TreeWidget>;
}) {
bind(WidgetFactory)
.toDynamicValue(({ container }) => ({
id: PLUGIN_VIEW_DATA_FACTORY_ID,
createWidget: (options: TreeViewWidgetOptions) => {
const props = {
contextMenuPath: VIEW_ITEM_CONTEXT_MENU,
expandOnlyOnExpansionToggleClick: true,
expansionTogglePadding: 22,
globalSelection: true,
leftPadding: 8,
search: true,
multiSelect: options.multiSelect,
};
const child = createTreeContainer(container, {
props,
tree: PluginTree,
model: PluginTreeModel,
widget,
decoratorService: TreeViewDecoratorService,
});
child.bind(TreeViewWidgetOptions).toConstantValue(options);
return child.get(TreeWidget);
},
}))
.inSingletonScope();
}

View File

@@ -137,6 +137,18 @@ const properties: ArduinoPreferenceSchemaProperties = {
'arduino.upload.verify': {
type: 'boolean',
default: false,
description: nls.localize(
'arduino/preferences/upload.verify',
'After upload, verify that the contents of the memory on the board match the uploaded binary.'
),
},
'arduino.upload.autoVerify': {
type: 'boolean',
default: true,
description: nls.localize(
'arduino/preferences/upload.autoVerify',
"True if the IDE should automatically verify the code before the upload. True by default. When this value is false, IDE does not recompile the code before uploading the binary to the board. It's highly advised to only set this value to false if you know what you are doing."
),
},
'arduino.window.autoScale': {
type: 'boolean',
@@ -268,14 +280,6 @@ const properties: ArduinoPreferenceSchemaProperties = {
),
default: 'https://auth.arduino.cc/login#/register',
},
'arduino.survey.notification': {
type: 'boolean',
description: nls.localize(
'arduino/preferences/survey.notification',
'True if users should be notified if a survey is available. True by default.'
),
default: true,
},
'arduino.cli.daemon.debug': {
type: 'boolean',
description: nls.localize(
@@ -327,6 +331,7 @@ export interface ArduinoConfiguration {
'arduino.compile.warnings': CompilerWarnings;
'arduino.upload.verbose': boolean;
'arduino.upload.verify': boolean;
'arduino.upload.autoVerify': boolean;
'arduino.window.autoScale': boolean;
'arduino.ide.updateChannel': UpdateChannel;
'arduino.ide.updateBaseUrl': string;
@@ -342,7 +347,6 @@ export interface ArduinoConfiguration {
'arduino.auth.domain': string;
'arduino.auth.audience': string;
'arduino.auth.registerUri': string;
'arduino.survey.notification': boolean;
'arduino.cli.daemon.debug': boolean;
'arduino.sketch.inoBlueprint': string;
'arduino.checkForUpdates': boolean;

View File

@@ -3,7 +3,7 @@ import { Emitter } from '@theia/core/lib/common/event';
import { JsonRpcProxy } from '@theia/core/lib/common/messaging/proxy-factory';
import { WindowService } from '@theia/core/lib/browser/window/window-service';
import { DisposableCollection } from '@theia/core/lib/common/disposable';
import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application';
import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application-contribution';
import {
CommandRegistry,
CommandContribution,

View File

@@ -1,4 +1,4 @@
import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application';
import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application-contribution';
import { DisposableCollection } from '@theia/core/lib/common/disposable';
import { MessageService } from '@theia/core/lib/common/message-service';
import { MessageType } from '@theia/core/lib/common/message-service-protocol';

View File

@@ -98,6 +98,7 @@ export class BoardsConfigDialog extends ReactDialog<BoardsConfigDialogState> {
}
override async open(
disposeOnResolve = true,
params?: EditBoardsConfigActionParams
): Promise<BoardsConfig | undefined> {
this._searchSet = undefined;
@@ -119,7 +120,7 @@ export class BoardsConfigDialog extends ReactDialog<BoardsConfigDialogState> {
this._searchSet = params.searchSet.slice();
}
}
return super.open();
return super.open(disposeOnResolve);
}
protected override onAfterAttach(msg: Message): void {

View File

@@ -1,4 +1,4 @@
import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application';
import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application-contribution';
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
import { StorageService } from '@theia/core/lib/browser/storage-service';
import type {

View File

@@ -1,4 +1,4 @@
import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application';
import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application-contribution';
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
import { StorageService } from '@theia/core/lib/browser/storage-service';
import {

View File

@@ -1,4 +1,4 @@
import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application';
import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application-contribution';
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
import { DisposableCollection } from '@theia/core/lib/common/disposable';
import { Emitter, Event } from '@theia/core/lib/common/event';

View File

@@ -3,10 +3,13 @@ import { LocalStorageService } from '@theia/core/lib/browser/storage-service';
import { inject, injectable } from '@theia/core/shared/inversify';
import {
IDEUpdater,
LAST_USED_IDE_VERSION,
SKIP_IDE_VERSION,
} from '../../common/protocol/ide-updater';
import { IDEUpdaterDialog } from '../dialogs/ide-updater/ide-updater-dialog';
import { Contribution } from './contribution';
import { AppService } from '../app-service';
import { SemVer } from 'semver';
@injectable()
export class CheckForIDEUpdates extends Contribution {
@@ -19,6 +22,9 @@ export class CheckForIDEUpdates extends Contribution {
@inject(LocalStorageService)
private readonly localStorage: LocalStorageService;
@inject(AppService)
private readonly appService: AppService;
override onStart(): void {
this.preferences.onPreferenceChanged(
({ preferenceName, newValue, oldValue }) => {
@@ -36,7 +42,7 @@ export class CheckForIDEUpdates extends Contribution {
);
}
override onReady(): void {
override async onReady(): Promise<void> {
this.updater
.init(
this.preferences.get('arduino.ide.updateChannel'),
@@ -50,11 +56,12 @@ export class CheckForIDEUpdates extends Contribution {
})
.then(async (updateInfo) => {
if (!updateInfo) return;
const versionToSkip = await this.localStorage.getData<string>(
SKIP_IDE_VERSION
);
if (versionToSkip === updateInfo.version) return;
this.updaterDialog.open(updateInfo);
this.updaterDialog.open(true, updateInfo);
})
.catch((e) => {
this.messageService.error(
@@ -64,6 +71,24 @@ export class CheckForIDEUpdates extends Contribution {
e.message
)
);
})
.finally(() => {
this.setCurrentIDEVersion();
});
}
/**
* This value is set in localStorage but currently not used.
* We keep this logic running anyway for eventual future needs
* (eg. show a new version welcome dialog)
*/
private async setCurrentIDEVersion(): Promise<void> {
try {
const { appVersion } = await this.appService.info();
const currSemVer = new SemVer(appVersion ?? '');
this.localStorage.setData(LAST_USED_IDE_VERSION, currSemVer.format());
} catch {
// ignore invalid versions
}
}
}

View File

@@ -1,8 +1,6 @@
import { Dialog } from '@theia/core/lib/browser/dialogs';
import type {
FrontendApplication,
OnWillStopAction,
} from '@theia/core/lib/browser/frontend-application';
import type { FrontendApplication } from '@theia/core/lib/browser/frontend-application';
import type { OnWillStopAction } from '@theia/core/lib/browser/frontend-application-contribution';
import { ApplicationShell } from '@theia/core/lib/browser/shell/application-shell';
import { nls } from '@theia/core/lib/common/nls';
import type { MaybePromise } from '@theia/core/lib/common/types';

View File

@@ -779,7 +779,7 @@ export class CompilerErrors
return undefined;
} else {
return this.editorManager
.getByUri(new URI(uriOrWidget))
.getByUri(new URI(uriOrWidget.toString()))
.then((editor) => {
if (editor) {
return this.monacoEditor(editor);

View File

@@ -1,8 +1,6 @@
import { ClipboardService } from '@theia/core/lib/browser/clipboard-service';
import {
FrontendApplication,
FrontendApplicationContribution,
} from '@theia/core/lib/browser/frontend-application';
import { FrontendApplication } from '@theia/core/lib/browser/frontend-application';
import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application-contribution';
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
import {
KeybindingContribution,

View File

@@ -1,7 +1,11 @@
import { nls } from '@theia/core/lib/common';
import { inject, injectable } from '@theia/core/shared/inversify';
import { CommonCommands } from '@theia/core/lib/browser/common-frontend-contribution';
import { ClipboardService } from '@theia/core/lib/browser/clipboard-service';
import { MonacoEditorService } from '@theia/monaco/lib/browser/monaco-editor-service';
import { StandaloneServices } from '@theia/monaco-editor-core/esm/vs/editor/standalone/browser/standaloneServices';
import { ICodeEditorService } from '@theia/monaco-editor-core/esm/vs/editor/browser/services/codeEditorService';
import type { ICodeEditor } from '@theia/monaco-editor-core/esm/vs/editor/browser/editorBrowser';
import type { StandaloneCodeEditor } from '@theia/monaco-editor-core/esm/vs/editor/standalone/browser/standaloneCodeEditor';
import {
Contribution,
Command,
@@ -10,17 +14,11 @@ import {
CommandRegistry,
} from './contribution';
import { ArduinoMenus } from '../menu/arduino-menus';
import { nls } from '@theia/core/lib/common';
import type { ICodeEditor } from '@theia/monaco-editor-core/esm/vs/editor/browser/editorBrowser';
import type { StandaloneCodeEditor } from '@theia/monaco-editor-core/esm/vs/editor/standalone/browser/standaloneCodeEditor';
// TODO: [macOS]: to remove `Start Dictation...` and `Emoji & Symbol` see this thread: https://github.com/electron/electron/issues/8283#issuecomment-269522072
// Depends on https://github.com/eclipse-theia/theia/pull/7964
@injectable()
export class EditContributions extends Contribution {
@inject(MonacoEditorService)
private readonly codeEditorService: MonacoEditorService;
@inject(ClipboardService)
private readonly clipboardService: ClipboardService;
@@ -208,9 +206,10 @@ ${value}
protected async current(): Promise<
ICodeEditor | StandaloneCodeEditor | undefined
> {
const codeEditorService = StandaloneServices.get(ICodeEditorService);
return (
this.codeEditorService.getFocusedCodeEditor() ||
this.codeEditorService.getActiveCodeEditor() ||
codeEditorService.getFocusedCodeEditor() ||
codeEditorService.getActiveCodeEditor() ||
undefined
);
}

View File

@@ -8,6 +8,7 @@ import {
ArduinoDaemon,
BoardIdentifier,
BoardsService,
CompileSummary,
ExecutableService,
isBoardIdentifierChangeEvent,
sanitizeFqbn,
@@ -23,6 +24,7 @@ import { HostedPluginEvents } from '../hosted/hosted-plugin-events';
import { NotificationCenter } from '../notification-center';
import { CurrentSketch } from '../sketches-service-client-impl';
import { SketchContribution, URI } from './contribution';
import { CompileSummaryProvider } from './verify-sketch';
interface DaemonAddress {
/**
@@ -107,6 +109,8 @@ export class InoLanguage extends SketchContribution {
private readonly notificationCenter: NotificationCenter;
@inject(BoardsDataStore)
private readonly boardDataStore: BoardsDataStore;
@inject(CompileSummaryProvider)
private readonly compileSummaryProvider: CompileSummaryProvider;
private readonly toDispose = new DisposableCollection();
private readonly languageServerStartMutex = new Mutex();
@@ -173,6 +177,13 @@ export class InoLanguage extends SketchContribution {
}
}
}),
this.compileSummaryProvider.onDidChangeCompileSummary(
(compileSummary) => {
if (compileSummary) {
this.fireBuildDidComplete(compileSummary);
}
}
),
]);
Promise.all([
this.boardsServiceProvider.ready,
@@ -317,4 +328,32 @@ export class InoLanguage extends SketchContribution {
params
);
}
// Execute the a command contributed by the Arduino Tools VSIX to send the `ino/buildDidComplete` notification to the language server
private async fireBuildDidComplete(
compileSummary: CompileSummary
): Promise<void> {
const params = {
...compileSummary,
};
console.info(
`Executing 'arduino.languageserver.notifyBuildDidComplete' with ${JSON.stringify(
params.buildOutputUri
)}`
);
try {
await this.commandService.executeCommand(
'arduino.languageserver.notifyBuildDidComplete',
params
);
} catch (err) {
console.error(
`Unexpected error when firing event on build did complete. ${JSON.stringify(
params.buildOutputUri
)}`,
err
);
}
}
}

View File

@@ -12,7 +12,7 @@ export class OpenBoardsConfig extends Contribution {
override registerCommands(registry: CommandRegistry): void {
registry.registerCommand(OpenBoardsConfig.Commands.OPEN_DIALOG, {
execute: async (params?: EditBoardsConfigActionParams) =>
this.boardsConfigDialog.open(params),
this.boardsConfigDialog.open(true, params),
});
}
}

View File

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

View File

@@ -1,13 +1,11 @@
import { DisposableCollection } from '@theia/core/lib/common/disposable';
import URI from '@theia/core/lib/common/uri';
import { inject, injectable } from '@theia/core/shared/inversify';
import { HostedPluginSupport } from '../hosted/hosted-plugin-support';
import type { ArduinoState } from 'vscode-arduino-api';
import {
BoardsConfig,
BoardsService,
CompileSummary,
isCompileSummary,
BoardsConfig,
PortIdentifier,
resolveDetectedPort,
} from '../../common/protocol';
@@ -18,8 +16,10 @@ import {
} from '../../common/protocol/arduino-context-mapper';
import { BoardsDataStore } from '../boards/boards-data-store';
import { BoardsServiceProvider } from '../boards/boards-service-provider';
import { HostedPluginSupport } from '../hosted/hosted-plugin-support';
import { CurrentSketch } from '../sketches-service-client-impl';
import { SketchContribution } from './contribution';
import { CompileSummaryProvider } from './verify-sketch';
/**
* (non-API) exported for tests
@@ -43,6 +43,8 @@ export class UpdateArduinoState extends SketchContribution {
private readonly boardsDataStore: BoardsDataStore;
@inject(HostedPluginSupport)
private readonly hostedPluginSupport: HostedPluginSupport;
@inject(CompileSummaryProvider)
private readonly compileSummaryProvider: CompileSummaryProvider;
private readonly toDispose = new DisposableCollection();
@@ -60,14 +62,13 @@ export class UpdateArduinoState extends SketchContribution {
this.configService.onDidChangeSketchDirUri((userDirUri) =>
this.updateUserDirPath(userDirUri)
),
this.commandService.onDidExecuteCommand(({ commandId, args }) => {
if (
commandId === 'arduino.languageserver.notifyBuildDidComplete' &&
isCompileSummary(args[0])
) {
this.updateCompileSummary(args[0]);
this.compileSummaryProvider.onDidChangeCompileSummary(
(compilerSummary) => {
if (compilerSummary) {
this.updateCompileSummary(compilerSummary);
}
}
}),
),
this.boardsDataStore.onDidChange((event) => {
const selectedFqbn =
this.boardsServiceProvider.boardsConfig.selectedBoard?.fqbn;
@@ -88,6 +89,10 @@ export class UpdateArduinoState extends SketchContribution {
this.updateSketchPath(this.sketchServiceClient.tryGetCurrentSketch());
this.updateUserDirPath(this.configService.tryGetSketchDirUri());
this.updateDataDirPath(this.configService.tryGetDataDirUri());
const { compileSummary } = this.compileSummaryProvider;
if (compileSummary) {
this.updateCompileSummary(compileSummary);
}
}
onStop(): void {

View File

@@ -104,6 +104,7 @@ export class UploadSketch extends CoreServiceContribution {
}
try {
const autoVerify = this.preferences['arduino.upload.autoVerify'];
// toggle the toolbar button and menu item state.
// uploadInProgress will be set to false whether the upload fails or not
this.uploadInProgress = true;
@@ -116,7 +117,7 @@ export class UploadSketch extends CoreServiceContribution {
'arduino-verify-sketch',
<VerifySketchParams>{
exportBinaries: false,
silent: true,
mode: autoVerify ? 'auto' : 'dry-run',
}
);
if (!verifyOptions) {

View File

@@ -1,7 +1,7 @@
import { Emitter } from '@theia/core/lib/common/event';
import { Emitter, Event } from '@theia/core/lib/common/event';
import { nls } from '@theia/core/lib/common/nls';
import { inject, injectable } from '@theia/core/shared/inversify';
import type { CoreService } from '../../common/protocol';
import type { CompileSummary, CoreService } from '../../common/protocol';
import { ArduinoMenus } from '../menu/arduino-menus';
import { CurrentSketch } from '../sketches-service-client-impl';
import { ArduinoToolbar } from '../toolbar/arduino-toolbar';
@@ -15,32 +15,57 @@ import {
} from './contribution';
import { CoreErrorHandler } from './core-error-handler';
export const CompileSummaryProvider = Symbol('CompileSummaryProvider');
export interface CompileSummaryProvider {
readonly compileSummary: CompileSummary | undefined;
readonly onDidChangeCompileSummary: Event<CompileSummary | undefined>;
}
export type VerifySketchMode =
/**
* When the user explicitly triggers the verify command from the primary UI: menu, toolbar, or keybinding. The UI shows the output, updates the toolbar items state, etc.
*/
| 'explicit'
/**
* When the verify phase automatically runs as part of the upload but there is no UI indication of the command: the toolbar items do not update.
*/
| 'auto'
/**
* The verify does not run. There is no UI indication of the command. For example, when the user decides to disable the auto verify (`'arduino.upload.autoVerify'`) to skips the code recompilation phase.
*/
| 'dry-run';
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 in the toolbar. It's `false` by default.
* The mode specifying how verify should run. It's `'explicit'` by default.
*/
readonly silent?: boolean;
readonly mode?: VerifySketchMode;
}
/**
* - `"idle"` when neither verify, nor upload is running,
* - `"explicit-verify"` when only verify is running triggered by the user, and
* - `"automatic-verify"` is when the automatic verify phase is running as part of an upload triggered by the user.
* - `"idle"` when neither verify, nor upload is running
*/
type VerifyProgress = 'idle' | 'explicit-verify' | 'automatic-verify';
type VerifyProgress = 'idle' | VerifySketchMode;
@injectable()
export class VerifySketch extends CoreServiceContribution {
export class VerifySketch
extends CoreServiceContribution
implements CompileSummaryProvider
{
@inject(CoreErrorHandler)
private readonly coreErrorHandler: CoreErrorHandler;
private readonly onDidChangeEmitter = new Emitter<void>();
private readonly onDidChange = this.onDidChangeEmitter.event;
private readonly onDidChangeCompileSummaryEmitter = new Emitter<
CompileSummary | undefined
>();
private verifyProgress: VerifyProgress = 'idle';
private _compileSummary: CompileSummary | undefined;
override registerCommands(registry: CommandRegistry): void {
registry.registerCommand(VerifySketch.Commands.VERIFY_SKETCH, {
@@ -54,10 +79,10 @@ export class VerifySketch extends CoreServiceContribution {
registry.registerCommand(VerifySketch.Commands.VERIFY_SKETCH_TOOLBAR, {
isVisible: (widget) =>
ArduinoToolbar.is(widget) && widget.side === 'left',
isEnabled: () => this.verifyProgress !== 'explicit-verify',
isEnabled: () => this.verifyProgress !== 'explicit',
// toggled only when verify is running, but not toggled when automatic verify is running before the upload
// https://github.com/arduino/arduino-ide/pull/1750#pullrequestreview-1214762975
isToggled: () => this.verifyProgress === 'explicit-verify',
isToggled: () => this.verifyProgress === 'explicit',
execute: () =>
registry.executeCommand(VerifySketch.Commands.VERIFY_SKETCH.id),
});
@@ -105,6 +130,21 @@ export class VerifySketch extends CoreServiceContribution {
super.handleError(error);
}
get compileSummary(): CompileSummary | undefined {
return this._compileSummary;
}
private updateCompileSummary(
compileSummary: CompileSummary | undefined
): void {
this._compileSummary = compileSummary;
this.onDidChangeCompileSummaryEmitter.fire(this._compileSummary);
}
get onDidChangeCompileSummary(): Event<CompileSummary | undefined> {
return this.onDidChangeCompileSummaryEmitter.event;
}
private async verifySketch(
params?: VerifySketchParams
): Promise<CoreService.Options.Compile | undefined> {
@@ -113,20 +153,23 @@ export class VerifySketch extends CoreServiceContribution {
}
try {
this.verifyProgress = params?.silent
? 'automatic-verify'
: 'explicit-verify';
this.verifyProgress = params?.mode ?? 'explicit';
this.onDidChangeEmitter.fire();
this.menuManager.update();
this.clearVisibleNotification();
this.coreErrorHandler.reset();
const dryRun = this.verifyProgress === 'dry-run';
const options = await this.options(params?.exportBinaries);
if (!options) {
return undefined;
}
await this.doWithProgress({
if (dryRun) {
return options;
}
const compileSummary = await this.doWithProgress({
progressText: nls.localize(
'arduino/sketch/compile',
'Compiling sketch...'
@@ -145,6 +188,9 @@ export class VerifySketch extends CoreServiceContribution {
nls.localize('arduino/sketch/doneCompiling', 'Done compiling.'),
{ timeout: 3000 }
);
this.updateCompileSummary(compileSummary);
// 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.

View File

@@ -1,4 +1,4 @@
import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application';
import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application-contribution';
import { DisposableCollection } from '@theia/core/lib/common/disposable';
import { Emitter, Event } from '@theia/core/lib/common/event';
import URI from '@theia/core/lib/common/uri';

View File

@@ -5,7 +5,7 @@ import {
Disposable,
DisposableCollection,
} from '@theia/core/lib/common/disposable';
import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application';
import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application-contribution';
import {
Stat,
FileType,

View File

@@ -4,7 +4,6 @@ import React from '@theia/core/shared/react';
import type { Options } from 'react-markdown';
import { ProgressInfo, UpdateInfo } from '../../../common/protocol/ide-updater';
import ProgressBar from '../../components/ProgressBar';
import { createPortal } from '@theia/core/shared/react-dom';
const ReactMarkdown = React.lazy<React.ComponentType<Options>>(
// @ts-expect-error see above
@@ -22,8 +21,6 @@ export interface IDEUpdaterComponentProps {
updateInfo: UpdateInfo;
updateProgress: UpdateProgress;
openExternal: (url: string) => undefined;
hasControls: boolean;
controlPanel: HTMLDivElement;
}
export const IDEUpdaterComponent = ({
@@ -35,8 +32,6 @@ export const IDEUpdaterComponent = ({
error,
},
openExternal,
hasControls,
controlPanel,
}: IDEUpdaterComponentProps): React.ReactElement => {
const { version, releaseNotes } = updateInfo;
const [changelog, setChangelog] = React.useState<string>('');
@@ -144,60 +139,17 @@ export const IDEUpdaterComponent = ({
</div>
);
const DonateFooter = (
<div
className={
hasControls
? 'ide-updater-dialog--content--child--footer-with-controls'
: 'ide-updater-dialog--content--child--footer'
}
>
<hr />
<span>
{nls.localize(
'arduino/ide-updater/donate-preface',
'Open source is love, '
)}
<a
className="donate-link"
onClick={() => openExternal('https://www.arduino.cc/en/donate/')}
>
{nls.localize(
'arduino/ide-updater/donate-link-text',
'donate to support us'
)}
<div
className="donate-link-icon"
title={nls.localize(
'arduino/ide-updater/donate-link-text',
'donate to support us'
)}
/>
</a>
</span>
</div>
);
const DonateFooterToRender =
hasControls && controlPanel.parentElement
? createPortal(DonateFooter, controlPanel.parentElement)
: DonateFooter;
const isPreDownload = !error && !downloadFinished && !downloadStarted;
return (
<div className="ide-updater-dialog--content">
<div>
{!!error ? (
<GoToDownloadPage />
) : downloadFinished ? (
<DownloadCompleted />
) : downloadStarted ? (
<DownloadStarted />
) : (
<PreDownload />
)}
{isPreDownload ? null : DonateFooterToRender}
</div>
{!!error ? (
<GoToDownloadPage />
) : downloadFinished ? (
<DownloadCompleted />
) : downloadStarted ? (
<DownloadStarted />
) : (
<PreDownload />
)}
</div>
);
};

View File

@@ -69,17 +69,12 @@ export class IDEUpdaterDialog extends ReactDialog<UpdateInfo | undefined> {
}
protected render(): React.ReactNode {
const hasControls = !!this.controlPanel.firstChild;
const controlPanel = this.controlPanel;
return (
this.updateInfo && (
<IDEUpdaterComponent
updateInfo={this.updateInfo}
updateProgress={this.updateProgress}
openExternal={this.openExternal}
hasControls={hasControls}
controlPanel={controlPanel}
/>
)
);
@@ -219,6 +214,7 @@ export class IDEUpdaterDialog extends ReactDialog<UpdateInfo | undefined> {
}
override async open(
disposeOnResolve = true,
data: UpdateInfo | undefined = undefined
): Promise<UpdateInfo | undefined> {
if (data && data.version) {
@@ -229,7 +225,7 @@ export class IDEUpdaterDialog extends ReactDialog<UpdateInfo | undefined> {
error: undefined,
});
this.updateInfo = data;
return super.open();
return super.open(disposeOnResolve);
}
}

View File

@@ -30,7 +30,7 @@ export class IDEUpdaterCommands implements CommandContribution {
try {
const updateInfo = await this.updater.checkForUpdates(initialCheck);
if (!!updateInfo) {
this.updaterDialog.open(updateInfo);
this.updaterDialog.open(true, updateInfo);
} else {
this.messageService.info(
nls.localize(

View File

@@ -6,7 +6,7 @@ import {
import { Emitter } from '@theia/core/lib/common/event';
import { JsonRpcProxy } from '@theia/core/lib/common/messaging/proxy-factory';
import { DisposableCollection } from '@theia/core/lib/common/disposable';
import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application';
import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application-contribution';
import {
IndexUpdateDidCompleteParams,
IndexUpdateDidFailParams,

View File

@@ -67,3 +67,7 @@ export function truncateLines(
}
return [lines, charCount];
}
export function joinLines(lines: Line[]): string {
return lines.map((line: Line) => line.message).join('');
}

View File

@@ -52,6 +52,9 @@ export namespace SerialMonitor {
},
'vscode/output.contribution/clearOutput.label'
);
export const COPY_OUTPUT = {
id: 'serial-monitor-copy-output',
};
}
}
@@ -149,6 +152,12 @@ export class MonitorViewContribution
'Clear Output'
),
});
registry.registerItem({
id: SerialMonitor.Commands.COPY_OUTPUT.id,
command: SerialMonitor.Commands.COPY_OUTPUT.id,
icon: codicon('copy'),
tooltip: nls.localize('arduino/serial/copyOutput', 'Copy Output'),
});
}
override registerCommands(commands: CommandRegistry): void {
@@ -161,6 +170,15 @@ export class MonitorViewContribution
}
},
});
commands.registerCommand(SerialMonitor.Commands.COPY_OUTPUT, {
isEnabled: (widget) => widget instanceof MonitorWidget,
isVisible: (widget) => widget instanceof MonitorWidget,
execute: (widget) => {
if (widget instanceof MonitorWidget) {
widget.copyOutput();
}
},
});
if (this.toggleCommand) {
commands.registerCommand(this.toggleCommand, {
execute: () => this.toggle(),

View File

@@ -28,6 +28,7 @@ import {
import { MonitorModel } from '../../monitor-model';
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
import { serialMonitorWidgetLabel } from '../../../common/nls';
import { ClipboardService } from '@theia/core/lib/browser/clipboard-service';
@injectable()
export class MonitorWidget extends ReactWidget {
@@ -47,6 +48,7 @@ export class MonitorWidget extends ReactWidget {
*/
protected closing = false;
protected readonly clearOutputEmitter = new Emitter<void>();
protected readonly copyOutputEmitter = new Emitter<void>();
@inject(MonitorModel)
private readonly monitorModel: MonitorModel;
@@ -56,6 +58,8 @@ export class MonitorWidget extends ReactWidget {
private readonly boardsServiceProvider: BoardsServiceProvider;
@inject(FrontendApplicationStateService)
private readonly appStateService: FrontendApplicationStateService;
@inject(ClipboardService)
private readonly clipboardService: ClipboardService;
private readonly toDisposeOnReset: DisposableCollection;
@@ -102,6 +106,10 @@ export class MonitorWidget extends ReactWidget {
this.clearOutputEmitter.fire(undefined);
this.update();
}
copyOutput(): void {
this.copyOutputEmitter.fire();
}
override dispose(): void {
this.toDisposeOnReset.dispose();
@@ -247,6 +255,8 @@ export class MonitorWidget extends ReactWidget {
monitorModel={this.monitorModel}
monitorManagerProxy={this.monitorManagerProxy}
clearConsoleEvent={this.clearOutputEmitter.event}
copyOutputEvent={this.copyOutputEmitter.event}
clipboardService={this.clipboardService}
height={Math.floor(this.widgetHeight - 50)}
/>
</div>

View File

@@ -3,9 +3,10 @@ import { Event } from '@theia/core/lib/common/event';
import { DisposableCollection } from '@theia/core/lib/common/disposable';
import { areEqual, FixedSizeList as List } from 'react-window';
import dateFormat from 'dateformat';
import { messagesToLines, truncateLines } from './monitor-utils';
import { messagesToLines, truncateLines, joinLines } from './monitor-utils';
import { MonitorManagerProxyClient } from '../../../common/protocol';
import { MonitorModel } from '../../monitor-model';
import { ClipboardService } from '@theia/core/lib/browser/clipboard-service';
export type Line = { message: string; timestamp?: Date; lineLen: number };
@@ -74,6 +75,9 @@ export class SerialMonitorOutput extends React.Component<
this.props.clearConsoleEvent(() =>
this.setState({ lines: [], charCount: 0 })
),
this.props.copyOutputEvent(() =>
this.props.clipboardService.writeText(joinLines(this.state.lines))
),
this.props.monitorModel.onChange(({ property }) => {
if (property === 'timestamp') {
const { timestamp } = this.props.monitorModel;
@@ -130,6 +134,8 @@ export namespace SerialMonitorOutput {
readonly monitorModel: MonitorModel;
readonly monitorManagerProxy: MonitorManagerProxyClient;
readonly clearConsoleEvent: Event<void>;
readonly copyOutputEvent: Event<void>;
readonly clipboardService: ClipboardService;
readonly height: number;
}

View File

@@ -9,7 +9,7 @@ import {
Disposable,
DisposableCollection,
} from '@theia/core/lib/common/disposable';
import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application';
import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application-contribution';
import { Sketch, SketchesService } from '../common/protocol';
import { ConfigServiceClient } from './config/config-service-client';
import {
@@ -74,6 +74,7 @@ export class SketchesServiceClientImpl
const sketchDirUri = this.configService.tryGetSketchDirUri();
this.watchSketchbookDir(sketchDirUri);
const refreshCurrentSketch = async () => {
await this.workspaceService.ready;
const currentSketch = await this.loadCurrentSketch();
const ideTempFolderUri = await this.getIdeTempFolderUriForSketch(
currentSketch
@@ -287,7 +288,7 @@ export class SketchesServiceClientImpl
* `true` if the `uri` is not contained in any of the opened workspaces. Otherwise, `false`.
*/
isReadOnly(uri: URI | monaco.Uri | string): boolean {
const toCheck = uri instanceof URI ? uri : new URI(uri);
const toCheck = uri instanceof URI ? uri : new URI(uri.toString());
if (toCheck.scheme === 'user-storage') {
return false;
}

View File

@@ -98,16 +98,12 @@
color: var(--theia-textLink-foreground);
}
.account-icon {
img.arduino-account-picture {
width: var(--theia-private-sidebar-icon-size);
height: var(--theia-private-sidebar-icon-size);
border-radius: 50%;
overflow: hidden;
}
.account-icon > img {
max-width: 100%;
max-height: 100%;
border-radius: 50%;
}
.connected-status-icon {

View File

@@ -34,44 +34,6 @@
min-width: 0;
}
.ide-updater-dialog--content--child--footer {
margin: 16px 0px 16px 0px;
}
.ide-updater-dialog--content--child--footer-with-controls {
margin: -16px 0px 16px 0px;
}
.ide-updater-dialog--content--child--footer > span,
.ide-updater-dialog--content--child--footer-with-controls > span {
float: right;
display: flex;
justify-content: space-between;
align-items: center;
}
.donate-link {
margin-left: 6px;
display: flex;
flex-direction: row;
align-items: center;
color: var(--theia-textLink-foreground);
font-weight: 500;
}
.donate-link:hover {
color: var(--theia-textLink-foreground);
cursor: pointer;
}
.donate-link-icon {
-webkit-mask: url(../icons/link-open-icon.svg) center no-repeat;
background-color: var(--theia-textLink-foreground);
height: 24px;
width: 24px;
cursor: pointer;
}
.ide-updater-dialog .changelog {
color: var(--theia-editor-foreground);
background-color: var(--theia-editor-background);

View File

@@ -13,6 +13,8 @@ import { MessageService } from '@theia/core/lib/common/message-service';
import { inject, injectable } from '@theia/core/shared/inversify';
import { ApplicationConnectionStatusContribution } from './connection-status-service';
import { ToolbarAwareTabBar } from './tab-bars';
import { find } from '@theia/core/shared/@phosphor/algorithm';
import { OutputWidget } from '@theia/output/lib/browser/output-widget';
@injectable()
export class ApplicationShell extends TheiaApplicationShell {
@@ -48,6 +50,38 @@ export class ApplicationShell extends TheiaApplicationShell {
return super.addWidget(widget, { ...options, ref });
}
override doRevealWidget(id: string): Widget | undefined {
let widget = find(this.mainPanel.widgets(), (w) => w.id === id);
if (!widget) {
widget = find(this.bottomPanel.widgets(), (w) => w.id === id);
if (widget) {
this.expandBottomPanel();
}
}
if (widget) {
const tabBar = this.getTabBarFor(widget);
if (tabBar) {
tabBar.currentTitle = widget.title;
}
}
if (!widget) {
widget = this.leftPanelHandler.expand(id);
}
if (!widget) {
widget = this.rightPanelHandler.expand(id);
}
if (widget) {
// Prevent focusing the output widget when is updated
// See https://github.com/arduino/arduino-ide/issues/2679
if (!(widget instanceof OutputWidget)) {
this.windowService.focus();
}
return widget;
} else {
return this.secondaryWindowHandler.revealWidget(id);
}
}
override handleEvent(): boolean {
// NOOP, dragging has been disabled
return false;

View File

@@ -2,7 +2,7 @@ import {
CommonCommands,
CommonFrontendContribution as TheiaCommonFrontendContribution,
} from '@theia/core/lib/browser/common-frontend-contribution';
import type { OnWillStopAction } from '@theia/core/lib/browser/frontend-application';
import type { OnWillStopAction } from '@theia/core/lib/browser/frontend-application-contribution';
import type { KeybindingRegistry } from '@theia/core/lib/browser/keybinding';
import type { CommandRegistry } from '@theia/core/lib/common/command';
import type { MenuModelRegistry } from '@theia/core/lib/common/menu';

View File

@@ -1,10 +1,10 @@
import {
ApplicationConnectionStatusContribution as TheiaApplicationConnectionStatusContribution,
ConnectionStatus,
ApplicationConnectionStatusContribution as TheiaApplicationConnectionStatusContribution,
FrontendConnectionStatusService as TheiaFrontendConnectionStatusService,
} from '@theia/core/lib/browser/connection-status-service';
import type { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application';
import { WebSocketConnectionProvider } from '@theia/core/lib/browser/index';
import type { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application-contribution';
import { WebSocketConnectionSource } from '@theia/core/lib/browser/messaging/ws-connection-source';
import { StatusBarAlignment } from '@theia/core/lib/browser/status-bar/status-bar';
import { Disposable } from '@theia/core/lib/common/disposable';
import { Emitter, Event } from '@theia/core/lib/common/event';
@@ -114,8 +114,8 @@ export class FrontendConnectionStatusService extends TheiaFrontendConnectionStat
private readonly daemonPort: DaemonPort;
@inject(IsOnline)
private readonly isOnline: IsOnline;
@inject(WebSocketConnectionProvider)
private readonly connectionProvider: WebSocketConnectionProvider;
@inject(WebSocketConnectionSource)
private readonly connectionSource: WebSocketConnectionSource;
@postConstruct()
protected override init(): void {
@@ -128,7 +128,7 @@ export class FrontendConnectionStatusService extends TheiaFrontendConnectionStat
}
protected override async performPingRequest(): Promise<void> {
if (!this.connectionProvider['socket'].connected) {
if (!this.connectionSource['socket'].connected) {
this.updateStatus(false);
return;
}
@@ -171,8 +171,8 @@ export class ApplicationConnectionStatusContribution extends TheiaApplicationCon
private readonly notificationManager: NotificationManager;
@inject(CreateFeatures)
private readonly createFeatures: CreateFeatures;
@inject(WebSocketConnectionProvider)
private readonly connectionProvider: WebSocketConnectionProvider;
@inject(WebSocketConnectionSource)
private readonly connectionSource: WebSocketConnectionSource;
private readonly offlineStatusDidChangeEmitter = new Emitter<
OfflineConnectionStatus | undefined
@@ -202,7 +202,7 @@ export class ApplicationConnectionStatusContribution extends TheiaApplicationCon
const params = <OfflineMessageParams>{
port: this.daemonPort.port,
online: this.isOnline.online,
backendConnected: this.connectionProvider['socket'].connected, // https://github.com/arduino/arduino-ide/issues/2081
backendConnected: this.connectionSource['socket'].connected, // https://github.com/arduino/arduino-ide/issues/2081
};
this._offlineStatus = offlineConnectionStatusType(params);
const { text, tooltip } = offlineMessage(params);

View File

@@ -1,5 +1,5 @@
import { SidebarBottomMenuWidget as TheiaSidebarBottomMenuWidget } from '@theia/core/lib/browser/shell/sidebar-bottom-menu-widget';
import type { SidebarMenu } from '@theia/core/lib/browser/shell/sidebar-menu-widget';
import type { SidebarMenuItem } from '@theia/core/lib/browser/shell/sidebar-menu-widget';
import type { MenuPath } from '@theia/core/lib/common/menu';
import { nls } from '@theia/core/lib/common/nls';
import {
@@ -46,46 +46,45 @@ export class SidebarBottomMenuWidget extends TheiaSidebarBottomMenuWidget {
this.contextMenuRenderer.render(options);
}
protected override render(): React.ReactNode {
return (
<React.Fragment>
{this.menus.map((menu) => this.renderMenu(menu))}
</React.Fragment>
);
}
private renderMenu(menu: SidebarMenu): React.ReactNode {
override renderItem(item: SidebarMenuItem): React.ReactNode {
// Removes the _Settings_ (cog) icon from the left sidebar
if (menu.id === 'settings-menu') {
if (item.menu.id === 'settings-menu') {
return undefined;
}
const arduinoAccount = menu.id === accountMenu.id;
const picture =
const arduinoAccount = item.menu.id === accountMenu.id;
const arduinoAccountPicture =
arduinoAccount &&
this.connectionStatue.offlineStatus !== 'internet' &&
this.createFeatures.session?.account.picture;
const className = typeof picture === 'string' ? undefined : menu.iconClass;
return (
<i
key={menu.id}
className={className}
title={menu.title}
onClick={(e) => this.onClick(e, menu.menuPath)}
<div
key={item.menu.id}
className="theia-sidebar-menu-item"
title={item.menu.title}
onClick={(e) => this.onClick(e, item.menu.menuPath)}
onMouseDown={this.onMouseDown}
onMouseEnter={(e) => this.onMouseEnter(e, item.menu.title)}
onMouseOut={this.onMouseOut}
>
{picture && (
<div className="account-icon">
{arduinoAccountPicture ? (
<i>
<img
src={picture}
className="arduino-account-picture"
src={arduinoAccountPicture}
alt={nls.localize(
'arduino/cloud/profilePicture',
'Profile picture'
)}
/>
</div>
</i>
) : (
<i className={item.menu.iconClass} />
)}
</i>
{item.badge && (
<div className="theia-badge-decorator-sidebar">{item.badge}</div>
)}
</div>
);
}
}

View File

@@ -1,6 +1,7 @@
import { injectable } from '@theia/core/shared/inversify';
import { DebugSessionConnection } from '@theia/debug/lib/browser/debug-session-connection';
import { DefaultDebugSessionFactory as TheiaDefaultDebugSessionFactory } from '@theia/debug/lib/browser/debug-session-contribution';
import { DebugSessionManager } from '@theia/debug/lib/browser/debug-session-manager';
import { DebugConfigurationSessionOptions } from '@theia/debug/lib/browser/debug-session-options';
import {
DebugAdapterPath,
@@ -12,6 +13,7 @@ import { DebugSession } from './debug-session';
@injectable()
export class DefaultDebugSessionFactory extends TheiaDefaultDebugSessionFactory {
override get(
manager: DebugSessionManager,
sessionId: string,
options: DebugConfigurationSessionOptions,
parentSession?: DebugSession
@@ -20,12 +22,12 @@ export class DefaultDebugSessionFactory extends TheiaDefaultDebugSessionFactory
sessionId,
() =>
new Promise<DebugChannel>((resolve) =>
this.connectionProvider.openChannel(
this.connectionProvider.listen(
`${DebugAdapterPath}/${sessionId}`,
(wsChannel) => {
(_, wsChannel) => {
resolve(new ForwardingDebugChannel(wsChannel));
},
{ reconnecting: false }
false
)
),
this.getTraceOutputChannel()
@@ -35,6 +37,9 @@ export class DefaultDebugSessionFactory extends TheiaDefaultDebugSessionFactory
sessionId,
options,
parentSession,
this.testService,
options.testRun,
manager,
connection,
this.terminalService,
this.editorManager,

View File

@@ -1,21 +0,0 @@
import { injectable, postConstruct } from '@theia/core/shared/inversify';
import { EditorCommandContribution as TheiaEditorCommandContribution } from '@theia/editor/lib/browser/editor-command';
@injectable()
export class EditorCommandContribution extends TheiaEditorCommandContribution {
@postConstruct()
protected override init(): void {
// Workaround for https://github.com/eclipse-theia/theia/issues/8722.
this.editorPreferences.onPreferenceChanged(
({ preferenceName, newValue, oldValue }) => {
if (preferenceName === 'files.autoSave') {
const autoSaveWasOnBeforeChange = !oldValue || oldValue !== 'off';
const autoSaveIsOnAfterChange = !newValue || newValue !== 'off';
if (!autoSaveWasOnBeforeChange && autoSaveIsOnAfterChange) {
this.shell.saveAll();
}
}
}
);
}
}

View File

@@ -1,19 +1,18 @@
import { injectable } from '@theia/core/shared/inversify';
import { TextEditor } from '@theia/editor/lib/browser';
import { EditorContribution as TheiaEditorContribution } from '@theia/editor/lib/browser/editor-contribution';
@injectable()
export class EditorContribution extends TheiaEditorContribution {
protected override updateLanguageStatus(
// eslint-disable-next-line @typescript-eslint/no-unused-vars, unused-imports/no-unused-vars
editor: TextEditor | undefined
// eslint-disable-next-line @typescript-eslint/no-unused-vars
..._: Parameters<TheiaEditorContribution['updateLanguageStatus']>
): void {
// NOOP
}
protected override updateEncodingStatus(
// eslint-disable-next-line @typescript-eslint/no-unused-vars, unused-imports/no-unused-vars
editor: TextEditor | undefined
// eslint-disable-next-line @typescript-eslint/no-unused-vars
..._: Parameters<TheiaEditorContribution['updateEncodingStatus']>
): void {
// https://github.com/arduino/arduino-ide/issues/1393
// NOOP

View File

@@ -36,7 +36,7 @@ export class FileResourceResolver extends TheiaFileResourceResolver {
);
}
return new WriteQueuedFileResource(uri, this.fileService, {
isReadonly: stat?.isReadonly ?? false,
readOnly: stat?.isReadonly ?? false,
shouldOverwrite: () => this.shouldOverwrite(uri),
shouldOpenAsText: (error) => this.shouldOpenAsText(uri, error),
});

View File

@@ -2,6 +2,7 @@ import React from '@theia/core/shared/react';
import { NotificationComponent as TheiaNotificationComponent } from '@theia/messages/lib/browser/notification-component';
import { nls } from '@theia/core/lib/common';
import { codicon } from '@theia/core/lib/browser';
import { sanitize } from 'dompurify';
export class NotificationComponent extends TheiaNotificationComponent {
override render(): React.ReactNode {
@@ -20,7 +21,7 @@ export class NotificationComponent extends TheiaNotificationComponent {
/>
<div className="theia-notification-message">
<span
dangerouslySetInnerHTML={{ __html: message }}
dangerouslySetInnerHTML={{ __html: sanitize(message) }}
onClick={this.onMessageClick}
/>
</div>

View File

@@ -117,7 +117,7 @@ export function maybeUpdateReadOnlyState(
const model = editor.document;
const oldReadOnly = model.readOnly;
const resource = model['resource'];
const newReadOnly = Boolean(resource.isReadonly) || isReadOnly(resource.uri);
const newReadOnly = Boolean(resource.readOnly) || isReadOnly(resource.uri);
if (oldReadOnly !== newReadOnly) {
editor.getControl().updateOptions({ readOnly: newReadOnly });
if (newReadOnly) {

View File

@@ -20,7 +20,7 @@ export class MonacoTextModelService extends TheiaMonacoTextModelService {
.getContributions()
.find(({ scheme }) => resource.uri.scheme === scheme);
const readOnly =
Boolean(resource.isReadonly) ||
Boolean(resource.readOnly) ||
this.sketchesServiceClient.isReadOnly(resource.uri);
return factory
? factory.createModel(resource)

View File

@@ -1,4 +1,4 @@
import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application';
import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application-contribution';
import { ThemeService } from '@theia/core/lib/browser/theming';
import {
Disposable,

View File

@@ -1,11 +1,9 @@
import { DisposableCollection } from '@theia/core/lib/common/disposable';
import type { DisposableCollection } from '@theia/core/lib/common/disposable';
import { Emitter, Event } from '@theia/core/lib/common/event';
import { injectable, interfaces } from '@theia/core/shared/inversify';
import {
PluginContributions,
HostedPluginSupport as TheiaHostedPluginSupport,
} from '@theia/plugin-ext/lib/hosted/browser/hosted-plugin';
import { HostedPluginSupport } from '../../hosted/hosted-plugin-support';
import { HostedPluginSupport as TheiaHostedPluginSupport } from '@theia/plugin-ext/lib/hosted/browser/hosted-plugin';
import type { PluginContributions } from '@theia/plugin-ext/lib/hosted/common/hosted-plugin';
import type { HostedPluginSupport } from '../../hosted/hosted-plugin-support';
@injectable()
export class HostedPluginSupportImpl

View File

@@ -1,241 +0,0 @@
import { LabelIcon } from '@theia/core/lib/browser/label-parser';
import { OpenerService, open } from '@theia/core/lib/browser/opener-service';
import { codicon } from '@theia/core/lib/browser/widgets/widget';
import { DisposableCollection } from '@theia/core/lib/common/disposable';
import { URI } from '@theia/core/lib/common/uri';
import { inject, injectable } from '@theia/core/shared/inversify';
import React from '@theia/core/shared/react';
import { URI as CodeUri } from '@theia/core/shared/vscode-uri';
import { TreeViewWidget as TheiaTreeViewWidget } from '@theia/plugin-ext/lib/main/browser/view/tree-view-widget';
// Copied back from https://github.com/eclipse-theia/theia/pull/14391
// Remove the patching when Arduino uses Eclipse Theia >1.55.0
// https://github.com/eclipse-theia/theia/blob/8d3c5a11af65448b6700bedd096f8d68f0675541/packages/core/src/browser/tree/tree-view-welcome-widget.tsx#L37-L54
// https://github.com/eclipse-theia/theia/blob/8d3c5a11af65448b6700bedd096f8d68f0675541/packages/core/src/browser/tree/tree-view-welcome-widget.tsx#L146-L298
interface ViewWelcome {
readonly view: string;
readonly content: string;
readonly when?: string;
readonly enablement?: string;
readonly order: number;
}
export interface IItem {
readonly welcomeInfo: ViewWelcome;
visible: boolean;
}
export interface ILink {
readonly label: string;
readonly href: string;
readonly title?: string;
}
type LinkedTextItem = string | ILink;
@injectable()
export class TreeViewWidget extends TheiaTreeViewWidget {
@inject(OpenerService)
private readonly openerService: OpenerService;
private readonly toDisposeBeforeUpdateViewWelcomeNodes =
new DisposableCollection();
protected override updateViewWelcomeNodes(): void {
this.viewWelcomeNodes = [];
this.toDisposeBeforeUpdateViewWelcomeNodes.dispose();
const items = this.visibleItems.sort((a, b) => a.order - b.order);
const enablementKeys: Set<string>[] = [];
// the plugin-view-registry will push the changes when there is a change in the `when` prop which controls the visibility
// this listener is to update the enablement of the components in the view welcome
this.toDisposeBeforeUpdateViewWelcomeNodes.push(
this.contextService.onDidChange((event) => {
if (enablementKeys.some((keys) => event.affects(keys))) {
this.updateViewWelcomeNodes();
this.update();
}
})
);
// Note: VS Code does not support the `renderSecondaryButtons` prop in welcome content either.
for (const item of items) {
const { content } = item;
const enablement = isEnablementAware(item) ? item.enablement : undefined;
const itemEnablementKeys = enablement
? this.contextService.parseKeys(enablement)
: undefined;
if (itemEnablementKeys) {
enablementKeys.push(itemEnablementKeys);
}
const lines = content.split('\n');
for (let line of lines) {
line = line.trim();
if (!line) {
continue;
}
const linkedTextItems = this.parseLinkedText_patch14309(line);
if (
linkedTextItems.length === 1 &&
typeof linkedTextItems[0] !== 'string'
) {
const node = linkedTextItems[0];
this.viewWelcomeNodes.push(
this.renderButtonNode_patch14309(
node,
this.viewWelcomeNodes.length,
enablement
)
);
} else {
const renderNode = (item: LinkedTextItem, index: number) =>
typeof item == 'string'
? this.renderTextNode_patch14309(item, index)
: this.renderLinkNode_patch14309(item, index, enablement);
this.viewWelcomeNodes.push(
<p key={`p-${this.viewWelcomeNodes.length}`}>
{...linkedTextItems.flatMap(renderNode)}
</p>
);
}
}
}
}
private renderButtonNode_patch14309(
node: ILink,
lineKey: string | number,
enablement: string | undefined
): React.ReactNode {
return (
<div key={`line-${lineKey}`} className="theia-WelcomeViewButtonWrapper">
<button
title={node.title}
className="theia-button theia-WelcomeViewButton"
disabled={!this.isEnabledClick_patch14309(enablement)}
onClick={(e) => this.openLinkOrCommand_patch14309(e, node.href)}
>
{node.label}
</button>
</div>
);
}
private renderTextNode_patch14309(
node: string,
textKey: string | number
): React.ReactNode {
return (
<span key={`text-${textKey}`}>
{this.labelParser
.parse(node)
.map((segment, index) =>
LabelIcon.is(segment) ? (
<span key={index} className={codicon(segment.name)} />
) : (
<span key={index}>{segment}</span>
)
)}
</span>
);
}
private renderLinkNode_patch14309(
node: ILink,
linkKey: string | number,
enablement: string | undefined
): React.ReactNode {
return (
<a
key={`link-${linkKey}`}
className={this.getLinkClassName_patch14309(node.href, enablement)}
title={node.title || ''}
onClick={(e) => this.openLinkOrCommand_patch14309(e, node.href)}
>
{node.label}
</a>
);
}
private getLinkClassName_patch14309(
href: string,
enablement: string | undefined
): string {
const classNames = ['theia-WelcomeViewCommandLink'];
// Only command-backed links can be disabled. All other, https:, file: remain enabled
if (
href.startsWith('command:') &&
!this.isEnabledClick_patch14309(enablement)
) {
classNames.push('disabled');
}
return classNames.join(' ');
}
private isEnabledClick_patch14309(enablement: string | undefined): boolean {
return typeof enablement === 'string'
? this.contextService.match(enablement)
: true;
}
private openLinkOrCommand_patch14309 = (
event: React.MouseEvent,
value: string
): void => {
event.stopPropagation();
if (value.startsWith('command:')) {
const command = value.replace('command:', '');
this.commands.executeCommand(command);
} else if (value.startsWith('file:')) {
const uri = value.replace('file:', '');
open(this.openerService, new URI(CodeUri.file(uri).toString()));
} else {
this.windowService.openNewWindow(value, { external: true });
}
};
private parseLinkedText_patch14309(text: string): LinkedTextItem[] {
const result: LinkedTextItem[] = [];
const linkRegex =
/\[([^\]]+)\]\(((?:https?:\/\/|command:|file:)[^\)\s]+)(?: (["'])(.+?)(\3))?\)/gi;
let index = 0;
let match: RegExpExecArray | null;
while ((match = linkRegex.exec(text))) {
if (match.index - index > 0) {
result.push(text.substring(index, match.index));
}
const [, label, href, , title] = match;
if (title) {
result.push({ label, href, title });
} else {
result.push({ label, href });
}
index = match.index + match[0].length;
}
if (index < text.length) {
result.push(text.substring(index));
}
return result;
}
}
interface EnablementAware {
readonly enablement: string | undefined;
}
function isEnablementAware(arg: unknown): arg is EnablementAware {
return !!arg && typeof arg === 'object' && 'enablement' in arg;
}

View File

@@ -0,0 +1,9 @@
import { TestViewContribution as TheiaTestViewContribution } from '@theia/test/lib/browser/view/test-view-contribution';
import { injectable } from 'inversify';
@injectable()
export class TestViewContribution extends TheiaTestViewContribution {
override async initializeLayout(): Promise<void> {
// NOOP
}
}

View File

@@ -4,6 +4,7 @@ import {
TabBarToolbarRegistry,
TabBarToolbarItem,
ReactTabBarToolbarItem,
RenderedToolbarItem,
} from '@theia/core/lib/browser/shell/tab-bar-toolbar';
import { CommandRegistry } from '@theia/core/lib/common/command';
import { ReactWidget } from '@theia/core/lib/browser';
@@ -14,7 +15,7 @@ export const ARDUINO_TOOLBAR_ITEM_CLASS = 'arduino-tool-item';
export namespace ArduinoToolbarComponent {
export interface Props {
side: 'left' | 'right';
items: (TabBarToolbarItem | ReactTabBarToolbarItem)[];
items: TabBarToolbarItem[];
commands: CommandRegistry;
labelParser: LabelParser;
commandIsEnabled: (id: string) => boolean;
@@ -34,7 +35,7 @@ export class ArduinoToolbarComponent extends React.Component<
this.state = { tooltip: '' };
}
protected renderItem = (item: TabBarToolbarItem) => {
protected renderItem = (item: RenderedToolbarItem) => {
let innerText = '';
let className = `arduino-tool-icon ${item.id}-icon`;
if (item.text) {
@@ -46,7 +47,8 @@ export class ArduinoToolbarComponent extends React.Component<
}
}
}
const command = this.props.commands.getCommand(item.command);
const command =
item.command && this.props.commands.getCommand(item.command);
const cls = `${ARDUINO_TOOLBAR_ITEM_CLASS} ${
TabBarToolbar.Styles.TAB_BAR_TOOLBAR_ITEM
} ${command && this.props.commandIsEnabled(command.id) ? 'enabled' : ''} ${
@@ -80,7 +82,9 @@ export class ArduinoToolbarComponent extends React.Component<
const items = [
<React.Fragment key={this.props.side + '-arduino-toolbar-tooltip'}>
{[...this.props.items].map((item) =>
TabBarToolbarItem.is(item) ? this.renderItem(item) : item.render()
ReactTabBarToolbarItem.is(item)
? item.render()
: this.renderItem(item)
)}
</React.Fragment>,
];
@@ -94,10 +98,7 @@ export class ArduinoToolbarComponent extends React.Component<
}
export class ArduinoToolbar extends ReactWidget {
protected items = new Map<
string,
TabBarToolbarItem | ReactTabBarToolbarItem
>();
protected items = new Map<string, TabBarToolbarItem>();
constructor(
protected readonly tabBarToolbarRegistry: TabBarToolbarRegistry,
@@ -112,9 +113,7 @@ export class ArduinoToolbar extends ReactWidget {
this.tabBarToolbarRegistry.onDidChange(() => this.updateToolbar());
}
protected updateItems(
items: Array<TabBarToolbarItem | ReactTabBarToolbarItem>
): void {
protected updateItems(items: Array<TabBarToolbarItem>): void {
this.items.clear();
const revItems = items
.sort(TabBarToolbarItem.PRIORITY_COMPARATOR)
@@ -163,7 +162,7 @@ export class ArduinoToolbar extends ReactWidget {
protected executeCommand = (e: React.MouseEvent<HTMLElement>) => {
const item = this.items.get(e.currentTarget.id);
if (TabBarToolbarItem.is(item)) {
if (item && item.command) {
this.commands.executeCommand(item.command, this, e.target);
}
};

View File

@@ -1,4 +1,4 @@
import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application';
import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application-contribution';
import {
OpenerOptions,
OpenHandler,

View File

@@ -3,7 +3,7 @@ import { CommandRegistry } from '@theia/core/lib/common/command';
import { MenuModelRegistry } from '@theia/core/lib/common/menu';
import { PreferenceService } from '@theia/core/lib/browser/preferences/preference-service';
import { AbstractViewContribution } from '@theia/core/lib/browser/shell/view-contribution';
import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application';
import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application-contribution';
import { MainMenuManager } from '../../../common/main-menu-manager';
import { ArduinoPreferences } from '../../arduino-preferences';
import { SketchbookWidget } from './sketchbook-widget';

View File

@@ -171,7 +171,7 @@ export interface CoreService {
compile(
options: CoreService.Options.Compile,
cancellationToken?: CancellationToken
): Promise<void>;
): Promise<CompileSummary | undefined>;
upload(
options: CoreService.Options.Upload,
cancellationToken?: CancellationToken

View File

@@ -71,3 +71,4 @@ export interface IDEUpdaterClient {
}
export const SKIP_IDE_VERSION = 'skipIDEVersion';
export const LAST_USED_IDE_VERSION = 'lastUsedIDEVersion';

View File

@@ -1,7 +0,0 @@
export const SurveyNotificationServicePath =
'/services/survey-notification-service';
export const SurveyNotificationService = Symbol('SurveyNotificationService');
export interface SurveyNotificationService {
isFirstInstance(): Promise<boolean>;
}

View File

@@ -38,3 +38,26 @@ export function uint8ArrayToString(uint8Array: Uint8Array): string {
export function stringToUint8Array(text: string): Uint8Array {
return Uint8Array.from(text, (char) => char.charCodeAt(0));
}
export function poolWhile(
whileCondition: () => boolean,
intervalMs: number,
timeoutMs: number
): Promise<void> {
return new Promise((resolve, reject) => {
if (!whileCondition) {
resolve();
}
const start = Date.now();
const interval = setInterval(() => {
if (!whileCondition()) {
clearInterval(interval);
resolve();
} else if (Date.now() - start > timeoutMs) {
clearInterval(interval);
reject(new Error('Timed out while polling.'));
}
}, intervalMs);
});
}

View File

@@ -38,33 +38,33 @@ export class ElectronMainMenuFactory extends TheiaElectronMainMenuFactory {
this.preferencesService.onPreferenceChanged(
debounce((e) => {
if (e.preferenceName === 'window.menuBarVisibility') {
this.setMenuBar();
this.doSetMenuBar();
}
if (this._menu) {
for (const cmd of this._toggledCommands) {
const menuItem = this.findMenuById(this._menu, cmd);
if (this.menu) {
for (const cmd of this.toggledCommands) {
const menuItem = this.findMenuById(this.menu, cmd);
if (menuItem) {
menuItem.checked = this.commandRegistry.isToggled(cmd);
}
}
window.electronArduino.setMenu(this._menu); // calls the IDE2-specific implementation
window.electronArduino.setMenu(this.menu); // calls the IDE2-specific implementation
}
}, 10)
);
this.keybindingRegistry.onKeybindingsChanged(() => {
this.setMenuBar();
this.doSetMenuBar();
});
// #endregion Theia `postConstruct`
this.appStateService.reachedState('ready').then(() => {
this.appReady = true;
if (this.updateWhenReady) {
this.setMenuBar();
this.doSetMenuBar();
}
});
}
override createElectronMenuBar(): MenuDto[] {
this._toggledCommands.clear(); // https://github.com/eclipse-theia/theia/issues/8977
this.toggledCommands.clear(); // https://github.com/eclipse-theia/theia/issues/8977
const menuModel = this.menuProvider.getMenu(MAIN_MENU_BAR);
const menu = this.fillMenuTemplate([], menuModel, [], {
rootMenuPath: MAIN_MENU_BAR,
@@ -73,11 +73,11 @@ export class ElectronMainMenuFactory extends TheiaElectronMainMenuFactory {
menu.unshift(this.createOSXMenu());
}
const escapedMenu = this.escapeAmpersand(menu);
this._menu = escapedMenu;
this.menu = escapedMenu;
return escapedMenu;
}
override async setMenuBar(): Promise<void> {
override async doSetMenuBar(): Promise<void> {
// Avoid updating menu items when the app is not ready.
// Getting the current electron window is not free and synchronous.
// Here, we defer all menu update requests, and fire one when the app is ready.
@@ -124,17 +124,17 @@ export class ElectronMainMenuFactory extends TheiaElectronMainMenuFactory {
...args
);
if (
this._menu &&
this.menu &&
this.menuCommandExecutor.isVisible(menuPath, commandId, ...args)
) {
const item = this.findMenuById(this._menu, commandId);
const item = this.findMenuById(this.menu, commandId);
if (item) {
item.checked = this.menuCommandExecutor.isToggled(
menuPath,
commandId,
...args
);
window.electronArduino.setMenu(this._menu); // overridden to call the IDE2-specific implementation.
window.electronArduino.setMenu(this.menu); // overridden to call the IDE2-specific implementation.
}
}
}
@@ -342,7 +342,7 @@ export class ElectronMainMenuFactory extends TheiaElectronMainMenuFactory {
parentItems.push(menuItem);
if (this.commandRegistry.getToggledHandler(commandId, ...args)) {
this._toggledCommands.add(commandId);
this.toggledCommands.add(commandId);
}
}
return parentItems;

View File

@@ -7,6 +7,7 @@ import {
hasStartupTasks,
StartupTasks,
} from '../../../electron-common/startup-task';
import { WindowReloadOptions } from '@theia/core/lib/browser/window/window-service';
@injectable()
export class ElectronWindowService
@@ -17,8 +18,12 @@ export class ElectronWindowService
@postConstruct()
protected override init(): void {
// NOOP
// Overridden to avoid calling the zoom level listener in super.
// IDE2 listens on the zoom level changes in `ArduinoFrontendContribution#onStart`
window.electronTheiaCore.onAboutToClose(() => {
this.connectionCloseService.markForClose(this.frontendIdProvider.getId());
});
}
async isFirstWindow(): Promise<boolean> {
@@ -38,11 +43,11 @@ export class ElectronWindowService
}
// Overridden to support optional task owner params and make `tsc` happy.
override reload(options?: StartupTasks): void {
override reload(options?: StartupTasks | WindowReloadOptions): void {
if (hasStartupTasks(options)) {
window.electronArduino.requestReload(options);
} else {
window.electronTheiaCore.requestReload();
super.reload(options);
}
}

View File

@@ -1,10 +1,10 @@
import { JsonRpcConnectionHandler } from '@theia/core/lib/common/messaging/proxy-factory';
import { ElectronConnectionHandler } from '@theia/core/lib/electron-main/messaging/electron-connection-handler';
import { RpcConnectionHandler } from '@theia/core/lib/common/messaging/proxy-factory';
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 { TheiaMainApi } from '@theia/core/lib/electron-main/electron-api-main';
import {
ElectronMainApplication as TheiaElectronMainApplication,
ElectronMainApplicationContribution,
ElectronMainApplication as TheiaElectronMainApplication,
} from '@theia/core/lib/electron-main/electron-main-application';
import { TheiaElectronWindow as DefaultTheiaElectronWindow } from '@theia/core/lib/electron-main/theia-electron-window';
import { ContainerModule } from '@theia/core/shared/inversify';
@@ -36,15 +36,12 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
bind(ElectronConnectionHandler)
.toDynamicValue(
(context) =>
new JsonRpcConnectionHandler<IDEUpdaterClient>(
IDEUpdaterPath,
(client) => {
const server = context.container.get<IDEUpdater>(IDEUpdater);
server.setClient(client);
client.onDidCloseConnection(() => server.disconnectClient(client));
return server;
}
)
new RpcConnectionHandler<IDEUpdaterClient>(IDEUpdaterPath, (client) => {
const server = context.container.get<IDEUpdater>(IDEUpdater);
server.setClient(client);
client.onDidCloseConnection(() => server.disconnectClient(client));
return server;
})
)
.inSingletonScope();

View File

@@ -62,7 +62,7 @@ export class IDEUpdaterImpl implements IDEUpdater {
await this.updater.checkForUpdates();
this.cancellationToken = cancellationToken;
if (true) {
if (this.updater.currentVersion.compare(updateInfo.version) === -1) {
/*
'latest.txt' points to the latest changelog that has been generated by the CI,
so we need to make a first GET request to get the filename of the changelog

View File

@@ -11,16 +11,16 @@ import {
Disposable,
DisposableCollection,
} from '@theia/core/lib/common/disposable';
import { FileUri } from '@theia/core/lib/common/file-uri';
import { isOSX } from '@theia/core/lib/common/os';
import { Deferred } from '@theia/core/lib/common/promise-util';
import { isObject, MaybePromise, Mutable } from '@theia/core/lib/common/types';
import { ElectronSecurityToken } from '@theia/core/lib/electron-common/electron-token';
import {
ElectronMainExecutionParams,
ElectronMainCommandOptions,
ElectronMainApplication as TheiaElectronMainApplication,
} from '@theia/core/lib/electron-main/electron-main-application';
import type { TheiaBrowserWindowOptions } from '@theia/core/lib/electron-main/theia-electron-window';
import { FileUri } from '@theia/core/lib/node/file-uri';
import { inject, injectable } from '@theia/core/shared/inversify';
import { URI } from '@theia/core/shared/vscode-uri';
import { log as logToFile, setup as setupFileLog } from 'node-log-rotate';
@@ -28,7 +28,9 @@ import { fork } from 'node:child_process';
import { promises as fs, readFileSync, rm, rmSync } from 'node:fs';
import type { AddressInfo } from 'node:net';
import { isAbsolute, join, resolve } from 'node:path';
import type { Argv } from 'yargs';
import { Sketch } from '../../common/protocol';
import { poolWhile } from '../../common/utils';
import {
AppInfo,
appInfoPropertyLiterals,
@@ -129,6 +131,11 @@ const APP_STARTED_WITH_CONTENT_TRACE =
typeof process !== 'undefined' &&
process.argv.indexOf('--content-trace') !== -1;
const createYargs: (
argv?: string[],
cwd?: string
) => Argv = require('yargs/yargs');
@injectable()
export class ElectronMainApplication extends TheiaElectronMainApplication {
@inject(IsTempSketch)
@@ -171,29 +178,59 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
private readonly scheduledDeletions: Disposable[] = [];
override async start(config: FrontendApplicationConfig): Promise<void> {
// Explicitly set the app name to have better menu items on macOS. ("About", "Hide", and "Quit")
// See: https://github.com/electron-userland/electron-builder/issues/2468
// Regression in Theia: https://github.com/eclipse-theia/theia/issues/8701
console.log(`${config.applicationName} ${app.getVersion()}`);
app.on('ready', () => app.setName(config.applicationName));
const cwd = process.cwd();
this.attachFileAssociations(cwd);
this.useNativeWindowFrame = this.getTitleBarStyle(config) === 'native';
this._config = await updateFrontendApplicationConfigFromPackageJson(config);
this._appInfo = updateAppInfo(this._appInfo, this._config);
this.hookApplicationEvents();
const [port] = await Promise.all([this.startBackend(), app.whenReady()]);
this.startContentTracing();
this._backendPort.resolve(port);
await Promise.all([
this.attachElectronSecurityToken(port),
this.startContributions(),
]);
return this.launch({
secondInstance: false,
argv: this.processArgv.getProcessArgvWithoutBin(process.argv),
cwd,
});
createYargs(this.argv, process.cwd())
.command(
'$0 [file]',
false,
(cmd) =>
cmd
.option('electronUserData', {
type: 'string',
describe:
'The area where the electron main process puts its data',
})
.positional('file', { type: 'string' }),
async (args) => {
if (args.electronUserData) {
console.info(
`using electron user data area : '${args.electronUserData}'`
);
await fs.mkdir(args.electronUserData, { recursive: true });
app.setPath('userData', args.electronUserData);
}
// Explicitly set the app name to have better menu items on macOS. ("About", "Hide", and "Quit")
// See: https://github.com/electron-userland/electron-builder/issues/2468
// Regression in Theia: https://github.com/eclipse-theia/theia/issues/8701
console.log(`${config.applicationName} ${app.getVersion()}`);
app.on('ready', () => app.setName(config.applicationName));
const cwd = process.cwd();
this.attachFileAssociations(cwd);
this.useNativeWindowFrame =
this.getTitleBarStyle(config) === 'native';
this._config = await updateFrontendApplicationConfigFromPackageJson(
config
);
this._appInfo = updateAppInfo(this._appInfo, this._config);
this.hookApplicationEvents();
this.showInitialWindow(undefined);
const [port] = await Promise.all([
this.startBackend(),
app.whenReady(),
]);
this.startContentTracing();
this._backendPort.resolve(port);
await Promise.all([
this.attachElectronSecurityToken(port),
this.startContributions(),
]);
this.handleMainCommand({
file: args.file,
cwd,
secondInstance: false,
});
}
)
.parse();
}
private startContentTracing(): void {
@@ -256,6 +293,16 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
);
if (sketchFolderPath) {
this.openFilePromise.reject(new InterruptWorkspaceRestoreError());
// open-file event is triggered before the app is ready and initialWindow is created.
// Wait for initialWindow to be set before opening the sketch on the first instance.
// See https://github.com/arduino/arduino-ide/pull/2693
try {
await app.whenReady();
if (!this.firstWindowId) {
await poolWhile(() => !this.initialWindow, 100, 3000);
}
} catch {}
await this.openSketch(sketchFolderPath);
}
}
@@ -284,8 +331,8 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
}
}
protected override async launch(
params: ElectronMainExecutionParams
protected override async handleMainCommand(
options: ElectronMainCommandOptions
): Promise<void> {
try {
// When running on MacOS, we either have to wait until
@@ -300,7 +347,7 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
throw err;
}
if (await this.launchFromArgs(params)) {
if (await this.launchFromArgs(options)) {
// Application has received a file in its arguments and will skip the default application launch
return;
}
@@ -314,7 +361,10 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
`Restoring workspace roots: ${workspaces.map(({ file }) => file)}`
);
for (const workspace of workspaces) {
const resolvedPath = await this.resolvePath(workspace.file, params.cwd);
const resolvedPath = await this.resolvePath(
workspace.file,
options.cwd
);
if (!resolvedPath) {
continue;
}
@@ -337,15 +387,20 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
}
this.startup = false;
if (useDefault) {
super.launch(params);
super.handleMainCommand(options);
}
}
private get argv(): string[] {
return this.processArgv.getProcessArgvWithoutBin(process.argv).slice();
}
private async launchFromArgs(
params: ElectronMainExecutionParams
params: ElectronMainCommandOptions,
argv?: string[]
): Promise<boolean> {
// Copy to prevent manipulation of original array
const argCopy = [...params.argv];
const argCopy = [...(argv || this.argv)];
let path: string | undefined;
for (const maybePath of argCopy) {
const resolvedPath = await this.resolvePath(maybePath, params.cwd);
@@ -383,7 +438,7 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
}
const [uri, electronWindow] = await Promise.all([
this.createWindowUri(),
this.createWindow(options),
this.reuseOrCreateWindow(options),
]);
electronWindow.loadURL(uri.withFragment(encodeURI(file)).toString(true));
return electronWindow;
@@ -483,7 +538,7 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
argv: string[],
cwd: string
): Promise<void> {
if (await this.launchFromArgs({ cwd, argv, secondInstance: true })) {
if (await this.launchFromArgs({ cwd, secondInstance: true }, argv)) {
// Application has received a file in its arguments
return;
}
@@ -779,7 +834,7 @@ class InterruptWorkspaceRestoreError extends Error {
// but it's the `package.json` inside the `resources/app/` folder if it's the final bundled app.
// See https://github.com/arduino/arduino-ide/pull/2144#pullrequestreview-1556343430.
async function updateFrontendApplicationConfigFromPackageJson(
config: FrontendApplicationConfig
config: Mutable<FrontendApplicationConfig>
): Promise<FrontendApplicationConfig> {
if (!isProductionMode) {
console.debug(
@@ -846,7 +901,11 @@ const fallbackFrontendAppConfig: FrontendApplicationConfig = {
defaultIconTheme: 'none',
validatePreferencesSchema: false,
defaultLocale: '',
electron: {},
electron: {
showWindowEarly: true,
uriScheme: 'arduino-ide',
},
reloadOnReconnect: true,
};
// When the package.json must go from `./lib/backend/electron-main.js` to `./package.json` when the app is webpacked.

View File

@@ -8,7 +8,7 @@ import { ElectronArduinoRenderer } from '../electron-arduino';
@injectable()
export class TheiaElectronWindow extends DefaultTheiaElectronWindow {
protected override reload(args?: unknown): void {
this.handleStopRequest(() => {
this.handleStopRequest(async () => {
this.applicationState = 'init';
if (hasStartupTasks(args)) {
const { webContents } = this._window;

View File

@@ -1,7 +1,7 @@
import { join } from 'node:path';
import { inject, injectable, named } from '@theia/core/shared/inversify';
import { spawn, ChildProcess } from 'node:child_process';
import { FileUri } from '@theia/core/lib/node/file-uri';
import { FileUri } from '@theia/core/lib/common/file-uri';
import { ILogger } from '@theia/core/lib/common/logger';
import { Deferred, retry } from '@theia/core/lib/common/promise-util';
import {

View File

@@ -105,14 +105,8 @@ import { ClangFormatter } from './clang-formatter';
import { FormatterPath } from '../common/protocol/formatter';
import { HostedPluginLocalizationService } from './theia/plugin-ext/hosted-plugin-localization-service';
import { HostedPluginLocalizationService as TheiaHostedPluginLocalizationService } from '@theia/plugin-ext/lib/hosted/node/hosted-plugin-localization-service';
import { SurveyNotificationServiceImpl } from './survey-service-impl';
import {
SurveyNotificationService,
SurveyNotificationServicePath,
} from '../common/protocol/survey-service';
import { IsTempSketch } from './is-temp-sketch';
import { rebindNsfwFileSystemWatcher } from './theia/filesystem/nsfw-bindings';
import { MessagingContribution } from './theia/core/messaging-contribution';
import { WebsocketEndpoint } from './theia/core/websocket-endpoint';
import { MessagingService } from '@theia/core/lib/node/messaging/messaging-service';
import { HostedPluginReader } from './theia/plugin-ext/plugin-reader';
import { HostedPluginReader as TheiaHostedPluginReader } from '@theia/plugin-ext/lib/hosted/node/plugin-reader';
@@ -126,6 +120,7 @@ import {
} from './theia/plugin-ext/plugin-deployer';
import { SettingsReader } from './settings-reader';
import { VsCodePluginScanner } from './theia/plugin-ext-vscode/scanner-vscode';
import { rebindParcelFileSystemWatcher } from './theia/filesystem/parcel-bindings';
export default new ContainerModule((bind, unbind, isBound, rebind) => {
bind(BackendApplication).toSelf().inSingletonScope();
@@ -305,7 +300,7 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
)
)
.inSingletonScope();
rebindNsfwFileSystemWatcher(rebind);
rebindParcelFileSystemWatcher(rebind);
// Output service per connection.
bind(ConnectionContainerModule).toConstantValue(
@@ -383,23 +378,8 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
HostedPluginLocalizationService
);
// 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();
bind(IsTempSketch).toSelf().inSingletonScope();
rebind(MessagingService.Identifier)
.to(MessagingContribution)
.inSingletonScope();
rebind(MessagingService.Identifier).to(WebsocketEndpoint).inSingletonScope();
// Removed undesired contributions from VS Code extensions
// Such as the RTOS view from the `cortex-debug` extension

View File

@@ -1,6 +1,6 @@
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';
import { FileUri } from '@theia/core/lib/common/file-uri';
import { inject, injectable } from '@theia/core/shared/inversify';
import { constants, promises as fs } from 'node:fs';
import { join } from 'node:path';

View File

@@ -1,6 +1,6 @@
import { notEmpty } from '@theia/core/lib/common/objects';
import { nls } from '@theia/core/lib/common/nls';
import { FileUri } from '@theia/core/lib/node/file-uri';
import { FileUri } from '@theia/core/lib/common/file-uri';
import {
Range,
Position,

View File

@@ -360,6 +360,8 @@ export class BoardListRequest extends jspb.Message {
setTimeout(value: number): BoardListRequest;
getFqbn(): string;
setFqbn(value: string): BoardListRequest;
getSkipCloudApiForBoardDetection(): boolean;
setSkipCloudApiForBoardDetection(value: boolean): BoardListRequest;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): BoardListRequest.AsObject;
@@ -376,6 +378,7 @@ export namespace BoardListRequest {
instance?: cc_arduino_cli_commands_v1_common_pb.Instance.AsObject,
timeout: number,
fqbn: string,
skipCloudApiForBoardDetection: boolean,
}
}
@@ -493,6 +496,8 @@ export class BoardListWatchRequest extends jspb.Message {
clearInstance(): void;
getInstance(): cc_arduino_cli_commands_v1_common_pb.Instance | undefined;
setInstance(value?: cc_arduino_cli_commands_v1_common_pb.Instance): BoardListWatchRequest;
getSkipCloudApiForBoardDetection(): boolean;
setSkipCloudApiForBoardDetection(value: boolean): BoardListWatchRequest;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): BoardListWatchRequest.AsObject;
@@ -507,6 +512,7 @@ export class BoardListWatchRequest extends jspb.Message {
export namespace BoardListWatchRequest {
export type AsObject = {
instance?: cc_arduino_cli_commands_v1_common_pb.Instance.AsObject,
skipCloudApiForBoardDetection: boolean,
}
}
@@ -621,3 +627,56 @@ export namespace BoardSearchResponse {
boardsList: Array<BoardListItem.AsObject>,
}
}
export class BoardIdentifyRequest extends jspb.Message {
hasInstance(): boolean;
clearInstance(): void;
getInstance(): cc_arduino_cli_commands_v1_common_pb.Instance | undefined;
setInstance(value?: cc_arduino_cli_commands_v1_common_pb.Instance): BoardIdentifyRequest;
getPropertiesMap(): jspb.Map<string, string>;
clearPropertiesMap(): void;
getUseCloudApiForUnknownBoardDetection(): boolean;
setUseCloudApiForUnknownBoardDetection(value: boolean): BoardIdentifyRequest;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): BoardIdentifyRequest.AsObject;
static toObject(includeInstance: boolean, msg: BoardIdentifyRequest): BoardIdentifyRequest.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: BoardIdentifyRequest, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): BoardIdentifyRequest;
static deserializeBinaryFromReader(message: BoardIdentifyRequest, reader: jspb.BinaryReader): BoardIdentifyRequest;
}
export namespace BoardIdentifyRequest {
export type AsObject = {
instance?: cc_arduino_cli_commands_v1_common_pb.Instance.AsObject,
propertiesMap: Array<[string, string]>,
useCloudApiForUnknownBoardDetection: boolean,
}
}
export class BoardIdentifyResponse extends jspb.Message {
clearBoardsList(): void;
getBoardsList(): Array<BoardListItem>;
setBoardsList(value: Array<BoardListItem>): BoardIdentifyResponse;
addBoards(value?: BoardListItem, index?: number): BoardListItem;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): BoardIdentifyResponse.AsObject;
static toObject(includeInstance: boolean, msg: BoardIdentifyResponse): BoardIdentifyResponse.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: BoardIdentifyResponse, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): BoardIdentifyResponse;
static deserializeBinaryFromReader(message: BoardIdentifyResponse, reader: jspb.BinaryReader): BoardIdentifyResponse;
}
export namespace BoardIdentifyResponse {
export type AsObject = {
boardsList: Array<BoardListItem.AsObject>,
}
}

View File

@@ -28,6 +28,8 @@ goog.object.extend(proto, cc_arduino_cli_commands_v1_port_pb);
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.BoardDetailsRequest', null, global);
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.BoardDetailsResponse', null, global);
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.BoardIdentificationProperties', null, global);
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.BoardIdentifyRequest', null, global);
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.BoardIdentifyResponse', null, global);
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.BoardListAllRequest', null, global);
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.BoardListAllResponse', null, global);
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.BoardListItem', null, global);
@@ -465,6 +467,48 @@ if (goog.DEBUG && !COMPILED) {
*/
proto.cc.arduino.cli.commands.v1.BoardSearchResponse.displayName = 'proto.cc.arduino.cli.commands.v1.BoardSearchResponse';
}
/**
* Generated by JsPbCodeGenerator.
* @param {Array=} opt_data Optional initial data array, typically from a
* server response, or constructed directly in Javascript. The array is used
* in place and becomes part of the constructed object. It is not cloned.
* If no data is provided, the constructed object will be empty, but still
* valid.
* @extends {jspb.Message}
* @constructor
*/
proto.cc.arduino.cli.commands.v1.BoardIdentifyRequest = function(opt_data) {
jspb.Message.initialize(this, opt_data, 0, -1, null, null);
};
goog.inherits(proto.cc.arduino.cli.commands.v1.BoardIdentifyRequest, jspb.Message);
if (goog.DEBUG && !COMPILED) {
/**
* @public
* @override
*/
proto.cc.arduino.cli.commands.v1.BoardIdentifyRequest.displayName = 'proto.cc.arduino.cli.commands.v1.BoardIdentifyRequest';
}
/**
* Generated by JsPbCodeGenerator.
* @param {Array=} opt_data Optional initial data array, typically from a
* server response, or constructed directly in Javascript. The array is used
* in place and becomes part of the constructed object. It is not cloned.
* If no data is provided, the constructed object will be empty, but still
* valid.
* @extends {jspb.Message}
* @constructor
*/
proto.cc.arduino.cli.commands.v1.BoardIdentifyResponse = function(opt_data) {
jspb.Message.initialize(this, opt_data, 0, -1, proto.cc.arduino.cli.commands.v1.BoardIdentifyResponse.repeatedFields_, null);
};
goog.inherits(proto.cc.arduino.cli.commands.v1.BoardIdentifyResponse, jspb.Message);
if (goog.DEBUG && !COMPILED) {
/**
* @public
* @override
*/
proto.cc.arduino.cli.commands.v1.BoardIdentifyResponse.displayName = 'proto.cc.arduino.cli.commands.v1.BoardIdentifyResponse';
}
@@ -3204,7 +3248,8 @@ proto.cc.arduino.cli.commands.v1.BoardListRequest.toObject = function(includeIns
var f, obj = {
instance: (f = msg.getInstance()) && cc_arduino_cli_commands_v1_common_pb.Instance.toObject(includeInstance, f),
timeout: jspb.Message.getFieldWithDefault(msg, 2, 0),
fqbn: jspb.Message.getFieldWithDefault(msg, 3, "")
fqbn: jspb.Message.getFieldWithDefault(msg, 3, ""),
skipCloudApiForBoardDetection: jspb.Message.getBooleanFieldWithDefault(msg, 4, false)
};
if (includeInstance) {
@@ -3254,6 +3299,10 @@ proto.cc.arduino.cli.commands.v1.BoardListRequest.deserializeBinaryFromReader =
var value = /** @type {string} */ (reader.readString());
msg.setFqbn(value);
break;
case 4:
var value = /** @type {boolean} */ (reader.readBool());
msg.setSkipCloudApiForBoardDetection(value);
break;
default:
reader.skipField();
break;
@@ -3305,6 +3354,13 @@ proto.cc.arduino.cli.commands.v1.BoardListRequest.serializeBinaryToWriter = func
f
);
}
f = message.getSkipCloudApiForBoardDetection();
if (f) {
writer.writeBool(
4,
f
);
}
};
@@ -3381,6 +3437,24 @@ proto.cc.arduino.cli.commands.v1.BoardListRequest.prototype.setFqbn = function(v
};
/**
* optional bool skip_cloud_api_for_board_detection = 4;
* @return {boolean}
*/
proto.cc.arduino.cli.commands.v1.BoardListRequest.prototype.getSkipCloudApiForBoardDetection = function() {
return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 4, false));
};
/**
* @param {boolean} value
* @return {!proto.cc.arduino.cli.commands.v1.BoardListRequest} returns this
*/
proto.cc.arduino.cli.commands.v1.BoardListRequest.prototype.setSkipCloudApiForBoardDetection = function(value) {
return jspb.Message.setProto3BooleanField(this, 4, value);
};
/**
* List of repeated fields within this message type.
@@ -4230,7 +4304,8 @@ proto.cc.arduino.cli.commands.v1.BoardListWatchRequest.prototype.toObject = func
*/
proto.cc.arduino.cli.commands.v1.BoardListWatchRequest.toObject = function(includeInstance, msg) {
var f, obj = {
instance: (f = msg.getInstance()) && cc_arduino_cli_commands_v1_common_pb.Instance.toObject(includeInstance, f)
instance: (f = msg.getInstance()) && cc_arduino_cli_commands_v1_common_pb.Instance.toObject(includeInstance, f),
skipCloudApiForBoardDetection: jspb.Message.getBooleanFieldWithDefault(msg, 2, false)
};
if (includeInstance) {
@@ -4272,6 +4347,10 @@ proto.cc.arduino.cli.commands.v1.BoardListWatchRequest.deserializeBinaryFromRead
reader.readMessage(value,cc_arduino_cli_commands_v1_common_pb.Instance.deserializeBinaryFromReader);
msg.setInstance(value);
break;
case 2:
var value = /** @type {boolean} */ (reader.readBool());
msg.setSkipCloudApiForBoardDetection(value);
break;
default:
reader.skipField();
break;
@@ -4309,6 +4388,13 @@ proto.cc.arduino.cli.commands.v1.BoardListWatchRequest.serializeBinaryToWriter =
cc_arduino_cli_commands_v1_common_pb.Instance.serializeBinaryToWriter
);
}
f = message.getSkipCloudApiForBoardDetection();
if (f) {
writer.writeBool(
2,
f
);
}
};
@@ -4349,6 +4435,24 @@ proto.cc.arduino.cli.commands.v1.BoardListWatchRequest.prototype.hasInstance = f
};
/**
* optional bool skip_cloud_api_for_board_detection = 2;
* @return {boolean}
*/
proto.cc.arduino.cli.commands.v1.BoardListWatchRequest.prototype.getSkipCloudApiForBoardDetection = function() {
return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 2, false));
};
/**
* @param {boolean} value
* @return {!proto.cc.arduino.cli.commands.v1.BoardListWatchRequest} returns this
*/
proto.cc.arduino.cli.commands.v1.BoardListWatchRequest.prototype.setSkipCloudApiForBoardDetection = function(value) {
return jspb.Message.setProto3BooleanField(this, 2, value);
};
@@ -5172,4 +5276,378 @@ proto.cc.arduino.cli.commands.v1.BoardSearchResponse.prototype.clearBoardsList =
};
if (jspb.Message.GENERATE_TO_OBJECT) {
/**
* Creates an object representation of this proto.
* Field names that are reserved in JavaScript and will be renamed to pb_name.
* Optional fields that are not set will be set to undefined.
* To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.
* For the list of reserved names please see:
* net/proto2/compiler/js/internal/generator.cc#kKeyword.
* @param {boolean=} opt_includeInstance Deprecated. whether to include the
* JSPB instance for transitional soy proto support:
* http://goto/soy-param-migration
* @return {!Object}
*/
proto.cc.arduino.cli.commands.v1.BoardIdentifyRequest.prototype.toObject = function(opt_includeInstance) {
return proto.cc.arduino.cli.commands.v1.BoardIdentifyRequest.toObject(opt_includeInstance, this);
};
/**
* Static version of the {@see toObject} method.
* @param {boolean|undefined} includeInstance Deprecated. Whether to include
* the JSPB instance for transitional soy proto support:
* http://goto/soy-param-migration
* @param {!proto.cc.arduino.cli.commands.v1.BoardIdentifyRequest} msg The msg instance to transform.
* @return {!Object}
* @suppress {unusedLocalVariables} f is only used for nested messages
*/
proto.cc.arduino.cli.commands.v1.BoardIdentifyRequest.toObject = function(includeInstance, msg) {
var f, obj = {
instance: (f = msg.getInstance()) && cc_arduino_cli_commands_v1_common_pb.Instance.toObject(includeInstance, f),
propertiesMap: (f = msg.getPropertiesMap()) ? f.toObject(includeInstance, undefined) : [],
useCloudApiForUnknownBoardDetection: jspb.Message.getBooleanFieldWithDefault(msg, 3, false)
};
if (includeInstance) {
obj.$jspbMessageInstance = msg;
}
return obj;
};
}
/**
* Deserializes binary data (in protobuf wire format).
* @param {jspb.ByteSource} bytes The bytes to deserialize.
* @return {!proto.cc.arduino.cli.commands.v1.BoardIdentifyRequest}
*/
proto.cc.arduino.cli.commands.v1.BoardIdentifyRequest.deserializeBinary = function(bytes) {
var reader = new jspb.BinaryReader(bytes);
var msg = new proto.cc.arduino.cli.commands.v1.BoardIdentifyRequest;
return proto.cc.arduino.cli.commands.v1.BoardIdentifyRequest.deserializeBinaryFromReader(msg, reader);
};
/**
* Deserializes binary data (in protobuf wire format) from the
* given reader into the given message object.
* @param {!proto.cc.arduino.cli.commands.v1.BoardIdentifyRequest} msg The message object to deserialize into.
* @param {!jspb.BinaryReader} reader The BinaryReader to use.
* @return {!proto.cc.arduino.cli.commands.v1.BoardIdentifyRequest}
*/
proto.cc.arduino.cli.commands.v1.BoardIdentifyRequest.deserializeBinaryFromReader = function(msg, reader) {
while (reader.nextField()) {
if (reader.isEndGroup()) {
break;
}
var field = reader.getFieldNumber();
switch (field) {
case 1:
var value = new cc_arduino_cli_commands_v1_common_pb.Instance;
reader.readMessage(value,cc_arduino_cli_commands_v1_common_pb.Instance.deserializeBinaryFromReader);
msg.setInstance(value);
break;
case 2:
var value = msg.getPropertiesMap();
reader.readMessage(value, function(message, reader) {
jspb.Map.deserializeBinary(message, reader, jspb.BinaryReader.prototype.readString, jspb.BinaryReader.prototype.readString, null, "", "");
});
break;
case 3:
var value = /** @type {boolean} */ (reader.readBool());
msg.setUseCloudApiForUnknownBoardDetection(value);
break;
default:
reader.skipField();
break;
}
}
return msg;
};
/**
* Serializes the message to binary data (in protobuf wire format).
* @return {!Uint8Array}
*/
proto.cc.arduino.cli.commands.v1.BoardIdentifyRequest.prototype.serializeBinary = function() {
var writer = new jspb.BinaryWriter();
proto.cc.arduino.cli.commands.v1.BoardIdentifyRequest.serializeBinaryToWriter(this, writer);
return writer.getResultBuffer();
};
/**
* Serializes the given message to binary data (in protobuf wire
* format), writing to the given BinaryWriter.
* @param {!proto.cc.arduino.cli.commands.v1.BoardIdentifyRequest} message
* @param {!jspb.BinaryWriter} writer
* @suppress {unusedLocalVariables} f is only used for nested messages
*/
proto.cc.arduino.cli.commands.v1.BoardIdentifyRequest.serializeBinaryToWriter = function(message, writer) {
var f = undefined;
f = message.getInstance();
if (f != null) {
writer.writeMessage(
1,
f,
cc_arduino_cli_commands_v1_common_pb.Instance.serializeBinaryToWriter
);
}
f = message.getPropertiesMap(true);
if (f && f.getLength() > 0) {
f.serializeBinary(2, writer, jspb.BinaryWriter.prototype.writeString, jspb.BinaryWriter.prototype.writeString);
}
f = message.getUseCloudApiForUnknownBoardDetection();
if (f) {
writer.writeBool(
3,
f
);
}
};
/**
* optional Instance instance = 1;
* @return {?proto.cc.arduino.cli.commands.v1.Instance}
*/
proto.cc.arduino.cli.commands.v1.BoardIdentifyRequest.prototype.getInstance = function() {
return /** @type{?proto.cc.arduino.cli.commands.v1.Instance} */ (
jspb.Message.getWrapperField(this, cc_arduino_cli_commands_v1_common_pb.Instance, 1));
};
/**
* @param {?proto.cc.arduino.cli.commands.v1.Instance|undefined} value
* @return {!proto.cc.arduino.cli.commands.v1.BoardIdentifyRequest} returns this
*/
proto.cc.arduino.cli.commands.v1.BoardIdentifyRequest.prototype.setInstance = function(value) {
return jspb.Message.setWrapperField(this, 1, value);
};
/**
* Clears the message field making it undefined.
* @return {!proto.cc.arduino.cli.commands.v1.BoardIdentifyRequest} returns this
*/
proto.cc.arduino.cli.commands.v1.BoardIdentifyRequest.prototype.clearInstance = function() {
return this.setInstance(undefined);
};
/**
* Returns whether this field is set.
* @return {boolean}
*/
proto.cc.arduino.cli.commands.v1.BoardIdentifyRequest.prototype.hasInstance = function() {
return jspb.Message.getField(this, 1) != null;
};
/**
* map<string, string> properties = 2;
* @param {boolean=} opt_noLazyCreate Do not create the map if
* empty, instead returning `undefined`
* @return {!jspb.Map<string,string>}
*/
proto.cc.arduino.cli.commands.v1.BoardIdentifyRequest.prototype.getPropertiesMap = function(opt_noLazyCreate) {
return /** @type {!jspb.Map<string,string>} */ (
jspb.Message.getMapField(this, 2, opt_noLazyCreate,
null));
};
/**
* Clears values from the map. The map will be non-null.
* @return {!proto.cc.arduino.cli.commands.v1.BoardIdentifyRequest} returns this
*/
proto.cc.arduino.cli.commands.v1.BoardIdentifyRequest.prototype.clearPropertiesMap = function() {
this.getPropertiesMap().clear();
return this;};
/**
* optional bool use_cloud_api_for_unknown_board_detection = 3;
* @return {boolean}
*/
proto.cc.arduino.cli.commands.v1.BoardIdentifyRequest.prototype.getUseCloudApiForUnknownBoardDetection = function() {
return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 3, false));
};
/**
* @param {boolean} value
* @return {!proto.cc.arduino.cli.commands.v1.BoardIdentifyRequest} returns this
*/
proto.cc.arduino.cli.commands.v1.BoardIdentifyRequest.prototype.setUseCloudApiForUnknownBoardDetection = function(value) {
return jspb.Message.setProto3BooleanField(this, 3, value);
};
/**
* List of repeated fields within this message type.
* @private {!Array<number>}
* @const
*/
proto.cc.arduino.cli.commands.v1.BoardIdentifyResponse.repeatedFields_ = [1];
if (jspb.Message.GENERATE_TO_OBJECT) {
/**
* Creates an object representation of this proto.
* Field names that are reserved in JavaScript and will be renamed to pb_name.
* Optional fields that are not set will be set to undefined.
* To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.
* For the list of reserved names please see:
* net/proto2/compiler/js/internal/generator.cc#kKeyword.
* @param {boolean=} opt_includeInstance Deprecated. whether to include the
* JSPB instance for transitional soy proto support:
* http://goto/soy-param-migration
* @return {!Object}
*/
proto.cc.arduino.cli.commands.v1.BoardIdentifyResponse.prototype.toObject = function(opt_includeInstance) {
return proto.cc.arduino.cli.commands.v1.BoardIdentifyResponse.toObject(opt_includeInstance, this);
};
/**
* Static version of the {@see toObject} method.
* @param {boolean|undefined} includeInstance Deprecated. Whether to include
* the JSPB instance for transitional soy proto support:
* http://goto/soy-param-migration
* @param {!proto.cc.arduino.cli.commands.v1.BoardIdentifyResponse} msg The msg instance to transform.
* @return {!Object}
* @suppress {unusedLocalVariables} f is only used for nested messages
*/
proto.cc.arduino.cli.commands.v1.BoardIdentifyResponse.toObject = function(includeInstance, msg) {
var f, obj = {
boardsList: jspb.Message.toObjectList(msg.getBoardsList(),
proto.cc.arduino.cli.commands.v1.BoardListItem.toObject, includeInstance)
};
if (includeInstance) {
obj.$jspbMessageInstance = msg;
}
return obj;
};
}
/**
* Deserializes binary data (in protobuf wire format).
* @param {jspb.ByteSource} bytes The bytes to deserialize.
* @return {!proto.cc.arduino.cli.commands.v1.BoardIdentifyResponse}
*/
proto.cc.arduino.cli.commands.v1.BoardIdentifyResponse.deserializeBinary = function(bytes) {
var reader = new jspb.BinaryReader(bytes);
var msg = new proto.cc.arduino.cli.commands.v1.BoardIdentifyResponse;
return proto.cc.arduino.cli.commands.v1.BoardIdentifyResponse.deserializeBinaryFromReader(msg, reader);
};
/**
* Deserializes binary data (in protobuf wire format) from the
* given reader into the given message object.
* @param {!proto.cc.arduino.cli.commands.v1.BoardIdentifyResponse} msg The message object to deserialize into.
* @param {!jspb.BinaryReader} reader The BinaryReader to use.
* @return {!proto.cc.arduino.cli.commands.v1.BoardIdentifyResponse}
*/
proto.cc.arduino.cli.commands.v1.BoardIdentifyResponse.deserializeBinaryFromReader = function(msg, reader) {
while (reader.nextField()) {
if (reader.isEndGroup()) {
break;
}
var field = reader.getFieldNumber();
switch (field) {
case 1:
var value = new proto.cc.arduino.cli.commands.v1.BoardListItem;
reader.readMessage(value,proto.cc.arduino.cli.commands.v1.BoardListItem.deserializeBinaryFromReader);
msg.addBoards(value);
break;
default:
reader.skipField();
break;
}
}
return msg;
};
/**
* Serializes the message to binary data (in protobuf wire format).
* @return {!Uint8Array}
*/
proto.cc.arduino.cli.commands.v1.BoardIdentifyResponse.prototype.serializeBinary = function() {
var writer = new jspb.BinaryWriter();
proto.cc.arduino.cli.commands.v1.BoardIdentifyResponse.serializeBinaryToWriter(this, writer);
return writer.getResultBuffer();
};
/**
* Serializes the given message to binary data (in protobuf wire
* format), writing to the given BinaryWriter.
* @param {!proto.cc.arduino.cli.commands.v1.BoardIdentifyResponse} message
* @param {!jspb.BinaryWriter} writer
* @suppress {unusedLocalVariables} f is only used for nested messages
*/
proto.cc.arduino.cli.commands.v1.BoardIdentifyResponse.serializeBinaryToWriter = function(message, writer) {
var f = undefined;
f = message.getBoardsList();
if (f.length > 0) {
writer.writeRepeatedMessage(
1,
f,
proto.cc.arduino.cli.commands.v1.BoardListItem.serializeBinaryToWriter
);
}
};
/**
* repeated BoardListItem boards = 1;
* @return {!Array<!proto.cc.arduino.cli.commands.v1.BoardListItem>}
*/
proto.cc.arduino.cli.commands.v1.BoardIdentifyResponse.prototype.getBoardsList = function() {
return /** @type{!Array<!proto.cc.arduino.cli.commands.v1.BoardListItem>} */ (
jspb.Message.getRepeatedWrapperField(this, proto.cc.arduino.cli.commands.v1.BoardListItem, 1));
};
/**
* @param {!Array<!proto.cc.arduino.cli.commands.v1.BoardListItem>} value
* @return {!proto.cc.arduino.cli.commands.v1.BoardIdentifyResponse} returns this
*/
proto.cc.arduino.cli.commands.v1.BoardIdentifyResponse.prototype.setBoardsList = function(value) {
return jspb.Message.setRepeatedWrapperField(this, 1, value);
};
/**
* @param {!proto.cc.arduino.cli.commands.v1.BoardListItem=} opt_value
* @param {number=} opt_index
* @return {!proto.cc.arduino.cli.commands.v1.BoardListItem}
*/
proto.cc.arduino.cli.commands.v1.BoardIdentifyResponse.prototype.addBoards = function(opt_value, opt_index) {
return jspb.Message.addToRepeatedWrapperField(this, 1, opt_value, proto.cc.arduino.cli.commands.v1.BoardListItem, opt_index);
};
/**
* Clears the list making it empty but non-null.
* @return {!proto.cc.arduino.cli.commands.v1.BoardIdentifyResponse} returns this
*/
proto.cc.arduino.cli.commands.v1.BoardIdentifyResponse.prototype.clearBoardsList = function() {
return this.setBoardsList([]);
};
goog.object.extend(exports, proto.cc.arduino.cli.commands.v1);

View File

@@ -32,6 +32,7 @@ interface IArduinoCoreServiceService extends grpc.ServiceDefinition<grpc.Untyped
boardList: IArduinoCoreServiceService_IBoardList;
boardListAll: IArduinoCoreServiceService_IBoardListAll;
boardSearch: IArduinoCoreServiceService_IBoardSearch;
boardIdentify: IArduinoCoreServiceService_IBoardIdentify;
boardListWatch: IArduinoCoreServiceService_IBoardListWatch;
compile: IArduinoCoreServiceService_ICompile;
platformInstall: IArduinoCoreServiceService_IPlatformInstall;
@@ -195,6 +196,15 @@ interface IArduinoCoreServiceService_IBoardSearch extends grpc.MethodDefinition<
responseSerialize: grpc.serialize<cc_arduino_cli_commands_v1_board_pb.BoardSearchResponse>;
responseDeserialize: grpc.deserialize<cc_arduino_cli_commands_v1_board_pb.BoardSearchResponse>;
}
interface IArduinoCoreServiceService_IBoardIdentify extends grpc.MethodDefinition<cc_arduino_cli_commands_v1_board_pb.BoardIdentifyRequest, cc_arduino_cli_commands_v1_board_pb.BoardIdentifyResponse> {
path: "/cc.arduino.cli.commands.v1.ArduinoCoreService/BoardIdentify";
requestStream: false;
responseStream: false;
requestSerialize: grpc.serialize<cc_arduino_cli_commands_v1_board_pb.BoardIdentifyRequest>;
requestDeserialize: grpc.deserialize<cc_arduino_cli_commands_v1_board_pb.BoardIdentifyRequest>;
responseSerialize: grpc.serialize<cc_arduino_cli_commands_v1_board_pb.BoardIdentifyResponse>;
responseDeserialize: grpc.deserialize<cc_arduino_cli_commands_v1_board_pb.BoardIdentifyResponse>;
}
interface IArduinoCoreServiceService_IBoardListWatch extends grpc.MethodDefinition<cc_arduino_cli_commands_v1_board_pb.BoardListWatchRequest, cc_arduino_cli_commands_v1_board_pb.BoardListWatchResponse> {
path: "/cc.arduino.cli.commands.v1.ArduinoCoreService/BoardListWatch";
requestStream: false;
@@ -528,6 +538,7 @@ export interface IArduinoCoreServiceServer extends grpc.UntypedServiceImplementa
boardList: grpc.handleUnaryCall<cc_arduino_cli_commands_v1_board_pb.BoardListRequest, cc_arduino_cli_commands_v1_board_pb.BoardListResponse>;
boardListAll: grpc.handleUnaryCall<cc_arduino_cli_commands_v1_board_pb.BoardListAllRequest, cc_arduino_cli_commands_v1_board_pb.BoardListAllResponse>;
boardSearch: grpc.handleUnaryCall<cc_arduino_cli_commands_v1_board_pb.BoardSearchRequest, cc_arduino_cli_commands_v1_board_pb.BoardSearchResponse>;
boardIdentify: grpc.handleUnaryCall<cc_arduino_cli_commands_v1_board_pb.BoardIdentifyRequest, cc_arduino_cli_commands_v1_board_pb.BoardIdentifyResponse>;
boardListWatch: grpc.handleServerStreamingCall<cc_arduino_cli_commands_v1_board_pb.BoardListWatchRequest, cc_arduino_cli_commands_v1_board_pb.BoardListWatchResponse>;
compile: grpc.handleServerStreamingCall<cc_arduino_cli_commands_v1_compile_pb.CompileRequest, cc_arduino_cli_commands_v1_compile_pb.CompileResponse>;
platformInstall: grpc.handleServerStreamingCall<cc_arduino_cli_commands_v1_core_pb.PlatformInstallRequest, cc_arduino_cli_commands_v1_core_pb.PlatformInstallResponse>;
@@ -605,6 +616,9 @@ export interface IArduinoCoreServiceClient {
boardSearch(request: cc_arduino_cli_commands_v1_board_pb.BoardSearchRequest, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_board_pb.BoardSearchResponse) => void): grpc.ClientUnaryCall;
boardSearch(request: cc_arduino_cli_commands_v1_board_pb.BoardSearchRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_board_pb.BoardSearchResponse) => void): grpc.ClientUnaryCall;
boardSearch(request: cc_arduino_cli_commands_v1_board_pb.BoardSearchRequest, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_board_pb.BoardSearchResponse) => void): grpc.ClientUnaryCall;
boardIdentify(request: cc_arduino_cli_commands_v1_board_pb.BoardIdentifyRequest, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_board_pb.BoardIdentifyResponse) => void): grpc.ClientUnaryCall;
boardIdentify(request: cc_arduino_cli_commands_v1_board_pb.BoardIdentifyRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_board_pb.BoardIdentifyResponse) => void): grpc.ClientUnaryCall;
boardIdentify(request: cc_arduino_cli_commands_v1_board_pb.BoardIdentifyRequest, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_board_pb.BoardIdentifyResponse) => void): grpc.ClientUnaryCall;
boardListWatch(request: cc_arduino_cli_commands_v1_board_pb.BoardListWatchRequest, options?: Partial<grpc.CallOptions>): grpc.ClientReadableStream<cc_arduino_cli_commands_v1_board_pb.BoardListWatchResponse>;
boardListWatch(request: cc_arduino_cli_commands_v1_board_pb.BoardListWatchRequest, metadata?: grpc.Metadata, options?: Partial<grpc.CallOptions>): grpc.ClientReadableStream<cc_arduino_cli_commands_v1_board_pb.BoardListWatchResponse>;
compile(request: cc_arduino_cli_commands_v1_compile_pb.CompileRequest, options?: Partial<grpc.CallOptions>): grpc.ClientReadableStream<cc_arduino_cli_commands_v1_compile_pb.CompileResponse>;
@@ -737,6 +751,9 @@ export class ArduinoCoreServiceClient extends grpc.Client implements IArduinoCor
public boardSearch(request: cc_arduino_cli_commands_v1_board_pb.BoardSearchRequest, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_board_pb.BoardSearchResponse) => void): grpc.ClientUnaryCall;
public boardSearch(request: cc_arduino_cli_commands_v1_board_pb.BoardSearchRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_board_pb.BoardSearchResponse) => void): grpc.ClientUnaryCall;
public boardSearch(request: cc_arduino_cli_commands_v1_board_pb.BoardSearchRequest, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_board_pb.BoardSearchResponse) => void): grpc.ClientUnaryCall;
public boardIdentify(request: cc_arduino_cli_commands_v1_board_pb.BoardIdentifyRequest, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_board_pb.BoardIdentifyResponse) => void): grpc.ClientUnaryCall;
public boardIdentify(request: cc_arduino_cli_commands_v1_board_pb.BoardIdentifyRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_board_pb.BoardIdentifyResponse) => void): grpc.ClientUnaryCall;
public boardIdentify(request: cc_arduino_cli_commands_v1_board_pb.BoardIdentifyRequest, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_board_pb.BoardIdentifyResponse) => void): grpc.ClientUnaryCall;
public boardListWatch(request: cc_arduino_cli_commands_v1_board_pb.BoardListWatchRequest, options?: Partial<grpc.CallOptions>): grpc.ClientReadableStream<cc_arduino_cli_commands_v1_board_pb.BoardListWatchResponse>;
public boardListWatch(request: cc_arduino_cli_commands_v1_board_pb.BoardListWatchRequest, metadata?: grpc.Metadata, options?: Partial<grpc.CallOptions>): grpc.ClientReadableStream<cc_arduino_cli_commands_v1_board_pb.BoardListWatchResponse>;
public compile(request: cc_arduino_cli_commands_v1_compile_pb.CompileRequest, options?: Partial<grpc.CallOptions>): grpc.ClientReadableStream<cc_arduino_cli_commands_v1_compile_pb.CompileResponse>;

View File

@@ -74,6 +74,28 @@ function deserialize_cc_arduino_cli_commands_v1_BoardDetailsResponse(buffer_arg)
return cc_arduino_cli_commands_v1_board_pb.BoardDetailsResponse.deserializeBinary(new Uint8Array(buffer_arg));
}
function serialize_cc_arduino_cli_commands_v1_BoardIdentifyRequest(arg) {
if (!(arg instanceof cc_arduino_cli_commands_v1_board_pb.BoardIdentifyRequest)) {
throw new Error('Expected argument of type cc.arduino.cli.commands.v1.BoardIdentifyRequest');
}
return Buffer.from(arg.serializeBinary());
}
function deserialize_cc_arduino_cli_commands_v1_BoardIdentifyRequest(buffer_arg) {
return cc_arduino_cli_commands_v1_board_pb.BoardIdentifyRequest.deserializeBinary(new Uint8Array(buffer_arg));
}
function serialize_cc_arduino_cli_commands_v1_BoardIdentifyResponse(arg) {
if (!(arg instanceof cc_arduino_cli_commands_v1_board_pb.BoardIdentifyResponse)) {
throw new Error('Expected argument of type cc.arduino.cli.commands.v1.BoardIdentifyResponse');
}
return Buffer.from(arg.serializeBinary());
}
function deserialize_cc_arduino_cli_commands_v1_BoardIdentifyResponse(buffer_arg) {
return cc_arduino_cli_commands_v1_board_pb.BoardIdentifyResponse.deserializeBinary(new Uint8Array(buffer_arg));
}
function serialize_cc_arduino_cli_commands_v1_BoardListAllRequest(arg) {
if (!(arg instanceof cc_arduino_cli_commands_v1_board_pb.BoardListAllRequest)) {
throw new Error('Expected argument of type cc.arduino.cli.commands.v1.BoardListAllRequest');
@@ -1282,6 +1304,18 @@ boardSearch: {
responseSerialize: serialize_cc_arduino_cli_commands_v1_BoardSearchResponse,
responseDeserialize: deserialize_cc_arduino_cli_commands_v1_BoardSearchResponse,
},
// Identify a board using the given properties.
boardIdentify: {
path: '/cc.arduino.cli.commands.v1.ArduinoCoreService/BoardIdentify',
requestStream: false,
responseStream: false,
requestType: cc_arduino_cli_commands_v1_board_pb.BoardIdentifyRequest,
responseType: cc_arduino_cli_commands_v1_board_pb.BoardIdentifyResponse,
requestSerialize: serialize_cc_arduino_cli_commands_v1_BoardIdentifyRequest,
requestDeserialize: deserialize_cc_arduino_cli_commands_v1_BoardIdentifyRequest,
responseSerialize: serialize_cc_arduino_cli_commands_v1_BoardIdentifyResponse,
responseDeserialize: deserialize_cc_arduino_cli_commands_v1_BoardIdentifyResponse,
},
// List boards connection and disconnected events.
boardListWatch: {
path: '/cc.arduino.cli.commands.v1.ArduinoCoreService/BoardListWatch',

View File

@@ -4,7 +4,7 @@ import yaml from 'js-yaml';
import { injectable, inject, named } from '@theia/core/shared/inversify';
import URI from '@theia/core/lib/common/uri';
import { ILogger } from '@theia/core/lib/common/logger';
import { FileUri } from '@theia/core/lib/node/file-uri';
import { FileUri } from '@theia/core/lib/common/file-uri';
import { Event, Emitter } from '@theia/core/lib/common/event';
import { BackendApplicationContribution } from '@theia/core/lib/node/backend-application';
import {

View File

@@ -1,17 +1,17 @@
import { type ClientReadableStream } from '@grpc/grpc-js';
import { ApplicationError } from '@theia/core/lib/common/application-error';
import type { CancellationToken } from '@theia/core/lib/common/cancellation';
import { CommandService } from '@theia/core/lib/common/command';
import {
Disposable,
DisposableCollection,
} from '@theia/core/lib/common/disposable';
import { FileUri } from '@theia/core/lib/common/file-uri';
import { nls } from '@theia/core/lib/common/nls';
import type { Mutable } from '@theia/core/lib/common/types';
import { FileUri } from '@theia/core/lib/node/file-uri';
import { inject, injectable } from '@theia/core/shared/inversify';
import * as jspb from 'google-protobuf';
import path from 'node:path';
import { userAbort } from '../common/nls';
import {
UploadResponse as ApiUploadResponse,
OutputMessage,
@@ -27,6 +27,7 @@ import {
isCompileSummary,
isUploadResponse,
} from '../common/protocol/core-service';
import { UserAbortApplicationError } from '../common/protocol/progressible';
import { ResponseService } from '../common/protocol/response-service';
import { firstToUpperCase, notEmpty } from '../common/utils';
import { BoardDiscovery, createApiPort } from './board-discovery';
@@ -53,8 +54,6 @@ import { ExecuteWithProgress, ProgressResponse } from './grpc-progressible';
import { MonitorManager } from './monitor-manager';
import { ServiceError } from './service-error';
import { AutoFlushingBuffer } from './utils/buffers';
import { userAbort } from '../common/nls';
import { UserAbortApplicationError } from '../common/protocol/progressible';
namespace Uploadable {
export type Request = UploadRequest | UploadUsingProgrammerRequest;
@@ -69,15 +68,13 @@ export class CoreServiceImpl extends CoreClientAware implements CoreService {
private readonly responseService: ResponseService;
@inject(MonitorManager)
private readonly monitorManager: MonitorManager;
@inject(CommandService)
private readonly commandService: CommandService;
@inject(BoardDiscovery)
private readonly boardDiscovery: BoardDiscovery;
async compile(
options: CoreService.Options.Compile,
cancellationToken?: CancellationToken
): Promise<void> {
): Promise<CompileSummary | undefined> {
const coreClient = await this.coreClient;
const { client, instance } = coreClient;
const request = this.compileRequest(options, instance);
@@ -91,7 +88,7 @@ export class CoreServiceImpl extends CoreClientAware implements CoreService {
);
const toDisposeOnFinally = new DisposableCollection(handler);
return new Promise<void>((resolve, reject) => {
return new Promise<CompileSummary | undefined>((resolve, reject) => {
let hasRetried = false;
const handleUnexpectedError = (error: Error) => {
@@ -164,50 +161,26 @@ export class CoreServiceImpl extends CoreClientAware implements CoreService {
call
.on('data', handler.onData)
.on('error', handleError)
.on('end', resolve);
.on('end', () => {
if (isCompileSummary(compileSummary)) {
resolve(compileSummary);
} else {
console.error(
`Have not received the full compile summary from the CLI while running the compilation. ${JSON.stringify(
compileSummary
)}`
);
resolve(undefined);
}
});
};
startCompileStream();
}).finally(() => {
toDisposeOnFinally.dispose();
if (!isCompileSummary(compileSummary)) {
if (cancellationToken && cancellationToken.isCancellationRequested) {
// NOOP
return;
}
console.error(
`Have not received the full compile summary from the CLI while running the compilation. ${JSON.stringify(
compileSummary
)}`
);
} else {
this.fireBuildDidComplete(compileSummary);
}
});
}
// This executes on the frontend, the VS Code extension receives it, and sends an `ino/buildDidComplete` notification to the language server.
private fireBuildDidComplete(compileSummary: CompileSummary): void {
const params = {
...compileSummary,
};
console.info(
`Executing 'arduino.languageserver.notifyBuildDidComplete' with ${JSON.stringify(
params.buildOutputUri
)}`
);
this.commandService
.executeCommand('arduino.languageserver.notifyBuildDidComplete', params)
.catch((err) =>
console.error(
`Unexpected error when firing event on build did complete. ${JSON.stringify(
params.buildOutputUri
)}`,
err
)
);
}
private compileRequest(
options: CoreService.Options.Compile & {
exportBinaries?: boolean;

View File

@@ -5,7 +5,7 @@ import {
} from '@theia/core/shared/inversify';
import { join } from 'node:path';
import fs from 'node:fs';
import { FileUri } from '@theia/core/lib/node/file-uri';
import { FileUri } from '@theia/core/lib/common/file-uri';
import {
SketchRef,
SketchContainer,

View File

@@ -1,4 +1,4 @@
import { FileUri } from '@theia/core/lib/node/file-uri';
import { FileUri } from '@theia/core/lib/common/file-uri';
import { injectable } from '@theia/core/shared/inversify';
import { ExecutableService } from '../common/protocol/executable-service';
import {

View File

@@ -6,7 +6,7 @@ import {
postConstruct,
} from '@theia/core/shared/inversify';
import { EnvVariablesServer } from '@theia/core/lib/common/env-variables';
import { FileUri } from '@theia/core/lib/node/file-uri';
import { FileUri } from '@theia/core/lib/common/file-uri';
import { promisify } from 'util';
import { MonitorSettingsProvider } from './monitor-settings-provider';
import { Deferred } from '@theia/core/lib/common/promise-util';

View File

@@ -1,5 +1,5 @@
import { injectable } from '@theia/core/shared/inversify';
import { FileUri } from '@theia/core/lib/node/file-uri';
import { FileUri } from '@theia/core/lib/common/file-uri';
import { FileSystemExt } from '../common/protocol/filesystem-ext';
@injectable()

View File

@@ -1,5 +1,5 @@
import { EnvVariablesServer } from '@theia/core/lib/common/env-variables';
import { FileUri } from '@theia/core/lib/node/file-uri';
import { FileUri } from '@theia/core/lib/common/file-uri';
import { inject, injectable } from '@theia/core/shared/inversify';
import { promises as fs } from 'node:fs';
import {

View File

@@ -6,7 +6,7 @@ import { Deferred } from '@theia/core/lib/common/promise-util';
import { escapeRegExpCharacters } from '@theia/core/lib/common/strings';
import type { Mutable } from '@theia/core/lib/common/types';
import URI from '@theia/core/lib/common/uri';
import { FileUri } from '@theia/core/lib/node/file-uri';
import { FileUri } from '@theia/core/lib/common/file-uri';
import { inject, injectable, named } from '@theia/core/shared/inversify';
import { glob } from 'glob';
import crypto from 'node:crypto';

View File

@@ -1,20 +0,0 @@
import { injectable } from '@theia/core/shared/inversify';
import { SurveyNotificationService } from '../common/protocol/survey-service';
/**
* Service for checking if it is the first instance of the IDE, in this case it sets a flag to true.
* This flag is used to prevent the survey notification from being visible in every open window. It must only be shown on one window.
*/
@injectable()
export class SurveyNotificationServiceImpl
implements SurveyNotificationService
{
private surveyDidShow = false;
async isFirstInstance(): Promise<boolean> {
if (this.surveyDidShow) {
return false;
}
this.surveyDidShow = true;
return this.surveyDidShow;
}
}

View File

@@ -1,7 +1,8 @@
import { MessagingContribution as TheiaMessagingContribution } from '@theia/core/lib/node/messaging/messaging-contribution';
import { WebsocketEndpoint as TheiaWebsocketEndpoint } from '@theia/core/lib/node/messaging/websocket-endpoint';
import { injectable } from '@theia/core/shared/inversify';
@injectable()
export class MessagingContribution extends TheiaMessagingContribution {
export class WebsocketEndpoint extends TheiaWebsocketEndpoint {
// https://github.com/eclipse-theia/theia/discussions/11543
protected override checkAliveTimeout = process.argv.includes(
'--no-ping-timeout'

View File

@@ -5,7 +5,7 @@ import {
import { isWindows } from '@theia/core/lib/common/os';
import URI from '@theia/core/lib/common/uri';
import { BackendApplicationConfigProvider } from '@theia/core/lib/node/backend-application-config-provider';
import { FileUri } from '@theia/core/lib/node/file-uri';
import { FileUri } from '@theia/core/lib/common/file-uri';
import {
inject,
injectable,

View File

@@ -1,42 +0,0 @@
import { join } from 'node:path';
import { interfaces } from '@theia/core/shared/inversify';
import {
NsfwFileSystemWatcherServiceProcessOptions,
NSFW_SINGLE_THREADED,
spawnNsfwFileSystemWatcherServiceProcess,
} from '@theia/filesystem/lib/node/filesystem-backend-module';
import { FileSystemWatcherService } from '@theia/filesystem/lib/common/filesystem-watcher-protocol';
import { NsfwFileSystemWatcherServerOptions } from '@theia/filesystem/lib/node/nsfw-watcher/nsfw-filesystem-service';
import { FileSystemWatcherServiceDispatcher } from '@theia/filesystem/lib/node/filesystem-watcher-dispatcher';
import { NoDelayDisposalTimeoutNsfwFileSystemWatcherService } from './nsfw-watcher/nsfw-filesystem-service';
export function rebindNsfwFileSystemWatcher(rebind: interfaces.Rebind): void {
rebind<NsfwFileSystemWatcherServiceProcessOptions>(
NsfwFileSystemWatcherServiceProcessOptions
).toConstantValue({
entryPoint: join(__dirname, 'nsfw-watcher'),
});
rebind<FileSystemWatcherService>(FileSystemWatcherService)
.toDynamicValue((context) =>
NSFW_SINGLE_THREADED
? createNsfwFileSystemWatcherService(context)
: spawnNsfwFileSystemWatcherServiceProcess(context)
)
.inSingletonScope();
}
function createNsfwFileSystemWatcherService({
container,
}: interfaces.Context): FileSystemWatcherService {
const options = container.get<NsfwFileSystemWatcherServerOptions>(
NsfwFileSystemWatcherServerOptions
);
const dispatcher = container.get<FileSystemWatcherServiceDispatcher>(
FileSystemWatcherServiceDispatcher
);
const server = new NoDelayDisposalTimeoutNsfwFileSystemWatcherService(
options
);
server.setClient(dispatcher);
return server;
}

View File

@@ -0,0 +1,42 @@
import { join } from 'node:path';
import { interfaces } from '@theia/core/shared/inversify';
import {
FileSystemWatcherServiceProcessOptions,
WATCHER_SINGLE_THREADED,
spawnParcelFileSystemWatcherServiceProcess,
} from '@theia/filesystem/lib/node/filesystem-backend-module';
import { FileSystemWatcherService } from '@theia/filesystem/lib/common/filesystem-watcher-protocol';
import { ParcelFileSystemWatcherServerOptions } from '@theia/filesystem/lib/node/parcel-watcher/parcel-filesystem-service';
import { FileSystemWatcherServiceDispatcher } from '@theia/filesystem/lib/node/filesystem-watcher-dispatcher';
import { NoDelayDisposalTimeoutParcelFileSystemWatcherService } from './parcel-watcher/parcel-filesystem-service';
export function rebindParcelFileSystemWatcher(rebind: interfaces.Rebind): void {
rebind<FileSystemWatcherServiceProcessOptions>(
FileSystemWatcherServiceProcessOptions
).toConstantValue({
entryPoint: join(__dirname, 'parcel-watcher'),
});
rebind<FileSystemWatcherService>(FileSystemWatcherService)
.toDynamicValue((context) =>
WATCHER_SINGLE_THREADED
? createParcelFileSystemWatcherService(context)
: spawnParcelFileSystemWatcherServiceProcess(context)
)
.inSingletonScope();
}
function createParcelFileSystemWatcherService({
container,
}: interfaces.Context): FileSystemWatcherService {
const options = container.get<ParcelFileSystemWatcherServerOptions>(
ParcelFileSystemWatcherServerOptions
);
const dispatcher = container.get<FileSystemWatcherServiceDispatcher>(
FileSystemWatcherServiceDispatcher
);
const server = new NoDelayDisposalTimeoutParcelFileSystemWatcherService(
options
);
server.setClient(dispatcher);
return server;
}

View File

@@ -1,6 +1,6 @@
import * as yargs from '@theia/core/shared/yargs';
import { JsonRpcProxyFactory } from '@theia/core/lib/common/messaging/proxy-factory';
import { NoDelayDisposalTimeoutNsfwFileSystemWatcherService } from './nsfw-filesystem-service';
import { NoDelayDisposalTimeoutParcelFileSystemWatcherService } from './parcel-filesystem-service';
import type { IPCEntryPoint } from '@theia/core/lib/node/messaging/ipc-protocol';
import type { FileSystemWatcherServiceClient } from '@theia/filesystem/lib/common/filesystem-watcher-protocol';
@@ -20,7 +20,7 @@ const options: {
}).argv as any;
export default <IPCEntryPoint>((connection) => {
const server = new NoDelayDisposalTimeoutNsfwFileSystemWatcherService(
const server = new NoDelayDisposalTimeoutParcelFileSystemWatcherService(
options
);
const factory = new JsonRpcProxyFactory<FileSystemWatcherServiceClient>(

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