mirror of
https://github.com/arduino/arduino-ide.git
synced 2025-10-06 01:58:32 +00:00
Compare commits
122 Commits
2.0.0-rc2
...
pluggable-
Author | SHA1 | Date | |
---|---|---|---|
![]() |
9a16cf9e02 | ||
![]() |
a4ff05a82b | ||
![]() |
0427759fdb | ||
![]() |
80ade4c37e | ||
![]() |
355dec8aaa | ||
![]() |
7bf4ea0637 | ||
![]() |
1982609c87 | ||
![]() |
62eaeb1c74 | ||
![]() |
9b58c9d0c8 | ||
![]() |
eff960bb7f | ||
![]() |
fbe8fb421a | ||
![]() |
a8d803e7c3 | ||
![]() |
397ca5665f | ||
![]() |
f9da9fc24b | ||
![]() |
b97af32bb8 | ||
![]() |
ce2f1c227a | ||
![]() |
7889f40834 | ||
![]() |
6b7b33356d | ||
![]() |
ad781f0bfc | ||
![]() |
6cf61c498a | ||
![]() |
50239c5756 | ||
![]() |
cbd5b4de1b | ||
![]() |
bf958fd8cf | ||
![]() |
ee265aec90 | ||
![]() |
9058abb015 | ||
![]() |
31b704cdb9 | ||
![]() |
61b8bdeec9 | ||
![]() |
c5695d3a76 | ||
![]() |
480492a7c8 | ||
![]() |
2c95e7f033 | ||
![]() |
116b3d5984 | ||
![]() |
750796d3a0 | ||
![]() |
3133b01c4a | ||
![]() |
ebab0b226f | ||
![]() |
2b2ea72643 | ||
![]() |
289f9d7946 | ||
![]() |
905b78008d | ||
![]() |
11961bb7c7 | ||
![]() |
2be1fac585 | ||
![]() |
b35340caa9 | ||
![]() |
e6b3e2ec23 | ||
![]() |
c07232698c | ||
![]() |
58e992af13 | ||
![]() |
a44b84ffd0 | ||
![]() |
a3640cf812 | ||
![]() |
03a75273e3 | ||
![]() |
6176e50acf | ||
![]() |
46a3466bc5 | ||
![]() |
aba9db6a6b | ||
![]() |
e5b34624ac | ||
![]() |
c430cf0d88 | ||
![]() |
1969e292f0 | ||
![]() |
0db119d7ba | ||
![]() |
c9b498fb08 | ||
![]() |
78004fa4ca | ||
![]() |
4de7737d14 | ||
![]() |
f36df02f5d | ||
![]() |
753872ea2a | ||
![]() |
ca1c24050d | ||
![]() |
61c2b1a007 | ||
![]() |
8cac0872a4 | ||
![]() |
70f1c5f8ec | ||
![]() |
b416e5f9e8 | ||
![]() |
bfe6835cab | ||
![]() |
9e89964df2 | ||
![]() |
04c3d0c1d3 | ||
![]() |
c9996df11c | ||
![]() |
49971ada07 | ||
![]() |
e6b9d4e2aa | ||
![]() |
93a374d0c6 | ||
![]() |
0fc7c78e11 | ||
![]() |
96b5edf427 | ||
![]() |
a5a6a0b611 | ||
![]() |
2a27a14a68 | ||
![]() |
f2d492b5dc | ||
![]() |
5979e5aad2 | ||
![]() |
baa9b5f7ab | ||
![]() |
481497e384 | ||
![]() |
0207778373 | ||
![]() |
d79f32efd7 | ||
![]() |
3ab03dd62f | ||
![]() |
bc3cb0c230 | ||
![]() |
473cb11053 | ||
![]() |
0a87fd00f3 | ||
![]() |
9b1f15def8 | ||
![]() |
77b430675d | ||
![]() |
f660058c75 | ||
![]() |
9ecff86bbe | ||
![]() |
5ab3a747a6 | ||
![]() |
877c1a1559 | ||
![]() |
2f9bf86d75 | ||
![]() |
112153fb96 | ||
![]() |
69ac1f4779 | ||
![]() |
a20899ff43 | ||
![]() |
ef2be1c086 | ||
![]() |
af33dce0f6 | ||
![]() |
b3b22795f8 | ||
![]() |
8a0454db51 | ||
![]() |
f1a5d87ab2 | ||
![]() |
cf0a2161af | ||
![]() |
dcebd863cc | ||
![]() |
e8477b14f3 | ||
![]() |
0230071b5f | ||
![]() |
1d88263c85 | ||
![]() |
a71ac4c44d | ||
![]() |
66fc27e58c | ||
![]() |
bc365f4a8d | ||
![]() |
a5891f9884 | ||
![]() |
fcdf16a937 | ||
![]() |
e0b6dbbf2a | ||
![]() |
9529e78647 | ||
![]() |
51da3c0668 | ||
![]() |
c00d3d33dd | ||
![]() |
cfa9b8aea6 | ||
![]() |
6106e9ff1a | ||
![]() |
b1d9f65a0d | ||
![]() |
f4008100e1 | ||
![]() |
11a6959a24 | ||
![]() |
3c6e11832b | ||
![]() |
c064673ce1 | ||
![]() |
cc5764e536 | ||
![]() |
9131f2d09e |
74
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
Normal file
74
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
name: Bug report
|
||||
description: Report a problem with the code or documentation in this repository.
|
||||
labels:
|
||||
- "type: imperfection"
|
||||
body:
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: Describe the problem
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: reproduce
|
||||
attributes:
|
||||
label: To reproduce
|
||||
description: Provide the specific set of steps we can follow to reproduce the problem.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: expected
|
||||
attributes:
|
||||
label: Expected behavior
|
||||
description: What would you expect to happen after following those instructions?
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: project-version
|
||||
attributes:
|
||||
label: Arduino IDE version
|
||||
description: |
|
||||
Which version of the Arduino IDE are you using?
|
||||
See **Help > About Arduino IDE** in the Arduino IDE menus (**Arduino IDE > About Arduino IDE** on macOS).
|
||||
This should be the latest [nightly build](https://github.com/arduino/arduino-ide#nightly-builds).
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: os
|
||||
attributes:
|
||||
label: Operating system
|
||||
description: Which operating system(s) are you using on your computer?
|
||||
multiple: true
|
||||
options:
|
||||
- Windows
|
||||
- Linux
|
||||
- macOS
|
||||
- N/A
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: os-version
|
||||
attributes:
|
||||
label: Operating system version
|
||||
description: Which version of the operating system are you using on your computer?
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: additional
|
||||
attributes:
|
||||
label: Additional context
|
||||
description: Add any additional information here.
|
||||
validations:
|
||||
required: false
|
||||
- type: checkboxes
|
||||
id: checklist
|
||||
attributes:
|
||||
label: Issue checklist
|
||||
description: Please double-check that you have done each of the following things before submitting the issue.
|
||||
options:
|
||||
- label: I searched for previous reports in [the issue tracker](https://github.com/arduino/arduino-ide/issues?q=)
|
||||
required: true
|
||||
- label: I verified the problem still occurs when using the latest [nightly build](https://github.com/arduino/arduino-ide#nightly-builds)
|
||||
required: true
|
||||
- label: My report contains all necessary details
|
||||
required: true
|
32
.github/ISSUE_TEMPLATE/bug_report.md
vendored
32
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -1,32 +0,0 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: 'type: imperfection'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Desktop (please complete the following information):**
|
||||
- OS: [e.g. Windows]
|
||||
- Version: [e.g. 2.0.0]
|
||||
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
13
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
13
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
# Source:
|
||||
# https://github.com/arduino/tooling-project-assets/blob/main/issue-templates/template-choosers/general/config.yml
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Learn about using this project
|
||||
url: https://github.com/arduino/arduino-ide#readme
|
||||
about: Detailed usage documentation is available here.
|
||||
- name: Support request
|
||||
url: https://forum.arduino.cc/
|
||||
about: We can help you out on the Arduino Forum!
|
||||
- name: Discuss development work on the project
|
||||
url: https://groups.google.com/a/arduino.cc/g/developers
|
||||
about: Arduino Developers Mailing List
|
69
.github/ISSUE_TEMPLATE/feature-request.yml
vendored
Normal file
69
.github/ISSUE_TEMPLATE/feature-request.yml
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
name: Feature request
|
||||
description: Suggest an enhancement to this project.
|
||||
labels:
|
||||
- "type: enhancement"
|
||||
body:
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: Describe the request
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: current
|
||||
attributes:
|
||||
label: Describe the current behavior
|
||||
description: |
|
||||
What is the current behavior of the Arduino IDE in relation to your request?
|
||||
How can we reproduce that behavior?
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: project-version
|
||||
attributes:
|
||||
label: Arduino IDE version
|
||||
description: |
|
||||
Which version of the Arduino IDE are you using?
|
||||
See **Help > About Arduino IDE** in the Arduino IDE menus (**Arduino IDE > About Arduino IDE** on macOS).
|
||||
This should be the latest [nightly build](https://github.com/arduino/arduino-ide#nightly-builds).
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: os
|
||||
attributes:
|
||||
label: Operating system
|
||||
description: Which operating system(s) are you using on your computer?
|
||||
multiple: true
|
||||
options:
|
||||
- Windows
|
||||
- Linux
|
||||
- macOS
|
||||
- N/A
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: os-version
|
||||
attributes:
|
||||
label: Operating system version
|
||||
description: Which version of the operating system are you using on your computer?
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: additional
|
||||
attributes:
|
||||
label: Additional context
|
||||
description: Add any additional information here.
|
||||
validations:
|
||||
required: false
|
||||
- type: checkboxes
|
||||
id: checklist
|
||||
attributes:
|
||||
label: Issue checklist
|
||||
description: Please double-check that you have done each of the following things before submitting the issue.
|
||||
options:
|
||||
- label: I searched for previous requests in [the issue tracker](https://github.com/arduino/arduino-ide/issues?q=)
|
||||
required: true
|
||||
- label: I verified the feature was still missing when using the latest [nightly build](https://github.com/arduino/arduino-ide#nightly-builds)
|
||||
required: true
|
||||
- label: My request contains all necessary details
|
||||
required: true
|
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -1,20 +0,0 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: 'type: enhancement'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
15
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
15
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
### Motivation
|
||||
<!-- Why this pull request? -->
|
||||
|
||||
### Change description
|
||||
<!-- What does your code do? -->
|
||||
|
||||
### Other information
|
||||
<!-- Any additional information that could help the review process -->
|
||||
|
||||
### Reviewer checklist
|
||||
|
||||
* [ ] PR addresses a single concern.
|
||||
* [ ] The PR has no duplicates (please search among the [Pull Requests](https://github.com/arduino/arduino-ide/pulls) before creating one)
|
||||
* [ ] PR title and description are properly filled.
|
||||
* [ ] Docs have been added / updated (for bug fixes / features)
|
115
.github/workflows/build.yml
vendored
115
.github/workflows/build.yml
vendored
@@ -15,15 +15,15 @@ on:
|
||||
|
||||
env:
|
||||
JOB_TRANSFER_ARTIFACT: build-artifacts
|
||||
CHANGELOG_ARTIFACTS: changelog
|
||||
|
||||
jobs:
|
||||
|
||||
build:
|
||||
if: github.repository == 'arduino/arduino-ide'
|
||||
strategy:
|
||||
matrix:
|
||||
config:
|
||||
- os: windows-latest
|
||||
- os: windows-2019
|
||||
- os: ubuntu-18.04 # https://github.com/arduino/arduino-ide/issues/259
|
||||
- os: macos-latest
|
||||
runs-on: ${{ matrix.config.os }}
|
||||
@@ -33,16 +33,16 @@ jobs:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Install Node.js 12.x
|
||||
- name: Install Node.js 14.x
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: '12.14.1'
|
||||
node-version: '14.x'
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
|
||||
- name: Install Python 2.7
|
||||
- name: Install Python 3.x
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '2.7'
|
||||
python-version: '3.x'
|
||||
|
||||
- name: Package
|
||||
shell: bash
|
||||
@@ -50,35 +50,36 @@ jobs:
|
||||
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: ${{ github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && github.ref == 'refs/heads/main') }}
|
||||
IS_RELEASE: ${{ startsWith(github.ref, 'refs/tags/') }}
|
||||
IS_FORK: ${{ github.event.pull_request.head.repo.fork == true }}
|
||||
run: |
|
||||
# See: https://www.electron.build/code-signing
|
||||
if [ $IS_FORK = true ]; then
|
||||
echo "Skipping the app signing: building from a fork."
|
||||
else
|
||||
if [ "${{ runner.OS }}" = "macOS" ]; then
|
||||
export CSC_LINK="${{ runner.temp }}/signing_certificate.p12"
|
||||
# APPLE_SIGNING_CERTIFICATE_P12 secret was produced by following the procedure from:
|
||||
# https://www.kencochrane.com/2020/08/01/build-and-sign-golang-binaries-for-macos-with-github-actions/#exporting-the-developer-certificate
|
||||
echo "${{ secrets.APPLE_SIGNING_CERTIFICATE_P12 }}" | base64 --decode > "$CSC_LINK"
|
||||
# See: https://www.electron.build/code-signing
|
||||
if [ $IS_FORK = true ]; then
|
||||
echo "Skipping the app signing: building from a fork."
|
||||
else
|
||||
if [ "${{ runner.OS }}" = "macOS" ]; then
|
||||
export CSC_LINK="${{ runner.temp }}/signing_certificate.p12"
|
||||
# APPLE_SIGNING_CERTIFICATE_P12 secret was produced by following the procedure from:
|
||||
# https://www.kencochrane.com/2020/08/01/build-and-sign-golang-binaries-for-macos-with-github-actions/#exporting-the-developer-certificate
|
||||
echo "${{ secrets.APPLE_SIGNING_CERTIFICATE_P12 }}" | base64 --decode > "$CSC_LINK"
|
||||
|
||||
export CSC_KEY_PASSWORD="${{ secrets.KEYCHAIN_PASSWORD }}"
|
||||
export CSC_KEY_PASSWORD="${{ secrets.KEYCHAIN_PASSWORD }}"
|
||||
|
||||
elif [ "${{ runner.OS }}" = "Windows" ]; then
|
||||
export CSC_LINK="${{ runner.temp }}/signing_certificate.pfx"
|
||||
npm config set msvs_version 2017 --global
|
||||
echo "${{ secrets.WINDOWS_SIGNING_CERTIFICATE_PFX }}" | base64 --decode > "$CSC_LINK"
|
||||
elif [ "${{ runner.OS }}" = "Windows" ]; then
|
||||
export CSC_LINK="${{ runner.temp }}/signing_certificate.pfx"
|
||||
npm config set msvs_version 2017 --global
|
||||
echo "${{ secrets.WINDOWS_SIGNING_CERTIFICATE_PFX }}" | base64 --decode > "$CSC_LINK"
|
||||
|
||||
export CSC_KEY_PASSWORD="${{ secrets.WINDOWS_SIGNING_CERTIFICATE_PASSWORD }}"
|
||||
fi
|
||||
export CSC_KEY_PASSWORD="${{ secrets.WINDOWS_SIGNING_CERTIFICATE_PASSWORD }}"
|
||||
fi
|
||||
fi
|
||||
|
||||
yarn --cwd ./electron/packager/
|
||||
yarn --cwd ./electron/packager/ package
|
||||
yarn --cwd ./electron/packager/
|
||||
yarn --cwd ./electron/packager/ package
|
||||
|
||||
- name: Upload [GitHub Actions]
|
||||
uses: actions/upload-artifact@v2
|
||||
@@ -95,15 +96,19 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
artifact:
|
||||
- path: "*Linux_64bit.zip"
|
||||
name: Linux_X86-64
|
||||
- path: "*macOS_64bit.dmg"
|
||||
name: macOS
|
||||
- path: "*Windows_64bit.exe"
|
||||
- path: '*Linux_64bit.zip'
|
||||
name: Linux_X86-64_zip
|
||||
- path: '*Linux_64bit.AppImage'
|
||||
name: Linux_X86-64_app_image
|
||||
- path: '*macOS_64bit.dmg'
|
||||
name: macOS_dmg
|
||||
- path: '*macOS_64bit.zip'
|
||||
name: macOS_zip
|
||||
- path: '*Windows_64bit.exe'
|
||||
name: Windows_X86-64_interactive_installer
|
||||
- path: "*Windows_64bit.msi"
|
||||
- path: '*Windows_64bit.msi'
|
||||
name: Windows_X86-64_MSI
|
||||
- path: "*Windows_64bit.zip"
|
||||
- path: '*Windows_64bit.zip'
|
||||
name: Windows_X86-64_zip
|
||||
|
||||
steps:
|
||||
@@ -112,7 +117,7 @@ jobs:
|
||||
with:
|
||||
name: ${{ env.JOB_TRANSFER_ARTIFACT }}
|
||||
path: ${{ env.JOB_TRANSFER_ARTIFACT }}
|
||||
|
||||
|
||||
- name: Upload tester build artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
@@ -135,24 +140,24 @@ jobs:
|
||||
env:
|
||||
IS_RELEASE: ${{ startsWith(github.ref, 'refs/tags/') }}
|
||||
run: |
|
||||
export LATEST_TAG=$(git describe --abbrev=0)
|
||||
export GIT_LOG=$(git log --pretty=" - %s [%h]" $LATEST_TAG..HEAD | sed 's/ *$//g')
|
||||
if [ "$IS_RELEASE" = true ]; then
|
||||
export BODY=$(echo -e "$GIT_LOG")
|
||||
else
|
||||
export LATEST_TAG_WITH_LINK=$(echo "[$LATEST_TAG](https://github.com/arduino/arduino-ide/releases/tag/$LATEST_TAG)")
|
||||
if [ -z "$GIT_LOG" ]; then
|
||||
export BODY="There were no changes since version $LATEST_TAG_WITH_LINK."
|
||||
else
|
||||
export BODY=$(echo -e "Changes since version $LATEST_TAG_WITH_LINK:\n$GIT_LOG")
|
||||
fi
|
||||
export LATEST_TAG=$(git describe --abbrev=0)
|
||||
export GIT_LOG=$(git log --pretty=" - %s [%h]" $LATEST_TAG..HEAD | sed 's/ *$//g')
|
||||
if [ "$IS_RELEASE" = true ]; then
|
||||
export BODY=$(echo -e "$GIT_LOG")
|
||||
else
|
||||
export LATEST_TAG_WITH_LINK=$(echo "[$LATEST_TAG](https://github.com/arduino/arduino-ide/releases/tag/$LATEST_TAG)")
|
||||
if [ -z "$GIT_LOG" ]; then
|
||||
export BODY="There were no changes since version $LATEST_TAG_WITH_LINK."
|
||||
else
|
||||
export BODY=$(echo -e "Changes since version $LATEST_TAG_WITH_LINK:\n$GIT_LOG")
|
||||
fi
|
||||
echo -e "$BODY"
|
||||
OUTPUT_SAFE_BODY="${BODY//'%'/'%25'}"
|
||||
OUTPUT_SAFE_BODY="${OUTPUT_SAFE_BODY//$'\n'/'%0A'}"
|
||||
OUTPUT_SAFE_BODY="${OUTPUT_SAFE_BODY//$'\r'/'%0D'}"
|
||||
echo "::set-output name=BODY::$OUTPUT_SAFE_BODY"
|
||||
echo "$BODY" > CHANGELOG.txt
|
||||
fi
|
||||
echo -e "$BODY"
|
||||
OUTPUT_SAFE_BODY="${BODY//'%'/'%25'}"
|
||||
OUTPUT_SAFE_BODY="${OUTPUT_SAFE_BODY//$'\n'/'%0A'}"
|
||||
OUTPUT_SAFE_BODY="${OUTPUT_SAFE_BODY//$'\r'/'%0D'}"
|
||||
echo "::set-output name=BODY::$OUTPUT_SAFE_BODY"
|
||||
echo "$BODY" > CHANGELOG.txt
|
||||
|
||||
- name: Upload Changelog [GitHub Actions]
|
||||
if: github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && github.ref == 'refs/heads/main')
|
||||
@@ -175,9 +180,9 @@ jobs:
|
||||
- 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_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 }}
|
||||
@@ -211,9 +216,9 @@ jobs:
|
||||
- name: Publish Release [S3]
|
||||
uses: docker://plugins/s3
|
||||
env:
|
||||
PLUGIN_SOURCE: "${{ env.JOB_TRANSFER_ARTIFACT }}/*"
|
||||
PLUGIN_STRIP_PREFIX: "${{ env.JOB_TRANSFER_ARTIFACT }}/"
|
||||
PLUGIN_TARGET: "/arduino-ide"
|
||||
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 }}
|
||||
|
4
.github/workflows/check-i18n-task.yml
vendored
4
.github/workflows/check-i18n-task.yml
vendored
@@ -25,10 +25,10 @@ jobs:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Install Node.js 12.x
|
||||
- name: Install Node.js 14.x
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '12.14.1'
|
||||
node-version: '14.x'
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
|
||||
- name: Install dependencies
|
||||
|
55
.github/workflows/compose-full-changelog.yaml
vendored
Normal file
55
.github/workflows/compose-full-changelog.yaml
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
name: Compose full changelog
|
||||
|
||||
on:
|
||||
release:
|
||||
types:
|
||||
- edited
|
||||
|
||||
env:
|
||||
CHANGELOG_ARTIFACTS: changelog
|
||||
# See: https://github.com/actions/setup-node/#readme
|
||||
NODE_VERSION: 14.x
|
||||
|
||||
jobs:
|
||||
create-changelog:
|
||||
if: github.repository == 'arduino/arduino-ide'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
|
||||
- name: Get Tag
|
||||
id: tag_name
|
||||
run: |
|
||||
echo ::set-output name=TAG_NAME::${GITHUB_REF#refs/tags/}
|
||||
|
||||
- name: Create full changelog
|
||||
id: full-changelog
|
||||
run: |
|
||||
yarn add @octokit/rest --ignore-workspace-root-check
|
||||
mkdir "${{ github.workspace }}/${{ env.CHANGELOG_ARTIFACTS }}"
|
||||
|
||||
# Get the changelog file name to build
|
||||
CHANGELOG_FILE_NAME="${{ steps.tag_name.outputs.TAG_NAME }}-$(date +%s).md"
|
||||
|
||||
# Create manifest file pointing to latest changelog file name
|
||||
echo "$CHANGELOG_FILE_NAME" >> "${{ github.workspace }}/${{ env.CHANGELOG_ARTIFACTS }}/latest.txt"
|
||||
|
||||
# Compose changelog
|
||||
yarn run compose-changelog "${{ github.workspace }}/${{ env.CHANGELOG_ARTIFACTS }}/$CHANGELOG_FILE_NAME"
|
||||
|
||||
- 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 }}
|
4
.github/workflows/i18n-nightly-push.yml
vendored
4
.github/workflows/i18n-nightly-push.yml
vendored
@@ -12,10 +12,10 @@ jobs:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Install Node.js 12.x
|
||||
- name: Install Node.js 14.x
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '12.14.1'
|
||||
node-version: '14.x'
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
|
||||
- name: Install dependencies
|
||||
|
4
.github/workflows/i18n-weekly-pull.yml
vendored
4
.github/workflows/i18n-weekly-pull.yml
vendored
@@ -12,10 +12,10 @@ jobs:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Install Node.js 12.x
|
||||
- name: Install Node.js 14.x
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '12.14.1'
|
||||
node-version: '14.x'
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
|
||||
- name: Install dependencies
|
||||
|
4
.github/workflows/sync-labels.yml
vendored
4
.github/workflows/sync-labels.yml
vendored
@@ -31,7 +31,7 @@ jobs:
|
||||
|
||||
- name: Download JSON schema for labels configuration file
|
||||
id: download-schema
|
||||
uses: carlosperate/download-file-action@v1.0.3
|
||||
uses: carlosperate/download-file-action@v1
|
||||
with:
|
||||
file-url: https://raw.githubusercontent.com/arduino/tooling-project-assets/main/workflow-templates/assets/sync-labels/arduino-tooling-gh-label-configuration-schema.json
|
||||
location: ${{ runner.temp }}/label-configuration-schema
|
||||
@@ -66,7 +66,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Download
|
||||
uses: carlosperate/download-file-action@v1.0.3
|
||||
uses: carlosperate/download-file-action@v1
|
||||
with:
|
||||
file-url: https://raw.githubusercontent.com/arduino/tooling-project-assets/main/workflow-templates/assets/sync-labels/${{ matrix.filename }}
|
||||
|
||||
|
16
.vscode/launch.json
vendored
16
.vscode/launch.json
vendored
@@ -37,6 +37,13 @@
|
||||
"internalConsoleOptions": "openOnSessionStart",
|
||||
"outputCapture": "std"
|
||||
},
|
||||
{
|
||||
"type": "chrome",
|
||||
"request": "attach",
|
||||
"name": "Attach to Electron Frontend",
|
||||
"port": 9222,
|
||||
"webRoot": "${workspaceFolder}/electron-app"
|
||||
},
|
||||
{
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
@@ -104,5 +111,14 @@
|
||||
"program": "${workspaceRoot}/electron/packager/index.js",
|
||||
"cwd": "${workspaceFolder}/electron/packager"
|
||||
}
|
||||
],
|
||||
"compounds": [
|
||||
{
|
||||
"name": "Launch Electron Backend & Frontend",
|
||||
"configurations": [
|
||||
"App (Electron)",
|
||||
"Attach to Electron Frontend"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
45
BUILDING.md
45
BUILDING.md
@@ -40,22 +40,38 @@ The _frontend_ is running as an Electron renderer process and can invoke service
|
||||
## Build from source
|
||||
|
||||
If you’re familiar with TypeScript, the [Theia IDE](https://theia-ide.org/), and if you want to contribute to the
|
||||
project, you should be able to build the Arduino IDE locally. Please refer to the [Theia IDE prerequisites](https://github.com/theia-ide/theia/blob/master/doc/) documentation for the setup instructions.
|
||||
project, you should be able to build the Arduino IDE locally.
|
||||
Please refer to the [Theia IDE prerequisites](https://github.com/theia-ide/theia/blob/master/doc/) documentation for the setup instructions.
|
||||
> **Note**: Node.js 14 must be used instead of the version 12 recommended at the link above.
|
||||
|
||||
### Build
|
||||
```sh
|
||||
yarn
|
||||
```
|
||||
Once you have all the tools installed, you can build the editor following these steps
|
||||
|
||||
### Rebuild the native dependencies
|
||||
```sh
|
||||
yarn rebuild:electron
|
||||
```
|
||||
1. Install the dependencies and build
|
||||
```sh
|
||||
yarn
|
||||
```
|
||||
|
||||
### Start
|
||||
```sh
|
||||
yarn start
|
||||
```
|
||||
2. Rebuild the dependencies
|
||||
```sh
|
||||
yarn rebuild:browser
|
||||
```
|
||||
|
||||
3. Rebuild the electron dependencies
|
||||
```sh
|
||||
yarn rebuild:electron
|
||||
```
|
||||
|
||||
4. Start the application
|
||||
```sh
|
||||
yarn start
|
||||
```
|
||||
|
||||
### Notes for Windows contributors
|
||||
Windows requires the Microsoft Visual C++ (MSVC) compiler toolset to be installed on your development machine.
|
||||
|
||||
In case it's not already present, it can be downloaded from the "**Tools for Visual Studio 20XX**" section of the Visual Studio [downloads page](https://visualstudio.microsoft.com/downloads/#build-tools-for-visual-studio-2022) via the "**Build Tools for Visual Studio 20XX**" (e.g., "**Build Tools for Visual Studio 2022**") download link.
|
||||
|
||||
Select "**Desktop development with C++**" from the "**Workloads**" tab during the installation procedure.
|
||||
|
||||
### CI
|
||||
|
||||
@@ -73,6 +89,7 @@ This project is built on [GitHub Actions](https://github.com/arduino/arduino-ide
|
||||
git push origin 1.2.3
|
||||
```
|
||||
|
||||
|
||||
## Notes for macOS contributors
|
||||
Beginning in macOS 10.14.5, the software [must be notarized to run](https://developer.apple.com/documentation/xcode/notarizing_macos_software_before_distribution). The signing and notarization processes for the Arduino IDE are managed by our Continuous Integration (CI) workflows, implemented with GitHub Actions. On every push and pull request, the Arduino IDE is built and saved to a workflow artifact. These artifacts can be used by contributors and beta testers who don't want to set up a build system locally.
|
||||
For security reasons, signing and notarization are disabled for workflow runs for pull requests from forks of this repository. This means that macOS will block you from running those artifacts.
|
||||
@@ -117,7 +134,7 @@ git add . \
|
||||
git tag -a 0.2.0 -m "0.2.0" \
|
||||
&& git push origin 0.2.0
|
||||
```
|
||||
- The release build starts automatically and uploads the artifacts with the changelog to the [release page](https://github.com/arduino/arduino-ide/releases).
|
||||
- The release build starts automatically and uploads the artifacts with the changelog to the [release page](https://github.com/arduino/arduino-ide/releases).
|
||||
- If you do not want to release the `EXE` and `MSI` installers, wipe them manually.
|
||||
- If you do not like the generated changelog, modify it and update the GH release.
|
||||
|
||||
|
43
README.md
43
README.md
@@ -15,29 +15,31 @@ The Arduino IDE 2.x is a major rewrite, sharing no code with the IDE 1.x. It is
|
||||
## Download
|
||||
|
||||
You can download the latest version from the [software download page on the Arduino website](https://www.arduino.cc/en/software#experimental-software).
|
||||
|
||||
### Nightly builds
|
||||
|
||||
These builds are generated every day at 03:00 GMT from the `main` branch and
|
||||
should be considered unstable:
|
||||
|
||||
Platform | 32 bit | 64 bit |
|
||||
--------- | ------------------------ | ------------------------------------------------------------------------------------------------------ |
|
||||
Linux | | [Nightly Linux 64 bit] |
|
||||
Linux ARM | [🚧 Work in progress...] | [🚧 Work in progress...] |
|
||||
Windows | | [Nightly Windows 64 bit installer]<br />[Nightly Windows 64 bit MSI]<br />[Nightly Windows 64 bit ZIP] |
|
||||
macOS | | [Nightly macOS 64 bit] |
|
||||
| Platform | 32 bit | 64 bit |
|
||||
| --------- | ------------------------ | ------------------------------------------------------------------------------------------------------ |
|
||||
| Linux | | [Nightly Linux AppImage 64 bit]<br />[Nightly Linux ZIP file 64 bit] |
|
||||
| Linux ARM | [🚧 Work in progress...] | [🚧 Work in progress...] |
|
||||
| Windows | | [Nightly Windows 64 bit installer]<br />[Nightly Windows 64 bit MSI]<br />[Nightly Windows 64 bit ZIP] |
|
||||
| macOS | | [Nightly macOS 64 bit] |
|
||||
|
||||
[🚧 Work in progress...]: https://github.com/arduino/arduino-ide/issues/107
|
||||
[Nightly Linux 64 bit]: https://downloads.arduino.cc/arduino-ide/nightly/arduino-ide_nightly-latest_Linux_64bit.zip
|
||||
[Nightly Windows 64 bit installer]: https://downloads.arduino.cc/arduino-ide/nightly/arduino-ide_nightly-latest_Windows_64bit.exe
|
||||
[Nightly Windows 64 bit MSI]: https://downloads.arduino.cc/arduino-ide/nightly/arduino-ide_nightly-latest_Windows_64bit.msi
|
||||
[Nightly Windows 64 bit ZIP]: https://downloads.arduino.cc/arduino-ide/nightly/arduino-ide_nightly-latest_Windows_64bit.zip
|
||||
[Nightly macOS 64 bit]: https://downloads.arduino.cc/arduino-ide/nightly/arduino-ide_nightly-latest_macOS_64bit.dmg
|
||||
[🚧 work in progress...]: https://github.com/arduino/arduino-ide/issues/107
|
||||
[nightly linux appimage 64 bit]: https://downloads.arduino.cc/arduino-ide/nightly/arduino-ide_nightly-latest_Linux_64bit.AppImage
|
||||
[nightly linux zip file 64 bit]: https://downloads.arduino.cc/arduino-ide/nightly/arduino-ide_nightly-latest_Linux_64bit.zip
|
||||
[nightly windows 64 bit installer]: https://downloads.arduino.cc/arduino-ide/nightly/arduino-ide_nightly-latest_Windows_64bit.exe
|
||||
[nightly windows 64 bit msi]: https://downloads.arduino.cc/arduino-ide/nightly/arduino-ide_nightly-latest_Windows_64bit.msi
|
||||
[nightly windows 64 bit zip]: https://downloads.arduino.cc/arduino-ide/nightly/arduino-ide_nightly-latest_Windows_64bit.zip
|
||||
[nightly macos 64 bit]: https://downloads.arduino.cc/arduino-ide/nightly/arduino-ide_nightly-latest_macOS_64bit.dmg
|
||||
|
||||
> These links return an HTTP `302: Found` response, redirecting to latest
|
||||
generated builds by replacing `latest` with the latest available build
|
||||
date, using the format YYYYMMDD (i.e for 2019/Aug/06 `latest` is
|
||||
replaced with `20190806`)
|
||||
> generated builds by replacing `latest` with the latest available build
|
||||
> date, using the format YYYYMMDD (i.e for 2019/Aug/06 `latest` is
|
||||
> replaced with `20190806`)
|
||||
|
||||
## Support
|
||||
|
||||
@@ -47,8 +49,8 @@ If you need assistance, see the [Help Center](https://support.arduino.cc/hc/en-u
|
||||
|
||||
If you want to report an issue, you can submit it to the [issue tracker](https://github.com/arduino/arduino-ide/issues) of this repository. A few rules apply:
|
||||
|
||||
* Before posting, please check if the same problem has been already reported by someone else to avoid duplicates.
|
||||
* Remember to include as much detail as you can about your hardware set-up, code and steps for reproducing the issue. Make sure you're using an original Arduino board.
|
||||
- Before posting, please check if the same problem has been already reported by someone else to avoid duplicates.
|
||||
- Remember to include as much detail as you can about your hardware set-up, code and steps for reproducing the issue. Make sure you're using an original Arduino board.
|
||||
|
||||
### Security
|
||||
|
||||
@@ -64,10 +66,13 @@ Contributions are very welcome! You can browse the list of open issues to see wh
|
||||
|
||||
This repository contains the main code, but two more repositories are included during the build process:
|
||||
|
||||
* [vscode-arduino-tools](https://github.com/arduino/vscode-arduino-tools): provides support for the language server and the debugger
|
||||
* [arduino-language-server](https://github.com/arduino/arduino-language-server): provides the language server that parses Arduino code
|
||||
- [vscode-arduino-tools](https://github.com/arduino/vscode-arduino-tools): provides support for the language server and the debugger
|
||||
- [arduino-language-server](https://github.com/arduino/arduino-language-server): provides the language server that parses Arduino code
|
||||
|
||||
See the [BUILDING.md](BUILDING.md) for a technical overview of the application and instructions for building the code.
|
||||
|
||||
You can help with the translation of the Arduino IDE to your language here: [Arduino IDE on Transifex](https://www.transifex.com/arduino-1/ide2/dashboard/).
|
||||
|
||||
## Donations
|
||||
|
||||
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.
|
||||
|
@@ -1,14 +1,16 @@
|
||||
{
|
||||
"name": "arduino-ide-extension",
|
||||
"version": "2.0.0-rc2",
|
||||
"version": "2.0.0-rc6",
|
||||
"description": "An extension for Theia building the Arduino IDE",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"scripts": {
|
||||
"prepare": "yarn download-cli && yarn download-fwuploader && yarn download-ls && yarn copy-serial-plotter && yarn clean && yarn download-examples && yarn build && yarn test",
|
||||
"prepare": "yarn download-cli && yarn download-fwuploader && yarn download-ls && yarn copy-serial-plotter && yarn copy-i18n && yarn clean && yarn download-examples && yarn build && yarn test",
|
||||
"clean": "rimraf lib",
|
||||
"compose-changelog": "node ./scripts/compose-changelog.js",
|
||||
"download-cli": "node ./scripts/download-cli.js",
|
||||
"download-fwuploader": "node ./scripts/download-fwuploader.js",
|
||||
"copy-serial-plotter": "npx ncp ../node_modules/arduino-serial-plotter-webapp ./build/arduino-serial-plotter-webapp",
|
||||
"copy-i18n": "npx ncp ../i18n ./build/i18n",
|
||||
"download-ls": "node ./scripts/download-ls.js",
|
||||
"download-examples": "node ./scripts/download-examples.js",
|
||||
"generate-protocol": "node ./scripts/generate-protocol.js",
|
||||
@@ -20,22 +22,23 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@grpc/grpc-js": "^1.3.7",
|
||||
"@theia/application-package": "1.19.0",
|
||||
"@theia/core": "1.19.0",
|
||||
"@theia/editor": "1.19.0",
|
||||
"@theia/editor-preview": "1.19.0",
|
||||
"@theia/filesystem": "1.19.0",
|
||||
"@theia/git": "1.19.0",
|
||||
"@theia/keymaps": "1.19.0",
|
||||
"@theia/markers": "1.19.0",
|
||||
"@theia/monaco": "1.19.0",
|
||||
"@theia/navigator": "1.19.0",
|
||||
"@theia/outline-view": "1.19.0",
|
||||
"@theia/output": "1.19.0",
|
||||
"@theia/preferences": "1.19.0",
|
||||
"@theia/search-in-workspace": "1.19.0",
|
||||
"@theia/terminal": "1.19.0",
|
||||
"@theia/workspace": "1.19.0",
|
||||
"@theia/application-package": "1.22.1",
|
||||
"@theia/core": "1.22.1",
|
||||
"@theia/editor": "1.22.1",
|
||||
"@theia/editor-preview": "1.22.1",
|
||||
"@theia/electron": "1.22.1",
|
||||
"@theia/filesystem": "1.22.1",
|
||||
"@theia/git": "1.22.1",
|
||||
"@theia/keymaps": "1.22.1",
|
||||
"@theia/markers": "1.22.1",
|
||||
"@theia/monaco": "1.22.1",
|
||||
"@theia/navigator": "1.22.1",
|
||||
"@theia/outline-view": "1.22.1",
|
||||
"@theia/output": "1.22.1",
|
||||
"@theia/preferences": "1.22.1",
|
||||
"@theia/search-in-workspace": "1.22.1",
|
||||
"@theia/terminal": "1.22.1",
|
||||
"@theia/workspace": "1.22.1",
|
||||
"@tippyjs/react": "^4.2.5",
|
||||
"@types/atob": "^2.1.2",
|
||||
"@types/auth0-js": "^9.14.0",
|
||||
@@ -55,7 +58,7 @@
|
||||
"@types/temp": "^0.8.34",
|
||||
"@types/which": "^1.3.1",
|
||||
"ajv": "^6.5.3",
|
||||
"arduino-serial-plotter-webapp": "0.0.15",
|
||||
"arduino-serial-plotter-webapp": "0.0.17",
|
||||
"async-mutex": "^0.3.0",
|
||||
"atob": "^2.1.2",
|
||||
"auth0-js": "^9.14.0",
|
||||
@@ -63,6 +66,7 @@
|
||||
"css-element-queries": "^1.2.0",
|
||||
"dateformat": "^3.0.3",
|
||||
"deepmerge": "2.0.1",
|
||||
"electron-updater": "^4.6.5",
|
||||
"fuzzy": "^0.1.3",
|
||||
"glob": "^7.1.6",
|
||||
"google-protobuf": "^3.11.4",
|
||||
@@ -80,6 +84,7 @@
|
||||
"ps-tree": "^1.2.0",
|
||||
"query-string": "^7.0.1",
|
||||
"react-disable": "^0.1.0",
|
||||
"react-markdown": "^8.0.0",
|
||||
"react-select": "^3.0.4",
|
||||
"react-tabs": "^3.1.2",
|
||||
"react-window": "^1.8.6",
|
||||
@@ -92,6 +97,7 @@
|
||||
"which": "^1.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@octokit/rest": "^18.12.0",
|
||||
"@types/chai": "^4.2.7",
|
||||
"@types/chai-string": "^1.4.2",
|
||||
"@types/mocha": "^5.2.7",
|
||||
@@ -151,10 +157,16 @@
|
||||
],
|
||||
"arduino": {
|
||||
"cli": {
|
||||
"version": "0.20.2"
|
||||
"version": "0.22.0"
|
||||
},
|
||||
"fwuploader": {
|
||||
"version": "2.0.0"
|
||||
},
|
||||
"clangd": {
|
||||
"version": "13.0.0"
|
||||
},
|
||||
"languageServer": {
|
||||
"version": "0.6.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
116
arduino-ide-extension/scripts/compose-changelog.js
Executable file
116
arduino-ide-extension/scripts/compose-changelog.js
Executable file
@@ -0,0 +1,116 @@
|
||||
// @ts-check
|
||||
|
||||
(async () => {
|
||||
const { Octokit } = require('@octokit/rest');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const octokit = new Octokit({
|
||||
userAgent: 'Arduino IDE compose-changelog.js',
|
||||
});
|
||||
|
||||
const response = await octokit.rest.repos
|
||||
.listReleases({
|
||||
owner: 'arduino',
|
||||
repo: 'arduino-ide',
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
const releases = response.data;
|
||||
|
||||
let fullChangelog = releases.reduce((acc, item, index) => {
|
||||
// Process each line separately
|
||||
const body = item.body.split('\n').map(processLine).join('\n');
|
||||
// item.name is the name of the release changelog
|
||||
return (
|
||||
acc +
|
||||
`## ${item.name}\n\n${body}${
|
||||
index !== releases.length - 1 ? '\n\n---\n\n' : '\n'
|
||||
}`
|
||||
);
|
||||
}, '');
|
||||
|
||||
const args = process.argv.slice(2);
|
||||
if (args.length == 0) {
|
||||
console.error('Missing argument to destination file');
|
||||
process.exit(1);
|
||||
}
|
||||
const changelogFile = path.resolve(args[0]);
|
||||
|
||||
await fs.writeFile(
|
||||
changelogFile,
|
||||
fullChangelog,
|
||||
{
|
||||
flag: 'w+',
|
||||
},
|
||||
(err) => {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
}
|
||||
console.log('Changelog written to', changelogFile);
|
||||
}
|
||||
);
|
||||
})();
|
||||
|
||||
// processLine applies different substitutions to line string.
|
||||
// We're assuming that there are no more than one substitution
|
||||
// per line to be applied.
|
||||
const processLine = (line) => {
|
||||
// Check if a link with one of the following format exists:
|
||||
// * [#123](https://github.com/arduino/arduino-ide/pull/123)
|
||||
// * [#123](https://github.com/arduino/arduino-ide/issues/123)
|
||||
// * [#123](https://github.com/arduino/arduino-ide/pull/123/)
|
||||
// * [#123](https://github.com/arduino/arduino-ide/issues/123/)
|
||||
// If it does return the line as is.
|
||||
let r =
|
||||
/(\(|\[)#\d+(\)|\])(\(|\[)https:\/\/github\.com\/arduino\/arduino-ide\/(pull|issues)\/(\d+)\/?(\)|\])/gm;
|
||||
if (r.test(line)) {
|
||||
return line;
|
||||
}
|
||||
|
||||
// Check if a issue or PR link with the following format exists:
|
||||
// * #123
|
||||
// If it does it's changed to:
|
||||
// * [#123](https://github.com/arduino/arduino-ide/pull/123)
|
||||
r = /(?<![\w\d\/_]{1})#((\d)+)(?![\w\d\/_]{1})/gm;
|
||||
if (r.test(line)) {
|
||||
return line.replace(
|
||||
r,
|
||||
`[#$1](https://github.com/arduino/arduino-ide/pull/$1)`
|
||||
);
|
||||
}
|
||||
|
||||
// Check if a link with one of the following format exists:
|
||||
// * https://github.com/arduino/arduino-ide/pull/123
|
||||
// * https://github.com/arduino/arduino-ide/issues/123
|
||||
// * https://github.com/arduino/arduino-ide/pull/123/
|
||||
// * https://github.com/arduino/arduino-ide/issues/123/
|
||||
// If it does it's changed respectively to:
|
||||
// * [#123](https://github.com/arduino/arduino-ide/pull/123)
|
||||
// * [#123](https://github.com/arduino/arduino-ide/issues/123)
|
||||
// * [#123](https://github.com/arduino/arduino-ide/pull/123/)
|
||||
// * [#123](https://github.com/arduino/arduino-ide/issues/123/)
|
||||
r =
|
||||
/(https:\/\/github\.com\/arduino\/arduino-ide\/(pull|issues)\/(\d+)\/?)/gm;
|
||||
if (r.test(line)) {
|
||||
return line.replace(r, `[#$3]($1)`);
|
||||
}
|
||||
|
||||
// Check if a link with the following format exists:
|
||||
// * https://github.com/arduino/arduino-ide/compare/2.0.0-rc2...2.0.0-rc3
|
||||
// * https://github.com/arduino/arduino-ide/compare/2.0.0-rc2...2.0.0-rc3/
|
||||
// If it does it's changed to:
|
||||
// * [`2.0.0-rc2...2.0.0-rc3`](https://github.com/arduino/arduino-ide/compare/2.0.0-rc2...2.0.0-rc3)
|
||||
r =
|
||||
/(https:\/\/github\.com\/arduino\/arduino-ide\/compare\/([^\/]*))\/?\s?/gm;
|
||||
if (r.test(line)) {
|
||||
return line.replace(r, '[`$2`]($1)');
|
||||
}
|
||||
|
||||
// If nothing matches just return the line as is
|
||||
return line;
|
||||
};
|
@@ -4,13 +4,38 @@
|
||||
// - https://downloads.arduino.cc/arduino-language-server/clangd/clangd_${VERSION}_${SUFFIX}
|
||||
|
||||
(() => {
|
||||
const DEFAULT_ALS_VERSION = '0.5.0';
|
||||
const DEFAULT_CLANGD_VERSION = 'snapshot_20210124';
|
||||
|
||||
const path = require('path');
|
||||
const shell = require('shelljs');
|
||||
const downloader = require('./downloader');
|
||||
|
||||
const [DEFAULT_ALS_VERSION, DEFAULT_CLANGD_VERSION] = (() => {
|
||||
const pkg = require(path.join(__dirname, '..', 'package.json'));
|
||||
if (!pkg) return undefined;
|
||||
|
||||
const { arduino } = pkg;
|
||||
if (!arduino) return undefined;
|
||||
|
||||
const { languageServer, clangd } = arduino;
|
||||
if (!languageServer) return undefined;
|
||||
if (!clangd) return undefined;
|
||||
|
||||
return [languageServer.version, clangd.version];
|
||||
})();
|
||||
|
||||
if (!DEFAULT_ALS_VERSION) {
|
||||
shell.echo(
|
||||
`Could not retrieve Arduino Language Server version info from the 'package.json'.`
|
||||
);
|
||||
shell.exit(1);
|
||||
}
|
||||
|
||||
if (!DEFAULT_CLANGD_VERSION) {
|
||||
shell.echo(
|
||||
`Could not retrieve clangd version info from the 'package.json'.`
|
||||
);
|
||||
shell.exit(1);
|
||||
}
|
||||
|
||||
const yargs = require('yargs')
|
||||
.option('ls-version', {
|
||||
alias: 'lv',
|
||||
@@ -20,7 +45,7 @@
|
||||
.option('clangd-version', {
|
||||
alias: 'cv',
|
||||
default: DEFAULT_CLANGD_VERSION,
|
||||
choices: ['snapshot_20210124'],
|
||||
choices: [DEFAULT_CLANGD_VERSION, 'snapshot_20210124'],
|
||||
describe: `The version of 'clangd' to download. Defaults to ${DEFAULT_CLANGD_VERSION}.`,
|
||||
})
|
||||
.option('force-download', {
|
||||
@@ -35,32 +60,32 @@
|
||||
const clangdVersion = yargs['clangd-version'];
|
||||
const force = yargs['force-download'];
|
||||
const { platform, arch } = process;
|
||||
|
||||
const platformArch = platform + '-' + arch;
|
||||
const build = path.join(__dirname, '..', 'build');
|
||||
const lsExecutablePath = path.join(
|
||||
build,
|
||||
`arduino-language-server${platform === 'win32' ? '.exe' : ''}`
|
||||
);
|
||||
let clangdExecutablePath, lsSuffix, clangdSuffix;
|
||||
|
||||
let clangdExecutablePath, lsSuffix, clangdPrefix;
|
||||
switch (platform) {
|
||||
case 'darwin':
|
||||
clangdExecutablePath = path.join(build, 'bin', 'clangd');
|
||||
switch (platformArch) {
|
||||
case 'darwin-x64':
|
||||
clangdExecutablePath = path.join(build, 'clangd');
|
||||
lsSuffix = 'macOS_64bit.tar.gz';
|
||||
clangdPrefix = 'mac';
|
||||
clangdSuffix = 'macOS_64bit';
|
||||
break;
|
||||
case 'linux':
|
||||
clangdExecutablePath = path.join(build, 'bin', 'clangd');
|
||||
case 'linux-x64':
|
||||
clangdExecutablePath = path.join(build, 'clangd');
|
||||
lsSuffix = 'Linux_64bit.tar.gz';
|
||||
clangdPrefix = 'linux';
|
||||
clangdSuffix = 'Linux_64bit';
|
||||
break;
|
||||
case 'win32':
|
||||
clangdExecutablePath = path.join(build, 'bin', 'clangd.exe');
|
||||
case 'win32-x64':
|
||||
clangdExecutablePath = path.join(build, 'clangd.exe');
|
||||
lsSuffix = 'Windows_64bit.zip';
|
||||
clangdPrefix = 'windows';
|
||||
clangdSuffix = 'Windows_64bit';
|
||||
break;
|
||||
}
|
||||
if (!lsSuffix) {
|
||||
if (!lsSuffix || !clangdSuffix) {
|
||||
shell.echo(
|
||||
`The arduino-language-server is not available for ${platform} ${arch}.`
|
||||
);
|
||||
@@ -74,7 +99,7 @@
|
||||
}_${lsSuffix}`;
|
||||
downloader.downloadUnzipAll(alsUrl, build, lsExecutablePath, force);
|
||||
|
||||
const clangdUrl = `https://downloads.arduino.cc/arduino-language-server/clangd/clangd-${clangdPrefix}-${clangdVersion}.zip`;
|
||||
const clangdUrl = `https://downloads.arduino.cc/tools/clangd_${clangdVersion}_${clangdSuffix}.tar.bz2`;
|
||||
downloader.downloadUnzipAll(clangdUrl, build, clangdExecutablePath, force, {
|
||||
strip: 1,
|
||||
}); // `strip`: the new clangd (12.x) is zipped into a folder, so we have to strip the outmost folder.
|
||||
|
@@ -5,16 +5,17 @@ const download = require('download');
|
||||
const decompress = require('decompress');
|
||||
const unzip = require('decompress-unzip');
|
||||
const untargz = require('decompress-targz');
|
||||
const untarbz2 = require('decompress-tarbz2');
|
||||
|
||||
process.on('unhandledRejection', (reason, _) => {
|
||||
shell.echo(String(reason));
|
||||
shell.exit(1);
|
||||
throw reason;
|
||||
shell.echo(String(reason));
|
||||
shell.exit(1);
|
||||
throw reason;
|
||||
});
|
||||
process.on('uncaughtException', error => {
|
||||
shell.echo(String(error));
|
||||
shell.exit(1);
|
||||
throw error;
|
||||
process.on('uncaughtException', (error) => {
|
||||
shell.echo(String(error));
|
||||
shell.exit(1);
|
||||
throw error;
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -23,55 +24,62 @@ process.on('uncaughtException', error => {
|
||||
* @param filePrefix {string} Prefix of the file name found in the archive
|
||||
* @param force {boolean} Whether to download even if the target file exists. `false` by default.
|
||||
*/
|
||||
exports.downloadUnzipFile = async (url, targetFile, filePrefix, force = false) => {
|
||||
if (fs.existsSync(targetFile) && !force) {
|
||||
shell.echo(`Skipping download because file already exists: ${targetFile}`);
|
||||
return;
|
||||
}
|
||||
if (!fs.existsSync(path.dirname(targetFile))) {
|
||||
if (shell.mkdir('-p', path.dirname(targetFile)).code !== 0) {
|
||||
shell.echo('Could not create new directory.');
|
||||
shell.exit(1);
|
||||
}
|
||||
exports.downloadUnzipFile = async (
|
||||
url,
|
||||
targetFile,
|
||||
filePrefix,
|
||||
force = false
|
||||
) => {
|
||||
if (fs.existsSync(targetFile) && !force) {
|
||||
shell.echo(`Skipping download because file already exists: ${targetFile}`);
|
||||
return;
|
||||
}
|
||||
if (!fs.existsSync(path.dirname(targetFile))) {
|
||||
if (shell.mkdir('-p', path.dirname(targetFile)).code !== 0) {
|
||||
shell.echo('Could not create new directory.');
|
||||
shell.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
const downloads = path.join(__dirname, '..', 'downloads');
|
||||
if (shell.rm('-rf', targetFile, downloads).code !== 0) {
|
||||
shell.exit(1);
|
||||
}
|
||||
const downloads = path.join(__dirname, '..', 'downloads');
|
||||
if (shell.rm('-rf', targetFile, downloads).code !== 0) {
|
||||
shell.exit(1);
|
||||
}
|
||||
|
||||
shell.echo(`>>> Downloading from '${url}'...`);
|
||||
const data = await download(url);
|
||||
shell.echo(`<<< Download succeeded.`);
|
||||
shell.echo(`>>> Downloading from '${url}'...`);
|
||||
const data = await download(url);
|
||||
shell.echo(`<<< Download succeeded.`);
|
||||
|
||||
shell.echo('>>> Decompressing...');
|
||||
const files = await decompress(data, downloads, {
|
||||
plugins: [
|
||||
unzip(),
|
||||
untargz()
|
||||
]
|
||||
});
|
||||
if (files.length === 0) {
|
||||
shell.echo('Error ocurred while decompressing the archive.');
|
||||
shell.exit(1);
|
||||
}
|
||||
const fileIndex = files.findIndex(f => f.path.startsWith(filePrefix));
|
||||
if (fileIndex === -1) {
|
||||
shell.echo(`The downloaded artifact does not contain any file with prefix ${filePrefix}.`);
|
||||
shell.exit(1);
|
||||
}
|
||||
shell.echo('<<< Decompressing succeeded.');
|
||||
shell.echo('>>> Decompressing...');
|
||||
const files = await decompress(data, downloads, {
|
||||
plugins: [unzip(), untargz(), untarbz2()],
|
||||
});
|
||||
if (files.length === 0) {
|
||||
shell.echo('Error ocurred while decompressing the archive.');
|
||||
shell.exit(1);
|
||||
}
|
||||
const fileIndex = files.findIndex((f) => f.path.startsWith(filePrefix));
|
||||
if (fileIndex === -1) {
|
||||
shell.echo(
|
||||
`The downloaded artifact does not contain any file with prefix ${filePrefix}.`
|
||||
);
|
||||
shell.exit(1);
|
||||
}
|
||||
shell.echo('<<< Decompressing succeeded.');
|
||||
|
||||
if (shell.mv('-f', path.join(downloads, files[fileIndex].path), targetFile).code !== 0) {
|
||||
shell.echo(`Could not move file to target path: ${targetFile}`);
|
||||
shell.exit(1);
|
||||
}
|
||||
if (!fs.existsSync(targetFile)) {
|
||||
shell.echo(`Could not find file: ${targetFile}`);
|
||||
shell.exit(1);
|
||||
}
|
||||
shell.echo(`Done: ${targetFile}`);
|
||||
}
|
||||
if (
|
||||
shell.mv('-f', path.join(downloads, files[fileIndex].path), targetFile)
|
||||
.code !== 0
|
||||
) {
|
||||
shell.echo(`Could not move file to target path: ${targetFile}`);
|
||||
shell.exit(1);
|
||||
}
|
||||
if (!fs.existsSync(targetFile)) {
|
||||
shell.echo(`Could not find file: ${targetFile}`);
|
||||
shell.exit(1);
|
||||
}
|
||||
shell.echo(`Done: ${targetFile}`);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param url {string} Download URL
|
||||
@@ -79,42 +87,45 @@ exports.downloadUnzipFile = async (url, targetFile, filePrefix, force = false) =
|
||||
* @param targetFile {string} Path to the main file expected after decompressing
|
||||
* @param force {boolean} Whether to download even if the target file exists
|
||||
*/
|
||||
exports.downloadUnzipAll = async (url, targetDir, targetFile, force, decompressOptions = undefined) => {
|
||||
if (fs.existsSync(targetFile) && !force) {
|
||||
shell.echo(`Skipping download because file already exists: ${targetFile}`);
|
||||
return;
|
||||
}
|
||||
if (!fs.existsSync(targetDir)) {
|
||||
if (shell.mkdir('-p', targetDir).code !== 0) {
|
||||
shell.echo('Could not create new directory.');
|
||||
shell.exit(1);
|
||||
}
|
||||
exports.downloadUnzipAll = async (
|
||||
url,
|
||||
targetDir,
|
||||
targetFile,
|
||||
force,
|
||||
decompressOptions = undefined
|
||||
) => {
|
||||
if (fs.existsSync(targetFile) && !force) {
|
||||
shell.echo(`Skipping download because file already exists: ${targetFile}`);
|
||||
return;
|
||||
}
|
||||
if (!fs.existsSync(targetDir)) {
|
||||
if (shell.mkdir('-p', targetDir).code !== 0) {
|
||||
shell.echo('Could not create new directory.');
|
||||
shell.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
shell.echo(`>>> Downloading from '${url}'...`);
|
||||
const data = await download(url);
|
||||
shell.echo(`<<< Download succeeded.`);
|
||||
shell.echo(`>>> Downloading from '${url}'...`);
|
||||
const data = await download(url);
|
||||
shell.echo(`<<< Download succeeded.`);
|
||||
|
||||
shell.echo('>>> Decompressing...');
|
||||
let options = {
|
||||
plugins: [
|
||||
unzip(),
|
||||
untargz()
|
||||
]
|
||||
};
|
||||
if (decompressOptions) {
|
||||
options = Object.assign(options, decompressOptions)
|
||||
}
|
||||
const files = await decompress(data, targetDir, options);
|
||||
if (files.length === 0) {
|
||||
shell.echo('Error ocurred while decompressing the archive.');
|
||||
shell.exit(1);
|
||||
}
|
||||
shell.echo('<<< Decompressing succeeded.');
|
||||
shell.echo('>>> Decompressing...');
|
||||
let options = {
|
||||
plugins: [unzip(), untargz(), untarbz2()],
|
||||
};
|
||||
if (decompressOptions) {
|
||||
options = Object.assign(options, decompressOptions);
|
||||
}
|
||||
const files = await decompress(data, targetDir, options);
|
||||
if (files.length === 0) {
|
||||
shell.echo('Error ocurred while decompressing the archive.');
|
||||
shell.exit(1);
|
||||
}
|
||||
shell.echo('<<< Decompressing succeeded.');
|
||||
|
||||
if (!fs.existsSync(targetFile)) {
|
||||
shell.echo(`Could not find file: ${targetFile}`);
|
||||
shell.exit(1);
|
||||
}
|
||||
shell.echo(`Done: ${targetFile}`);
|
||||
}
|
||||
if (!fs.existsSync(targetFile)) {
|
||||
shell.echo(`Could not find file: ${targetFile}`);
|
||||
shell.exit(1);
|
||||
}
|
||||
shell.echo(`Done: ${targetFile}`);
|
||||
};
|
||||
|
@@ -1,9 +1,8 @@
|
||||
import { inject, injectable, postConstruct } from 'inversify';
|
||||
import * as React from 'react';
|
||||
import { remote } from 'electron';
|
||||
import * as remote from '@theia/core/electron-shared/@electron/remote';
|
||||
import {
|
||||
BoardsService,
|
||||
Port,
|
||||
SketchesService,
|
||||
ExecutableService,
|
||||
Sketch,
|
||||
@@ -21,6 +20,7 @@ import {
|
||||
FrontendApplication,
|
||||
FrontendApplicationContribution,
|
||||
LocalStorageService,
|
||||
SaveableWidget,
|
||||
StatusBar,
|
||||
StatusBarAlignment,
|
||||
} from '@theia/core/lib/browser';
|
||||
@@ -39,13 +39,14 @@ import {
|
||||
import { MessageService } from '@theia/core/lib/common/message-service';
|
||||
import URI from '@theia/core/lib/common/uri';
|
||||
import {
|
||||
EditorCommands,
|
||||
EditorMainMenu,
|
||||
EditorManager,
|
||||
EditorOpenerOptions,
|
||||
} from '@theia/editor/lib/browser';
|
||||
import { ProblemContribution } from '@theia/markers/lib/browser/problem/problem-contribution';
|
||||
import { MonacoMenus } from '@theia/monaco/lib/browser/monaco-menu';
|
||||
import { FileNavigatorContribution } from '@theia/navigator/lib/browser/navigator-contribution';
|
||||
import { FileNavigatorCommands, FileNavigatorContribution } from '@theia/navigator/lib/browser/navigator-contribution';
|
||||
import { OutlineViewContribution } from '@theia/outline-view/lib/browser/outline-view-contribution';
|
||||
import { OutputContribution } from '@theia/output/lib/browser/output-contribution';
|
||||
import { ScmContribution } from '@theia/scm/lib/browser/scm-contribution';
|
||||
@@ -69,8 +70,12 @@ import { ArduinoPreferences } from './arduino-preferences';
|
||||
import { SketchesServiceClientImpl } from '../common/protocol/sketches-service-client-impl';
|
||||
import { SaveAsSketch } from './contributions/save-as-sketch';
|
||||
import { SketchbookWidgetContribution } from './widgets/sketchbook/sketchbook-widget-contribution';
|
||||
import { IDEUpdaterDialog } from './dialogs/ide-updater/ide-updater-dialog';
|
||||
import { IDEUpdater } from '../common/protocol/ide-updater';
|
||||
import { FileSystemFrontendContribution } from '@theia/filesystem/lib/browser/filesystem-frontend-contribution';
|
||||
|
||||
const INIT_LIBS_AND_PACKAGES = 'initializedLibsAndPackages';
|
||||
export const SKIP_IDE_VERSION = 'skipIDEVersion';
|
||||
|
||||
@injectable()
|
||||
export class ArduinoFrontendContribution
|
||||
@@ -79,8 +84,7 @@ export class ArduinoFrontendContribution
|
||||
TabBarToolbarContribution,
|
||||
CommandContribution,
|
||||
MenuContribution,
|
||||
ColorContribution
|
||||
{
|
||||
ColorContribution {
|
||||
@inject(ILogger)
|
||||
protected logger: ILogger;
|
||||
|
||||
@@ -158,6 +162,15 @@ export class ArduinoFrontendContribution
|
||||
@inject(LocalStorageService)
|
||||
protected readonly localStorageService: LocalStorageService;
|
||||
|
||||
@inject(FileSystemFrontendContribution)
|
||||
protected readonly fileSystemFrontendContribution: FileSystemFrontendContribution;
|
||||
|
||||
@inject(IDEUpdater)
|
||||
protected readonly updater: IDEUpdater;
|
||||
|
||||
@inject(IDEUpdaterDialog)
|
||||
protected readonly updaterDialog: IDEUpdaterDialog;
|
||||
|
||||
protected invalidConfigPopup:
|
||||
| Promise<void | 'No' | 'Yes' | undefined>
|
||||
| undefined;
|
||||
@@ -216,7 +229,7 @@ export class ArduinoFrontendContribution
|
||||
? nls.localize(
|
||||
'arduino/common/selectedOn',
|
||||
'on {0}',
|
||||
Port.toString(selectedPort)
|
||||
selectedPort.address
|
||||
)
|
||||
: nls.localize('arduino/common/notConnected', '[not connected]'),
|
||||
className: 'arduino-selected-port',
|
||||
@@ -252,7 +265,7 @@ export class ArduinoFrontendContribution
|
||||
});
|
||||
}
|
||||
|
||||
onStart(app: FrontendApplication): void {
|
||||
async onStart(app: FrontendApplication): Promise<void> {
|
||||
// Initialize all `pro-mode` widgets. This is a NOOP if in normal mode.
|
||||
for (const viewContribution of [
|
||||
this.fileNavigatorContributions,
|
||||
@@ -267,6 +280,31 @@ export class ArduinoFrontendContribution
|
||||
viewContribution.initializeLayout(app);
|
||||
}
|
||||
}
|
||||
|
||||
this.updater
|
||||
.init(
|
||||
this.arduinoPreferences.get('arduino.ide.updateChannel'),
|
||||
this.arduinoPreferences.get('arduino.ide.updateBaseUrl')
|
||||
)
|
||||
.then(() => this.updater.checkForUpdates(true))
|
||||
.then(async (updateInfo) => {
|
||||
if (!updateInfo) return;
|
||||
const versionToSkip = await this.localStorageService.getData<string>(
|
||||
SKIP_IDE_VERSION
|
||||
);
|
||||
if (versionToSkip === updateInfo.version) return;
|
||||
this.updaterDialog.open(updateInfo);
|
||||
})
|
||||
.catch((e) => {
|
||||
this.messageService.error(
|
||||
nls.localize(
|
||||
'arduino/ide-updater/errorCheckingForUpdates',
|
||||
'Error while checking for Arduino IDE updates.\n{0}',
|
||||
e.message
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
const start = async ({ selectedBoard }: BoardsConfig.Config) => {
|
||||
if (selectedBoard) {
|
||||
const { name, fqbn } = selectedBoard;
|
||||
@@ -277,11 +315,25 @@ export class ArduinoFrontendContribution
|
||||
};
|
||||
this.boardsServiceClientImpl.onBoardsConfigChanged(start);
|
||||
this.arduinoPreferences.onPreferenceChanged((event) => {
|
||||
if (
|
||||
event.preferenceName === 'arduino.language.log' &&
|
||||
event.newValue !== event.oldValue
|
||||
) {
|
||||
start(this.boardsServiceClientImpl.boardsConfig);
|
||||
if (event.newValue !== event.oldValue) {
|
||||
switch (event.preferenceName) {
|
||||
case 'arduino.language.log':
|
||||
start(this.boardsServiceClientImpl.boardsConfig);
|
||||
break;
|
||||
case 'arduino.window.zoomLevel':
|
||||
if (typeof event.newValue === 'number') {
|
||||
const webContents = remote.getCurrentWebContents();
|
||||
webContents.setZoomLevel(event.newValue || 0);
|
||||
}
|
||||
break;
|
||||
case 'arduino.ide.updateChannel':
|
||||
case 'arduino.ide.updateBaseUrl':
|
||||
this.updater.init(
|
||||
this.arduinoPreferences.get('arduino.ide.updateChannel'),
|
||||
this.arduinoPreferences.get('arduino.ide.updateBaseUrl')
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
this.arduinoPreferences.ready.then(() => {
|
||||
@@ -289,17 +341,19 @@ export class ArduinoFrontendContribution
|
||||
const zoomLevel = this.arduinoPreferences.get('arduino.window.zoomLevel');
|
||||
webContents.setZoomLevel(zoomLevel);
|
||||
});
|
||||
this.arduinoPreferences.onPreferenceChanged((event) => {
|
||||
if (
|
||||
event.preferenceName === 'arduino.window.zoomLevel' &&
|
||||
typeof event.newValue === 'number' &&
|
||||
event.newValue !== event.oldValue
|
||||
) {
|
||||
const webContents = remote.getCurrentWebContents();
|
||||
webContents.setZoomLevel(event.newValue || 0);
|
||||
|
||||
app.shell.leftPanelHandler.removeBottomMenu('settings-menu');
|
||||
|
||||
this.fileSystemFrontendContribution.onDidChangeEditorFile(e => {
|
||||
if (e.type === FileChangeType.DELETED) {
|
||||
const editorWidget = e.editor;
|
||||
if (SaveableWidget.is(editorWidget)) {
|
||||
editorWidget.closeWithoutSaving();
|
||||
} else {
|
||||
editorWidget.close();
|
||||
}
|
||||
}
|
||||
});
|
||||
app.shell.leftPanelHandler.removeBottomMenu('settings-menu');
|
||||
}
|
||||
|
||||
onStop(): void {
|
||||
@@ -432,6 +486,18 @@ export class ArduinoFrontendContribution
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
for (const command of [
|
||||
EditorCommands.SPLIT_EDITOR_DOWN,
|
||||
EditorCommands.SPLIT_EDITOR_LEFT,
|
||||
EditorCommands.SPLIT_EDITOR_RIGHT,
|
||||
EditorCommands.SPLIT_EDITOR_UP,
|
||||
EditorCommands.SPLIT_EDITOR_VERTICAL,
|
||||
EditorCommands.SPLIT_EDITOR_HORIZONTAL,
|
||||
FileNavigatorCommands.REVEAL_IN_NAVIGATOR
|
||||
]) {
|
||||
registry.unregisterCommand(command);
|
||||
}
|
||||
}
|
||||
|
||||
registerMenus(registry: MenuModelRegistry) {
|
||||
|
@@ -69,20 +69,12 @@ import { ScmContribution } from './theia/scm/scm-contribution';
|
||||
import { SearchInWorkspaceFrontendContribution as TheiaSearchInWorkspaceFrontendContribution } from '@theia/search-in-workspace/lib/browser/search-in-workspace-frontend-contribution';
|
||||
import { SearchInWorkspaceFrontendContribution } from './theia/search-in-workspace/search-in-workspace-frontend-contribution';
|
||||
import { LibraryListWidgetFrontendContribution } from './library/library-widget-frontend-contribution';
|
||||
import { SerialServiceClientImpl } from './serial/serial-service-client-impl';
|
||||
import {
|
||||
SerialServicePath,
|
||||
SerialService,
|
||||
SerialServiceClient,
|
||||
} from '../common/protocol/serial-service';
|
||||
import {
|
||||
ConfigService,
|
||||
ConfigServicePath,
|
||||
} from '../common/protocol/config-service';
|
||||
import { MonitorWidget } from './serial/monitor/monitor-widget';
|
||||
import { MonitorViewContribution } from './serial/monitor/monitor-view-contribution';
|
||||
import { SerialConnectionManager } from './serial/serial-connection-manager';
|
||||
import { SerialModel } from './serial/serial-model';
|
||||
import { TabBarDecoratorService as TheiaTabBarDecoratorService } from '@theia/core/lib/browser/shell/tab-bar-decorator';
|
||||
import { TabBarDecoratorService } from './theia/core/tab-bar-decorator';
|
||||
import { ProblemManager as TheiaProblemManager } from '@theia/markers/lib/browser';
|
||||
@@ -160,7 +152,7 @@ import {
|
||||
OutputChannelRegistryMainImpl as TheiaOutputChannelRegistryMainImpl,
|
||||
OutputChannelRegistryMainImpl,
|
||||
} from './theia/plugin-ext/output-channel-registry-main';
|
||||
import { ExecutableService, ExecutableServicePath } from '../common/protocol';
|
||||
import { ExecutableService, ExecutableServicePath, MonitorManagerProxy, MonitorManagerProxyClient, MonitorManagerProxyFactory, MonitorManagerProxyPath } from '../common/protocol';
|
||||
import { MonacoTextModelService as TheiaMonacoTextModelService } from '@theia/monaco/lib/browser/monaco-text-model-service';
|
||||
import { MonacoTextModelService } from './theia/monaco/monaco-text-model-service';
|
||||
import { ResponseServiceImpl } from './response-service-impl';
|
||||
@@ -262,6 +254,21 @@ import {
|
||||
UserFieldsDialogWidget,
|
||||
} from './dialogs/user-fields/user-fields-dialog';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
import { IDEUpdaterCommands } from './ide-updater/ide-updater-commands';
|
||||
import {
|
||||
IDEUpdater,
|
||||
IDEUpdaterClient,
|
||||
IDEUpdaterPath,
|
||||
} from '../common/protocol/ide-updater';
|
||||
import { IDEUpdaterClientImpl } from './ide-updater/ide-updater-client-impl';
|
||||
import {
|
||||
IDEUpdaterDialog,
|
||||
IDEUpdaterDialogProps,
|
||||
IDEUpdaterDialogWidget,
|
||||
} from './dialogs/ide-updater/ide-updater-dialog';
|
||||
import { ElectronIpcConnectionProvider } from '@theia/core/lib/electron-browser/messaging/electron-ipc-connection-provider';
|
||||
import { MonitorModel } from './monitor-model';
|
||||
import { MonitorManagerProxyClientImpl } from './monitor-manager-proxy-client-impl';
|
||||
|
||||
const ElementQueries = require('css-element-queries/src/ElementQueries');
|
||||
|
||||
@@ -394,28 +401,31 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
.inSingletonScope();
|
||||
|
||||
// Serial monitor
|
||||
bind(SerialModel).toSelf().inSingletonScope();
|
||||
bind(FrontendApplicationContribution).toService(SerialModel);
|
||||
bind(MonitorWidget).toSelf();
|
||||
bind(FrontendApplicationContribution).toService(MonitorModel);
|
||||
bind(MonitorModel).toSelf().inSingletonScope();
|
||||
bindViewContribution(bind, MonitorViewContribution);
|
||||
bind(TabBarToolbarContribution).toService(MonitorViewContribution);
|
||||
bind(WidgetFactory).toDynamicValue((context) => ({
|
||||
id: MonitorWidget.ID,
|
||||
createWidget: () => context.container.get(MonitorWidget),
|
||||
createWidget: () => {
|
||||
return new MonitorWidget(
|
||||
context.container.get<MonitorModel>(MonitorModel),
|
||||
context.container.get<MonitorManagerProxyClient>(MonitorManagerProxyClient),
|
||||
context.container.get<BoardsServiceProvider>(BoardsServiceProvider),
|
||||
);
|
||||
}
|
||||
}));
|
||||
// Frontend binding for the serial service
|
||||
bind(SerialService)
|
||||
.toDynamicValue((context) => {
|
||||
const connection = context.container.get(WebSocketConnectionProvider);
|
||||
const client =
|
||||
context.container.get<SerialServiceClient>(SerialServiceClient);
|
||||
return connection.createProxy(SerialServicePath, client);
|
||||
})
|
||||
.inSingletonScope();
|
||||
bind(SerialConnectionManager).toSelf().inSingletonScope();
|
||||
|
||||
// Serial service client to receive and delegate notifications from the backend.
|
||||
bind(SerialServiceClient).to(SerialServiceClientImpl).inSingletonScope();
|
||||
bind(MonitorManagerProxyFactory).toFactory((context) => () => context.container.get<MonitorManagerProxy>(MonitorManagerProxy))
|
||||
|
||||
bind(MonitorManagerProxy).toDynamicValue((context) =>
|
||||
WebSocketConnectionProvider.createProxy(context.container, MonitorManagerProxyPath, context.container.get(MonitorManagerProxyClient))
|
||||
).inSingletonScope();
|
||||
|
||||
// Monitor manager proxy client to receive and delegate pluggable monitors
|
||||
// notifications from the backend
|
||||
bind(MonitorManagerProxyClient).to(MonitorManagerProxyClientImpl).inSingletonScope();
|
||||
|
||||
bind(WorkspaceService).toSelf().inSingletonScope();
|
||||
rebind(TheiaWorkspaceService).toService(WorkspaceService);
|
||||
@@ -472,12 +482,11 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
.inSingletonScope();
|
||||
rebind(TheiaEditorWidgetFactory).to(EditorWidgetFactory).inSingletonScope();
|
||||
rebind(TabBarToolbarFactory).toFactory(
|
||||
({ container: parentContainer }) =>
|
||||
() => {
|
||||
const container = parentContainer.createChild();
|
||||
container.bind(TabBarToolbar).toSelf().inSingletonScope();
|
||||
return container.get(TabBarToolbar);
|
||||
}
|
||||
({ container: parentContainer }) => () => {
|
||||
const container = parentContainer.createChild();
|
||||
container.bind(TabBarToolbar).toSelf().inSingletonScope();
|
||||
return container.get(TabBarToolbar);
|
||||
}
|
||||
);
|
||||
bind(OutputWidget).toSelf().inSingletonScope();
|
||||
rebind(TheiaOutputWidget).toService(OutputWidget);
|
||||
@@ -642,13 +651,15 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
|
||||
// Enable the dirty indicator on uncloseable widgets.
|
||||
rebind(TabBarRendererFactory).toFactory((context) => () => {
|
||||
const contextMenuRenderer =
|
||||
context.container.get<ContextMenuRenderer>(ContextMenuRenderer);
|
||||
const contextMenuRenderer = context.container.get<ContextMenuRenderer>(
|
||||
ContextMenuRenderer
|
||||
);
|
||||
const decoratorService = context.container.get<TabBarDecoratorService>(
|
||||
TabBarDecoratorService
|
||||
);
|
||||
const iconThemeService =
|
||||
context.container.get<IconThemeService>(IconThemeService);
|
||||
const iconThemeService = context.container.get<IconThemeService>(
|
||||
IconThemeService
|
||||
);
|
||||
return new TabBarRenderer(
|
||||
contextMenuRenderer,
|
||||
decoratorService,
|
||||
@@ -756,9 +767,32 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
title: 'UploadCertificate',
|
||||
});
|
||||
|
||||
bind(IDEUpdaterDialogWidget).toSelf().inSingletonScope();
|
||||
bind(IDEUpdaterDialog).toSelf().inSingletonScope();
|
||||
bind(IDEUpdaterDialogProps).toConstantValue({
|
||||
title: 'IDEUpdater',
|
||||
});
|
||||
|
||||
bind(UserFieldsDialogWidget).toSelf().inSingletonScope();
|
||||
bind(UserFieldsDialog).toSelf().inSingletonScope();
|
||||
bind(UserFieldsDialogProps).toConstantValue({
|
||||
title: 'UserFields',
|
||||
});
|
||||
|
||||
bind(IDEUpdaterCommands).toSelf().inSingletonScope();
|
||||
bind(CommandContribution).toService(IDEUpdaterCommands);
|
||||
|
||||
// Frontend binding for the IDE Updater service
|
||||
bind(IDEUpdaterClientImpl).toSelf().inSingletonScope();
|
||||
bind(IDEUpdaterClient).toService(IDEUpdaterClientImpl);
|
||||
bind(IDEUpdater)
|
||||
.toDynamicValue((context) => {
|
||||
const client = context.container.get(IDEUpdaterClientImpl);
|
||||
return ElectronIpcConnectionProvider.createProxy(
|
||||
context.container,
|
||||
IDEUpdaterPath,
|
||||
client
|
||||
);
|
||||
})
|
||||
.inSingletonScope();
|
||||
});
|
||||
|
@@ -9,6 +9,11 @@ import {
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
import { CompilerWarningLiterals, CompilerWarnings } from '../common/protocol';
|
||||
|
||||
export enum UpdateChannel {
|
||||
Stable = 'stable',
|
||||
Nightly = 'nightly',
|
||||
}
|
||||
|
||||
export const ArduinoConfigSchema: PreferenceSchema = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
@@ -64,13 +69,22 @@ export const ArduinoConfigSchema: PreferenceSchema = {
|
||||
),
|
||||
default: 0,
|
||||
},
|
||||
'arduino.ide.autoUpdate': {
|
||||
type: 'boolean',
|
||||
'arduino.ide.updateChannel': {
|
||||
type: 'string',
|
||||
enum: Object.values(UpdateChannel) as UpdateChannel[],
|
||||
default: UpdateChannel.Stable,
|
||||
description: nls.localize(
|
||||
'arduino/preferences/ide.autoUpdate',
|
||||
'True to enable automatic update checks. The IDE will check for updates automatically and periodically.'
|
||||
'arduino/preferences/ide.updateChannel',
|
||||
"Release channel to get updated from. 'stable' is the stable release, 'nightly' is the latest development build."
|
||||
),
|
||||
},
|
||||
'arduino.ide.updateBaseUrl': {
|
||||
type: 'string',
|
||||
default: 'https://downloads.arduino.cc/arduino-ide',
|
||||
description: nls.localize(
|
||||
'arduino/preferences/ide.updateBaseUrl',
|
||||
`The base URL where to download updates from. Defaults to 'https://downloads.arduino.cc/arduino-ide'`
|
||||
),
|
||||
default: true,
|
||||
},
|
||||
'arduino.board.certificates': {
|
||||
type: 'string',
|
||||
@@ -171,7 +185,8 @@ export interface ArduinoConfiguration {
|
||||
'arduino.upload.verify': boolean;
|
||||
'arduino.window.autoScale': boolean;
|
||||
'arduino.window.zoomLevel': number;
|
||||
'arduino.ide.autoUpdate': boolean;
|
||||
'arduino.ide.updateChannel': UpdateChannel;
|
||||
'arduino.ide.updateBaseUrl': string;
|
||||
'arduino.board.certificates': string;
|
||||
'arduino.sketchbook.showAllFiles': boolean;
|
||||
'arduino.cloud.enabled': boolean;
|
||||
@@ -188,16 +203,10 @@ export interface ArduinoConfiguration {
|
||||
export const ArduinoPreferences = Symbol('ArduinoPreferences');
|
||||
export type ArduinoPreferences = PreferenceProxy<ArduinoConfiguration>;
|
||||
|
||||
export function createArduinoPreferences(
|
||||
preferences: PreferenceService
|
||||
): ArduinoPreferences {
|
||||
return createPreferenceProxy(preferences, ArduinoConfigSchema);
|
||||
}
|
||||
|
||||
export function bindArduinoPreferences(bind: interfaces.Bind): void {
|
||||
bind(ArduinoPreferences).toDynamicValue((ctx) => {
|
||||
const preferences = ctx.container.get<PreferenceService>(PreferenceService);
|
||||
return createArduinoPreferences(preferences);
|
||||
return createPreferenceProxy(preferences, ArduinoConfigSchema);
|
||||
});
|
||||
bind(PreferenceContribution).toConstantValue({
|
||||
schema: ArduinoConfigSchema,
|
||||
|
@@ -1,5 +1,4 @@
|
||||
import { toUnix } from 'upath';
|
||||
import URI from '@theia/core/lib/common/uri';
|
||||
import { URI } from '@theia/core/shared/vscode-uri';
|
||||
import { isWindows } from '@theia/core/lib/common/os';
|
||||
import { notEmpty } from '@theia/core/lib/common/objects';
|
||||
import { MaybePromise } from '@theia/core/lib/common/types';
|
||||
@@ -61,12 +60,8 @@ export class ArduinoWorkspaceRootResolver {
|
||||
// - https://github.com/eclipse-theia/theia/blob/8196e9dcf9c8de8ea0910efeb5334a974f426966/packages/workspace/src/browser/workspace-service.ts#L423
|
||||
protected hashToUri(hash: string | undefined): string | undefined {
|
||||
if (hash && hash.length > 1 && hash.startsWith('#')) {
|
||||
const path = hash.slice(1); // Trim the leading `#`.
|
||||
return new URI(
|
||||
toUnix(path.slice(isWindows && hash.startsWith('/') ? 1 : 0))
|
||||
)
|
||||
.withScheme('file')
|
||||
.toString();
|
||||
const path = decodeURI(hash.slice(1)).replace(/\\/g, '/'); // Trim the leading `#`, decode the URI and replace Windows separators
|
||||
return URI.file(path.slice(isWindows && hash.startsWith('/') ? 1 : 0)).toString();
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
@@ -84,7 +84,7 @@ export class BoardsConfigDialog extends AbstractDialog<BoardsConfig.Config> {
|
||||
),
|
||||
nls.localize(
|
||||
'arduino/board/configDialog2',
|
||||
'If you only select a Board you will be able just to compile, but not to upload your sketch.'
|
||||
'If you only select a Board you will be able to compile, but not to upload your sketch.'
|
||||
),
|
||||
]) {
|
||||
const p = document.createElement('div');
|
||||
|
@@ -167,7 +167,7 @@ export class BoardsConfig extends React.Component<
|
||||
this.queryPorts(Promise.resolve(ports)).then(({ knownPorts }) => {
|
||||
let { selectedPort } = this.state;
|
||||
// If the currently selected port is not available anymore, unset the selected port.
|
||||
if (removedPorts.some((port) => Port.equals(port, selectedPort))) {
|
||||
if (removedPorts.some((port) => Port.sameAs(port, selectedPort))) {
|
||||
selectedPort = undefined;
|
||||
}
|
||||
this.setState({ knownPorts, selectedPort }, () =>
|
||||
@@ -213,11 +213,11 @@ export class BoardsConfig extends React.Component<
|
||||
} else if (left.protocol === right.protocol) {
|
||||
// We show ports, including those that have guessed
|
||||
// or unrecognized boards, so we must sort those too.
|
||||
const leftBoard = this.availableBoards.find((board) =>
|
||||
Port.sameAs(board.port, left)
|
||||
const leftBoard = this.availableBoards.find(
|
||||
(board) => board.port === left
|
||||
);
|
||||
const rightBoard = this.availableBoards.find((board) =>
|
||||
Port.sameAs(board.port, right)
|
||||
const rightBoard = this.availableBoards.find(
|
||||
(board) => board.port === right
|
||||
);
|
||||
if (leftBoard && !rightBoard) {
|
||||
return -1;
|
||||
@@ -348,10 +348,10 @@ export class BoardsConfig extends React.Component<
|
||||
<div className="ports list">
|
||||
{ports.map((port) => (
|
||||
<Item<Port>
|
||||
key={Port.toString(port)}
|
||||
key={`${port.id}`}
|
||||
item={port}
|
||||
label={Port.toString(port)}
|
||||
selected={Port.equals(this.state.selectedPort, port)}
|
||||
selected={Port.sameAs(this.state.selectedPort, port)}
|
||||
onClick={this.selectPort}
|
||||
/>
|
||||
))}
|
||||
@@ -410,7 +410,7 @@ export namespace BoardsConfig {
|
||||
return options.default;
|
||||
}
|
||||
const { name } = selectedBoard;
|
||||
return `${name}${port ? ' at ' + Port.toString(port) : ''}`;
|
||||
return `${name}${port ? ` at ${port.address}` : ''}`;
|
||||
}
|
||||
|
||||
export function setConfig(
|
||||
|
@@ -1,7 +1,6 @@
|
||||
import { injectable, inject, named } from 'inversify';
|
||||
import { ILogger } from '@theia/core/lib/common/logger';
|
||||
import { deepClone } from '@theia/core/lib/common/objects';
|
||||
import { MaybePromise } from '@theia/core/lib/common/types';
|
||||
import { Event, Emitter } from '@theia/core/lib/common/event';
|
||||
import {
|
||||
FrontendApplicationContribution,
|
||||
@@ -11,7 +10,6 @@ import { notEmpty } from '../../common/utils';
|
||||
import {
|
||||
BoardsService,
|
||||
ConfigOption,
|
||||
Installable,
|
||||
BoardDetails,
|
||||
Programmer,
|
||||
} from '../../common/protocol';
|
||||
@@ -36,16 +34,12 @@ export class BoardsDataStore implements FrontendApplicationContribution {
|
||||
|
||||
onStart(): void {
|
||||
this.notificationCenter.onPlatformInstalled(async ({ item }) => {
|
||||
const { installedVersion: version } = item;
|
||||
if (!version) {
|
||||
return;
|
||||
}
|
||||
let shouldFireChanged = false;
|
||||
for (const fqbn of item.boards
|
||||
.map(({ fqbn }) => fqbn)
|
||||
.filter(notEmpty)
|
||||
.filter((fqbn) => !!fqbn)) {
|
||||
const key = this.getStorageKey(fqbn, version);
|
||||
const key = this.getStorageKey(fqbn);
|
||||
let data = await this.storageService.getData<
|
||||
ConfigOption[] | undefined
|
||||
>(key);
|
||||
@@ -72,33 +66,20 @@ export class BoardsDataStore implements FrontendApplicationContribution {
|
||||
|
||||
async appendConfigToFqbn(
|
||||
fqbn: string | undefined,
|
||||
boardsPackageVersion: MaybePromise<
|
||||
Installable.Version | undefined
|
||||
> = this.getBoardsPackageVersion(fqbn)
|
||||
): Promise<string | undefined> {
|
||||
if (!fqbn) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const { configOptions } = await this.getData(fqbn, boardsPackageVersion);
|
||||
const { configOptions } = await this.getData(fqbn);
|
||||
return ConfigOption.decorate(fqbn, configOptions);
|
||||
}
|
||||
|
||||
async getData(
|
||||
fqbn: string | undefined,
|
||||
boardsPackageVersion: MaybePromise<
|
||||
Installable.Version | undefined
|
||||
> = this.getBoardsPackageVersion(fqbn)
|
||||
): Promise<BoardsDataStore.Data> {
|
||||
async getData(fqbn: string | undefined): Promise<BoardsDataStore.Data> {
|
||||
if (!fqbn) {
|
||||
return BoardsDataStore.Data.EMPTY;
|
||||
}
|
||||
|
||||
const version = await boardsPackageVersion;
|
||||
if (!version) {
|
||||
return BoardsDataStore.Data.EMPTY;
|
||||
}
|
||||
const key = this.getStorageKey(fqbn, version);
|
||||
const key = this.getStorageKey(fqbn);
|
||||
let data = await this.storageService.getData<
|
||||
BoardsDataStore.Data | undefined
|
||||
>(key, undefined);
|
||||
@@ -124,25 +105,16 @@ export class BoardsDataStore implements FrontendApplicationContribution {
|
||||
fqbn,
|
||||
selectedProgrammer,
|
||||
}: { fqbn: string; selectedProgrammer: Programmer },
|
||||
boardsPackageVersion: MaybePromise<
|
||||
Installable.Version | undefined
|
||||
> = this.getBoardsPackageVersion(fqbn)
|
||||
): Promise<boolean> {
|
||||
const data = deepClone(await this.getData(fqbn, boardsPackageVersion));
|
||||
const data = deepClone(await this.getData(fqbn));
|
||||
const { programmers } = data;
|
||||
if (!programmers.find((p) => Programmer.equals(selectedProgrammer, p))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const version = await boardsPackageVersion;
|
||||
if (!version) {
|
||||
return false;
|
||||
}
|
||||
|
||||
await this.setData({
|
||||
fqbn,
|
||||
data: { ...data, selectedProgrammer },
|
||||
version,
|
||||
});
|
||||
this.fireChanged();
|
||||
return true;
|
||||
@@ -153,12 +125,9 @@ export class BoardsDataStore implements FrontendApplicationContribution {
|
||||
fqbn,
|
||||
option,
|
||||
selectedValue,
|
||||
}: { fqbn: string; option: string; selectedValue: string },
|
||||
boardsPackageVersion: MaybePromise<
|
||||
Installable.Version | undefined
|
||||
> = this.getBoardsPackageVersion(fqbn)
|
||||
}: { fqbn: string; option: string; selectedValue: string }
|
||||
): Promise<boolean> {
|
||||
const data = deepClone(await this.getData(fqbn, boardsPackageVersion));
|
||||
const data = deepClone(await this.getData(fqbn));
|
||||
const { configOptions } = data;
|
||||
const configOption = configOptions.find((c) => c.option === option);
|
||||
if (!configOption) {
|
||||
@@ -176,12 +145,7 @@ export class BoardsDataStore implements FrontendApplicationContribution {
|
||||
if (!updated) {
|
||||
return false;
|
||||
}
|
||||
const version = await boardsPackageVersion;
|
||||
if (!version) {
|
||||
return false;
|
||||
}
|
||||
|
||||
await this.setData({ fqbn, data, version });
|
||||
await this.setData({ fqbn, data });
|
||||
this.fireChanged();
|
||||
return true;
|
||||
}
|
||||
@@ -189,18 +153,16 @@ export class BoardsDataStore implements FrontendApplicationContribution {
|
||||
protected async setData({
|
||||
fqbn,
|
||||
data,
|
||||
version,
|
||||
}: {
|
||||
fqbn: string;
|
||||
data: BoardsDataStore.Data;
|
||||
version: Installable.Version;
|
||||
}): Promise<void> {
|
||||
const key = this.getStorageKey(fqbn, version);
|
||||
const key = this.getStorageKey(fqbn);
|
||||
return this.storageService.setData(key, data);
|
||||
}
|
||||
|
||||
protected getStorageKey(fqbn: string, version: Installable.Version): string {
|
||||
return `.arduinoIDE-configOptions-${version}-${fqbn}`;
|
||||
protected getStorageKey(fqbn: string): string {
|
||||
return `.arduinoIDE-configOptions-${fqbn}`;
|
||||
}
|
||||
|
||||
protected async getBoardDetailsSafe(
|
||||
@@ -231,21 +193,6 @@ export class BoardsDataStore implements FrontendApplicationContribution {
|
||||
protected fireChanged(): void {
|
||||
this.onChangedEmitter.fire();
|
||||
}
|
||||
|
||||
protected async getBoardsPackageVersion(
|
||||
fqbn: string | undefined
|
||||
): Promise<Installable.Version | undefined> {
|
||||
if (!fqbn) {
|
||||
return undefined;
|
||||
}
|
||||
const boardsPackage = await this.boardsService.getContainerBoardPackage({
|
||||
fqbn,
|
||||
});
|
||||
if (!boardsPackage) {
|
||||
return undefined;
|
||||
}
|
||||
return boardsPackage.installedVersion;
|
||||
}
|
||||
}
|
||||
|
||||
export namespace BoardsDataStore {
|
||||
|
@@ -185,8 +185,8 @@ export class BoardsServiceProvider implements FrontendApplicationContribution {
|
||||
const selectedAvailableBoard = AvailableBoard.is(selectedBoard)
|
||||
? selectedBoard
|
||||
: this._availableBoards.find((availableBoard) =>
|
||||
Board.sameAs(availableBoard, selectedBoard)
|
||||
);
|
||||
Board.sameAs(availableBoard, selectedBoard)
|
||||
);
|
||||
if (
|
||||
selectedAvailableBoard &&
|
||||
selectedAvailableBoard.selected &&
|
||||
@@ -230,7 +230,8 @@ export class BoardsServiceProvider implements FrontendApplicationContribution {
|
||||
)) {
|
||||
if (
|
||||
this.latestValidBoardsConfig.selectedBoard.fqbn === board.fqbn &&
|
||||
this.latestValidBoardsConfig.selectedBoard.name === board.name
|
||||
this.latestValidBoardsConfig.selectedBoard.name === board.name &&
|
||||
this.latestValidBoardsConfig.selectedPort.protocol === board.port?.protocol
|
||||
) {
|
||||
this.boardsConfig = {
|
||||
...this.latestValidBoardsConfig,
|
||||
@@ -244,7 +245,7 @@ export class BoardsServiceProvider implements FrontendApplicationContribution {
|
||||
}
|
||||
|
||||
set boardsConfig(config: BoardsConfig.Config) {
|
||||
this.doSetBoardsConfig(config);
|
||||
this.setBoardsConfig(config);
|
||||
this.saveState().finally(() =>
|
||||
this.reconcileAvailableBoards().finally(() =>
|
||||
this.onBoardsConfigChangedEmitter.fire(this._boardsConfig)
|
||||
@@ -256,7 +257,7 @@ export class BoardsServiceProvider implements FrontendApplicationContribution {
|
||||
return this._boardsConfig;
|
||||
}
|
||||
|
||||
protected doSetBoardsConfig(config: BoardsConfig.Config): void {
|
||||
protected setBoardsConfig(config: BoardsConfig.Config): void {
|
||||
this.logger.info('Board config changed: ', JSON.stringify(config));
|
||||
this._boardsConfig = config;
|
||||
this.latestBoardsConfig = this._boardsConfig;
|
||||
@@ -370,19 +371,19 @@ export class BoardsServiceProvider implements FrontendApplicationContribution {
|
||||
const find = (needle: Board & { port: Port }, haystack: AvailableBoard[]) =>
|
||||
haystack.find(
|
||||
(board) =>
|
||||
Board.equals(needle, board) && Port.equals(needle.port, board.port)
|
||||
Board.equals(needle, board) && Port.sameAs(needle.port, board.port)
|
||||
);
|
||||
const timeoutTask =
|
||||
!!timeout && timeout > 0
|
||||
? new Promise<void>((_, reject) =>
|
||||
setTimeout(
|
||||
() => reject(new Error(`Timeout after ${timeout} ms.`)),
|
||||
timeout
|
||||
)
|
||||
setTimeout(
|
||||
() => reject(new Error(`Timeout after ${timeout} ms.`)),
|
||||
timeout
|
||||
)
|
||||
)
|
||||
: new Promise<void>(() => {
|
||||
/* never */
|
||||
});
|
||||
/* never */
|
||||
});
|
||||
const waitUntilTask = new Promise<void>((resolve) => {
|
||||
let candidate = find(what, this.availableBoards);
|
||||
if (candidate) {
|
||||
@@ -409,7 +410,7 @@ export class BoardsServiceProvider implements FrontendApplicationContribution {
|
||||
Port.sameAs(port, this.boardsConfig.selectedPort)
|
||||
)
|
||||
) {
|
||||
this.doSetBoardsConfig({
|
||||
this.setBoardsConfig({
|
||||
selectedBoard: this.boardsConfig.selectedBoard,
|
||||
selectedPort: undefined,
|
||||
});
|
||||
@@ -533,9 +534,8 @@ export class BoardsServiceProvider implements FrontendApplicationContribution {
|
||||
|
||||
protected getLastSelectedBoardOnPortKey(port: Port | string): string {
|
||||
// TODO: we lose the port's `protocol` info (`serial`, `network`, etc.) here if the `port` is a `string`.
|
||||
return `last-selected-board-on-port:${
|
||||
typeof port === 'string' ? port : Port.toString(port)
|
||||
}`;
|
||||
return `last-selected-board-on-port:${typeof port === 'string' ? port : port.address
|
||||
}`;
|
||||
}
|
||||
|
||||
protected async loadState(): Promise<void> {
|
||||
|
28
arduino-ide-extension/src/browser/components/ProgressBar.tsx
Normal file
28
arduino-ide-extension/src/browser/components/ProgressBar.tsx
Normal file
@@ -0,0 +1,28 @@
|
||||
import * as React from 'react';
|
||||
|
||||
export type ProgressBarProps = {
|
||||
percent?: number;
|
||||
showPercentage?: boolean;
|
||||
};
|
||||
|
||||
export default function ProgressBar({
|
||||
percent = 0,
|
||||
showPercentage = false,
|
||||
}: ProgressBarProps): React.ReactElement {
|
||||
const roundedPercent = Math.round(percent);
|
||||
return (
|
||||
<div className="progress-bar">
|
||||
<div className="progress-bar--outer">
|
||||
<div
|
||||
className="progress-bar--inner"
|
||||
style={{ width: `${roundedPercent}%` }}
|
||||
/>
|
||||
</div>
|
||||
{showPercentage && (
|
||||
<div className="progress-bar--percentage">
|
||||
<div className="progress-bar--percentage-text">{roundedPercent}%</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
import { inject, injectable } from 'inversify';
|
||||
import * as moment from 'moment';
|
||||
import { remote } from 'electron';
|
||||
import * as remote from '@theia/core/electron-shared/@electron/remote';
|
||||
import { isOSX, isWindows } from '@theia/core/lib/common/os';
|
||||
import { ClipboardService } from '@theia/core/lib/browser/clipboard-service';
|
||||
import { FrontendApplicationConfigProvider } from '@theia/core/lib/browser/frontend-application-config-provider';
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { inject, injectable } from 'inversify';
|
||||
import { remote } from 'electron';
|
||||
import * as remote from '@theia/core/electron-shared/@electron/remote';
|
||||
import { ArduinoMenus } from '../menu/arduino-menus';
|
||||
import {
|
||||
SketchContribution,
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { inject, injectable } from 'inversify';
|
||||
import { remote } from 'electron';
|
||||
import * as remote from '@theia/core/electron-shared/@electron/remote';
|
||||
import URI from '@theia/core/lib/common/uri';
|
||||
import { ConfirmDialog } from '@theia/core/lib/browser/dialogs';
|
||||
import { EnvVariablesServer } from '@theia/core/lib/common/env-variables';
|
||||
@@ -39,10 +39,6 @@ export class AddZipLibrary extends SketchContribution {
|
||||
...ArduinoMenus.SKETCH__UTILS_GROUP,
|
||||
'0_include',
|
||||
];
|
||||
// TODO: do we need it? calling `registerSubmenu` multiple times is noop, so it does not hurt.
|
||||
registry.registerSubmenu(includeLibMenuPath, 'Include Library', {
|
||||
order: '1',
|
||||
});
|
||||
registry.registerMenuAction([...includeLibMenuPath, '1_install'], {
|
||||
commandId: AddZipLibrary.Commands.ADD_ZIP_LIBRARY.id,
|
||||
label: nls.localize('arduino/library/addZip', 'Add .ZIP Library...'),
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { injectable } from 'inversify';
|
||||
import { remote } from 'electron';
|
||||
import * as remote from '@theia/core/electron-shared/@electron/remote';
|
||||
import * as dateFormat from 'dateformat';
|
||||
import URI from '@theia/core/lib/common/uri';
|
||||
import { ArduinoMenus } from '../menu/arduino-menus';
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { inject, injectable } from 'inversify';
|
||||
import { remote } from 'electron';
|
||||
import * as remote from '@theia/core/electron-shared/@electron/remote';
|
||||
import { MenuModelRegistry } from '@theia/core/lib/common/menu';
|
||||
import {
|
||||
DisposableCollection,
|
||||
@@ -204,10 +204,9 @@ PID: ${PID}`;
|
||||
|
||||
const packageLabel =
|
||||
packageName +
|
||||
`${
|
||||
manuallyInstalled
|
||||
? nls.localize('arduino/board/inSketchbook', ' (in Sketchbook)')
|
||||
: ''
|
||||
`${manuallyInstalled
|
||||
? nls.localize('arduino/board/inSketchbook', ' (in Sketchbook)')
|
||||
: ''
|
||||
}`;
|
||||
// Platform submenu
|
||||
const platformMenuPath = [...boardsPackagesGroup, packageId];
|
||||
@@ -255,8 +254,8 @@ PID: ${PID}`;
|
||||
protocolOrder: number,
|
||||
ports: AvailablePorts
|
||||
) => {
|
||||
const addresses = Object.keys(ports);
|
||||
if (!addresses.length) {
|
||||
const portIDs = Object.keys(ports);
|
||||
if (!portIDs.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -279,27 +278,26 @@ PID: ${PID}`;
|
||||
|
||||
// First we show addresses with recognized boards connected,
|
||||
// then all the rest.
|
||||
const sortedAddresses = Object.keys(ports);
|
||||
sortedAddresses.sort((left: string, right: string): number => {
|
||||
const sortedIDs = Object.keys(ports).sort((left: string, right: string): number => {
|
||||
const [, leftBoards] = ports[left];
|
||||
const [, rightBoards] = ports[right];
|
||||
return rightBoards.length - leftBoards.length;
|
||||
});
|
||||
|
||||
for (let i = 0; i < sortedAddresses.length; i++) {
|
||||
const address = sortedAddresses[i];
|
||||
const [port, boards] = ports[address];
|
||||
let label = `${address}`;
|
||||
for (let i = 0; i < sortedIDs.length; i++) {
|
||||
const portID = sortedIDs[i];
|
||||
const [port, boards] = ports[portID];
|
||||
let label = `${port.address}`;
|
||||
if (boards.length) {
|
||||
const boardsList = boards.map((board) => board.name).join(', ');
|
||||
label = `${label} (${boardsList})`;
|
||||
}
|
||||
const id = `arduino-select-port--${address}`;
|
||||
const id = `arduino-select-port--${portID}`;
|
||||
const command = { id };
|
||||
const handler = {
|
||||
execute: () => {
|
||||
if (
|
||||
!Port.equals(
|
||||
!Port.sameAs(
|
||||
port,
|
||||
this.boardsServiceProvider.boardsConfig.selectedPort
|
||||
)
|
||||
@@ -312,7 +310,7 @@ PID: ${PID}`;
|
||||
}
|
||||
},
|
||||
isToggled: () =>
|
||||
Port.equals(
|
||||
Port.sameAs(
|
||||
port,
|
||||
this.boardsServiceProvider.boardsConfig.selectedPort
|
||||
),
|
||||
|
@@ -3,7 +3,6 @@ import { OutputChannelManager } from '@theia/output/lib/browser/output-channel';
|
||||
import { CoreService } from '../../common/protocol';
|
||||
import { ArduinoMenus } from '../menu/arduino-menus';
|
||||
import { BoardsDataStore } from '../boards/boards-data-store';
|
||||
import { SerialConnectionManager } from '../serial/serial-connection-manager';
|
||||
import { BoardsServiceProvider } from '../boards/boards-service-provider';
|
||||
import {
|
||||
SketchContribution,
|
||||
@@ -18,8 +17,6 @@ export class BurnBootloader extends SketchContribution {
|
||||
@inject(CoreService)
|
||||
protected readonly coreService: CoreService;
|
||||
|
||||
@inject(SerialConnectionManager)
|
||||
protected readonly serialConnection: SerialConnectionManager;
|
||||
|
||||
@inject(BoardsDataStore)
|
||||
protected readonly boardsDataStore: BoardsDataStore;
|
||||
@@ -60,9 +57,15 @@ export class BurnBootloader extends SketchContribution {
|
||||
this.preferences.get('arduino.upload.verify'),
|
||||
this.preferences.get('arduino.upload.verbose'),
|
||||
]);
|
||||
|
||||
const board = {
|
||||
...boardsConfig.selectedBoard,
|
||||
name: boardsConfig.selectedBoard?.name || '',
|
||||
fqbn,
|
||||
}
|
||||
this.outputChannelManager.getChannel('Arduino').clear();
|
||||
await this.coreService.burnBootloader({
|
||||
fqbn,
|
||||
board,
|
||||
programmer,
|
||||
port,
|
||||
verify,
|
||||
@@ -85,8 +88,6 @@ export class BurnBootloader extends SketchContribution {
|
||||
errorMessage = e.toString();
|
||||
}
|
||||
this.messageService.error(errorMessage);
|
||||
} finally {
|
||||
await this.serialConnection.reconnectAfterUpload();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import { inject, injectable } from 'inversify';
|
||||
import { toArray } from '@phosphor/algorithm';
|
||||
import { remote } from 'electron';
|
||||
import * as remote from '@theia/core/electron-shared/@electron/remote';
|
||||
import { MonacoEditor } from '@theia/monaco/lib/browser/monaco-editor';
|
||||
import { EditorManager } from '@theia/editor/lib/browser/editor-manager';
|
||||
import { ApplicationShell } from '@theia/core/lib/browser/shell/application-shell';
|
||||
|
@@ -43,10 +43,10 @@ export class EditContributions extends Contribution {
|
||||
execute: () => this.run('actions.find'),
|
||||
});
|
||||
registry.registerCommand(EditContributions.Commands.FIND_NEXT, {
|
||||
execute: () => this.run('actions.findWithSelection'),
|
||||
execute: () => this.run('editor.action.nextMatchFindAction'),
|
||||
});
|
||||
registry.registerCommand(EditContributions.Commands.FIND_PREVIOUS, {
|
||||
execute: () => this.run('editor.action.nextMatchFindAction'),
|
||||
execute: () => this.run('editor.action.previousMatchFindAction'),
|
||||
});
|
||||
registry.registerCommand(EditContributions.Commands.USE_FOR_FIND, {
|
||||
execute: () => this.run('editor.action.previousSelectionMatchFindAction'),
|
||||
|
@@ -13,6 +13,8 @@ import {
|
||||
KeybindingRegistry,
|
||||
} from './contribution';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
import { IDEUpdaterCommands } from '../ide-updater/ide-updater-commands';
|
||||
import { ElectronCommands } from '@theia/core/lib/electron-browser/menu/electron-menu-contribution';
|
||||
|
||||
@injectable()
|
||||
export class Help extends Contribution {
|
||||
@@ -83,9 +85,17 @@ export class Help extends Contribution {
|
||||
Help.Commands.VISIT_ARDUINO,
|
||||
createOpenHandler('https://www.arduino.cc/')
|
||||
);
|
||||
registry.registerCommand(
|
||||
Help.Commands.PRIVACY_POLICY,
|
||||
createOpenHandler('https://www.arduino.cc/en/privacy-policy')
|
||||
);
|
||||
}
|
||||
|
||||
registerMenus(registry: MenuModelRegistry): void {
|
||||
registry.unregisterMenuAction({
|
||||
commandId: ElectronCommands.TOGGLE_DEVELOPER_TOOLS.id,
|
||||
});
|
||||
|
||||
registry.registerMenuAction(ArduinoMenus.HELP__MAIN_GROUP, {
|
||||
commandId: Help.Commands.GETTING_STARTED.id,
|
||||
order: '0',
|
||||
@@ -115,6 +125,14 @@ export class Help extends Contribution {
|
||||
commandId: Help.Commands.VISIT_ARDUINO.id,
|
||||
order: '6',
|
||||
});
|
||||
registry.registerMenuAction(ArduinoMenus.HELP__FIND_GROUP, {
|
||||
commandId: Help.Commands.PRIVACY_POLICY.id,
|
||||
order: '7',
|
||||
});
|
||||
registry.registerMenuAction(ArduinoMenus.HELP__FIND_GROUP, {
|
||||
commandId: IDEUpdaterCommands.CHECK_FOR_UPDATES.id,
|
||||
order: '8',
|
||||
});
|
||||
}
|
||||
|
||||
registerKeybindings(registry: KeybindingRegistry): void {
|
||||
@@ -162,5 +180,10 @@ export namespace Help {
|
||||
label: nls.localize('arduino/help/visit', 'Visit Arduino.cc'),
|
||||
category: 'Arduino',
|
||||
};
|
||||
export const PRIVACY_POLICY: Command = {
|
||||
id: 'arduino-privacy-policy',
|
||||
label: nls.localize('arduino/help/privacyPolicy', 'Privacy Policy'),
|
||||
category: 'Arduino',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { injectable } from 'inversify';
|
||||
import { remote } from 'electron';
|
||||
import * as remote from '@theia/core/electron-shared/@electron/remote';
|
||||
import URI from '@theia/core/lib/common/uri';
|
||||
import { ArduinoMenus } from '../menu/arduino-menus';
|
||||
import {
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { inject, injectable } from 'inversify';
|
||||
import { remote } from 'electron';
|
||||
import * as remote from '@theia/core/electron-shared/@electron/remote';
|
||||
import { MaybePromise } from '@theia/core/lib/common/types';
|
||||
import { Widget, ContextMenuRenderer } from '@theia/core/lib/browser';
|
||||
import {
|
||||
@@ -190,7 +190,7 @@ export class OpenSketch extends SketchContribution {
|
||||
],
|
||||
message: nls.localize(
|
||||
'arduino/sketch/movingMsg',
|
||||
'The file "{0}" needs to be inside a sketch folder named as "{1}".\nCreate this folder, move the file, and continue?',
|
||||
'The file "{0}" needs to be inside a sketch folder named "{1}".\nCreate this folder, move the file, and continue?',
|
||||
nameWithExt,
|
||||
name
|
||||
),
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { injectable } from 'inversify';
|
||||
import { remote } from 'electron';
|
||||
import * as remote from '@theia/core/electron-shared/@electron/remote';
|
||||
import { isOSX } from '@theia/core/lib/common/os';
|
||||
import {
|
||||
Contribution,
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { injectable } from 'inversify';
|
||||
import { remote } from 'electron';
|
||||
import { inject, injectable } from 'inversify';
|
||||
import * as remote from '@theia/core/electron-shared/@electron/remote';
|
||||
import * as dateFormat from 'dateformat';
|
||||
import { ArduinoMenus } from '../menu/arduino-menus';
|
||||
import {
|
||||
@@ -11,9 +11,22 @@ import {
|
||||
KeybindingRegistry,
|
||||
} from './contribution';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
import { ApplicationShell, NavigatableWidget, Saveable } from '@theia/core/lib/browser';
|
||||
import { EditorManager } from '@theia/editor/lib/browser';
|
||||
import { WindowService } from '@theia/core/lib/browser/window/window-service';
|
||||
|
||||
@injectable()
|
||||
export class SaveAsSketch extends SketchContribution {
|
||||
|
||||
@inject(ApplicationShell)
|
||||
protected readonly applicationShell: ApplicationShell;
|
||||
|
||||
@inject(EditorManager)
|
||||
protected readonly editorManager: EditorManager;
|
||||
|
||||
@inject(WindowService)
|
||||
protected readonly windowService: WindowService;
|
||||
|
||||
registerCommands(registry: CommandRegistry): void {
|
||||
registry.registerCommand(SaveAsSketch.Commands.SAVE_AS_SKETCH, {
|
||||
execute: (args) => this.saveAs(args),
|
||||
@@ -90,6 +103,9 @@ export class SaveAsSketch extends SketchContribution {
|
||||
const workspaceUri = await this.sketchService.copy(sketch, {
|
||||
destinationUri,
|
||||
});
|
||||
if (workspaceUri) {
|
||||
await this.saveOntoCopiedSketch(sketch.mainFileUri, sketch.uri, workspaceUri);
|
||||
}
|
||||
if (workspaceUri && openAfterMove) {
|
||||
if (wipeOriginal || (openAfterMove && execOnlyIfTemp)) {
|
||||
try {
|
||||
@@ -100,12 +116,48 @@ export class SaveAsSketch extends SketchContribution {
|
||||
/* NOOP: from time to time, it's not possible to wipe the old resource from the temp dir on Windows */
|
||||
}
|
||||
}
|
||||
this.windowService.setSafeToShutDown();
|
||||
this.workspaceService.open(new URI(workspaceUri), {
|
||||
preserveWindow: true,
|
||||
});
|
||||
}
|
||||
return !!workspaceUri;
|
||||
}
|
||||
|
||||
private async saveOntoCopiedSketch(mainFileUri: string, sketchUri: string, newSketchUri: string): Promise<void> {
|
||||
const widgets = this.applicationShell.widgets;
|
||||
const snapshots = new Map<string, object>();
|
||||
for (const widget of widgets) {
|
||||
const saveable = Saveable.getDirty(widget);
|
||||
const uri = NavigatableWidget.getUri(widget);
|
||||
const uriString = uri?.toString();
|
||||
let relativePath: string;
|
||||
if (uri && uriString!.includes(sketchUri) && saveable && saveable.createSnapshot) {
|
||||
// The main file will change its name during the copy process
|
||||
// We need to store the new name in the map
|
||||
if (mainFileUri === uriString) {
|
||||
const lastPart = new URI(newSketchUri).path.base + uri.path.ext;
|
||||
relativePath = '/' + lastPart;
|
||||
} else {
|
||||
relativePath = uri.toString().substring(sketchUri.length);
|
||||
}
|
||||
snapshots.set(relativePath, saveable.createSnapshot());
|
||||
}
|
||||
}
|
||||
await Promise.all(Array.from(snapshots.entries()).map(async ([path, snapshot]) => {
|
||||
const widgetUri = new URI(newSketchUri + path);
|
||||
try {
|
||||
const widget = await this.editorManager.getOrCreateByUri(widgetUri);
|
||||
const saveable = Saveable.get(widget);
|
||||
if (saveable && saveable.applySnapshot) {
|
||||
saveable.applySnapshot(snapshot);
|
||||
await saveable.save();
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
export namespace SaveAsSketch {
|
||||
|
@@ -45,7 +45,7 @@ export namespace UploadFirmware {
|
||||
id: 'arduino-upload-firmware-open',
|
||||
label: nls.localize(
|
||||
'arduino/firmware/updater',
|
||||
'WiFi101 / WiFiNINA Firmware Updater'
|
||||
'WiFi101 / WiFiNINA Firmware Updater'
|
||||
),
|
||||
category: 'Arduino',
|
||||
};
|
||||
|
@@ -4,7 +4,6 @@ import { BoardUserField, CoreService } from '../../common/protocol';
|
||||
import { ArduinoMenus, PlaceholderMenuNode } from '../menu/arduino-menus';
|
||||
import { ArduinoToolbar } from '../toolbar/arduino-toolbar';
|
||||
import { BoardsDataStore } from '../boards/boards-data-store';
|
||||
import { SerialConnectionManager } from '../serial/serial-connection-manager';
|
||||
import { BoardsServiceProvider } from '../boards/boards-service-provider';
|
||||
import {
|
||||
SketchContribution,
|
||||
@@ -22,9 +21,6 @@ export class UploadSketch extends SketchContribution {
|
||||
@inject(CoreService)
|
||||
protected readonly coreService: CoreService;
|
||||
|
||||
@inject(SerialConnectionManager)
|
||||
protected readonly serialConnection: SerialConnectionManager;
|
||||
|
||||
@inject(MenuModelRegistry)
|
||||
protected readonly menuRegistry: MenuModelRegistry;
|
||||
|
||||
@@ -226,6 +222,11 @@ export class UploadSketch extends SketchContribution {
|
||||
this.sourceOverride(),
|
||||
]);
|
||||
|
||||
const board = {
|
||||
...boardsConfig.selectedBoard,
|
||||
name: boardsConfig.selectedBoard?.name || '',
|
||||
fqbn,
|
||||
}
|
||||
let options: CoreService.Upload.Options | undefined = undefined;
|
||||
const sketchUri = sketch.uri;
|
||||
const optimizeForDebug = this.editorMode.compileForDebug;
|
||||
@@ -247,7 +248,7 @@ export class UploadSketch extends SketchContribution {
|
||||
const programmer = selectedProgrammer;
|
||||
options = {
|
||||
sketchUri,
|
||||
fqbn,
|
||||
board,
|
||||
optimizeForDebug,
|
||||
programmer,
|
||||
port,
|
||||
@@ -259,7 +260,7 @@ export class UploadSketch extends SketchContribution {
|
||||
} else {
|
||||
options = {
|
||||
sketchUri,
|
||||
fqbn,
|
||||
board,
|
||||
optimizeForDebug,
|
||||
port,
|
||||
verbose,
|
||||
@@ -289,8 +290,6 @@ export class UploadSketch extends SketchContribution {
|
||||
} finally {
|
||||
this.uploadInProgress = false;
|
||||
this.onDidChangeEmitter.fire();
|
||||
|
||||
setTimeout(() => this.serialConnection.reconnectAfterUpload(), 5000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -110,12 +110,17 @@ export class VerifySketch extends SketchContribution {
|
||||
),
|
||||
this.sourceOverride(),
|
||||
]);
|
||||
const board = {
|
||||
...boardsConfig.selectedBoard,
|
||||
name: boardsConfig.selectedBoard?.name || '',
|
||||
fqbn,
|
||||
}
|
||||
const verbose = this.preferences.get('arduino.compile.verbose');
|
||||
const compilerWarnings = this.preferences.get('arduino.compile.warnings');
|
||||
this.outputChannelManager.getChannel('Arduino').clear();
|
||||
await this.coreService.compile({
|
||||
sketchUri: sketch.uri,
|
||||
fqbn,
|
||||
board,
|
||||
optimizeForDebug: this.editorMode.compileForDebug,
|
||||
verbose,
|
||||
exportBinaries,
|
||||
|
@@ -15,6 +15,47 @@ export namespace ResponseResultProvider {
|
||||
export const JSON: ResponseResultProvider = (response) => response.json();
|
||||
}
|
||||
|
||||
export function Utf8ArrayToStr(array: Uint8Array): string {
|
||||
let out, i, c;
|
||||
let char2, char3;
|
||||
|
||||
out = '';
|
||||
const len = array.length;
|
||||
i = 0;
|
||||
while (i < len) {
|
||||
c = array[i++];
|
||||
switch (c >> 4) {
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
case 5:
|
||||
case 6:
|
||||
case 7:
|
||||
// 0xxxxxxx
|
||||
out += String.fromCharCode(c);
|
||||
break;
|
||||
case 12:
|
||||
case 13:
|
||||
// 110x xxxx 10xx xxxx
|
||||
char2 = array[i++];
|
||||
out += String.fromCharCode(((c & 0x1f) << 6) | (char2 & 0x3f));
|
||||
break;
|
||||
case 14:
|
||||
// 1110 xxxx 10xx xxxx 10xx xxxx
|
||||
char2 = array[i++];
|
||||
char3 = array[i++];
|
||||
out += String.fromCharCode(
|
||||
((c & 0x0f) << 12) | ((char2 & 0x3f) << 6) | ((char3 & 0x3f) << 0)
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
type ResourceType = 'f' | 'd';
|
||||
|
||||
@injectable()
|
||||
@@ -59,14 +100,29 @@ export class CreateApi {
|
||||
return result;
|
||||
}
|
||||
|
||||
async sketches(): Promise<Create.Sketch[]> {
|
||||
async sketches(limit = 50): Promise<Create.Sketch[]> {
|
||||
const url = new URL(`${this.domain()}/sketches`);
|
||||
url.searchParams.set('user_id', 'me');
|
||||
url.searchParams.set('limit', limit.toString());
|
||||
const headers = await this.headers();
|
||||
const result = await this.run<{ sketches: Create.Sketch[] }>(url, {
|
||||
method: 'GET',
|
||||
headers,
|
||||
});
|
||||
const result: { sketches: Create.Sketch[] } = { sketches: [] };
|
||||
|
||||
let partialSketches: Create.Sketch[] = [];
|
||||
let currentOffset = 0;
|
||||
do {
|
||||
url.searchParams.set('offset', currentOffset.toString());
|
||||
partialSketches = (
|
||||
await this.run<{ sketches: Create.Sketch[] }>(url, {
|
||||
method: 'GET',
|
||||
headers,
|
||||
})
|
||||
).sketches;
|
||||
if (partialSketches.length != 0) {
|
||||
result.sketches = result.sketches.concat(partialSketches);
|
||||
}
|
||||
currentOffset = currentOffset + limit;
|
||||
} while (partialSketches.length != 0);
|
||||
|
||||
result.sketches.forEach((sketch) => this.sketchCache.addSketch(sketch));
|
||||
return result.sketches;
|
||||
}
|
||||
@@ -275,9 +331,7 @@ export class CreateApi {
|
||||
|
||||
// parse the secret file
|
||||
const secrets = (
|
||||
typeof content === 'string'
|
||||
? content
|
||||
: new TextDecoder().decode(content)
|
||||
typeof content === 'string' ? content : Utf8ArrayToStr(content)
|
||||
)
|
||||
.split(/\r?\n/)
|
||||
.reduce((prev, curr) => {
|
||||
@@ -341,7 +395,7 @@ export class CreateApi {
|
||||
const headers = await this.headers();
|
||||
|
||||
let data: string =
|
||||
typeof content === 'string' ? content : new TextDecoder().decode(content);
|
||||
typeof content === 'string' ? content : Utf8ArrayToStr(content);
|
||||
data = await this.toggleSecretsInclude(posixPath, data, 'remove');
|
||||
|
||||
const payload = { data: btoa(data) };
|
||||
|
@@ -1,5 +1,6 @@
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
import * as React from 'react';
|
||||
import { Port } from '../../../common/protocol';
|
||||
import {
|
||||
ArduinoFirmwareUploader,
|
||||
FirmwareInfo,
|
||||
@@ -20,7 +21,7 @@ export const FirmwareUploaderComponent = ({
|
||||
availableBoards: AvailableBoard[];
|
||||
firmwareUploader: ArduinoFirmwareUploader;
|
||||
updatableFqbns: string[];
|
||||
flashFirmware: (firmware: FirmwareInfo, port: string) => Promise<any>;
|
||||
flashFirmware: (firmware: FirmwareInfo, port: Port) => Promise<any>;
|
||||
isOpen: any;
|
||||
}): React.ReactElement => {
|
||||
// boolean states for buttons
|
||||
@@ -81,7 +82,7 @@ export const FirmwareUploaderComponent = ({
|
||||
const installStatus =
|
||||
!!firmwareToFlash &&
|
||||
!!selectedBoard?.port &&
|
||||
(await flashFirmware(firmwareToFlash, selectedBoard?.port.address));
|
||||
(await flashFirmware(firmwareToFlash, selectedBoard?.port));
|
||||
|
||||
setInstallFeedback((installStatus && 'ok') || 'fail');
|
||||
} catch {
|
||||
@@ -201,7 +202,7 @@ export const FirmwareUploaderComponent = ({
|
||||
<i className="fa fa-info status-icon" />
|
||||
{nls.localize(
|
||||
'arduino/firmware/successfullyInstalled',
|
||||
'Firmware succesfully installed.'
|
||||
'Firmware successfully installed.'
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
@@ -15,6 +15,7 @@ import {
|
||||
} from '../../../common/protocol/arduino-firmware-uploader';
|
||||
import { FirmwareUploaderComponent } from './firmware-uploader-component';
|
||||
import { UploadFirmware } from '../../contributions/upload-firmware';
|
||||
import { Port } from '../../../common/protocol';
|
||||
|
||||
@injectable()
|
||||
export class UploadFirmwareDialogWidget extends ReactWidget {
|
||||
@@ -49,7 +50,7 @@ export class UploadFirmwareDialogWidget extends ReactWidget {
|
||||
});
|
||||
}
|
||||
|
||||
protected flashFirmware(firmware: FirmwareInfo, port: string): Promise<any> {
|
||||
protected flashFirmware(firmware: FirmwareInfo, port: Port): Promise<any> {
|
||||
this.busyCallback(true);
|
||||
return this.arduinoFirmwareUploader
|
||||
.flash(firmware, port)
|
||||
|
@@ -0,0 +1,210 @@
|
||||
import { WindowService } from '@theia/core/lib/browser/window/window-service';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
import { shell } from 'electron';
|
||||
import * as React from 'react';
|
||||
import * as ReactDOM from 'react-dom';
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import { ProgressInfo, UpdateInfo } from '../../../common/protocol/ide-updater';
|
||||
import ProgressBar from '../../components/ProgressBar';
|
||||
|
||||
export type IDEUpdaterComponentProps = {
|
||||
updateInfo: UpdateInfo;
|
||||
windowService: WindowService;
|
||||
downloadFinished?: boolean;
|
||||
downloadStarted?: boolean;
|
||||
progress?: ProgressInfo;
|
||||
error?: Error;
|
||||
onDownload: () => void;
|
||||
onClose: () => void;
|
||||
onSkipVersion: () => void;
|
||||
onCloseAndInstall: () => void;
|
||||
};
|
||||
|
||||
export const IDEUpdaterComponent = ({
|
||||
updateInfo: { version, releaseNotes },
|
||||
downloadStarted = false,
|
||||
downloadFinished = false,
|
||||
windowService,
|
||||
progress,
|
||||
error,
|
||||
onDownload,
|
||||
onClose,
|
||||
onSkipVersion,
|
||||
onCloseAndInstall,
|
||||
}: IDEUpdaterComponentProps): React.ReactElement => {
|
||||
const changelogDivRef = React.useRef() as React.MutableRefObject<
|
||||
HTMLDivElement
|
||||
>;
|
||||
React.useEffect(() => {
|
||||
if (!!releaseNotes) {
|
||||
let changelog: string;
|
||||
if (typeof releaseNotes === 'string') changelog = releaseNotes;
|
||||
else
|
||||
changelog = releaseNotes.reduce((acc, item) => {
|
||||
return item.note ? (acc += `${item.note}\n\n`) : acc;
|
||||
}, '');
|
||||
ReactDOM.render(
|
||||
<ReactMarkdown
|
||||
components={{
|
||||
a: ({ href, children, ...props }) => (
|
||||
<a onClick={() => href && shell.openExternal(href)} {...props}>
|
||||
{children}
|
||||
</a>
|
||||
),
|
||||
}}
|
||||
>
|
||||
{changelog}
|
||||
</ReactMarkdown>,
|
||||
changelogDivRef.current
|
||||
);
|
||||
}
|
||||
}, [releaseNotes]);
|
||||
const closeButton = (
|
||||
<button onClick={onClose} type="button" className="theia-button secondary">
|
||||
{nls.localize('arduino/ide-updater/notNowButton', 'Not now')}
|
||||
</button>
|
||||
);
|
||||
|
||||
const DownloadCompleted: () => React.ReactElement = () => (
|
||||
<div className="ide-updater-dialog--downloaded">
|
||||
<div>
|
||||
{nls.localize(
|
||||
'arduino/ide-updater/versionDownloaded',
|
||||
'Arduino IDE {0} has been downloaded.',
|
||||
version
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
{nls.localize(
|
||||
'arduino/ide-updater/closeToInstallNotice',
|
||||
'Close the software and install the update on your machine.'
|
||||
)}
|
||||
</div>
|
||||
<div className="buttons-container">
|
||||
{closeButton}
|
||||
<button
|
||||
onClick={onCloseAndInstall}
|
||||
type="button"
|
||||
className="theia-button close-and-install"
|
||||
>
|
||||
{nls.localize(
|
||||
'arduino/ide-updater/closeAndInstallButton',
|
||||
'Close and Install'
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const DownloadStarted: () => React.ReactElement = () => (
|
||||
<div className="ide-updater-dialog--downloading">
|
||||
<div>
|
||||
{nls.localize(
|
||||
'arduino/ide-updater/downloadingNotice',
|
||||
'Downloading the latest version of the Arduino IDE.'
|
||||
)}
|
||||
</div>
|
||||
<ProgressBar percent={progress?.percent} showPercentage />
|
||||
</div>
|
||||
);
|
||||
|
||||
const PreDownload: () => React.ReactElement = () => (
|
||||
<div className="ide-updater-dialog--pre-download">
|
||||
<div className="ide-updater-dialog--logo-container">
|
||||
<div className="ide-updater-dialog--logo"></div>
|
||||
</div>
|
||||
<div className="ide-updater-dialog--new-version-text dialogSection">
|
||||
<div className="dialogRow">
|
||||
<div className="bold">
|
||||
{nls.localize(
|
||||
'arduino/ide-updater/updateAvailable',
|
||||
'Update Available'
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="dialogRow">
|
||||
{nls.localize(
|
||||
'arduino/ide-updater/newVersionAvailable',
|
||||
'A new version of Arduino IDE ({0}) is available for download.',
|
||||
version
|
||||
)}
|
||||
</div>
|
||||
{releaseNotes && (
|
||||
<div className="dialogRow">
|
||||
<div className="changelog-container" ref={changelogDivRef} />
|
||||
</div>
|
||||
)}
|
||||
<div className="buttons-container">
|
||||
<button
|
||||
onClick={onSkipVersion}
|
||||
type="button"
|
||||
className="theia-button secondary skip-version"
|
||||
>
|
||||
{nls.localize(
|
||||
'arduino/ide-updater/skipVersionButton',
|
||||
'Skip Version'
|
||||
)}
|
||||
</button>
|
||||
<div className="push"></div>
|
||||
{closeButton}
|
||||
<button
|
||||
onClick={onDownload}
|
||||
type="button"
|
||||
className="theia-button primary"
|
||||
>
|
||||
{nls.localize('arduino/ide-updater/downloadButton', 'Download')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const onGoToDownloadClick = (
|
||||
event: React.SyntheticEvent<HTMLAnchorElement, Event>
|
||||
) => {
|
||||
const { target } = event.nativeEvent;
|
||||
if (target instanceof HTMLAnchorElement) {
|
||||
event.nativeEvent.preventDefault();
|
||||
windowService.openNewWindow(target.href, { external: true });
|
||||
onClose();
|
||||
}
|
||||
};
|
||||
|
||||
const GoToDownloadPage: () => React.ReactElement = () => (
|
||||
<div className="ide-updater-dialog--go-to-download-page">
|
||||
<div>
|
||||
{nls.localize(
|
||||
'arduino/ide-updater/goToDownloadPage',
|
||||
"An update for the Arduino IDE is available, but we're not able to download and install it automatically. Please go to the download page and download the latest version from there."
|
||||
)}
|
||||
</div>
|
||||
<div className="buttons-container">
|
||||
{closeButton}
|
||||
<a
|
||||
className="theia-button primary"
|
||||
href="https://www.arduino.cc/en/software#experimental-software"
|
||||
onClick={onGoToDownloadClick}
|
||||
>
|
||||
{nls.localize(
|
||||
'arduino/ide-updater/goToDownloadButton',
|
||||
'Go To Download'
|
||||
)}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="ide-updater-dialog--content">
|
||||
{!!error ? (
|
||||
<GoToDownloadPage />
|
||||
) : downloadFinished ? (
|
||||
<DownloadCompleted />
|
||||
) : downloadStarted ? (
|
||||
<DownloadStarted />
|
||||
) : (
|
||||
<PreDownload />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
@@ -0,0 +1,173 @@
|
||||
import * as React from 'react';
|
||||
import { inject, injectable } from 'inversify';
|
||||
import { DialogProps } from '@theia/core/lib/browser/dialogs';
|
||||
import { AbstractDialog } from '../../theia/dialogs/dialogs';
|
||||
import { Widget } from '@phosphor/widgets';
|
||||
import { Message } from '@phosphor/messaging';
|
||||
import { ReactWidget } from '@theia/core/lib/browser/widgets/react-widget';
|
||||
import { nls } from '@theia/core';
|
||||
import { IDEUpdaterComponent } from './ide-updater-component';
|
||||
|
||||
import {
|
||||
IDEUpdater,
|
||||
IDEUpdaterClient,
|
||||
ProgressInfo,
|
||||
UpdateInfo,
|
||||
} from '../../../common/protocol/ide-updater';
|
||||
import { LocalStorageService } from '@theia/core/lib/browser';
|
||||
import { SKIP_IDE_VERSION } from '../../arduino-frontend-contribution';
|
||||
import { WindowService } from '@theia/core/lib/browser/window/window-service';
|
||||
|
||||
@injectable()
|
||||
export class IDEUpdaterDialogWidget extends ReactWidget {
|
||||
protected isOpen = new Object();
|
||||
updateInfo: UpdateInfo;
|
||||
progressInfo: ProgressInfo | undefined;
|
||||
error: Error | undefined;
|
||||
downloadFinished: boolean;
|
||||
downloadStarted: boolean;
|
||||
onClose: () => void;
|
||||
|
||||
@inject(IDEUpdater)
|
||||
protected readonly updater: IDEUpdater;
|
||||
|
||||
@inject(IDEUpdaterClient)
|
||||
protected readonly updaterClient: IDEUpdaterClient;
|
||||
|
||||
@inject(LocalStorageService)
|
||||
protected readonly localStorageService: LocalStorageService;
|
||||
|
||||
@inject(WindowService)
|
||||
protected windowService: WindowService;
|
||||
|
||||
init(updateInfo: UpdateInfo, onClose: () => void): void {
|
||||
this.updateInfo = updateInfo;
|
||||
this.progressInfo = undefined;
|
||||
this.error = undefined;
|
||||
this.downloadStarted = false;
|
||||
this.downloadFinished = false;
|
||||
this.onClose = onClose;
|
||||
|
||||
this.updaterClient.onError((e) => {
|
||||
this.error = e;
|
||||
this.update();
|
||||
});
|
||||
this.updaterClient.onDownloadProgressChanged((e) => {
|
||||
this.progressInfo = e;
|
||||
this.update();
|
||||
});
|
||||
this.updaterClient.onDownloadFinished((e) => {
|
||||
this.downloadFinished = true;
|
||||
this.update();
|
||||
});
|
||||
}
|
||||
|
||||
async onSkipVersion(): Promise<void> {
|
||||
this.localStorageService.setData<string>(
|
||||
SKIP_IDE_VERSION,
|
||||
this.updateInfo.version
|
||||
);
|
||||
this.close();
|
||||
}
|
||||
|
||||
close(): void {
|
||||
super.close();
|
||||
this.onClose();
|
||||
}
|
||||
|
||||
onDispose(): void {
|
||||
if (this.downloadStarted && !this.downloadFinished)
|
||||
this.updater.stopDownload();
|
||||
}
|
||||
|
||||
async onDownload(): Promise<void> {
|
||||
this.progressInfo = undefined;
|
||||
this.downloadStarted = true;
|
||||
this.error = undefined;
|
||||
this.updater.downloadUpdate();
|
||||
this.update();
|
||||
}
|
||||
|
||||
onCloseAndInstall(): void {
|
||||
this.updater.quitAndInstall();
|
||||
}
|
||||
|
||||
protected render(): React.ReactNode {
|
||||
return !!this.updateInfo ? (
|
||||
<form>
|
||||
<IDEUpdaterComponent
|
||||
updateInfo={this.updateInfo}
|
||||
windowService={this.windowService}
|
||||
downloadStarted={this.downloadStarted}
|
||||
downloadFinished={this.downloadFinished}
|
||||
progress={this.progressInfo}
|
||||
error={this.error}
|
||||
onClose={this.close.bind(this)}
|
||||
onSkipVersion={this.onSkipVersion.bind(this)}
|
||||
onDownload={this.onDownload.bind(this)}
|
||||
onCloseAndInstall={this.onCloseAndInstall.bind(this)}
|
||||
/>
|
||||
</form>
|
||||
) : null;
|
||||
}
|
||||
}
|
||||
|
||||
@injectable()
|
||||
export class IDEUpdaterDialogProps extends DialogProps {}
|
||||
|
||||
@injectable()
|
||||
export class IDEUpdaterDialog extends AbstractDialog<UpdateInfo> {
|
||||
@inject(IDEUpdaterDialogWidget)
|
||||
protected readonly widget: IDEUpdaterDialogWidget;
|
||||
|
||||
constructor(
|
||||
@inject(IDEUpdaterDialogProps)
|
||||
protected readonly props: IDEUpdaterDialogProps
|
||||
) {
|
||||
super({
|
||||
title: nls.localize(
|
||||
'arduino/ide-updater/ideUpdaterDialog',
|
||||
'Software Update'
|
||||
),
|
||||
});
|
||||
this.contentNode.classList.add('ide-updater-dialog');
|
||||
this.acceptButton = undefined;
|
||||
}
|
||||
|
||||
get value(): UpdateInfo {
|
||||
return this.widget.updateInfo;
|
||||
}
|
||||
|
||||
protected onAfterAttach(msg: Message): void {
|
||||
if (this.widget.isAttached) {
|
||||
Widget.detach(this.widget);
|
||||
}
|
||||
Widget.attach(this.widget, this.contentNode);
|
||||
super.onAfterAttach(msg);
|
||||
this.update();
|
||||
}
|
||||
|
||||
async open(
|
||||
data: UpdateInfo | undefined = undefined
|
||||
): Promise<UpdateInfo | undefined> {
|
||||
if (data && data.version) {
|
||||
this.widget.init(data, this.close.bind(this));
|
||||
return super.open();
|
||||
}
|
||||
}
|
||||
|
||||
protected onUpdateRequest(msg: Message): void {
|
||||
super.onUpdateRequest(msg);
|
||||
this.widget.update();
|
||||
}
|
||||
|
||||
protected onActivateRequest(msg: Message): void {
|
||||
super.onActivateRequest(msg);
|
||||
this.widget.activate();
|
||||
}
|
||||
|
||||
close(): void {
|
||||
this.widget.dispose();
|
||||
super.close();
|
||||
}
|
||||
}
|
@@ -9,6 +9,7 @@ import { WindowService } from '@theia/core/lib/browser/window/window-service';
|
||||
import { FileDialogService } from '@theia/filesystem/lib/browser/file-dialog/file-dialog-service';
|
||||
import { DisposableCollection } from '@theia/core/lib/common/disposable';
|
||||
import {
|
||||
AdditionalUrls,
|
||||
CompilerWarningLiterals,
|
||||
Network,
|
||||
ProxySettings,
|
||||
@@ -35,21 +36,32 @@ export class SettingsComponent extends React.Component<
|
||||
if (
|
||||
this.state &&
|
||||
prevState &&
|
||||
JSON.stringify(this.state) !== JSON.stringify(prevState)
|
||||
JSON.stringify(SettingsComponent.State.toSettings(this.state)) !==
|
||||
JSON.stringify(SettingsComponent.State.toSettings(prevState))
|
||||
) {
|
||||
this.props.settingsService.update(this.state, true);
|
||||
this.props.settingsService.update(
|
||||
SettingsComponent.State.toSettings(this.state),
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount(): void {
|
||||
this.props.settingsService
|
||||
.settings()
|
||||
.then((settings) => this.setState(settings));
|
||||
this.toDispose.push(
|
||||
.then((settings) =>
|
||||
this.setState(SettingsComponent.State.fromSettings(settings))
|
||||
);
|
||||
this.toDispose.pushAll([
|
||||
this.props.settingsService.onDidChange((settings) =>
|
||||
this.setState(settings)
|
||||
)
|
||||
);
|
||||
this.setState((prevState) => ({
|
||||
...SettingsComponent.State.merge(prevState, settings),
|
||||
}))
|
||||
),
|
||||
this.props.settingsService.onDidReset((settings) =>
|
||||
this.setState(SettingsComponent.State.fromSettings(settings))
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
componentWillUnmount(): void {
|
||||
@@ -260,18 +272,6 @@ export class SettingsComponent extends React.Component<
|
||||
'Verify code after upload'
|
||||
)}
|
||||
</label>
|
||||
<label className="flex-line">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={this.state.checkForUpdates}
|
||||
onChange={this.checkForUpdatesDidChange}
|
||||
disabled={true}
|
||||
/>
|
||||
{nls.localize(
|
||||
'arduino/preferences/checkForUpdates',
|
||||
'Check for updates on startup'
|
||||
)}
|
||||
</label>
|
||||
<label className="flex-line">
|
||||
<input
|
||||
type="checkbox"
|
||||
@@ -302,8 +302,8 @@ export class SettingsComponent extends React.Component<
|
||||
<input
|
||||
className="theia-input stretch with-margin"
|
||||
type="text"
|
||||
value={this.state.additionalUrls.join(',')}
|
||||
onChange={this.additionalUrlsDidChange}
|
||||
value={this.state.rawAdditionalUrlsValue}
|
||||
onChange={this.rawAdditionalUrlsValueDidChange}
|
||||
/>
|
||||
<i
|
||||
className="fa fa-window-restore theia-button shrink"
|
||||
@@ -444,7 +444,9 @@ export class SettingsComponent extends React.Component<
|
||||
);
|
||||
}
|
||||
|
||||
protected noopKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
|
||||
protected noopKeyDown = (
|
||||
event: React.KeyboardEvent<HTMLInputElement>
|
||||
): void => {
|
||||
if (this.isControlKey(event)) {
|
||||
return;
|
||||
}
|
||||
@@ -454,7 +456,7 @@ export class SettingsComponent extends React.Component<
|
||||
|
||||
protected numbersOnlyKeyDown = (
|
||||
event: React.KeyboardEvent<HTMLInputElement>
|
||||
) => {
|
||||
): void => {
|
||||
if (this.isControlKey(event)) {
|
||||
return;
|
||||
}
|
||||
@@ -466,7 +468,7 @@ export class SettingsComponent extends React.Component<
|
||||
}
|
||||
};
|
||||
|
||||
protected browseSketchbookDidClick = async () => {
|
||||
protected browseSketchbookDidClick = async (): Promise<void> => {
|
||||
const uri = await this.props.fileDialogService.showOpenDialog({
|
||||
title: nls.localize(
|
||||
'arduino/preferences/newSketchbookLocation',
|
||||
@@ -483,42 +485,44 @@ export class SettingsComponent extends React.Component<
|
||||
}
|
||||
};
|
||||
|
||||
protected editAdditionalUrlDidClick = async () => {
|
||||
protected editAdditionalUrlDidClick = async (): Promise<void> => {
|
||||
const additionalUrls = await new AdditionalUrlsDialog(
|
||||
this.state.additionalUrls,
|
||||
AdditionalUrls.parse(this.state.rawAdditionalUrlsValue, ','),
|
||||
this.props.windowService
|
||||
).open();
|
||||
if (additionalUrls) {
|
||||
this.setState({ additionalUrls });
|
||||
this.setState({
|
||||
rawAdditionalUrlsValue: AdditionalUrls.stringify(additionalUrls),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
protected editorFontSizeDidChange = (
|
||||
event: React.ChangeEvent<HTMLInputElement>
|
||||
) => {
|
||||
): void => {
|
||||
const { value } = event.target;
|
||||
if (value) {
|
||||
this.setState({ editorFontSize: parseInt(value, 10) });
|
||||
}
|
||||
};
|
||||
|
||||
protected additionalUrlsDidChange = (
|
||||
protected rawAdditionalUrlsValueDidChange = (
|
||||
event: React.ChangeEvent<HTMLInputElement>
|
||||
) => {
|
||||
): void => {
|
||||
this.setState({
|
||||
additionalUrls: event.target.value.split(',').map((url) => url.trim()),
|
||||
rawAdditionalUrlsValue: event.target.value,
|
||||
});
|
||||
};
|
||||
|
||||
protected autoScaleInterfaceDidChange = (
|
||||
event: React.ChangeEvent<HTMLInputElement>
|
||||
) => {
|
||||
): void => {
|
||||
this.setState({ autoScaleInterface: event.target.checked });
|
||||
};
|
||||
|
||||
protected interfaceScaleDidChange = (
|
||||
event: React.ChangeEvent<HTMLInputElement>
|
||||
) => {
|
||||
): void => {
|
||||
const { value } = event.target;
|
||||
const percentage = parseInt(value, 10);
|
||||
if (isNaN(percentage)) {
|
||||
@@ -532,31 +536,25 @@ export class SettingsComponent extends React.Component<
|
||||
|
||||
protected verifyAfterUploadDidChange = (
|
||||
event: React.ChangeEvent<HTMLInputElement>
|
||||
) => {
|
||||
): void => {
|
||||
this.setState({ verifyAfterUpload: event.target.checked });
|
||||
};
|
||||
|
||||
protected checkForUpdatesDidChange = (
|
||||
event: React.ChangeEvent<HTMLInputElement>
|
||||
) => {
|
||||
this.setState({ checkForUpdates: event.target.checked });
|
||||
};
|
||||
|
||||
protected sketchbookShowAllFilesDidChange = (
|
||||
event: React.ChangeEvent<HTMLInputElement>
|
||||
) => {
|
||||
): void => {
|
||||
this.setState({ sketchbookShowAllFiles: event.target.checked });
|
||||
};
|
||||
|
||||
protected autoSaveDidChange = (
|
||||
event: React.ChangeEvent<HTMLInputElement>
|
||||
) => {
|
||||
): void => {
|
||||
this.setState({ autoSave: event.target.checked ? 'on' : 'off' });
|
||||
};
|
||||
|
||||
protected quickSuggestionsOtherDidChange = (
|
||||
event: React.ChangeEvent<HTMLInputElement>
|
||||
) => {
|
||||
): void => {
|
||||
// need to persist react events through lifecycle https://reactjs.org/docs/events.html#event-pooling
|
||||
const newVal = event.target.checked ? true : false;
|
||||
|
||||
@@ -570,7 +568,9 @@ export class SettingsComponent extends React.Component<
|
||||
});
|
||||
};
|
||||
|
||||
protected themeDidChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
|
||||
protected themeDidChange = (
|
||||
event: React.ChangeEvent<HTMLSelectElement>
|
||||
): void => {
|
||||
const { selectedIndex } = event.target.options;
|
||||
const theme = ThemeService.get().getThemes()[selectedIndex];
|
||||
if (theme) {
|
||||
@@ -580,14 +580,14 @@ export class SettingsComponent extends React.Component<
|
||||
|
||||
protected languageDidChange = (
|
||||
event: React.ChangeEvent<HTMLSelectElement>
|
||||
) => {
|
||||
): void => {
|
||||
const selectedLanguage = event.target.value;
|
||||
this.setState({ currentLanguage: selectedLanguage });
|
||||
};
|
||||
|
||||
protected compilerWarningsDidChange = (
|
||||
event: React.ChangeEvent<HTMLSelectElement>
|
||||
) => {
|
||||
): void => {
|
||||
const { selectedIndex } = event.target.options;
|
||||
const compilerWarnings = CompilerWarningLiterals[selectedIndex];
|
||||
if (compilerWarnings) {
|
||||
@@ -597,26 +597,28 @@ export class SettingsComponent extends React.Component<
|
||||
|
||||
protected verboseOnCompileDidChange = (
|
||||
event: React.ChangeEvent<HTMLInputElement>
|
||||
) => {
|
||||
): void => {
|
||||
this.setState({ verboseOnCompile: event.target.checked });
|
||||
};
|
||||
|
||||
protected verboseOnUploadDidChange = (
|
||||
event: React.ChangeEvent<HTMLInputElement>
|
||||
) => {
|
||||
): void => {
|
||||
this.setState({ verboseOnUpload: event.target.checked });
|
||||
};
|
||||
|
||||
protected sketchpathDidChange = (
|
||||
event: React.ChangeEvent<HTMLInputElement>
|
||||
) => {
|
||||
): void => {
|
||||
const sketchbookPath = event.target.value;
|
||||
if (sketchbookPath) {
|
||||
this.setState({ sketchbookPath });
|
||||
}
|
||||
};
|
||||
|
||||
protected noProxyDidChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
protected noProxyDidChange = (
|
||||
event: React.ChangeEvent<HTMLInputElement>
|
||||
): void => {
|
||||
if (event.target.checked) {
|
||||
this.setState({ network: 'none' });
|
||||
} else {
|
||||
@@ -626,7 +628,7 @@ export class SettingsComponent extends React.Component<
|
||||
|
||||
protected manualProxyDidChange = (
|
||||
event: React.ChangeEvent<HTMLInputElement>
|
||||
) => {
|
||||
): void => {
|
||||
if (event.target.checked) {
|
||||
this.setState({ network: Network.Default() });
|
||||
} else {
|
||||
@@ -636,7 +638,7 @@ export class SettingsComponent extends React.Component<
|
||||
|
||||
protected httpProtocolDidChange = (
|
||||
event: React.ChangeEvent<HTMLInputElement>
|
||||
) => {
|
||||
): void => {
|
||||
if (this.state.network !== 'none') {
|
||||
const network = this.cloneProxySettings;
|
||||
network.protocol = event.target.checked ? 'http' : 'socks';
|
||||
@@ -646,7 +648,7 @@ export class SettingsComponent extends React.Component<
|
||||
|
||||
protected socksProtocolDidChange = (
|
||||
event: React.ChangeEvent<HTMLInputElement>
|
||||
) => {
|
||||
): void => {
|
||||
if (this.state.network !== 'none') {
|
||||
const network = this.cloneProxySettings;
|
||||
network.protocol = event.target.checked ? 'socks' : 'http';
|
||||
@@ -656,7 +658,7 @@ export class SettingsComponent extends React.Component<
|
||||
|
||||
protected hostnameDidChange = (
|
||||
event: React.ChangeEvent<HTMLInputElement>
|
||||
) => {
|
||||
): void => {
|
||||
if (this.state.network !== 'none') {
|
||||
const network = this.cloneProxySettings;
|
||||
network.hostname = event.target.value;
|
||||
@@ -664,7 +666,9 @@ export class SettingsComponent extends React.Component<
|
||||
}
|
||||
};
|
||||
|
||||
protected portDidChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
protected portDidChange = (
|
||||
event: React.ChangeEvent<HTMLInputElement>
|
||||
): void => {
|
||||
if (this.state.network !== 'none') {
|
||||
const network = this.cloneProxySettings;
|
||||
network.port = event.target.value;
|
||||
@@ -674,7 +678,7 @@ export class SettingsComponent extends React.Component<
|
||||
|
||||
protected usernameDidChange = (
|
||||
event: React.ChangeEvent<HTMLInputElement>
|
||||
) => {
|
||||
): void => {
|
||||
if (this.state.network !== 'none') {
|
||||
const network = this.cloneProxySettings;
|
||||
network.username = event.target.value;
|
||||
@@ -684,7 +688,7 @@ export class SettingsComponent extends React.Component<
|
||||
|
||||
protected passwordDidChange = (
|
||||
event: React.ChangeEvent<HTMLInputElement>
|
||||
) => {
|
||||
): void => {
|
||||
if (this.state.network !== 'none') {
|
||||
const network = this.cloneProxySettings;
|
||||
network.password = event.target.value;
|
||||
@@ -709,5 +713,48 @@ export namespace SettingsComponent {
|
||||
readonly windowService: WindowService;
|
||||
readonly localizationProvider: AsyncLocalizationProvider;
|
||||
}
|
||||
export type State = Settings & { languages: string[] };
|
||||
export type State = Settings & {
|
||||
rawAdditionalUrlsValue: string;
|
||||
};
|
||||
export namespace State {
|
||||
export function fromSettings(settings: Settings): State {
|
||||
return {
|
||||
...settings,
|
||||
rawAdditionalUrlsValue: AdditionalUrls.stringify(
|
||||
settings.additionalUrls
|
||||
),
|
||||
};
|
||||
}
|
||||
export function toSettings(state: State): Settings {
|
||||
const parsedAdditionalUrls = AdditionalUrls.parse(
|
||||
state.rawAdditionalUrlsValue,
|
||||
','
|
||||
);
|
||||
return {
|
||||
...state,
|
||||
additionalUrls: AdditionalUrls.sameAs(
|
||||
state.additionalUrls,
|
||||
parsedAdditionalUrls
|
||||
)
|
||||
? state.additionalUrls
|
||||
: parsedAdditionalUrls,
|
||||
};
|
||||
}
|
||||
export function merge(prevState: State, settings: Settings): State {
|
||||
const prevAdditionalUrls = AdditionalUrls.parse(
|
||||
prevState.rawAdditionalUrlsValue,
|
||||
','
|
||||
);
|
||||
return {
|
||||
...settings,
|
||||
rawAdditionalUrlsValue: prevState.rawAdditionalUrlsValue,
|
||||
additionalUrls: AdditionalUrls.sameAs(
|
||||
prevAdditionalUrls,
|
||||
settings.additionalUrls
|
||||
)
|
||||
? prevAdditionalUrls
|
||||
: settings.additionalUrls,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -11,6 +11,7 @@ import { FileDialogService } from '@theia/filesystem/lib/browser/file-dialog/fil
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
import { SettingsComponent } from './settings-component';
|
||||
import { AsyncLocalizationProvider } from '@theia/core/lib/common/i18n/localization';
|
||||
import { AdditionalUrls } from '../../../common/protocol';
|
||||
|
||||
@injectable()
|
||||
export class SettingsWidget extends ReactWidget {
|
||||
@@ -96,7 +97,7 @@ export class SettingsDialog extends AbstractDialog<Promise<Settings>> {
|
||||
this.update();
|
||||
}
|
||||
|
||||
protected onUpdateRequest(msg: Message) {
|
||||
protected onUpdateRequest(msg: Message): void {
|
||||
super.onUpdateRequest(msg);
|
||||
this.widget.update();
|
||||
}
|
||||
@@ -105,7 +106,7 @@ export class SettingsDialog extends AbstractDialog<Promise<Settings>> {
|
||||
super.onActivateRequest(msg);
|
||||
|
||||
// calling settingsService.reset() in order to reload the settings from the preferenceService
|
||||
// and update the UI including changes triggerd from the command palette
|
||||
// and update the UI including changes triggered from the command palette
|
||||
this.settingsService.reset();
|
||||
|
||||
this.widget.activate();
|
||||
@@ -168,10 +169,7 @@ export class AdditionalUrlsDialog extends AbstractDialog<string[]> {
|
||||
}
|
||||
|
||||
get value(): string[] {
|
||||
return this.textArea.value
|
||||
.split('\n')
|
||||
.map((url) => url.trim())
|
||||
.filter((url) => !!url);
|
||||
return AdditionalUrls.parse(this.textArea.value, 'newline');
|
||||
}
|
||||
|
||||
protected onAfterAttach(message: Message): void {
|
||||
|
@@ -1,43 +1,42 @@
|
||||
import { injectable, inject, postConstruct } from 'inversify';
|
||||
import URI from '@theia/core/lib/common/uri';
|
||||
import { Emitter } from '@theia/core/lib/common/event';
|
||||
import { Deferred } from '@theia/core/lib/common/promise-util';
|
||||
import { Deferred, timeout } from '@theia/core/lib/common/promise-util';
|
||||
import { deepClone } from '@theia/core/lib/common/objects';
|
||||
import { FileService } from '@theia/filesystem/lib/browser/file-service';
|
||||
import { ThemeService } from '@theia/core/lib/browser/theming';
|
||||
import { MaybePromise } from '@theia/core/lib/common/types';
|
||||
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
|
||||
import { PreferenceService, PreferenceScope } from '@theia/core/lib/browser';
|
||||
import { Index } from '../../../common/types';
|
||||
import {
|
||||
AdditionalUrls,
|
||||
CompilerWarnings,
|
||||
ConfigService,
|
||||
FileSystemExt,
|
||||
Network,
|
||||
} from '../../../common/protocol';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
import { CommandService, nls } from '@theia/core/lib/common';
|
||||
import { AsyncLocalizationProvider } from '@theia/core/lib/common/i18n/localization';
|
||||
import { ElectronCommands } from '@theia/core/lib/electron-browser/menu/electron-menu-contribution';
|
||||
|
||||
const EDITOR_SETTING = 'editor';
|
||||
const FONT_SIZE_SETTING = `${EDITOR_SETTING}.fontSize`;
|
||||
const AUTO_SAVE_SETTING = `${EDITOR_SETTING}.autoSave`;
|
||||
const QUICK_SUGGESTIONS_SETTING = `${EDITOR_SETTING}.quickSuggestions`;
|
||||
const ARDUINO_SETTING = 'arduino';
|
||||
const WINDOW_SETTING = `${ARDUINO_SETTING}.window`;
|
||||
// const IDE_SETTING = `${ARDUINO_SETTING}.ide`;
|
||||
const COMPILE_SETTING = `${ARDUINO_SETTING}.compile`;
|
||||
const UPLOAD_SETTING = `${ARDUINO_SETTING}.upload`;
|
||||
const SKETCHBOOK_SETTING = `${ARDUINO_SETTING}.sketchbook`;
|
||||
const AUTO_SCALE_SETTING = `${WINDOW_SETTING}.autoScale`;
|
||||
const ZOOM_LEVEL_SETTING = `${WINDOW_SETTING}.zoomLevel`;
|
||||
// const AUTO_UPDATE_SETTING = `${IDE_SETTING}.autoUpdate`;
|
||||
const COMPILE_VERBOSE_SETTING = `${COMPILE_SETTING}.verbose`;
|
||||
const COMPILE_WARNINGS_SETTING = `${COMPILE_SETTING}.warnings`;
|
||||
const UPLOAD_VERBOSE_SETTING = `${UPLOAD_SETTING}.verbose`;
|
||||
const UPLOAD_VERIFY_SETTING = `${UPLOAD_SETTING}.verify`;
|
||||
const SHOW_ALL_FILES_SETTING = `${SKETCHBOOK_SETTING}.showAllFiles`;
|
||||
export const EDITOR_SETTING = 'editor';
|
||||
export const FONT_SIZE_SETTING = `${EDITOR_SETTING}.fontSize`;
|
||||
export const AUTO_SAVE_SETTING = `${EDITOR_SETTING}.autoSave`;
|
||||
export const QUICK_SUGGESTIONS_SETTING = `${EDITOR_SETTING}.quickSuggestions`;
|
||||
export const ARDUINO_SETTING = 'arduino';
|
||||
export const WINDOW_SETTING = `${ARDUINO_SETTING}.window`;
|
||||
export const COMPILE_SETTING = `${ARDUINO_SETTING}.compile`;
|
||||
export const UPLOAD_SETTING = `${ARDUINO_SETTING}.upload`;
|
||||
export const SKETCHBOOK_SETTING = `${ARDUINO_SETTING}.sketchbook`;
|
||||
export const AUTO_SCALE_SETTING = `${WINDOW_SETTING}.autoScale`;
|
||||
export const ZOOM_LEVEL_SETTING = `${WINDOW_SETTING}.zoomLevel`;
|
||||
export const COMPILE_VERBOSE_SETTING = `${COMPILE_SETTING}.verbose`;
|
||||
export const COMPILE_WARNINGS_SETTING = `${COMPILE_SETTING}.warnings`;
|
||||
export const UPLOAD_VERBOSE_SETTING = `${UPLOAD_SETTING}.verbose`;
|
||||
export const UPLOAD_VERIFY_SETTING = `${UPLOAD_SETTING}.verify`;
|
||||
export const SHOW_ALL_FILES_SETTING = `${SKETCHBOOK_SETTING}.showAllFiles`;
|
||||
|
||||
export interface Settings extends Index {
|
||||
export interface Settings {
|
||||
editorFontSize: number; // `editor.fontSize`
|
||||
themeId: string; // `workbench.colorTheme`
|
||||
autoSave: 'on' | 'off'; // `editor.autoSave`
|
||||
@@ -48,7 +47,6 @@ export interface Settings extends Index {
|
||||
|
||||
autoScaleInterface: boolean; // `arduino.window.autoScale`
|
||||
interfaceScale: number; // `arduino.window.zoomLevel` https://github.com/eclipse-theia/theia/issues/8751
|
||||
checkForUpdates?: boolean; // `arduino.ide.autoUpdate`
|
||||
verboseOnCompile: boolean; // `arduino.compile.verbose`
|
||||
compilerWarnings: CompilerWarnings; // `arduino.compile.warnings`
|
||||
verboseOnUpload: boolean; // `arduino.upload.verbose`
|
||||
@@ -56,7 +54,7 @@ export interface Settings extends Index {
|
||||
sketchbookShowAllFiles: boolean; // `arduino.sketchbook.showAllFiles`
|
||||
|
||||
sketchbookPath: string; // CLI
|
||||
additionalUrls: string[]; // CLI
|
||||
additionalUrls: AdditionalUrls; // CLI
|
||||
network: Network; // CLI
|
||||
}
|
||||
export namespace Settings {
|
||||
@@ -85,15 +83,19 @@ export class SettingsService {
|
||||
@inject(AsyncLocalizationProvider)
|
||||
protected readonly localizationProvider: AsyncLocalizationProvider;
|
||||
|
||||
@inject(CommandService)
|
||||
protected commandService: CommandService;
|
||||
|
||||
protected readonly onDidChangeEmitter = new Emitter<Readonly<Settings>>();
|
||||
readonly onDidChange = this.onDidChangeEmitter.event;
|
||||
protected readonly onDidResetEmitter = new Emitter<Readonly<Settings>>();
|
||||
readonly onDidReset = this.onDidResetEmitter.event;
|
||||
|
||||
protected ready = new Deferred<void>();
|
||||
protected _settings: Settings;
|
||||
|
||||
@postConstruct()
|
||||
protected async init(): Promise<void> {
|
||||
await this.appStateService.reachedState('ready'); // Hack for https://github.com/eclipse-theia/theia/issues/8993
|
||||
const settings = await this.loadSettings();
|
||||
this._settings = deepClone(settings);
|
||||
this.ready.resolve();
|
||||
@@ -110,7 +112,6 @@ export class SettingsService {
|
||||
quickSuggestions,
|
||||
autoScaleInterface,
|
||||
interfaceScale,
|
||||
// checkForUpdates,
|
||||
verboseOnCompile,
|
||||
compilerWarnings,
|
||||
verboseOnUpload,
|
||||
@@ -135,7 +136,6 @@ export class SettingsService {
|
||||
}),
|
||||
this.preferenceService.get<boolean>(AUTO_SCALE_SETTING, true),
|
||||
this.preferenceService.get<number>(ZOOM_LEVEL_SETTING, 0),
|
||||
// this.preferenceService.get<string>(AUTO_UPDATE_SETTING, true),
|
||||
this.preferenceService.get<boolean>(COMPILE_VERBOSE_SETTING, true),
|
||||
this.preferenceService.get<any>(COMPILE_WARNINGS_SETTING, 'None'),
|
||||
this.preferenceService.get<boolean>(UPLOAD_VERBOSE_SETTING, true),
|
||||
@@ -154,7 +154,6 @@ export class SettingsService {
|
||||
quickSuggestions,
|
||||
autoScaleInterface,
|
||||
interfaceScale,
|
||||
// checkForUpdates,
|
||||
verboseOnCompile,
|
||||
compilerWarnings,
|
||||
verboseOnUpload,
|
||||
@@ -174,7 +173,10 @@ export class SettingsService {
|
||||
async update(settings: Settings, fireDidChange = false): Promise<void> {
|
||||
await this.ready.promise;
|
||||
for (const key of Object.keys(settings)) {
|
||||
this._settings[key] = settings[key];
|
||||
if (key in this._settings) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(this._settings as any)[key] = (settings as any)[key];
|
||||
}
|
||||
}
|
||||
if (fireDidChange) {
|
||||
this.onDidChangeEmitter.fire(this._settings);
|
||||
@@ -183,7 +185,8 @@ export class SettingsService {
|
||||
|
||||
async reset(): Promise<void> {
|
||||
const settings = await this.loadSettings();
|
||||
return this.update(settings, true);
|
||||
await this.update(settings, false);
|
||||
this.onDidResetEmitter.fire(this._settings);
|
||||
}
|
||||
|
||||
async validate(
|
||||
@@ -224,6 +227,11 @@ export class SettingsService {
|
||||
}
|
||||
}
|
||||
|
||||
private async savePreference(name: string, value: unknown): Promise<void> {
|
||||
await this.preferenceService.set(name, value, PreferenceScope.User);
|
||||
await timeout(5);
|
||||
}
|
||||
|
||||
async save(): Promise<string | true> {
|
||||
await this.ready.promise;
|
||||
const {
|
||||
@@ -234,7 +242,6 @@ export class SettingsService {
|
||||
quickSuggestions,
|
||||
autoScaleInterface,
|
||||
interfaceScale,
|
||||
// checkForUpdates,
|
||||
verboseOnCompile,
|
||||
compilerWarnings,
|
||||
verboseOnUpload,
|
||||
@@ -253,71 +260,33 @@ export class SettingsService {
|
||||
(config as any).network = network;
|
||||
(config as any).locale = currentLanguage;
|
||||
|
||||
await Promise.all([
|
||||
this.preferenceService.set(
|
||||
'editor.fontSize',
|
||||
editorFontSize,
|
||||
PreferenceScope.User
|
||||
),
|
||||
this.preferenceService.set(
|
||||
'workbench.colorTheme',
|
||||
themeId,
|
||||
PreferenceScope.User
|
||||
),
|
||||
this.preferenceService.set(
|
||||
'editor.autoSave',
|
||||
autoSave,
|
||||
PreferenceScope.User
|
||||
),
|
||||
this.preferenceService.set(
|
||||
'editor.quickSuggestions',
|
||||
quickSuggestions,
|
||||
PreferenceScope.User
|
||||
),
|
||||
this.preferenceService.set(
|
||||
AUTO_SCALE_SETTING,
|
||||
autoScaleInterface,
|
||||
PreferenceScope.User
|
||||
),
|
||||
this.preferenceService.set(
|
||||
ZOOM_LEVEL_SETTING,
|
||||
interfaceScale,
|
||||
PreferenceScope.User
|
||||
),
|
||||
// this.preferenceService.set(AUTO_UPDATE_SETTING, checkForUpdates, PreferenceScope.User),
|
||||
this.preferenceService.set(
|
||||
COMPILE_VERBOSE_SETTING,
|
||||
verboseOnCompile,
|
||||
PreferenceScope.User
|
||||
),
|
||||
this.preferenceService.set(
|
||||
COMPILE_WARNINGS_SETTING,
|
||||
compilerWarnings,
|
||||
PreferenceScope.User
|
||||
),
|
||||
this.preferenceService.set(
|
||||
UPLOAD_VERBOSE_SETTING,
|
||||
verboseOnUpload,
|
||||
PreferenceScope.User
|
||||
),
|
||||
this.preferenceService.set(
|
||||
UPLOAD_VERIFY_SETTING,
|
||||
verifyAfterUpload,
|
||||
PreferenceScope.User
|
||||
),
|
||||
this.preferenceService.set(
|
||||
SHOW_ALL_FILES_SETTING,
|
||||
sketchbookShowAllFiles,
|
||||
PreferenceScope.User
|
||||
),
|
||||
this.configService.setConfiguration(config),
|
||||
]);
|
||||
await this.savePreference('editor.fontSize', editorFontSize);
|
||||
await this.savePreference('workbench.colorTheme', themeId);
|
||||
await this.savePreference('editor.autoSave', autoSave);
|
||||
await this.savePreference('editor.quickSuggestions', quickSuggestions);
|
||||
await this.savePreference(AUTO_SCALE_SETTING, autoScaleInterface);
|
||||
await this.savePreference(ZOOM_LEVEL_SETTING, interfaceScale);
|
||||
await this.savePreference(ZOOM_LEVEL_SETTING, interfaceScale);
|
||||
await this.savePreference(COMPILE_VERBOSE_SETTING, verboseOnCompile);
|
||||
await this.savePreference(COMPILE_WARNINGS_SETTING, compilerWarnings);
|
||||
await this.savePreference(UPLOAD_VERBOSE_SETTING, verboseOnUpload);
|
||||
await this.savePreference(UPLOAD_VERIFY_SETTING, verifyAfterUpload);
|
||||
await this.savePreference(SHOW_ALL_FILES_SETTING, sketchbookShowAllFiles);
|
||||
await this.configService.setConfiguration(config);
|
||||
this.onDidChangeEmitter.fire(this._settings);
|
||||
|
||||
// after saving all the settings, if we need to change the language we need to perform a reload
|
||||
if (currentLanguage !== nls.locale) {
|
||||
window.localStorage.setItem(nls.localeId, currentLanguage);
|
||||
window.location.reload();
|
||||
// Only reload if the language differs from the current locale. `nls.locale === undefined` signals english as well
|
||||
if (
|
||||
currentLanguage !== (await this.localizationProvider.getCurrentLanguage())
|
||||
) {
|
||||
await this.localizationProvider.setCurrentLanguage(currentLanguage);
|
||||
if (currentLanguage === 'en') {
|
||||
window.localStorage.removeItem(nls.localeId);
|
||||
} else {
|
||||
window.localStorage.setItem(nls.localeId, currentLanguage);
|
||||
}
|
||||
this.commandService.executeCommand(ElectronCommands.RELOAD.id);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@@ -0,0 +1,40 @@
|
||||
import { Emitter } from '@theia/core';
|
||||
import { injectable } from '@theia/core/shared/inversify';
|
||||
import { UpdateInfo, ProgressInfo } from 'electron-updater';
|
||||
import { IDEUpdaterClient } from '../../common/protocol/ide-updater';
|
||||
|
||||
@injectable()
|
||||
export class IDEUpdaterClientImpl implements IDEUpdaterClient {
|
||||
protected readonly onErrorEmitter = new Emitter<Error>();
|
||||
protected readonly onCheckingForUpdateEmitter = new Emitter<void>();
|
||||
protected readonly onUpdateAvailableEmitter = new Emitter<UpdateInfo>();
|
||||
protected readonly onUpdateNotAvailableEmitter = new Emitter<UpdateInfo>();
|
||||
protected readonly onDownloadProgressEmitter = new Emitter<ProgressInfo>();
|
||||
protected readonly onDownloadFinishedEmitter = new Emitter<UpdateInfo>();
|
||||
|
||||
readonly onError = this.onErrorEmitter.event;
|
||||
readonly onCheckingForUpdate = this.onCheckingForUpdateEmitter.event;
|
||||
readonly onUpdateAvailable = this.onUpdateAvailableEmitter.event;
|
||||
readonly onUpdateNotAvailable = this.onUpdateNotAvailableEmitter.event;
|
||||
readonly onDownloadProgressChanged = this.onDownloadProgressEmitter.event;
|
||||
readonly onDownloadFinished = this.onDownloadFinishedEmitter.event;
|
||||
|
||||
notifyError(message: Error): void {
|
||||
this.onErrorEmitter.fire(message);
|
||||
}
|
||||
notifyCheckingForUpdate(message: void): void {
|
||||
this.onCheckingForUpdateEmitter.fire(message);
|
||||
}
|
||||
notifyUpdateAvailable(message: UpdateInfo): void {
|
||||
this.onUpdateAvailableEmitter.fire(message);
|
||||
}
|
||||
notifyUpdateNotAvailable(message: UpdateInfo): void {
|
||||
this.onUpdateNotAvailableEmitter.fire(message);
|
||||
}
|
||||
notifyDownloadProgressChanged(message: ProgressInfo): void {
|
||||
this.onDownloadProgressEmitter.fire(message);
|
||||
}
|
||||
notifyDownloadFinished(message: UpdateInfo): void {
|
||||
this.onDownloadFinishedEmitter.fire(message);
|
||||
}
|
||||
}
|
@@ -0,0 +1,60 @@
|
||||
import {
|
||||
Command,
|
||||
CommandContribution,
|
||||
CommandRegistry,
|
||||
MessageService,
|
||||
nls,
|
||||
} from '@theia/core';
|
||||
import { injectable, inject } from 'inversify';
|
||||
import { IDEUpdater, UpdateInfo } from '../../common/protocol/ide-updater';
|
||||
import { IDEUpdaterDialog } from '../dialogs/ide-updater/ide-updater-dialog';
|
||||
|
||||
@injectable()
|
||||
export class IDEUpdaterCommands implements CommandContribution {
|
||||
constructor(
|
||||
@inject(IDEUpdater)
|
||||
private readonly updater: IDEUpdater,
|
||||
@inject(MessageService)
|
||||
protected readonly messageService: MessageService,
|
||||
@inject(IDEUpdaterDialog)
|
||||
protected readonly updaterDialog: IDEUpdaterDialog
|
||||
) {}
|
||||
|
||||
registerCommands(registry: CommandRegistry): void {
|
||||
registry.registerCommand(IDEUpdaterCommands.CHECK_FOR_UPDATES, {
|
||||
execute: this.checkForUpdates.bind(this),
|
||||
});
|
||||
}
|
||||
|
||||
async checkForUpdates(initialCheck?: boolean): Promise<UpdateInfo | void> {
|
||||
try {
|
||||
const updateInfo = await this.updater.checkForUpdates(initialCheck);
|
||||
if (!!updateInfo) {
|
||||
this.updaterDialog.open(updateInfo);
|
||||
} else {
|
||||
this.messageService.info(
|
||||
nls.localize(
|
||||
'arduino/ide-updater/noUpdatesAvailable',
|
||||
'There are no recent updates available for the Arduino IDE'
|
||||
)
|
||||
);
|
||||
}
|
||||
return updateInfo;
|
||||
} catch (e) {
|
||||
this.messageService.error(
|
||||
nls.localize(
|
||||
'arduino/ide-updater/errorCheckingForUpdates',
|
||||
'Error while checking for Arduino IDE updates.\n{0}',
|
||||
e.message
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
export namespace IDEUpdaterCommands {
|
||||
export const CHECK_FOR_UPDATES: Command = {
|
||||
id: 'arduino-ide-check-for-updates',
|
||||
category: 'Arduino',
|
||||
label: 'Check for Arduino IDE updates',
|
||||
};
|
||||
}
|
@@ -0,0 +1,127 @@
|
||||
import { Emitter, MessageService } from '@theia/core';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import { Board, Port } from '../common/protocol';
|
||||
import {
|
||||
Monitor,
|
||||
MonitorManagerProxyClient,
|
||||
MonitorManagerProxyFactory,
|
||||
} from '../common/protocol/monitor-service';
|
||||
import {
|
||||
PluggableMonitorSettings,
|
||||
MonitorSettings,
|
||||
} from '../node/monitor-settings/monitor-settings-provider';
|
||||
|
||||
@injectable()
|
||||
export class MonitorManagerProxyClientImpl
|
||||
implements MonitorManagerProxyClient
|
||||
{
|
||||
// When pluggable monitor messages are received from the backend
|
||||
// this event is triggered.
|
||||
// Ideally a frontend component is connected to this event
|
||||
// to update the UI.
|
||||
protected readonly onMessagesReceivedEmitter = new Emitter<{
|
||||
messages: string[];
|
||||
}>();
|
||||
readonly onMessagesReceived = this.onMessagesReceivedEmitter.event;
|
||||
|
||||
protected readonly onWSConnectionChangedEmitter = new Emitter<boolean>();
|
||||
readonly onWSConnectionChanged = this.onWSConnectionChangedEmitter.event;
|
||||
|
||||
// WebSocket used to handle pluggable monitor communication between
|
||||
// frontend and backend.
|
||||
private webSocket?: WebSocket;
|
||||
private wsPort?: number;
|
||||
|
||||
getWebSocketPort(): number | undefined {
|
||||
return this.wsPort;
|
||||
}
|
||||
|
||||
constructor(
|
||||
@inject(MessageService)
|
||||
protected messageService: MessageService,
|
||||
|
||||
// This is necessary to call the backend methods from the frontend
|
||||
@inject(MonitorManagerProxyFactory)
|
||||
protected server: MonitorManagerProxyFactory
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Connects a localhost WebSocket using the specified port.
|
||||
* @param addressPort port of the WebSocket
|
||||
*/
|
||||
connect(addressPort: number): void {
|
||||
if (this.webSocket) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
this.webSocket = new WebSocket(`ws://localhost:${addressPort}`);
|
||||
this.onWSConnectionChangedEmitter.fire(true);
|
||||
} catch {
|
||||
this.messageService.error('Unable to connect to websocket');
|
||||
return;
|
||||
}
|
||||
|
||||
this.webSocket.onmessage = (res) => {
|
||||
const messages = JSON.parse(res.data);
|
||||
this.onMessagesReceivedEmitter.fire({ messages });
|
||||
};
|
||||
this.wsPort = addressPort;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disconnects the WebSocket if connected.
|
||||
*/
|
||||
disconnect(): void {
|
||||
try {
|
||||
this.webSocket?.close();
|
||||
this.webSocket = undefined;
|
||||
this.onWSConnectionChangedEmitter.fire(false);
|
||||
} catch {
|
||||
this.messageService.error('Unable to close websocket');
|
||||
}
|
||||
}
|
||||
|
||||
async isWSConnected(): Promise<boolean> {
|
||||
return !!this.webSocket;
|
||||
}
|
||||
|
||||
async startMonitor(
|
||||
board: Board,
|
||||
port: Port,
|
||||
settings?: PluggableMonitorSettings
|
||||
): Promise<void> {
|
||||
return this.server().startMonitor(board, port, settings);
|
||||
}
|
||||
|
||||
getCurrentSettings(board: Board, port: Port): MonitorSettings {
|
||||
return this.server().getCurrentSettings(board, port);
|
||||
}
|
||||
|
||||
send(message: string): void {
|
||||
if (!this.webSocket) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.webSocket.send(
|
||||
JSON.stringify({
|
||||
command: Monitor.Command.SEND_MESSAGE,
|
||||
data: message,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
changeSettings(settings: MonitorSettings): void {
|
||||
if (!this.webSocket) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.webSocket.send(
|
||||
JSON.stringify({
|
||||
command: Monitor.Command.CHANGE_SETTINGS,
|
||||
// TODO: This might be wrong, verify if it works
|
||||
// SPOILER: It doesn't
|
||||
data: settings,
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
@@ -1,69 +1,78 @@
|
||||
import { injectable, inject } from 'inversify';
|
||||
import { Emitter, Event } from '@theia/core/lib/common/event';
|
||||
import { SerialConfig } from '../../common/protocol';
|
||||
import { Emitter, Event } from '@theia/core';
|
||||
import {
|
||||
FrontendApplicationContribution,
|
||||
LocalStorageService,
|
||||
} from '@theia/core/lib/browser';
|
||||
import { BoardsServiceProvider } from '../boards/boards-service-provider';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
|
||||
@injectable()
|
||||
export class SerialModel implements FrontendApplicationContribution {
|
||||
protected static STORAGE_ID = 'arduino-serial-model';
|
||||
export class MonitorModel implements FrontendApplicationContribution {
|
||||
protected static STORAGE_ID = 'arduino-monitor-model';
|
||||
|
||||
@inject(LocalStorageService)
|
||||
protected readonly localStorageService: LocalStorageService;
|
||||
|
||||
@inject(BoardsServiceProvider)
|
||||
protected readonly boardsServiceClient: BoardsServiceProvider;
|
||||
|
||||
protected readonly onChangeEmitter: Emitter<
|
||||
SerialModel.State.Change<keyof SerialModel.State>
|
||||
MonitorModel.State.Change<keyof MonitorModel.State>
|
||||
>;
|
||||
|
||||
protected _autoscroll: boolean;
|
||||
protected _timestamp: boolean;
|
||||
protected _baudRate: SerialConfig.BaudRate;
|
||||
protected _lineEnding: SerialModel.EOL;
|
||||
protected _lineEnding: MonitorModel.EOL;
|
||||
protected _interpolate: boolean;
|
||||
|
||||
constructor() {
|
||||
this._autoscroll = true;
|
||||
this._timestamp = false;
|
||||
this._baudRate = SerialConfig.BaudRate.DEFAULT;
|
||||
this._lineEnding = SerialModel.EOL.DEFAULT;
|
||||
this._interpolate = false;
|
||||
this._lineEnding = MonitorModel.EOL.DEFAULT;
|
||||
|
||||
this.onChangeEmitter = new Emitter<
|
||||
SerialModel.State.Change<keyof SerialModel.State>
|
||||
MonitorModel.State.Change<keyof MonitorModel.State>
|
||||
>();
|
||||
}
|
||||
|
||||
onStart(): void {
|
||||
this.localStorageService
|
||||
.getData<SerialModel.State>(SerialModel.STORAGE_ID)
|
||||
.then((state) => {
|
||||
if (state) {
|
||||
this.restoreState(state);
|
||||
}
|
||||
});
|
||||
.getData<MonitorModel.State>(MonitorModel.STORAGE_ID)
|
||||
.then(this.restoreState);
|
||||
}
|
||||
|
||||
get onChange(): Event<SerialModel.State.Change<keyof SerialModel.State>> {
|
||||
get onChange(): Event<MonitorModel.State.Change<keyof MonitorModel.State>> {
|
||||
return this.onChangeEmitter.event;
|
||||
}
|
||||
|
||||
protected restoreState(state: MonitorModel.State): void {
|
||||
if (!state) {
|
||||
return;
|
||||
}
|
||||
this._autoscroll = state.autoscroll;
|
||||
this._timestamp = state.timestamp;
|
||||
this._lineEnding = state.lineEnding;
|
||||
this._interpolate = state.interpolate;
|
||||
}
|
||||
|
||||
protected async storeState(): Promise<void> {
|
||||
return this.localStorageService.setData(MonitorModel.STORAGE_ID, {
|
||||
autoscroll: this._autoscroll,
|
||||
timestamp: this._timestamp,
|
||||
lineEnding: this._lineEnding,
|
||||
interpolate: this._interpolate,
|
||||
});
|
||||
}
|
||||
|
||||
get autoscroll(): boolean {
|
||||
return this._autoscroll;
|
||||
}
|
||||
|
||||
toggleAutoscroll(): void {
|
||||
this._autoscroll = !this._autoscroll;
|
||||
this.storeState();
|
||||
this.storeState().then(() =>
|
||||
this.storeState().then(() => {
|
||||
this.onChangeEmitter.fire({
|
||||
property: 'autoscroll',
|
||||
value: this._autoscroll,
|
||||
})
|
||||
);
|
||||
value: this._timestamp,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
get timestamp(): boolean {
|
||||
@@ -80,25 +89,11 @@ export class SerialModel implements FrontendApplicationContribution {
|
||||
);
|
||||
}
|
||||
|
||||
get baudRate(): SerialConfig.BaudRate {
|
||||
return this._baudRate;
|
||||
}
|
||||
|
||||
set baudRate(baudRate: SerialConfig.BaudRate) {
|
||||
this._baudRate = baudRate;
|
||||
this.storeState().then(() =>
|
||||
this.onChangeEmitter.fire({
|
||||
property: 'baudRate',
|
||||
value: this._baudRate,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
get lineEnding(): SerialModel.EOL {
|
||||
get lineEnding(): MonitorModel.EOL {
|
||||
return this._lineEnding;
|
||||
}
|
||||
|
||||
set lineEnding(lineEnding: SerialModel.EOL) {
|
||||
set lineEnding(lineEnding: MonitorModel.EOL) {
|
||||
this._lineEnding = lineEnding;
|
||||
this.storeState().then(() =>
|
||||
this.onChangeEmitter.fire({
|
||||
@@ -121,31 +116,13 @@ export class SerialModel implements FrontendApplicationContribution {
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
protected restoreState(state: SerialModel.State): void {
|
||||
this._autoscroll = state.autoscroll;
|
||||
this._timestamp = state.timestamp;
|
||||
this._baudRate = state.baudRate;
|
||||
this._lineEnding = state.lineEnding;
|
||||
this._interpolate = state.interpolate;
|
||||
}
|
||||
|
||||
protected async storeState(): Promise<void> {
|
||||
return this.localStorageService.setData(SerialModel.STORAGE_ID, {
|
||||
autoscroll: this._autoscroll,
|
||||
timestamp: this._timestamp,
|
||||
baudRate: this._baudRate,
|
||||
lineEnding: this._lineEnding,
|
||||
interpolate: this._interpolate,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export namespace SerialModel {
|
||||
// TODO: Move this to /common
|
||||
export namespace MonitorModel {
|
||||
export interface State {
|
||||
autoscroll: boolean;
|
||||
timestamp: boolean;
|
||||
baudRate: SerialConfig.BaudRate;
|
||||
lineEnding: EOL;
|
||||
interpolate: boolean;
|
||||
}
|
@@ -8,9 +8,9 @@ import {
|
||||
TabBarToolbarRegistry,
|
||||
} from '@theia/core/lib/browser/shell/tab-bar-toolbar';
|
||||
import { ArduinoToolbar } from '../../toolbar/arduino-toolbar';
|
||||
import { SerialModel } from '../serial-model';
|
||||
import { ArduinoMenus } from '../../menu/arduino-menus';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
import { MonitorModel } from '../../monitor-model';
|
||||
|
||||
export namespace SerialMonitor {
|
||||
export namespace Commands {
|
||||
@@ -48,7 +48,8 @@ export class MonitorViewContribution
|
||||
static readonly TOGGLE_SERIAL_MONITOR_TOOLBAR =
|
||||
MonitorWidget.ID + ':toggle-toolbar';
|
||||
|
||||
@inject(SerialModel) protected readonly model: SerialModel;
|
||||
@inject(MonitorModel)
|
||||
protected readonly model: MonitorModel;
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
|
@@ -9,14 +9,14 @@ import {
|
||||
Widget,
|
||||
MessageLoop,
|
||||
} from '@theia/core/lib/browser/widgets';
|
||||
import { SerialConfig } from '../../../common/protocol/serial-service';
|
||||
import { ArduinoSelect } from '../../widgets/arduino-select';
|
||||
import { SerialModel } from '../serial-model';
|
||||
import { SerialConnectionManager } from '../serial-connection-manager';
|
||||
import { SerialMonitorSendInput } from './serial-monitor-send-input';
|
||||
import { SerialMonitorOutput } from './serial-monitor-send-output';
|
||||
import { BoardsServiceProvider } from '../../boards/boards-service-provider';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
import { MonitorManagerProxyClient } from '../../../common/protocol';
|
||||
import { MonitorModel } from '../../monitor-model';
|
||||
import { MonitorSettings } from '../../../node/monitor-settings/monitor-settings-provider';
|
||||
|
||||
@injectable()
|
||||
export class MonitorWidget extends ReactWidget {
|
||||
@@ -26,15 +26,6 @@ export class MonitorWidget extends ReactWidget {
|
||||
);
|
||||
static readonly ID = 'serial-monitor';
|
||||
|
||||
@inject(SerialModel)
|
||||
protected readonly serialModel: SerialModel;
|
||||
|
||||
@inject(SerialConnectionManager)
|
||||
protected readonly serialConnection: SerialConnectionManager;
|
||||
|
||||
@inject(BoardsServiceProvider)
|
||||
protected readonly boardsServiceProvider: BoardsServiceProvider;
|
||||
|
||||
protected widgetHeight: number;
|
||||
|
||||
/**
|
||||
@@ -48,7 +39,16 @@ export class MonitorWidget extends ReactWidget {
|
||||
protected closing = false;
|
||||
protected readonly clearOutputEmitter = new Emitter<void>();
|
||||
|
||||
constructor() {
|
||||
constructor(
|
||||
@inject(MonitorModel)
|
||||
protected readonly monitorModel: MonitorModel,
|
||||
|
||||
@inject(MonitorManagerProxyClient)
|
||||
protected readonly monitorManagerProxy: MonitorManagerProxyClient,
|
||||
|
||||
@inject(BoardsServiceProvider)
|
||||
protected readonly boardsServiceProvider: BoardsServiceProvider
|
||||
) {
|
||||
super();
|
||||
this.id = MonitorWidget.ID;
|
||||
this.title.label = MonitorWidget.LABEL;
|
||||
@@ -57,17 +57,35 @@ export class MonitorWidget extends ReactWidget {
|
||||
this.scrollOptions = undefined;
|
||||
this.toDispose.push(this.clearOutputEmitter);
|
||||
this.toDispose.push(
|
||||
Disposable.create(() => this.serialConnection.closeWStoBE())
|
||||
Disposable.create(() => this.monitorManagerProxy.disconnect())
|
||||
);
|
||||
|
||||
// Start monitor right away if there is already a board/port combination selected
|
||||
const { selectedBoard, selectedPort } =
|
||||
this.boardsServiceProvider.boardsConfig;
|
||||
if (selectedBoard && selectedBoard.fqbn && selectedPort) {
|
||||
this.monitorManagerProxy.startMonitor(selectedBoard, selectedPort);
|
||||
}
|
||||
|
||||
this.toDispose.push(
|
||||
this.boardsServiceProvider.onBoardsConfigChanged(
|
||||
async ({ selectedBoard, selectedPort }) => {
|
||||
if (selectedBoard && selectedBoard.fqbn && selectedPort) {
|
||||
await this.monitorManagerProxy.startMonitor(
|
||||
selectedBoard,
|
||||
selectedPort
|
||||
);
|
||||
this.update();
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@postConstruct()
|
||||
protected init(): void {
|
||||
this.update();
|
||||
this.toDispose.push(
|
||||
this.serialConnection.onConnectionChanged(() => this.clearConsole())
|
||||
);
|
||||
this.toDispose.push(this.serialModel.onChange(() => this.update()));
|
||||
this.toDispose.push(this.monitorModel.onChange(() => this.update()));
|
||||
}
|
||||
|
||||
clearConsole(): void {
|
||||
@@ -79,11 +97,6 @@ export class MonitorWidget extends ReactWidget {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
protected onAfterAttach(msg: Message): void {
|
||||
super.onAfterAttach(msg);
|
||||
this.serialConnection.openWSToBE();
|
||||
}
|
||||
|
||||
onCloseRequest(msg: Message): void {
|
||||
this.closing = true;
|
||||
super.onCloseRequest(msg);
|
||||
@@ -119,7 +132,7 @@ export class MonitorWidget extends ReactWidget {
|
||||
};
|
||||
|
||||
protected get lineEndings(): OptionsType<
|
||||
SerialMonitorOutput.SelectOption<SerialModel.EOL>
|
||||
SerialMonitorOutput.SelectOption<MonitorModel.EOL>
|
||||
> {
|
||||
return [
|
||||
{
|
||||
@@ -144,32 +157,63 @@ export class MonitorWidget extends ReactWidget {
|
||||
];
|
||||
}
|
||||
|
||||
protected get baudRates(): OptionsType<
|
||||
SerialMonitorOutput.SelectOption<SerialConfig.BaudRate>
|
||||
> {
|
||||
const baudRates: Array<SerialConfig.BaudRate> = [
|
||||
300, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200,
|
||||
];
|
||||
return baudRates.map((baudRate) => ({
|
||||
label: baudRate + ' baud',
|
||||
value: baudRate,
|
||||
}));
|
||||
private getCurrentSettings(): MonitorSettings {
|
||||
const board = this.boardsServiceProvider.boardsConfig.selectedBoard;
|
||||
const port = this.boardsServiceProvider.boardsConfig.selectedPort;
|
||||
if (!board || !port) {
|
||||
return {};
|
||||
}
|
||||
return this.monitorManagerProxy.getCurrentSettings(board, port);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
////////////////////IMPORTANT/////////////////////
|
||||
//////////////////////////////////////////////////
|
||||
// baudRates and selectedBaudRates as of now are hardcoded
|
||||
// like this to retrieve the baudrate settings from the ones
|
||||
// received by the monitor.
|
||||
// We're doing it like since the frontend as of now doesn't
|
||||
// support a fully customizable list of options that would
|
||||
// be require to support pluggable monitors completely.
|
||||
// As soon as the frontend UI is updated to support
|
||||
// any custom settings this methods MUST be removed and
|
||||
// made generic.
|
||||
//
|
||||
// This breaks if the user tries to open a monitor that
|
||||
// doesn't support the baudrate setting.
|
||||
protected get baudRates(): string[] {
|
||||
const { pluggableMonitorSettings } = this.getCurrentSettings();
|
||||
if (!pluggableMonitorSettings || !pluggableMonitorSettings['baudrate']) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const baudRateSettings = pluggableMonitorSettings['baudrate'];
|
||||
|
||||
return baudRateSettings.values;
|
||||
}
|
||||
|
||||
protected get selectedBaudRate(): string {
|
||||
const { pluggableMonitorSettings } = this.getCurrentSettings();
|
||||
if (!pluggableMonitorSettings || !pluggableMonitorSettings['baudrate']) {
|
||||
return '';
|
||||
}
|
||||
const baudRateSettings = pluggableMonitorSettings['baudrate'];
|
||||
return baudRateSettings.selectedValue;
|
||||
}
|
||||
|
||||
protected render(): React.ReactNode {
|
||||
const { baudRates, lineEndings } = this;
|
||||
const lineEnding =
|
||||
lineEndings.find((item) => item.value === this.serialModel.lineEnding) ||
|
||||
lineEndings.find((item) => item.value === this.monitorModel.lineEnding) ||
|
||||
lineEndings[1]; // Defaults to `\n`.
|
||||
const baudRate =
|
||||
baudRates.find((item) => item.value === this.serialModel.baudRate) ||
|
||||
baudRates[4]; // Defaults to `9600`.
|
||||
const baudRate = baudRates.find((item) => item === this.selectedBaudRate);
|
||||
return (
|
||||
<div className="serial-monitor">
|
||||
<div className="head">
|
||||
<div className="send">
|
||||
<SerialMonitorSendInput
|
||||
serialConnection={this.serialConnection}
|
||||
boardsServiceProvider={this.boardsServiceProvider}
|
||||
monitorManagerProxy={this.monitorManagerProxy}
|
||||
resolveFocus={this.onFocusResolved}
|
||||
onSend={this.onSend}
|
||||
/>
|
||||
@@ -196,8 +240,8 @@ export class MonitorWidget extends ReactWidget {
|
||||
</div>
|
||||
<div className="body">
|
||||
<SerialMonitorOutput
|
||||
serialModel={this.serialModel}
|
||||
serialConnection={this.serialConnection}
|
||||
monitorModel={this.monitorModel}
|
||||
monitorManagerProxy={this.monitorManagerProxy}
|
||||
clearConsoleEvent={this.clearOutputEmitter.event}
|
||||
height={Math.floor(this.widgetHeight - 50)}
|
||||
/>
|
||||
@@ -208,18 +252,21 @@ export class MonitorWidget extends ReactWidget {
|
||||
|
||||
protected readonly onSend = (value: string) => this.doSend(value);
|
||||
protected async doSend(value: string): Promise<void> {
|
||||
this.serialConnection.send(value);
|
||||
this.monitorManagerProxy.send(value);
|
||||
}
|
||||
|
||||
protected readonly onChangeLineEnding = (
|
||||
option: SerialMonitorOutput.SelectOption<SerialModel.EOL>
|
||||
option: SerialMonitorOutput.SelectOption<MonitorModel.EOL>
|
||||
) => {
|
||||
this.serialModel.lineEnding = option.value;
|
||||
this.monitorModel.lineEnding = option.value;
|
||||
};
|
||||
|
||||
protected readonly onChangeBaudRate = (
|
||||
option: SerialMonitorOutput.SelectOption<SerialConfig.BaudRate>
|
||||
) => {
|
||||
this.serialModel.baudRate = option.value;
|
||||
protected readonly onChangeBaudRate = (value: string) => {
|
||||
const { pluggableMonitorSettings } = this.getCurrentSettings();
|
||||
if (!pluggableMonitorSettings || !pluggableMonitorSettings['baudrate'])
|
||||
return;
|
||||
const baudRateSettings = pluggableMonitorSettings['baudrate'];
|
||||
baudRateSettings.selectedValue = value;
|
||||
this.monitorManagerProxy.changeSettings(pluggableMonitorSettings);
|
||||
};
|
||||
}
|
||||
|
@@ -1,14 +1,16 @@
|
||||
import * as React from 'react';
|
||||
import { Key, KeyCode } from '@theia/core/lib/browser/keys';
|
||||
import { Board, Port } from '../../../common/protocol/boards-service';
|
||||
import { Board } from '../../../common/protocol/boards-service';
|
||||
import { isOSX } from '@theia/core/lib/common/os';
|
||||
import { DisposableCollection, nls } from '@theia/core/lib/common';
|
||||
import { SerialConnectionManager } from '../serial-connection-manager';
|
||||
import { SerialPlotter } from '../plotter/protocol';
|
||||
import { MonitorManagerProxyClient } from '../../../common/protocol';
|
||||
import { BoardsServiceProvider } from '../../boards/boards-service-provider';
|
||||
import { timeout } from '@theia/core/lib/common/promise-util';
|
||||
|
||||
export namespace SerialMonitorSendInput {
|
||||
export interface Props {
|
||||
readonly serialConnection: SerialConnectionManager;
|
||||
readonly boardsServiceProvider: BoardsServiceProvider;
|
||||
readonly monitorManagerProxy: MonitorManagerProxyClient;
|
||||
readonly onSend: (text: string) => void;
|
||||
readonly resolveFocus: (element: HTMLElement | undefined) => void;
|
||||
}
|
||||
@@ -26,28 +28,33 @@ export class SerialMonitorSendInput extends React.Component<
|
||||
|
||||
constructor(props: Readonly<SerialMonitorSendInput.Props>) {
|
||||
super(props);
|
||||
this.state = { text: '', connected: false };
|
||||
this.state = { text: '', connected: true };
|
||||
this.onChange = this.onChange.bind(this);
|
||||
this.onSend = this.onSend.bind(this);
|
||||
this.onKeyDown = this.onKeyDown.bind(this);
|
||||
}
|
||||
|
||||
componentDidMount(): void {
|
||||
this.props.serialConnection.isBESerialConnected().then((connected) => {
|
||||
this.setState({ connected });
|
||||
this.setState({ connected: true });
|
||||
|
||||
const checkWSConnection = new Promise<boolean>((resolve) => {
|
||||
this.props.monitorManagerProxy.onWSConnectionChanged((connected) => {
|
||||
this.setState({ connected });
|
||||
resolve(true);
|
||||
});
|
||||
});
|
||||
|
||||
this.toDisposeBeforeUnmount.pushAll([
|
||||
this.props.serialConnection.onRead(({ messages }) => {
|
||||
if (
|
||||
messages.command ===
|
||||
SerialPlotter.Protocol.Command.MIDDLEWARE_CONFIG_CHANGED &&
|
||||
'connected' in messages.data
|
||||
) {
|
||||
this.setState({ connected: messages.data.connected });
|
||||
const checkWSTimeout = timeout(1000).then(() => false);
|
||||
|
||||
Promise.race<boolean>([checkWSConnection, checkWSTimeout]).then(
|
||||
async (resolved) => {
|
||||
if (!resolved) {
|
||||
const connected =
|
||||
await this.props.monitorManagerProxy.isWSConnected();
|
||||
this.setState({ connected });
|
||||
}
|
||||
}),
|
||||
]);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
componentWillUnmount(): void {
|
||||
@@ -60,7 +67,7 @@ export class SerialMonitorSendInput extends React.Component<
|
||||
<input
|
||||
ref={this.setRef}
|
||||
type="text"
|
||||
className={`theia-input ${this.state.connected ? '' : 'warning'}`}
|
||||
className={`theia-input ${this.shouldShowWarning() ? 'warning' : ''}`}
|
||||
placeholder={this.placeholder}
|
||||
value={this.state.text}
|
||||
onChange={this.onChange}
|
||||
@@ -69,25 +76,32 @@ export class SerialMonitorSendInput extends React.Component<
|
||||
);
|
||||
}
|
||||
|
||||
protected shouldShowWarning(): boolean {
|
||||
const board = this.props.boardsServiceProvider.boardsConfig.selectedBoard;
|
||||
const port = this.props.boardsServiceProvider.boardsConfig.selectedPort;
|
||||
return !this.state.connected || !board || !port;
|
||||
}
|
||||
|
||||
protected get placeholder(): string {
|
||||
const serialConfig = this.props.serialConnection.getConfig();
|
||||
if (!this.state.connected || !serialConfig) {
|
||||
if (this.shouldShowWarning()) {
|
||||
return nls.localize(
|
||||
'arduino/serial/notConnected',
|
||||
'Not connected. Select a board and a port to connect automatically.'
|
||||
);
|
||||
}
|
||||
const { board, port } = serialConfig;
|
||||
|
||||
const board = this.props.boardsServiceProvider.boardsConfig.selectedBoard;
|
||||
const port = this.props.boardsServiceProvider.boardsConfig.selectedPort;
|
||||
return nls.localize(
|
||||
'arduino/serial/message',
|
||||
"Message ({0} + Enter to send message to '{1}' on '{2}'",
|
||||
"Message ({0} + Enter to send message to '{1}' on '{2}')",
|
||||
isOSX ? '⌘' : nls.localize('vscode/keybindingLabels/ctrlKey', 'Ctrl'),
|
||||
board
|
||||
? Board.toString(board, {
|
||||
useFqbn: false,
|
||||
})
|
||||
: 'unknown',
|
||||
port ? Port.toString(port) : 'unknown'
|
||||
port ? port.address : 'unknown'
|
||||
);
|
||||
}
|
||||
|
||||
|
@@ -2,10 +2,10 @@ import * as React from 'react';
|
||||
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 { SerialModel } from '../serial-model';
|
||||
import { SerialConnectionManager } from '../serial-connection-manager';
|
||||
import dateFormat = require('dateformat');
|
||||
import { messagesToLines, truncateLines } from './monitor-utils';
|
||||
import { MonitorManagerProxyClient } from '../../../common/protocol';
|
||||
import { MonitorModel } from '../../monitor-model';
|
||||
|
||||
export type Line = { message: string; timestamp?: Date; lineLen: number };
|
||||
|
||||
@@ -24,7 +24,7 @@ export class SerialMonitorOutput extends React.Component<
|
||||
this.listRef = React.createRef();
|
||||
this.state = {
|
||||
lines: [],
|
||||
timestamp: this.props.serialModel.timestamp,
|
||||
timestamp: this.props.monitorModel.timestamp,
|
||||
charCount: 0,
|
||||
};
|
||||
}
|
||||
@@ -43,6 +43,7 @@ export class SerialMonitorOutput extends React.Component<
|
||||
itemCount={this.state.lines.length}
|
||||
itemSize={18}
|
||||
width={'100%'}
|
||||
style={{ whiteSpace: 'nowrap' }}
|
||||
ref={this.listRef}
|
||||
>
|
||||
{Row}
|
||||
@@ -57,14 +58,13 @@ export class SerialMonitorOutput extends React.Component<
|
||||
componentDidMount(): void {
|
||||
this.scrollToBottom();
|
||||
this.toDisposeBeforeUnmount.pushAll([
|
||||
this.props.serialConnection.onRead(({ messages }) => {
|
||||
this.props.monitorManagerProxy.onMessagesReceived(({ messages }) => {
|
||||
const [newLines, totalCharCount] = messagesToLines(
|
||||
messages,
|
||||
this.state.lines,
|
||||
this.state.charCount
|
||||
);
|
||||
const [lines, charCount] = truncateLines(newLines, totalCharCount);
|
||||
|
||||
this.setState({
|
||||
lines,
|
||||
charCount,
|
||||
@@ -74,9 +74,9 @@ export class SerialMonitorOutput extends React.Component<
|
||||
this.props.clearConsoleEvent(() =>
|
||||
this.setState({ lines: [], charCount: 0 })
|
||||
),
|
||||
this.props.serialModel.onChange(({ property }) => {
|
||||
this.props.monitorModel.onChange(({ property }) => {
|
||||
if (property === 'timestamp') {
|
||||
const { timestamp } = this.props.serialModel;
|
||||
const { timestamp } = this.props.monitorModel;
|
||||
this.setState({ timestamp });
|
||||
}
|
||||
if (property === 'autoscroll') {
|
||||
@@ -92,7 +92,7 @@ export class SerialMonitorOutput extends React.Component<
|
||||
}
|
||||
|
||||
scrollToBottom = ((): void => {
|
||||
if (this.listRef.current && this.props.serialModel.autoscroll) {
|
||||
if (this.listRef.current && this.props.monitorModel.autoscroll) {
|
||||
this.listRef.current.scrollToItem(this.state.lines.length, 'end');
|
||||
}
|
||||
}).bind(this);
|
||||
@@ -114,8 +114,10 @@ const _Row = ({
|
||||
return (
|
||||
(data.lines[index].lineLen && (
|
||||
<div style={style}>
|
||||
{timestamp}
|
||||
{data.lines[index].message}
|
||||
<pre>
|
||||
{timestamp}
|
||||
{data.lines[index].message}
|
||||
</pre>
|
||||
</div>
|
||||
)) ||
|
||||
null
|
||||
@@ -125,8 +127,8 @@ const Row = React.memo(_Row, areEqual);
|
||||
|
||||
export namespace SerialMonitorOutput {
|
||||
export interface Props {
|
||||
readonly serialModel: SerialModel;
|
||||
readonly serialConnection: SerialConnectionManager;
|
||||
readonly monitorModel: MonitorModel;
|
||||
readonly monitorManagerProxy: MonitorManagerProxyClient;
|
||||
readonly clearConsoleEvent: Event<void>;
|
||||
readonly height: number;
|
||||
}
|
||||
|
@@ -6,15 +6,14 @@ import {
|
||||
MaybePromise,
|
||||
MenuModelRegistry,
|
||||
} from '@theia/core';
|
||||
import { SerialModel } from '../serial-model';
|
||||
import { ArduinoMenus } from '../../menu/arduino-menus';
|
||||
import { Contribution } from '../../contributions/contribution';
|
||||
import { Endpoint, FrontendApplication } from '@theia/core/lib/browser';
|
||||
import { ipcRenderer } from '@theia/core/shared/electron';
|
||||
import { SerialConfig } from '../../../common/protocol';
|
||||
import { SerialConnectionManager } from '../serial-connection-manager';
|
||||
import { ipcRenderer } from '@theia/electron/shared/electron';
|
||||
import { MonitorManagerProxyClient } from '../../../common/protocol';
|
||||
import { SerialPlotter } from './protocol';
|
||||
import { BoardsServiceProvider } from '../../boards/boards-service-provider';
|
||||
import { MonitorModel } from '../../monitor-model';
|
||||
const queryString = require('query-string');
|
||||
|
||||
export namespace SerialPlotterContribution {
|
||||
@@ -33,14 +32,14 @@ export class PlotterFrontendContribution extends Contribution {
|
||||
protected url: string;
|
||||
protected wsPort: number;
|
||||
|
||||
@inject(SerialModel)
|
||||
protected readonly model: SerialModel;
|
||||
@inject(MonitorModel)
|
||||
protected readonly model: MonitorModel;
|
||||
|
||||
@inject(ThemeService)
|
||||
protected readonly themeService: ThemeService;
|
||||
|
||||
@inject(SerialConnectionManager)
|
||||
protected readonly serialConnection: SerialConnectionManager;
|
||||
@inject(MonitorManagerProxyClient)
|
||||
protected readonly monitorManagerProxy: MonitorManagerProxyClient;
|
||||
|
||||
@inject(BoardsServiceProvider)
|
||||
protected readonly boardsServiceProvider: BoardsServiceProvider;
|
||||
@@ -75,7 +74,7 @@ export class PlotterFrontendContribution extends Contribution {
|
||||
this.window.focus();
|
||||
return;
|
||||
}
|
||||
const wsPort = this.serialConnection.getWsPort();
|
||||
const wsPort = this.monitorManagerProxy.getWebSocketPort();
|
||||
if (wsPort) {
|
||||
this.open(wsPort);
|
||||
} else {
|
||||
@@ -84,14 +83,28 @@ export class PlotterFrontendContribution extends Contribution {
|
||||
}
|
||||
|
||||
protected async open(wsPort: number): Promise<void> {
|
||||
const board = this.boardsServiceProvider.boardsConfig.selectedBoard;
|
||||
const port = this.boardsServiceProvider.boardsConfig.selectedPort;
|
||||
let baudrates: number[] = [];
|
||||
let currentBaudrate = -1;
|
||||
if (board && port) {
|
||||
const { pluggableMonitorSettings } =
|
||||
this.monitorManagerProxy.getCurrentSettings(board, port);
|
||||
if (pluggableMonitorSettings && 'baudrate' in pluggableMonitorSettings) {
|
||||
// Convert from string to numbers
|
||||
baudrates = pluggableMonitorSettings['baudrate'].values.map((b) => +b);
|
||||
currentBaudrate = +pluggableMonitorSettings['baudrate'].selectedValue;
|
||||
}
|
||||
}
|
||||
|
||||
const initConfig: Partial<SerialPlotter.Config> = {
|
||||
baudrates: SerialConfig.BaudRates.map((b) => b),
|
||||
currentBaudrate: this.model.baudRate,
|
||||
baudrates,
|
||||
currentBaudrate,
|
||||
currentLineEnding: this.model.lineEnding,
|
||||
darkTheme: this.themeService.getCurrentTheme().type === 'dark',
|
||||
wsPort,
|
||||
interpolate: this.model.interpolate,
|
||||
connected: await this.serialConnection.isBESerialConnected(),
|
||||
connected: await this.monitorManagerProxy.isWSConnected(),
|
||||
serialPort: this.boardsServiceProvider.boardsConfig.selectedPort?.address,
|
||||
};
|
||||
const urlWithParams = queryString.stringifyUrl(
|
||||
|
@@ -1,361 +0,0 @@
|
||||
import { injectable, inject } from 'inversify';
|
||||
import { Emitter, Event } from '@theia/core/lib/common/event';
|
||||
import { MessageService } from '@theia/core/lib/common/message-service';
|
||||
import {
|
||||
SerialService,
|
||||
SerialConfig,
|
||||
SerialError,
|
||||
Status,
|
||||
SerialServiceClient,
|
||||
} from '../../common/protocol/serial-service';
|
||||
import { BoardsServiceProvider } from '../boards/boards-service-provider';
|
||||
import {
|
||||
Port,
|
||||
Board,
|
||||
BoardsService,
|
||||
} from '../../common/protocol/boards-service';
|
||||
import { BoardsConfig } from '../boards/boards-config';
|
||||
import { SerialModel } from './serial-model';
|
||||
import { ThemeService } from '@theia/core/lib/browser/theming';
|
||||
import { CoreService } from '../../common/protocol';
|
||||
import { nls } from '@theia/core/lib/common/nls';
|
||||
|
||||
@injectable()
|
||||
export class SerialConnectionManager {
|
||||
protected config: Partial<SerialConfig> = {
|
||||
board: undefined,
|
||||
port: undefined,
|
||||
baudRate: undefined,
|
||||
};
|
||||
|
||||
protected readonly onConnectionChangedEmitter = new Emitter<boolean>();
|
||||
|
||||
/**
|
||||
* This emitter forwards all read events **if** the connection is established.
|
||||
*/
|
||||
protected readonly onReadEmitter = new Emitter<{ messages: string[] }>();
|
||||
|
||||
/**
|
||||
* Array for storing previous serial errors received from the server, and based on the number of elements in this array,
|
||||
* we adjust the reconnection delay.
|
||||
* Super naive way: we wait `array.length * 1000` ms. Once we hit 10 errors, we do not try to reconnect and clean the array.
|
||||
*/
|
||||
protected serialErrors: SerialError[] = [];
|
||||
protected reconnectTimeout?: number;
|
||||
|
||||
/**
|
||||
* When the websocket server is up on the backend, we save the port here, so that the client knows how to connect to it
|
||||
* */
|
||||
protected wsPort?: number;
|
||||
protected webSocket?: WebSocket;
|
||||
|
||||
constructor(
|
||||
@inject(SerialModel) protected readonly serialModel: SerialModel,
|
||||
@inject(SerialService) protected readonly serialService: SerialService,
|
||||
@inject(SerialServiceClient)
|
||||
protected readonly serialServiceClient: SerialServiceClient,
|
||||
@inject(BoardsService) protected readonly boardsService: BoardsService,
|
||||
@inject(BoardsServiceProvider)
|
||||
protected readonly boardsServiceProvider: BoardsServiceProvider,
|
||||
@inject(MessageService) protected messageService: MessageService,
|
||||
@inject(ThemeService) protected readonly themeService: ThemeService,
|
||||
@inject(CoreService) protected readonly core: CoreService,
|
||||
@inject(BoardsServiceProvider)
|
||||
protected readonly boardsServiceClientImpl: BoardsServiceProvider
|
||||
) {
|
||||
this.serialServiceClient.onWebSocketChanged(
|
||||
this.handleWebSocketChanged.bind(this)
|
||||
);
|
||||
this.serialServiceClient.onBaudRateChanged((baudRate) => {
|
||||
if (this.serialModel.baudRate !== baudRate) {
|
||||
this.serialModel.baudRate = baudRate;
|
||||
}
|
||||
});
|
||||
this.serialServiceClient.onLineEndingChanged((lineending) => {
|
||||
if (this.serialModel.lineEnding !== lineending) {
|
||||
this.serialModel.lineEnding = lineending;
|
||||
}
|
||||
});
|
||||
this.serialServiceClient.onInterpolateChanged((interpolate) => {
|
||||
if (this.serialModel.interpolate !== interpolate) {
|
||||
this.serialModel.interpolate = interpolate;
|
||||
}
|
||||
});
|
||||
|
||||
this.serialServiceClient.onError(this.handleError.bind(this));
|
||||
this.boardsServiceProvider.onBoardsConfigChanged(
|
||||
this.handleBoardConfigChange.bind(this)
|
||||
);
|
||||
|
||||
// Handles the `baudRate` changes by reconnecting if required.
|
||||
this.serialModel.onChange(async ({ property }) => {
|
||||
if (
|
||||
property === 'baudRate' &&
|
||||
(await this.serialService.isSerialPortOpen())
|
||||
) {
|
||||
const { boardsConfig } = this.boardsServiceProvider;
|
||||
this.handleBoardConfigChange(boardsConfig);
|
||||
}
|
||||
|
||||
// update the current values in the backend and propagate to websocket clients
|
||||
this.serialService.updateWsConfigParam({
|
||||
...(property === 'lineEnding' && {
|
||||
currentLineEnding: this.serialModel.lineEnding,
|
||||
}),
|
||||
...(property === 'interpolate' && {
|
||||
interpolate: this.serialModel.interpolate,
|
||||
}),
|
||||
});
|
||||
});
|
||||
|
||||
this.themeService.onDidColorThemeChange((theme) => {
|
||||
this.serialService.updateWsConfigParam({
|
||||
darkTheme: theme.newTheme.type === 'dark',
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Updated the config in the BE passing only the properties that has changed.
|
||||
* BE will create a new connection if needed.
|
||||
*
|
||||
* @param newConfig the porperties of the config that has changed
|
||||
*/
|
||||
async setConfig(newConfig: Partial<SerialConfig>): Promise<void> {
|
||||
let configHasChanged = false;
|
||||
Object.keys(this.config).forEach((key: keyof SerialConfig) => {
|
||||
if (newConfig[key] !== this.config[key]) {
|
||||
configHasChanged = true;
|
||||
this.config = { ...this.config, [key]: newConfig[key] };
|
||||
}
|
||||
});
|
||||
|
||||
if (configHasChanged) {
|
||||
this.serialService.updateWsConfigParam({
|
||||
currentBaudrate: this.config.baudRate,
|
||||
serialPort: this.config.port?.address,
|
||||
});
|
||||
|
||||
if (isSerialConfig(this.config)) {
|
||||
this.serialService.setSerialConfig(this.config);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getConfig(): Partial<SerialConfig> {
|
||||
return this.config;
|
||||
}
|
||||
|
||||
getWsPort(): number | undefined {
|
||||
return this.wsPort;
|
||||
}
|
||||
|
||||
protected handleWebSocketChanged(wsPort: number): void {
|
||||
this.wsPort = wsPort;
|
||||
}
|
||||
|
||||
get serialConfig(): SerialConfig | undefined {
|
||||
return isSerialConfig(this.config)
|
||||
? (this.config as SerialConfig)
|
||||
: undefined;
|
||||
}
|
||||
|
||||
async isBESerialConnected(): Promise<boolean> {
|
||||
return await this.serialService.isSerialPortOpen();
|
||||
}
|
||||
|
||||
openWSToBE(): void {
|
||||
if (!isSerialConfig(this.config)) {
|
||||
this.messageService.error(
|
||||
`Please select a board and a port to open the serial connection.`
|
||||
);
|
||||
}
|
||||
|
||||
if (!this.webSocket && this.wsPort) {
|
||||
try {
|
||||
this.webSocket = new WebSocket(`ws://localhost:${this.wsPort}`);
|
||||
this.webSocket.onmessage = (res) => {
|
||||
const messages = JSON.parse(res.data);
|
||||
this.onReadEmitter.fire({ messages });
|
||||
};
|
||||
} catch {
|
||||
this.messageService.error(`Unable to connect to websocket`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
closeWStoBE(): void {
|
||||
if (this.webSocket) {
|
||||
try {
|
||||
this.webSocket.close();
|
||||
this.webSocket = undefined;
|
||||
} catch {
|
||||
this.messageService.error(`Unable to close websocket`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles error on the SerialServiceClient and try to reconnect, eventually
|
||||
*/
|
||||
async handleError(error: SerialError): Promise<void> {
|
||||
if (!(await this.serialService.isSerialPortOpen())) return;
|
||||
const { code, config } = error;
|
||||
const { board, port } = config;
|
||||
const options = { timeout: 3000 };
|
||||
switch (code) {
|
||||
case SerialError.ErrorCodes.CLIENT_CANCEL: {
|
||||
console.debug(
|
||||
`Serial connection was canceled by client: ${Serial.Config.toString(
|
||||
this.config
|
||||
)}.`
|
||||
);
|
||||
break;
|
||||
}
|
||||
case SerialError.ErrorCodes.DEVICE_BUSY: {
|
||||
this.messageService.warn(
|
||||
nls.localize(
|
||||
'arduino/serial/connectionBusy',
|
||||
'Connection failed. Serial port is busy: {0}',
|
||||
Port.toString(port)
|
||||
),
|
||||
options
|
||||
);
|
||||
this.serialErrors.push(error);
|
||||
break;
|
||||
}
|
||||
case SerialError.ErrorCodes.DEVICE_NOT_CONFIGURED: {
|
||||
this.messageService.info(
|
||||
nls.localize(
|
||||
'arduino/serial/disconnected',
|
||||
'Disconnected {0} from {1}.',
|
||||
Board.toString(board, {
|
||||
useFqbn: false,
|
||||
}),
|
||||
Port.toString(port)
|
||||
),
|
||||
options
|
||||
);
|
||||
break;
|
||||
}
|
||||
case undefined: {
|
||||
this.messageService.error(
|
||||
nls.localize(
|
||||
'arduino/serial/unexpectedError',
|
||||
'Unexpected error. Reconnecting {0} on port {1}.',
|
||||
Board.toString(board),
|
||||
Port.toString(port)
|
||||
),
|
||||
options
|
||||
);
|
||||
console.error(JSON.stringify(error));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ((await this.serialService.clientsAttached()) > 0) {
|
||||
if (this.serialErrors.length >= 10) {
|
||||
this.messageService.warn(
|
||||
nls.localize(
|
||||
'arduino/serial/failedReconnect',
|
||||
'Failed to reconnect {0} to serial port after 10 consecutive attempts. The {1} serial port is busy.',
|
||||
Board.toString(board, {
|
||||
useFqbn: false,
|
||||
}),
|
||||
Port.toString(port)
|
||||
)
|
||||
);
|
||||
this.serialErrors.length = 0;
|
||||
} else {
|
||||
const attempts = this.serialErrors.length || 1;
|
||||
if (this.reconnectTimeout !== undefined) {
|
||||
// Clear the previous timer.
|
||||
window.clearTimeout(this.reconnectTimeout);
|
||||
}
|
||||
const timeout = attempts * 1000;
|
||||
this.messageService.warn(
|
||||
nls.localize(
|
||||
'arduino/serial/reconnect',
|
||||
'Reconnecting {0} to {1} in {2} seconds...',
|
||||
Board.toString(board, {
|
||||
useFqbn: false,
|
||||
}),
|
||||
Port.toString(port),
|
||||
attempts.toString()
|
||||
)
|
||||
);
|
||||
this.reconnectTimeout = window.setTimeout(
|
||||
() => this.reconnectAfterUpload(),
|
||||
timeout
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async reconnectAfterUpload(): Promise<void> {
|
||||
try {
|
||||
if (isSerialConfig(this.config)) {
|
||||
await this.boardsServiceClientImpl.waitUntilAvailable(
|
||||
Object.assign(this.config.board, { port: this.config.port }),
|
||||
10_000
|
||||
);
|
||||
this.serialService.connectSerialIfRequired();
|
||||
}
|
||||
} catch (waitError) {
|
||||
this.messageService.error(
|
||||
nls.localize(
|
||||
'arduino/sketch/couldNotConnectToSerial',
|
||||
'Could not reconnect to serial port. {0}',
|
||||
waitError.toString()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the data to the connected serial port.
|
||||
* The desired EOL is appended to `data`, you do not have to add it.
|
||||
* It is a NOOP if connected.
|
||||
*/
|
||||
async send(data: string): Promise<Status> {
|
||||
if (!(await this.serialService.isSerialPortOpen())) {
|
||||
return Status.NOT_CONNECTED;
|
||||
}
|
||||
return new Promise<Status>((resolve) => {
|
||||
this.serialService
|
||||
.sendMessageToSerial(data + this.serialModel.lineEnding)
|
||||
.then(() => resolve(Status.OK));
|
||||
});
|
||||
}
|
||||
|
||||
get onConnectionChanged(): Event<boolean> {
|
||||
return this.onConnectionChangedEmitter.event;
|
||||
}
|
||||
|
||||
get onRead(): Event<{ messages: any }> {
|
||||
return this.onReadEmitter.event;
|
||||
}
|
||||
|
||||
protected async handleBoardConfigChange(
|
||||
boardsConfig: BoardsConfig.Config
|
||||
): Promise<void> {
|
||||
const { selectedBoard: board, selectedPort: port } = boardsConfig;
|
||||
const { baudRate } = this.serialModel;
|
||||
const newConfig: Partial<SerialConfig> = { board, port, baudRate };
|
||||
this.setConfig(newConfig);
|
||||
}
|
||||
}
|
||||
|
||||
export namespace Serial {
|
||||
export namespace Config {
|
||||
export function toString(config: Partial<SerialConfig>): string {
|
||||
if (!isSerialConfig(config)) return '';
|
||||
const { board, port } = config;
|
||||
return `${Board.toString(board)} ${Port.toString(port)}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isSerialConfig(config: Partial<SerialConfig>): config is SerialConfig {
|
||||
return !!config.board && !!config.baudRate && !!config.port;
|
||||
}
|
@@ -1,48 +0,0 @@
|
||||
import { injectable } from 'inversify';
|
||||
import { Emitter } from '@theia/core/lib/common/event';
|
||||
import {
|
||||
SerialServiceClient,
|
||||
SerialError,
|
||||
SerialConfig,
|
||||
} from '../../common/protocol/serial-service';
|
||||
import { SerialModel } from './serial-model';
|
||||
|
||||
@injectable()
|
||||
export class SerialServiceClientImpl implements SerialServiceClient {
|
||||
protected readonly onErrorEmitter = new Emitter<SerialError>();
|
||||
readonly onError = this.onErrorEmitter.event;
|
||||
|
||||
protected readonly onWebSocketChangedEmitter = new Emitter<number>();
|
||||
readonly onWebSocketChanged = this.onWebSocketChangedEmitter.event;
|
||||
|
||||
protected readonly onBaudRateChangedEmitter =
|
||||
new Emitter<SerialConfig.BaudRate>();
|
||||
readonly onBaudRateChanged = this.onBaudRateChangedEmitter.event;
|
||||
|
||||
protected readonly onLineEndingChangedEmitter =
|
||||
new Emitter<SerialModel.EOL>();
|
||||
readonly onLineEndingChanged = this.onLineEndingChangedEmitter.event;
|
||||
|
||||
protected readonly onInterpolateChangedEmitter = new Emitter<boolean>();
|
||||
readonly onInterpolateChanged = this.onInterpolateChangedEmitter.event;
|
||||
|
||||
notifyError(error: SerialError): void {
|
||||
this.onErrorEmitter.fire(error);
|
||||
}
|
||||
|
||||
notifyWebSocketChanged(message: number): void {
|
||||
this.onWebSocketChangedEmitter.fire(message);
|
||||
}
|
||||
|
||||
notifyBaudRateChanged(message: SerialConfig.BaudRate): void {
|
||||
this.onBaudRateChangedEmitter.fire(message);
|
||||
}
|
||||
|
||||
notifyLineEndingChanged(message: SerialModel.EOL): void {
|
||||
this.onLineEndingChangedEmitter.fire(message);
|
||||
}
|
||||
|
||||
notifyInterpolateChanged(message: boolean): void {
|
||||
this.onInterpolateChangedEmitter.fire(message);
|
||||
}
|
||||
}
|
BIN
arduino-ide-extension/src/browser/style/ide-logo.png
Normal file
BIN
arduino-ide-extension/src/browser/style/ide-logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 13 KiB |
@@ -0,0 +1,78 @@
|
||||
.ide-updater-dialog {
|
||||
width: 546px;
|
||||
}
|
||||
|
||||
.ide-updater-dialog .bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.ide-updater-dialog--pre-download {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.ide-updater-dialog--logo-container {
|
||||
margin-right: 28px;
|
||||
}
|
||||
|
||||
.ide-updater-dialog--logo {
|
||||
background: url('./ide-logo.png') round;
|
||||
width: 52px;
|
||||
height: 52px;
|
||||
}
|
||||
|
||||
.dialogContent.ide-updater-dialog
|
||||
.ide-updater-dialog--content
|
||||
.ide-updater-dialog--new-version-text.dialogSection {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.ide-updater-dialog .changelog-container {
|
||||
background: white;
|
||||
border: 1px solid #dae3e3;
|
||||
border-radius: 2px;
|
||||
font-size: 12px;
|
||||
height: 180px;
|
||||
overflow: auto;
|
||||
padding: 0 12px;
|
||||
cursor: text;
|
||||
}
|
||||
|
||||
.ide-updater-dialog .changelog-container a {
|
||||
color: #018184;
|
||||
}
|
||||
|
||||
.ide-updater-dialog .changelog-container a:hover {
|
||||
text-decoration: underline;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.ide-updater-dialog .changelog-container code {
|
||||
background: #ecf1f1;
|
||||
border-radius: 2px;
|
||||
padding: 0 2px;
|
||||
}
|
||||
|
||||
.ide-updater-dialog .changelog-container a code {
|
||||
color: #018184;
|
||||
}
|
||||
|
||||
.ide-updater-dialog .buttons-container {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-top: 28px;
|
||||
}
|
||||
|
||||
.ide-updater-dialog .buttons-container a.theia-button {
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.ide-updater-dialog .buttons-container a.theia-button:hover {
|
||||
color: var(--theia-button-foreground);
|
||||
}
|
||||
|
||||
.ide-updater-dialog .buttons-container .push {
|
||||
margin-right: auto;
|
||||
}
|
@@ -9,6 +9,7 @@
|
||||
@import './editor.css';
|
||||
@import './settings-dialog.css';
|
||||
@import './firmware-uploader-dialog.css';
|
||||
@import './ide-updater-dialog.css';
|
||||
@import './certificate-uploader-dialog.css';
|
||||
@import './user-fields-dialog.css';
|
||||
@import './debug.css';
|
||||
@@ -16,6 +17,7 @@
|
||||
@import './cloud-sketchbook.css';
|
||||
@import './fonts.css';
|
||||
@import './custom-codicon.css';
|
||||
@import './progress-bar.css';
|
||||
|
||||
.theia-input.warning:focus {
|
||||
outline-width: 1px;
|
||||
|
32
arduino-ide-extension/src/browser/style/progress-bar.css
Normal file
32
arduino-ide-extension/src/browser/style/progress-bar.css
Normal file
@@ -0,0 +1,32 @@
|
||||
.progress-bar {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.progress-bar--outer {
|
||||
background: #e5e5e5;
|
||||
border-radius: 11px;
|
||||
height: 6px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.progress-bar--inner {
|
||||
transition: width 1s;
|
||||
height: 100%;
|
||||
background: #008184;
|
||||
border-radius: 11px;
|
||||
}
|
||||
|
||||
.progress-bar--percentage {
|
||||
align-items: flex-end;
|
||||
display: flex;
|
||||
height: 40px;
|
||||
justify-content: center;
|
||||
margin-top: 10px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.progress-bar--percentage-text {
|
||||
font-size: 14px;
|
||||
line-height: 24px;
|
||||
}
|
@@ -57,3 +57,7 @@
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.p-Widget.dialogOverlay .dialogBlock .dialogContent.additional-urls-dialog {
|
||||
display: block;
|
||||
}
|
||||
|
@@ -9,6 +9,7 @@ import {
|
||||
} from '@theia/core/lib/browser/connection-status-service';
|
||||
import {
|
||||
ApplicationShell as TheiaApplicationShell,
|
||||
DockPanel,
|
||||
Panel,
|
||||
Widget,
|
||||
} from '@theia/core/lib/browser';
|
||||
@@ -16,6 +17,7 @@ import { Sketch } from '../../../common/protocol';
|
||||
import { SaveAsSketch } from '../../contributions/save-as-sketch';
|
||||
import { SketchesServiceClientImpl } from '../../../common/protocol/sketches-service-client-impl';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
import URI from '@theia/core/lib/common/uri';
|
||||
|
||||
@injectable()
|
||||
export class ApplicationShell extends TheiaApplicationShell {
|
||||
@@ -40,6 +42,9 @@ export class ApplicationShell extends TheiaApplicationShell {
|
||||
// Make the editor un-closeable asynchronously.
|
||||
this.sketchesServiceClient.currentSketch().then((sketch) => {
|
||||
if (sketch) {
|
||||
if (!this.isSketchFile(widget.editor.uri, sketch.uri)) {
|
||||
return;
|
||||
}
|
||||
if (Sketch.isInSketch(widget.editor.uri, sketch)) {
|
||||
widget.title.closable = false;
|
||||
}
|
||||
@@ -48,6 +53,14 @@ export class ApplicationShell extends TheiaApplicationShell {
|
||||
}
|
||||
}
|
||||
|
||||
private isSketchFile(uri: URI, sketchUriString: string): boolean {
|
||||
const sketchUri = new URI(sketchUriString);
|
||||
if (uri.parent.isEqual(sketchUri)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
async addWidget(
|
||||
widget: Widget,
|
||||
options: Readonly<TheiaApplicationShell.WidgetOptions> = {}
|
||||
@@ -74,6 +87,11 @@ export class ApplicationShell extends TheiaApplicationShell {
|
||||
return super.addWidget(widget, { ...options, ref });
|
||||
}
|
||||
|
||||
handleEvent(): boolean {
|
||||
// NOOP, dragging has been disabled
|
||||
return false
|
||||
}
|
||||
|
||||
// Avoid hiding top panel as we use it for arduino toolbar
|
||||
protected createTopPanel(): Panel {
|
||||
const topPanel = super.createTopPanel();
|
||||
@@ -101,3 +119,16 @@ export class ApplicationShell extends TheiaApplicationShell {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const originalHandleEvent = DockPanel.prototype.handleEvent;
|
||||
|
||||
DockPanel.prototype.handleEvent = function (event) {
|
||||
switch (event.type) {
|
||||
case 'p-dragenter':
|
||||
case 'p-dragleave':
|
||||
case 'p-dragover':
|
||||
case 'p-drop':
|
||||
return;
|
||||
}
|
||||
originalHandleEvent(event);
|
||||
};
|
||||
|
@@ -11,7 +11,15 @@ export class CommonFrontendContribution extends TheiaCommonFrontendContribution
|
||||
registerCommands(commandRegistry: CommandRegistry): void {
|
||||
super.registerCommands(commandRegistry);
|
||||
|
||||
for (const command of [CommonCommands.CONFIGURE_DISPLAY_LANGUAGE]) {
|
||||
for (const command of [
|
||||
CommonCommands.CONFIGURE_DISPLAY_LANGUAGE,
|
||||
CommonCommands.CLOSE_TAB,
|
||||
CommonCommands.CLOSE_SAVED_TABS,
|
||||
CommonCommands.CLOSE_OTHER_TABS,
|
||||
CommonCommands.CLOSE_ALL_TABS,
|
||||
CommonCommands.COLLAPSE_PANEL,
|
||||
CommonCommands.TOGGLE_MAXIMIZED,
|
||||
]) {
|
||||
commandRegistry.unregisterCommand(command);
|
||||
}
|
||||
}
|
||||
@@ -32,10 +40,6 @@ export class CommonFrontendContribution extends TheiaCommonFrontendContribution
|
||||
CommonCommands.SELECT_ICON_THEME,
|
||||
CommonCommands.SELECT_COLOR_THEME,
|
||||
CommonCommands.ABOUT_COMMAND,
|
||||
CommonCommands.CLOSE_TAB,
|
||||
CommonCommands.CLOSE_OTHER_TABS,
|
||||
CommonCommands.CLOSE_ALL_TABS,
|
||||
CommonCommands.COLLAPSE_PANEL,
|
||||
CommonCommands.SAVE_WITHOUT_FORMATTING, // Patched for https://github.com/eclipse-theia/theia/pull/8877
|
||||
]) {
|
||||
registry.unregisterMenuAction(command);
|
||||
|
@@ -62,9 +62,15 @@ export class DebugSessionManager extends TheiaDebugSessionManager {
|
||||
}
|
||||
);
|
||||
}
|
||||
// TODO: remove as https://github.com/eclipse-theia/theia/issues/10164 is fixed
|
||||
async terminateSessions(): Promise<void> {
|
||||
await super.terminateSessions();
|
||||
this.destroy(this.currentSession?.id);
|
||||
async terminateSession(session?: DebugSession): Promise<void> {
|
||||
if (!session) {
|
||||
this.updateCurrentSession(this._currentSession);
|
||||
session = this._currentSession;
|
||||
}
|
||||
// The cortex-debug extension does not respond to close requests
|
||||
// So we simply terminate the debug session immediately
|
||||
// Alternatively the `super.terminateSession` call will terminate it after 5 seconds without a response
|
||||
await this.debug.terminateDebugSession(session!.id);
|
||||
await super.terminateSession(session);
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { inject, injectable } from 'inversify';
|
||||
import { remote } from 'electron';
|
||||
import * as remote from '@theia/core/electron-shared/@electron/remote';
|
||||
import URI from '@theia/core/lib/common/uri';
|
||||
import { WorkspaceDeleteHandler as TheiaWorkspaceDeleteHandler } from '@theia/workspace/lib/browser/workspace-delete-handler';
|
||||
import { SketchesServiceClientImpl } from '../../../common/protocol/sketches-service-client-impl';
|
||||
|
@@ -1,3 +1,4 @@
|
||||
import * as remote from '@theia/core/electron-shared/@electron/remote';
|
||||
import { injectable, inject } from 'inversify';
|
||||
import URI from '@theia/core/lib/common/uri';
|
||||
import { EditorWidget } from '@theia/editor/lib/browser';
|
||||
@@ -18,6 +19,7 @@ import { ArduinoWorkspaceRootResolver } from '../../arduino-workspace-resolver';
|
||||
import { BoardsServiceProvider } from '../../boards/boards-service-provider';
|
||||
import { BoardsConfig } from '../../boards/boards-config';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
import { URI as VSCodeUri } from '@theia/core/shared/vscode-uri';
|
||||
|
||||
@injectable()
|
||||
export class WorkspaceService extends TheiaWorkspaceService {
|
||||
@@ -67,7 +69,7 @@ export class WorkspaceService extends TheiaWorkspaceService {
|
||||
this.workspaceUri = (async () => {
|
||||
try {
|
||||
const hash = window.location.hash;
|
||||
const [recentWorkspaces, recentSketches] = await Promise.all([
|
||||
const [recentWorkspacesPaths, recentSketches] = await Promise.all([
|
||||
this.server.getRecentWorkspaces(),
|
||||
this.sketchService
|
||||
.getSketches({})
|
||||
@@ -75,6 +77,10 @@ export class WorkspaceService extends TheiaWorkspaceService {
|
||||
SketchContainer.toArray(container).map((s) => s.uri)
|
||||
),
|
||||
]);
|
||||
// On Dindows, `getRecentWorkspaces` returns only file paths, not URIs as expected by the `isValid` method.
|
||||
const recentWorkspaces = recentWorkspacesPaths.map((e) =>
|
||||
VSCodeUri.file(e).toString()
|
||||
);
|
||||
const toOpen = await new ArduinoWorkspaceRootResolver({
|
||||
isValid: this.isValid.bind(this),
|
||||
}).resolve({ hash, recentWorkspaces, recentSketches });
|
||||
@@ -124,6 +130,8 @@ export class WorkspaceService extends TheiaWorkspaceService {
|
||||
}: FocusTracker.IChangedArgs<Widget>): void {
|
||||
if (newValue instanceof EditorWidget) {
|
||||
const { uri } = newValue.editor;
|
||||
const currentWindow = remote.getCurrentWindow();
|
||||
currentWindow.setRepresentedFilename(uri.path.toString());
|
||||
if (Sketch.isSketchFile(uri.toString())) {
|
||||
this.updateTitle();
|
||||
} else {
|
||||
|
2
arduino-ide-extension/src/browser/utils/constants.ts
Normal file
2
arduino-ide-extension/src/browser/utils/constants.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export const REMOTE_SKETCHBOOK_FOLDER = 'RemoteSketchbook';
|
||||
export const ARDUINO_CLOUD_FOLDER = 'ArduinoCloud';
|
@@ -1,4 +1,4 @@
|
||||
import { remote } from 'electron';
|
||||
import * as remote from '@theia/core/electron-shared/@electron/remote';
|
||||
import { inject, injectable } from 'inversify';
|
||||
import { CommandRegistry } from '@theia/core/lib/common/command';
|
||||
import { MenuModelRegistry } from '@theia/core/lib/common/menu';
|
||||
@@ -80,7 +80,7 @@ export class SketchbookWidgetContribution
|
||||
}
|
||||
|
||||
onStart(): void {
|
||||
this.shell.currentChanged.connect(() =>
|
||||
this.shell.onDidChangeCurrentWidget(() =>
|
||||
this.onCurrentWidgetChangedHandler()
|
||||
);
|
||||
|
||||
|
@@ -1,3 +1,5 @@
|
||||
import { Port } from "./boards-service";
|
||||
|
||||
export const ArduinoFirmwareUploaderPath =
|
||||
'/services/arduino-firmware-uploader';
|
||||
export const ArduinoFirmwareUploader = Symbol('ArduinoFirmwareUploader');
|
||||
@@ -10,7 +12,7 @@ export type FirmwareInfo = {
|
||||
};
|
||||
export interface ArduinoFirmwareUploader {
|
||||
list(fqbn?: string): Promise<FirmwareInfo[]>;
|
||||
flash(firmware: FirmwareInfo, port: string): Promise<string>;
|
||||
flash(firmware: FirmwareInfo, port: Port): Promise<string>;
|
||||
uploadCertificates(command: string): Promise<any>;
|
||||
updatableBoards(): Promise<string[]>;
|
||||
availableFirmwares(fqbn: string): Promise<FirmwareInfo[]>;
|
||||
|
@@ -7,13 +7,13 @@ export type AvailablePorts = Record<string, [Port, Array<Board>]>;
|
||||
export namespace AvailablePorts {
|
||||
export function byProtocol(availablePorts: AvailablePorts): Map<string, AvailablePorts> {
|
||||
const grouped = new Map<string, AvailablePorts>();
|
||||
for (const address of Object.keys(availablePorts)) {
|
||||
const [port, boards] = availablePorts[address];
|
||||
for (const portID of Object.keys(availablePorts)) {
|
||||
const [port, boards] = availablePorts[portID];
|
||||
let ports = grouped.get(port.protocol);
|
||||
if (!ports) {
|
||||
ports = {} as AvailablePorts;
|
||||
}
|
||||
ports[address] = [port, boards];
|
||||
ports[portID] = [port, boards];
|
||||
grouped.set(port.protocol, ports);
|
||||
}
|
||||
return grouped;
|
||||
@@ -43,7 +43,7 @@ export namespace AttachedBoardsChangeEvent {
|
||||
const visitedDetachedPorts: Port[] = [];
|
||||
for (const board of attached.boards) {
|
||||
const port = board.port
|
||||
? ` on ${Port.toString(board.port, { useLabel: true })}`
|
||||
? ` on ${Port.toString(board.port)}`
|
||||
: '';
|
||||
rows.push(` - Attached board: ${Board.toString(board)}${port}`);
|
||||
if (board.port) {
|
||||
@@ -52,7 +52,7 @@ export namespace AttachedBoardsChangeEvent {
|
||||
}
|
||||
for (const board of detached.boards) {
|
||||
const port = board.port
|
||||
? ` from ${Port.toString(board.port, { useLabel: true })}`
|
||||
? ` from ${Port.toString(board.port)}`
|
||||
: '';
|
||||
rows.push(` - Detached board: ${Board.toString(board)}${port}`);
|
||||
if (board.port) {
|
||||
@@ -62,18 +62,14 @@ export namespace AttachedBoardsChangeEvent {
|
||||
for (const port of attached.ports) {
|
||||
if (!visitedAttachedPorts.find((p) => Port.sameAs(port, p))) {
|
||||
rows.push(
|
||||
` - New port is available on ${Port.toString(port, {
|
||||
useLabel: true,
|
||||
})}`
|
||||
` - New port is available on ${Port.toString(port)}`
|
||||
);
|
||||
}
|
||||
}
|
||||
for (const port of detached.ports) {
|
||||
if (!visitedDetachedPorts.find((p) => Port.sameAs(port, p))) {
|
||||
rows.push(
|
||||
` - Port is no longer available on ${Port.toString(port, {
|
||||
useLabel: true,
|
||||
})}`
|
||||
` - Port is no longer available on ${Port.toString(port)}`
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -147,12 +143,14 @@ export interface BoardsService
|
||||
}
|
||||
|
||||
export interface Port {
|
||||
// id is the combination of address and protocol
|
||||
// formatted like "<address>|<protocol>" used
|
||||
// to univocally recognize a port
|
||||
readonly id: string;
|
||||
readonly address: string;
|
||||
readonly addressLabel: string;
|
||||
readonly protocol: string;
|
||||
/**
|
||||
* Optional label for the protocol. For example: `Serial Port (USB)`.
|
||||
*/
|
||||
readonly label?: string;
|
||||
readonly protocolLabel: string;
|
||||
}
|
||||
export namespace Port {
|
||||
export function is(arg: any): arg is Port {
|
||||
@@ -165,14 +163,8 @@ export namespace Port {
|
||||
);
|
||||
}
|
||||
|
||||
export function toString(
|
||||
port: Port,
|
||||
options: { useLabel: boolean } = { useLabel: false }
|
||||
): string {
|
||||
if (options.useLabel && port.label) {
|
||||
return `${port.address} ${port.label}`;
|
||||
}
|
||||
return port.address;
|
||||
export function toString(port: Port): string {
|
||||
return `${port.addressLabel} ${port.protocolLabel}`;
|
||||
}
|
||||
|
||||
export function compare(left: Port, right: Port): number {
|
||||
@@ -192,29 +184,12 @@ export namespace Port {
|
||||
return naturalCompare(left.address!, right.address!);
|
||||
}
|
||||
|
||||
export function equals(
|
||||
export function sameAs(
|
||||
left: Port | undefined,
|
||||
right: Port | undefined
|
||||
): boolean {
|
||||
if (left && right) {
|
||||
return (
|
||||
left.address === right.address &&
|
||||
left.protocol === right.protocol &&
|
||||
(left.label || '') === (right.label || '')
|
||||
);
|
||||
}
|
||||
return left === right;
|
||||
}
|
||||
|
||||
export function sameAs(
|
||||
left: Port | undefined,
|
||||
right: Port | string | undefined
|
||||
) {
|
||||
if (left && right) {
|
||||
if (typeof right === 'string') {
|
||||
return left.address === right;
|
||||
}
|
||||
return left.address === right.address;
|
||||
return left.address === right.address && left.protocol === right.protocol;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@@ -116,7 +116,7 @@ export interface Config {
|
||||
readonly sketchDirUri: string;
|
||||
readonly dataDirUri: string;
|
||||
readonly downloadsDirUri: string;
|
||||
readonly additionalUrls: string[];
|
||||
readonly additionalUrls: AdditionalUrls;
|
||||
readonly network: Network;
|
||||
readonly daemon: Daemon;
|
||||
}
|
||||
@@ -141,3 +141,32 @@ export namespace Config {
|
||||
);
|
||||
}
|
||||
}
|
||||
export type AdditionalUrls = string[];
|
||||
export namespace AdditionalUrls {
|
||||
export function parse(value: string, delimiter: ',' | 'newline'): string[] {
|
||||
return value
|
||||
.trim()
|
||||
.split(delimiter === ',' ? delimiter : /\r?\n/)
|
||||
.map((url) => url.trim())
|
||||
.filter((url) => !!url);
|
||||
}
|
||||
export function stringify(additionalUrls: AdditionalUrls): string {
|
||||
return additionalUrls.join(',');
|
||||
}
|
||||
export function sameAs(left: AdditionalUrls, right: AdditionalUrls): boolean {
|
||||
if (left.length !== right.length) {
|
||||
return false;
|
||||
}
|
||||
const localeCompare = (left: string, right: string) =>
|
||||
left.localeCompare(right);
|
||||
const normalize = (url: string) => url.toLowerCase();
|
||||
const normalizedLeft = left.map(normalize).sort(localeCompare);
|
||||
const normalizedRight = right.map(normalize).sort(localeCompare);
|
||||
for (let i = 0; i < normalizedLeft.length; i++) {
|
||||
if (normalizedLeft[i] !== normalizedRight[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { BoardUserField } from '.';
|
||||
import { Port } from '../../common/protocol/boards-service';
|
||||
import { Board, Port } from '../../common/protocol/boards-service';
|
||||
import { Programmer } from './boards-service';
|
||||
|
||||
export const CompilerWarningLiterals = [
|
||||
@@ -33,7 +33,7 @@ export namespace CoreService {
|
||||
* `file` URI to the sketch folder.
|
||||
*/
|
||||
readonly sketchUri: string;
|
||||
readonly fqbn?: string | undefined;
|
||||
readonly board?: Board;
|
||||
readonly optimizeForDebug: boolean;
|
||||
readonly verbose: boolean;
|
||||
readonly sourceOverride: Record<string, string>;
|
||||
@@ -42,7 +42,7 @@ export namespace CoreService {
|
||||
|
||||
export namespace Upload {
|
||||
export interface Options extends Compile.Options {
|
||||
readonly port?: Port | undefined;
|
||||
readonly port?: Port;
|
||||
readonly programmer?: Programmer | undefined;
|
||||
readonly verify: boolean;
|
||||
readonly userFields: BoardUserField[];
|
||||
@@ -51,8 +51,8 @@ export namespace CoreService {
|
||||
|
||||
export namespace Bootloader {
|
||||
export interface Options {
|
||||
readonly fqbn?: string | undefined;
|
||||
readonly port?: Port | undefined;
|
||||
readonly board?: Board;
|
||||
readonly port?: Port;
|
||||
readonly programmer?: Programmer | undefined;
|
||||
readonly verbose: boolean;
|
||||
readonly verify: boolean;
|
||||
|
71
arduino-ide-extension/src/common/protocol/ide-updater.ts
Normal file
71
arduino-ide-extension/src/common/protocol/ide-updater.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
import { JsonRpcServer } from '@theia/core/lib/common/messaging/proxy-factory';
|
||||
import { Event } from '@theia/core/lib/common/event';
|
||||
import { UpdateChannel } from '../../browser/arduino-preferences';
|
||||
|
||||
export interface ProgressInfo {
|
||||
total: number;
|
||||
delta: number;
|
||||
transferred: number;
|
||||
percent: number;
|
||||
bytesPerSecond: number;
|
||||
}
|
||||
|
||||
export interface ReleaseNoteInfo {
|
||||
readonly version: string;
|
||||
readonly note: string | null;
|
||||
}
|
||||
|
||||
export interface BlockMapDataHolder {
|
||||
size?: number;
|
||||
blockMapSize?: number;
|
||||
readonly sha512: string;
|
||||
readonly isAdminRightsRequired?: boolean;
|
||||
}
|
||||
|
||||
export interface UpdateFileInfo extends BlockMapDataHolder {
|
||||
url: string;
|
||||
}
|
||||
|
||||
export type UpdateInfo = {
|
||||
readonly version: string;
|
||||
readonly files: Array<UpdateFileInfo>;
|
||||
releaseName?: string | null;
|
||||
releaseNotes?: string | Array<ReleaseNoteInfo> | null;
|
||||
releaseDate: string;
|
||||
readonly stagingPercentage?: number;
|
||||
};
|
||||
|
||||
export interface ProgressInfo {
|
||||
total: number;
|
||||
delta: number;
|
||||
transferred: number;
|
||||
percent: number;
|
||||
bytesPerSecond: number;
|
||||
}
|
||||
|
||||
export const IDEUpdaterPath = '/services/ide-updater';
|
||||
export const IDEUpdater = Symbol('IDEUpdater');
|
||||
export interface IDEUpdater extends JsonRpcServer<IDEUpdaterClient> {
|
||||
init(channel: UpdateChannel, baseUrl: string): Promise<void>;
|
||||
checkForUpdates(initialCheck?: boolean): Promise<UpdateInfo | void>;
|
||||
downloadUpdate(): Promise<void>;
|
||||
quitAndInstall(): void;
|
||||
stopDownload(): void;
|
||||
disconnectClient(client: IDEUpdaterClient): void;
|
||||
}
|
||||
|
||||
export const IDEUpdaterClient = Symbol('IDEUpdaterClient');
|
||||
export interface IDEUpdaterClient {
|
||||
onError: Event<Error>;
|
||||
onCheckingForUpdate: Event<void>;
|
||||
onUpdateAvailable: Event<UpdateInfo>;
|
||||
onUpdateNotAvailable: Event<UpdateInfo>;
|
||||
onDownloadProgressChanged: Event<ProgressInfo>;
|
||||
onDownloadFinished: Event<UpdateInfo>;
|
||||
notifyError(message: Error): void;
|
||||
notifyCheckingForUpdate(message: void): void;
|
||||
notifyUpdateAvailable(message: UpdateInfo): void;
|
||||
notifyUpdateNotAvailable(message: UpdateInfo): void;
|
||||
notifyDownloadProgressChanged(message: ProgressInfo): void;
|
||||
notifyDownloadFinished(message: UpdateInfo): void;
|
||||
}
|
@@ -6,10 +6,10 @@ export * from './core-service';
|
||||
export * from './filesystem-ext';
|
||||
export * from './installable';
|
||||
export * from './library-service';
|
||||
export * from './serial-service';
|
||||
export * from './searchable';
|
||||
export * from './sketches-service';
|
||||
export * from './examples-service';
|
||||
export * from './executable-service';
|
||||
export * from './response-service';
|
||||
export * from './notification-service';
|
||||
export * from './monitor-service';
|
||||
|
92
arduino-ide-extension/src/common/protocol/monitor-service.ts
Normal file
92
arduino-ide-extension/src/common/protocol/monitor-service.ts
Normal file
@@ -0,0 +1,92 @@
|
||||
import { Event, JsonRpcServer } from '@theia/core';
|
||||
import {
|
||||
PluggableMonitorSettings,
|
||||
MonitorSettings,
|
||||
} from '../../node/monitor-settings/monitor-settings-provider';
|
||||
import { Board, Port } from './boards-service';
|
||||
|
||||
export const MonitorManagerProxyFactory = Symbol('MonitorManagerProxyFactory');
|
||||
export type MonitorManagerProxyFactory = () => MonitorManagerProxy;
|
||||
|
||||
export const MonitorManagerProxyPath = '/services/monitor-manager-proxy';
|
||||
export const MonitorManagerProxy = Symbol('MonitorManagerProxy');
|
||||
export interface MonitorManagerProxy
|
||||
extends JsonRpcServer<MonitorManagerProxyClient> {
|
||||
startMonitor(
|
||||
board: Board,
|
||||
port: Port,
|
||||
settings?: PluggableMonitorSettings
|
||||
): Promise<void>;
|
||||
changeMonitorSettings(
|
||||
board: Board,
|
||||
port: Port,
|
||||
settings: PluggableMonitorSettings
|
||||
): Promise<void>;
|
||||
stopMonitor(board: Board, port: Port): Promise<void>;
|
||||
getCurrentSettings(board: Board, port: Port): PluggableMonitorSettings;
|
||||
}
|
||||
|
||||
export const MonitorManagerProxyClient = Symbol('MonitorManagerProxyClient');
|
||||
export interface MonitorManagerProxyClient {
|
||||
onMessagesReceived: Event<{ messages: string[] }>;
|
||||
onWSConnectionChanged: Event<boolean>;
|
||||
connect(addressPort: number): void;
|
||||
disconnect(): void;
|
||||
getWebSocketPort(): number | undefined;
|
||||
isWSConnected(): Promise<boolean>;
|
||||
startMonitor(
|
||||
board: Board,
|
||||
port: Port,
|
||||
settings?: PluggableMonitorSettings
|
||||
): Promise<void>;
|
||||
getCurrentSettings(board: Board, port: Port): MonitorSettings;
|
||||
send(message: string): void;
|
||||
changeSettings(settings: MonitorSettings): void;
|
||||
}
|
||||
|
||||
export interface PluggableMonitorSetting {
|
||||
// The setting identifier
|
||||
readonly id: string;
|
||||
// A human-readable label of the setting (to be displayed on the GUI)
|
||||
readonly label: string;
|
||||
// The setting type (at the moment only "enum" is avaiable)
|
||||
readonly type: string;
|
||||
// The values allowed on "enum" types
|
||||
readonly values: string[];
|
||||
// The selected value
|
||||
selectedValue: string;
|
||||
}
|
||||
|
||||
export namespace Monitor {
|
||||
export enum Command {
|
||||
SEND_MESSAGE = 'MONITOR_SEND_MESSAGE',
|
||||
CHANGE_SETTINGS = 'MONITOR_CHANGE_SETTINGS',
|
||||
}
|
||||
|
||||
export type Message = {
|
||||
command: Monitor.Command;
|
||||
data: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface Status {}
|
||||
export type OK = Status;
|
||||
export interface ErrorStatus extends Status {
|
||||
readonly message: string;
|
||||
}
|
||||
export namespace Status {
|
||||
export function isOK(status: Status & { message?: string }): status is OK {
|
||||
return !!status && typeof status.message !== 'string';
|
||||
}
|
||||
export const OK: OK = {};
|
||||
export const NOT_CONNECTED: ErrorStatus = { message: 'Not connected.' };
|
||||
export const ALREADY_CONNECTED: ErrorStatus = {
|
||||
message: 'Already connected.',
|
||||
};
|
||||
export const CONFIG_MISSING: ErrorStatus = {
|
||||
message: 'Serial Config missing.',
|
||||
};
|
||||
export const UPLOAD_IN_PROGRESS: ErrorStatus = {
|
||||
message: 'Upload in progress.',
|
||||
};
|
||||
}
|
@@ -1,102 +0,0 @@
|
||||
import { JsonRpcServer } from '@theia/core/lib/common/messaging/proxy-factory';
|
||||
import { Board, Port } from './boards-service';
|
||||
import { Event } from '@theia/core/lib/common/event';
|
||||
import { SerialPlotter } from '../../browser/serial/plotter/protocol';
|
||||
import { SerialModel } from '../../browser/serial/serial-model';
|
||||
|
||||
export interface Status {}
|
||||
export type OK = Status;
|
||||
export interface ErrorStatus extends Status {
|
||||
readonly message: string;
|
||||
}
|
||||
export namespace Status {
|
||||
export function isOK(status: Status & { message?: string }): status is OK {
|
||||
return !!status && typeof status.message !== 'string';
|
||||
}
|
||||
export const OK: OK = {};
|
||||
export const NOT_CONNECTED: ErrorStatus = { message: 'Not connected.' };
|
||||
export const ALREADY_CONNECTED: ErrorStatus = {
|
||||
message: 'Already connected.',
|
||||
};
|
||||
export const CONFIG_MISSING: ErrorStatus = {
|
||||
message: 'Serial Config missing.',
|
||||
};
|
||||
}
|
||||
|
||||
export const SerialServicePath = '/services/serial';
|
||||
export const SerialService = Symbol('SerialService');
|
||||
export interface SerialService extends JsonRpcServer<SerialServiceClient> {
|
||||
clientsAttached(): Promise<number>;
|
||||
setSerialConfig(config: SerialConfig): Promise<void>;
|
||||
sendMessageToSerial(message: string): Promise<Status>;
|
||||
updateWsConfigParam(config: Partial<SerialPlotter.Config>): Promise<void>;
|
||||
isSerialPortOpen(): Promise<boolean>;
|
||||
connectSerialIfRequired(): Promise<void>;
|
||||
disconnect(reason?: SerialError): Promise<Status>;
|
||||
uploadInProgress: boolean;
|
||||
}
|
||||
|
||||
export interface SerialConfig {
|
||||
readonly board: Board;
|
||||
readonly port: Port;
|
||||
/**
|
||||
* Defaults to [`SERIAL`](MonitorConfig#ConnectionType#SERIAL).
|
||||
*/
|
||||
readonly type?: SerialConfig.ConnectionType;
|
||||
/**
|
||||
* Defaults to `9600`.
|
||||
*/
|
||||
readonly baudRate?: SerialConfig.BaudRate;
|
||||
}
|
||||
export namespace SerialConfig {
|
||||
export const BaudRates = [
|
||||
300, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200,
|
||||
] as const;
|
||||
export type BaudRate = typeof SerialConfig.BaudRates[number];
|
||||
export namespace BaudRate {
|
||||
export const DEFAULT: BaudRate = 9600;
|
||||
}
|
||||
|
||||
export enum ConnectionType {
|
||||
SERIAL = 0,
|
||||
}
|
||||
}
|
||||
|
||||
export const SerialServiceClient = Symbol('SerialServiceClient');
|
||||
export interface SerialServiceClient {
|
||||
onError: Event<SerialError>;
|
||||
onWebSocketChanged: Event<number>;
|
||||
onLineEndingChanged: Event<SerialModel.EOL>;
|
||||
onBaudRateChanged: Event<SerialConfig.BaudRate>;
|
||||
onInterpolateChanged: Event<boolean>;
|
||||
notifyError(event: SerialError): void;
|
||||
notifyWebSocketChanged(message: number): void;
|
||||
notifyLineEndingChanged(message: SerialModel.EOL): void;
|
||||
notifyBaudRateChanged(message: SerialConfig.BaudRate): void;
|
||||
notifyInterpolateChanged(message: boolean): void;
|
||||
}
|
||||
|
||||
export interface SerialError {
|
||||
readonly message: string;
|
||||
/**
|
||||
* If no `code` is available, clients must reestablish the serial connection.
|
||||
*/
|
||||
readonly code: number | undefined;
|
||||
readonly config: SerialConfig;
|
||||
}
|
||||
export namespace SerialError {
|
||||
export namespace ErrorCodes {
|
||||
/**
|
||||
* The frontend has refreshed the browser, for instance.
|
||||
*/
|
||||
export const CLIENT_CANCEL = 1;
|
||||
/**
|
||||
* When detaching a physical device when the duplex channel is still opened.
|
||||
*/
|
||||
export const DEVICE_NOT_CONFIGURED = 2;
|
||||
/**
|
||||
* Another serial connection was opened on this port. For another electron-instance, Java IDE.
|
||||
*/
|
||||
export const DEVICE_BUSY = 3;
|
||||
}
|
||||
}
|
@@ -11,12 +11,13 @@ import { FrontendApplicationContribution } from '@theia/core/lib/browser/fronten
|
||||
import { Sketch, SketchesService } from '../../common/protocol';
|
||||
import { ConfigService } from './config-service';
|
||||
import { SketchContainer } from './sketches-service';
|
||||
import {
|
||||
ARDUINO_CLOUD_FOLDER,
|
||||
REMOTE_SKETCHBOOK_FOLDER,
|
||||
} from '../../browser/utils/constants';
|
||||
|
||||
const READ_ONLY_FILES = [
|
||||
'thingProperties.h',
|
||||
'thingsProperties.h',
|
||||
'sketch.json',
|
||||
];
|
||||
const READ_ONLY_FILES = ['sketch.json'];
|
||||
const READ_ONLY_FILES_REMOTE = ['thingProperties.h', 'thingsProperties.h'];
|
||||
|
||||
@injectable()
|
||||
export class SketchesServiceClientImpl
|
||||
@@ -178,7 +179,17 @@ export class SketchesServiceClientImpl
|
||||
if (toCheck.scheme === 'user-storage') {
|
||||
return false;
|
||||
}
|
||||
if (READ_ONLY_FILES.includes(toCheck?.path?.base)) {
|
||||
|
||||
const isCloudSketch = toCheck
|
||||
.toString()
|
||||
.includes(`${REMOTE_SKETCHBOOK_FOLDER}/${ARDUINO_CLOUD_FOLDER}`);
|
||||
|
||||
const filesToCheck = [
|
||||
...READ_ONLY_FILES,
|
||||
...(isCloudSketch ? READ_ONLY_FILES_REMOTE : []),
|
||||
];
|
||||
|
||||
if (filesToCheck.includes(toCheck?.path?.base)) {
|
||||
return true;
|
||||
}
|
||||
const readOnly = !this.workspaceService
|
||||
|
@@ -1,7 +1,3 @@
|
||||
export type RecursiveRequired<T> = {
|
||||
[P in keyof T]-?: RecursiveRequired<T[P]>;
|
||||
};
|
||||
|
||||
export interface Index {
|
||||
[key: string]: any;
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { inject, injectable, postConstruct } from 'inversify';
|
||||
import { remote } from 'electron';
|
||||
import * as remote from '@theia/core/electron-shared/@electron/remote';
|
||||
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
|
||||
import {
|
||||
ConnectionStatus,
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { injectable } from 'inversify';
|
||||
import { remote } from 'electron';
|
||||
import * as remote from '@theia/core/electron-shared/@electron/remote';
|
||||
import { isOSX } from '@theia/core/lib/common/os';
|
||||
import { Keybinding } from '@theia/core/lib/common/keybinding';
|
||||
import {
|
||||
@@ -15,7 +15,6 @@ import {
|
||||
ArduinoMenus,
|
||||
PlaceholderMenuNode,
|
||||
} from '../../../browser/menu/arduino-menus';
|
||||
import electron = require('@theia/core/shared/electron');
|
||||
|
||||
@injectable()
|
||||
export class ElectronMainMenuFactory extends TheiaElectronMainMenuFactory {
|
||||
@@ -35,9 +34,9 @@ export class ElectronMainMenuFactory extends TheiaElectronMainMenuFactory {
|
||||
await this.preferencesService.ready;
|
||||
const createdMenuBar = this.createElectronMenuBar();
|
||||
if (isOSX) {
|
||||
electron.remote.Menu.setApplicationMenu(createdMenuBar);
|
||||
remote.Menu.setApplicationMenu(createdMenuBar);
|
||||
} else {
|
||||
electron.remote.getCurrentWindow().setMenu(createdMenuBar);
|
||||
remote.getCurrentWindow().setMenu(createdMenuBar);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,7 +80,7 @@ export class ElectronMainMenuFactory extends TheiaElectronMainMenuFactory {
|
||||
protected createOSXMenu(): Electron.MenuItemConstructorOptions {
|
||||
const { submenu } = super.createOSXMenu();
|
||||
const label = 'Arduino IDE';
|
||||
if (!!submenu && !(submenu instanceof remote.Menu)) {
|
||||
if (!!submenu && Array.isArray(submenu)) {
|
||||
const [, , /* about */ /* preferences */ ...rest] = submenu;
|
||||
const about = this.fillMenuTemplate(
|
||||
[],
|
||||
|
@@ -2,7 +2,10 @@ import { ContainerModule } from 'inversify';
|
||||
import { JsonRpcConnectionHandler } from '@theia/core/lib/common/messaging/proxy-factory';
|
||||
import { ElectronConnectionHandler } from '@theia/core/lib/electron-common/messaging/electron-connection-handler';
|
||||
import { ElectronMainWindowService } from '@theia/core/lib/electron-common/electron-main-window-service';
|
||||
import { ElectronMainApplication as TheiaElectronMainApplication } from '@theia/core/lib/electron-main/electron-main-application';
|
||||
import {
|
||||
ElectronMainApplication as TheiaElectronMainApplication,
|
||||
ElectronMainApplicationContribution,
|
||||
} from '@theia/core/lib/electron-main/electron-main-application';
|
||||
import {
|
||||
SplashService,
|
||||
splashServicePath,
|
||||
@@ -10,6 +13,12 @@ import {
|
||||
import { SplashServiceImpl } from './splash/splash-service-impl';
|
||||
import { ElectronMainApplication } from './theia/electron-main-application';
|
||||
import { ElectronMainWindowServiceImpl } from './theia/electron-main-window-service';
|
||||
import {
|
||||
IDEUpdater,
|
||||
IDEUpdaterClient,
|
||||
IDEUpdaterPath,
|
||||
} from '../common/protocol/ide-updater';
|
||||
import { IDEUpdaterImpl } from './ide-updater/ide-updater-impl';
|
||||
|
||||
export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
bind(ElectronMainApplication).toSelf().inSingletonScope();
|
||||
@@ -28,4 +37,23 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
)
|
||||
)
|
||||
.inSingletonScope();
|
||||
|
||||
// IDE updater bindings
|
||||
bind(IDEUpdaterImpl).toSelf().inSingletonScope();
|
||||
bind(IDEUpdater).toService(IDEUpdaterImpl);
|
||||
bind(ElectronMainApplicationContribution).toService(IDEUpdater);
|
||||
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;
|
||||
}
|
||||
)
|
||||
)
|
||||
.inSingletonScope();
|
||||
});
|
||||
|
@@ -0,0 +1,129 @@
|
||||
import { injectable } from '@theia/core/shared/inversify';
|
||||
import { UpdateInfo, CancellationToken, autoUpdater } from 'electron-updater';
|
||||
import fetch, { Response } from 'node-fetch';
|
||||
import { UpdateChannel } from '../../browser/arduino-preferences';
|
||||
import {
|
||||
IDEUpdater,
|
||||
IDEUpdaterClient,
|
||||
} from '../../common/protocol/ide-updater';
|
||||
|
||||
const CHANGELOG_BASE_URL = 'https://downloads.arduino.cc/arduino-ide/changelog';
|
||||
|
||||
@injectable()
|
||||
export class IDEUpdaterImpl implements IDEUpdater {
|
||||
private isAlreadyChecked = false;
|
||||
private updater = autoUpdater;
|
||||
private cancellationToken?: CancellationToken;
|
||||
protected theiaFEClient?: IDEUpdaterClient;
|
||||
protected clients: Array<IDEUpdaterClient> = [];
|
||||
|
||||
constructor() {
|
||||
this.updater.on('checking-for-update', (e) => {
|
||||
this.clients.forEach((c) => c.notifyCheckingForUpdate(e));
|
||||
});
|
||||
this.updater.on('update-available', (e) => {
|
||||
this.clients.forEach((c) => c.notifyUpdateAvailable(e));
|
||||
});
|
||||
this.updater.on('update-not-available', (e) => {
|
||||
this.clients.forEach((c) => c.notifyUpdateNotAvailable(e));
|
||||
});
|
||||
this.updater.on('download-progress', (e) => {
|
||||
this.clients.forEach((c) => c.notifyDownloadProgressChanged(e));
|
||||
});
|
||||
this.updater.on('update-downloaded', (e) => {
|
||||
this.clients.forEach((c) => c.notifyDownloadFinished(e));
|
||||
});
|
||||
this.updater.on('error', (e) => {
|
||||
this.clients.forEach((c) => c.notifyError(e));
|
||||
});
|
||||
}
|
||||
|
||||
async init(channel: UpdateChannel, baseUrl: string): Promise<void> {
|
||||
this.updater.autoDownload = false;
|
||||
this.updater.channel = channel;
|
||||
this.updater.setFeedURL({
|
||||
provider: 'generic',
|
||||
url: `${baseUrl}/${channel === UpdateChannel.Nightly ? 'nightly' : ''}`,
|
||||
channel,
|
||||
});
|
||||
}
|
||||
|
||||
setClient(client: IDEUpdaterClient | undefined): void {
|
||||
if (client) this.clients.push(client);
|
||||
}
|
||||
|
||||
async checkForUpdates(initialCheck?: boolean): Promise<UpdateInfo | void> {
|
||||
if (initialCheck) {
|
||||
if (this.isAlreadyChecked) return Promise.resolve();
|
||||
this.isAlreadyChecked = true;
|
||||
}
|
||||
|
||||
const {
|
||||
updateInfo,
|
||||
cancellationToken,
|
||||
} = await this.updater.checkForUpdates();
|
||||
|
||||
this.cancellationToken = cancellationToken;
|
||||
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
|
||||
and a second GET to the actual changelog file
|
||||
*/
|
||||
try {
|
||||
let response: Response | null = await fetch(
|
||||
`${CHANGELOG_BASE_URL}/latest.txt`
|
||||
);
|
||||
const latestChangelogFileName = response.ok
|
||||
? await response.text()
|
||||
: null;
|
||||
response = latestChangelogFileName
|
||||
? await fetch(`${CHANGELOG_BASE_URL}/${latestChangelogFileName}`)
|
||||
: null;
|
||||
const changelog = response?.ok ? await response?.text() : null;
|
||||
const currentVersionHeader = `\n\n---\n\n## ${this.updater.currentVersion}\n\n`;
|
||||
// We only want to see the release notes of newer versions
|
||||
const currentVersionIndex = changelog?.indexOf(currentVersionHeader);
|
||||
const newChangelog =
|
||||
currentVersionIndex && currentVersionIndex > 0
|
||||
? changelog?.slice(0, currentVersionIndex)
|
||||
: changelog;
|
||||
updateInfo.releaseNotes = newChangelog;
|
||||
} catch {
|
||||
/*
|
||||
if the request for the changelog fails, we'll just avoid to show it
|
||||
to the user, but we will still show the update info
|
||||
*/
|
||||
}
|
||||
return updateInfo;
|
||||
}
|
||||
}
|
||||
|
||||
async downloadUpdate(): Promise<void> {
|
||||
try {
|
||||
await this.updater.downloadUpdate(this.cancellationToken);
|
||||
} catch (e) {
|
||||
if (e.message === 'cancelled') return;
|
||||
this.clients.forEach((c) => c.notifyError(e));
|
||||
}
|
||||
}
|
||||
|
||||
stopDownload(): void {
|
||||
this.cancellationToken?.cancel();
|
||||
}
|
||||
|
||||
quitAndInstall(): void {
|
||||
this.updater.quitAndInstall();
|
||||
}
|
||||
|
||||
disconnectClient(client: IDEUpdaterClient): void {
|
||||
const index = this.clients.indexOf(client);
|
||||
if (index !== -1) {
|
||||
this.clients.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.clients.forEach(this.disconnectClient.bind(this));
|
||||
}
|
||||
}
|
@@ -1,29 +1,45 @@
|
||||
import { inject, injectable } from 'inversify';
|
||||
import {
|
||||
app,
|
||||
BrowserWindow,
|
||||
BrowserWindowConstructorOptions,
|
||||
screen,
|
||||
} from 'electron';
|
||||
import { app, BrowserWindow, BrowserWindowConstructorOptions, ipcMain, screen, Event as ElectronEvent } from '@theia/core/electron-shared/electron';
|
||||
import { fork } from 'child_process';
|
||||
import { AddressInfo } from 'net';
|
||||
import { join } from 'path';
|
||||
import { join, dirname } from 'path';
|
||||
import * as fs from 'fs-extra';
|
||||
import { initSplashScreen } from '../splash/splash-screen';
|
||||
import { MaybePromise } from '@theia/core/lib/common/types';
|
||||
import { ElectronSecurityToken } from '@theia/core/lib/electron-common/electron-token';
|
||||
import { FrontendApplicationConfig } from '@theia/application-package/lib/application-props';
|
||||
import {
|
||||
ElectronMainApplication as TheiaElectronMainApplication,
|
||||
ElectronMainExecutionParams,
|
||||
TheiaBrowserWindowOptions,
|
||||
} from '@theia/core/lib/electron-main/electron-main-application';
|
||||
import { SplashServiceImpl } from '../splash/splash-service-impl';
|
||||
import { ipcMain } from '@theia/core/shared/electron';
|
||||
import { URI } from '@theia/core/shared/vscode-uri';
|
||||
import * as electronRemoteMain from '@theia/core/electron-shared/@electron/remote/main';
|
||||
import { Deferred } from '@theia/core/lib/common/promise-util';
|
||||
import * as os from '@theia/core/lib/common/os';
|
||||
import { RELOAD_REQUESTED_SIGNAL, Restart } from '@theia/core/lib/electron-common/messaging/electron-messages';
|
||||
|
||||
app.commandLine.appendSwitch('disable-http-cache');
|
||||
|
||||
interface WorkspaceOptions {
|
||||
file: string
|
||||
x: number
|
||||
y: number
|
||||
width: number
|
||||
height: number
|
||||
isMaximized: boolean
|
||||
isFullScreen: boolean
|
||||
time: number
|
||||
}
|
||||
|
||||
const WORKSPACES = 'workspaces';
|
||||
|
||||
@injectable()
|
||||
export class ElectronMainApplication extends TheiaElectronMainApplication {
|
||||
protected _windows: BrowserWindow[] = [];
|
||||
protected startup = false;
|
||||
protected openFilePromise = new Deferred();
|
||||
|
||||
@inject(SplashServiceImpl)
|
||||
protected readonly splashService: SplashServiceImpl;
|
||||
@@ -33,9 +49,106 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
|
||||
// See: https://github.com/electron-userland/electron-builder/issues/2468
|
||||
// Regression in Theia: https://github.com/eclipse-theia/theia/issues/8701
|
||||
app.on('ready', () => app.setName(config.applicationName));
|
||||
this.attachFileAssociations();
|
||||
return super.start(config);
|
||||
}
|
||||
|
||||
attachFileAssociations() {
|
||||
// OSX: register open-file event
|
||||
if (os.isOSX) {
|
||||
app.on('open-file', async (event, uri) => {
|
||||
event.preventDefault();
|
||||
if (uri.endsWith('.ino') && await fs.pathExists(uri)) {
|
||||
this.openFilePromise.reject();
|
||||
await this.openSketch(dirname(uri));
|
||||
}
|
||||
});
|
||||
setTimeout(() => this.openFilePromise.resolve(), 500);
|
||||
} else {
|
||||
this.openFilePromise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
protected async isValidSketchPath(uri: string): Promise<boolean | undefined> {
|
||||
return typeof uri === 'string' && await fs.pathExists(uri);
|
||||
}
|
||||
|
||||
protected async launch(params: ElectronMainExecutionParams): Promise<void> {
|
||||
try {
|
||||
// When running on MacOS, we either have to wait until
|
||||
// 1. The `open-file` command has been received by the app, rejecting the promise
|
||||
// 2. A short timeout resolves the promise automatically, falling back to the usual app launch
|
||||
await this.openFilePromise.promise;
|
||||
} catch {
|
||||
// Application has received the `open-file` event and will skip the default application launch
|
||||
return;
|
||||
}
|
||||
|
||||
if (!os.isOSX && await this.launchFromArgs(params)) {
|
||||
// Application has received a file in its arguments and will skip the default application launch
|
||||
return;
|
||||
}
|
||||
|
||||
this.startup = true;
|
||||
const workspaces: WorkspaceOptions[] | undefined = this.electronStore.get(WORKSPACES);
|
||||
let useDefault = true;
|
||||
if (workspaces && workspaces.length > 0) {
|
||||
for (const workspace of workspaces) {
|
||||
if (await this.isValidSketchPath(workspace.file)) {
|
||||
useDefault = false;
|
||||
await this.openSketch(workspace);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.startup = false;
|
||||
if (useDefault) {
|
||||
super.launch(params);
|
||||
}
|
||||
}
|
||||
|
||||
protected async launchFromArgs(params: ElectronMainExecutionParams): Promise<boolean> {
|
||||
// Copy to prevent manipulation of original array
|
||||
const argCopy = [...params.argv];
|
||||
let uri: string | undefined;
|
||||
for (const possibleUri of argCopy) {
|
||||
if (possibleUri.endsWith('.ino') && await this.isValidSketchPath(possibleUri)) {
|
||||
uri = possibleUri;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (uri) {
|
||||
await this.openSketch(dirname(uri));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected async openSketch(workspace: WorkspaceOptions | string): Promise<BrowserWindow> {
|
||||
const options = await this.getLastWindowOptions();
|
||||
let file: string;
|
||||
if (typeof workspace === 'object') {
|
||||
options.x = workspace.x;
|
||||
options.y = workspace.y;
|
||||
options.width = workspace.width;
|
||||
options.height = workspace.height;
|
||||
options.isMaximized = workspace.isMaximized;
|
||||
options.isFullScreen = workspace.isFullScreen;
|
||||
file = workspace.file;
|
||||
} else {
|
||||
file = workspace;
|
||||
}
|
||||
const [uri, electronWindow] = await Promise.all([this.createWindowUri(), this.createWindow(options)]);
|
||||
electronWindow.loadURL(uri.withFragment(encodeURI(file)).toString(true));
|
||||
return electronWindow;
|
||||
}
|
||||
|
||||
protected avoidOverlap(options: TheiaBrowserWindowOptions): TheiaBrowserWindowOptions {
|
||||
if (this.startup) {
|
||||
return options;
|
||||
}
|
||||
return super.avoidOverlap(options);
|
||||
}
|
||||
|
||||
protected getTitleBarStyle(): 'native' | 'custom' {
|
||||
return 'native';
|
||||
}
|
||||
@@ -45,11 +158,21 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
|
||||
app.on('second-instance', this.onSecondInstance.bind(this));
|
||||
app.on('window-all-closed', this.onWindowAllClosed.bind(this));
|
||||
|
||||
ipcMain.on('restart', ({ sender }) => {
|
||||
ipcMain.on(RELOAD_REQUESTED_SIGNAL, event => this.handleReload(event));
|
||||
|
||||
ipcMain.on(Restart, ({ sender }) => {
|
||||
this.restart(sender.id);
|
||||
});
|
||||
}
|
||||
|
||||
protected async onSecondInstance(event: ElectronEvent, argv: string[], cwd: string): Promise<void> {
|
||||
if (!os.isOSX && await this.launchFromArgs({ cwd, argv, secondInstance: true })) {
|
||||
// Application has received a file in its arguments
|
||||
return;
|
||||
}
|
||||
super.onSecondInstance(event, argv, cwd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this rather than creating `BrowserWindow` instances from scratch, since some security parameters need to be set, this method will do it.
|
||||
*
|
||||
@@ -148,10 +271,12 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
|
||||
}
|
||||
}
|
||||
});
|
||||
this.attachClosedWorkspace(electronWindow);
|
||||
this.attachReadyToShow(electronWindow);
|
||||
this.attachSaveWindowState(electronWindow);
|
||||
this.attachGlobalShortcuts(electronWindow);
|
||||
this.restoreMaximizedState(electronWindow, options);
|
||||
electronRemoteMain.enable(electronWindow.webContents);
|
||||
return electronWindow;
|
||||
}
|
||||
|
||||
@@ -218,6 +343,44 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
|
||||
}
|
||||
}
|
||||
|
||||
protected closedWorkspaces: WorkspaceOptions[] = [];
|
||||
|
||||
protected attachClosedWorkspace(window: BrowserWindow): void {
|
||||
// Since the `before-quit` event is only fired when closing the *last* window
|
||||
// We need to keep track of recently closed windows/workspaces manually
|
||||
window.on('close', () => {
|
||||
const url = window.webContents.getURL();
|
||||
const workspace = URI.parse(url).fragment;
|
||||
if (workspace) {
|
||||
const workspaceUri = URI.file(workspace);
|
||||
const bounds = window.getNormalBounds();
|
||||
this.closedWorkspaces.push({
|
||||
...bounds,
|
||||
isMaximized: window.isMaximized(),
|
||||
isFullScreen: window.isFullScreen(),
|
||||
file: workspaceUri.fsPath,
|
||||
time: Date.now()
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected onWillQuit(event: Electron.Event): void {
|
||||
// Only add workspaces which were closed within the last second (1000 milliseconds)
|
||||
const threshold = Date.now() - 1000;
|
||||
const visited = new Set<string>();
|
||||
const workspaces = this.closedWorkspaces.filter(e => {
|
||||
if (e.time < threshold || visited.has(e.file)) {
|
||||
return false;
|
||||
}
|
||||
visited.add(e.file);
|
||||
return true;
|
||||
}).sort((a, b) => a.file.localeCompare(b.file));
|
||||
this.electronStore.set(WORKSPACES, workspaces);
|
||||
|
||||
super.onWillQuit(event);
|
||||
}
|
||||
|
||||
get windows(): BrowserWindow[] {
|
||||
return this._windows.slice();
|
||||
}
|
||||
|
@@ -12,7 +12,6 @@ import { Event, Emitter } from '@theia/core/lib/common/event';
|
||||
import { environment } from '@theia/application-package/lib/environment';
|
||||
import { EnvVariablesServer } from '@theia/core/lib/common/env-variables';
|
||||
import { BackendApplicationContribution } from '@theia/core/lib/node/backend-application';
|
||||
import { LocalizationProvider } from '@theia/core/lib/node/i18n/localization-provider';
|
||||
import { ArduinoDaemon, NotificationServiceServer } from '../common/protocol';
|
||||
import { DaemonLog } from './daemon-log';
|
||||
import { CLI_CONFIG } from './cli-config';
|
||||
@@ -32,9 +31,6 @@ export class ArduinoDaemonImpl
|
||||
@inject(NotificationServiceServer)
|
||||
protected readonly notificationService: NotificationServiceServer;
|
||||
|
||||
@inject(LocalizationProvider)
|
||||
protected readonly localizationProvider: LocalizationProvider;
|
||||
|
||||
protected readonly toDispose = new DisposableCollection();
|
||||
protected readonly onDaemonStartedEmitter = new Emitter<void>();
|
||||
protected readonly onDaemonStoppedEmitter = new Emitter<void>();
|
||||
|
@@ -3,9 +3,10 @@ import {
|
||||
FirmwareInfo,
|
||||
} from '../common/protocol/arduino-firmware-uploader';
|
||||
import { injectable, inject, named } from 'inversify';
|
||||
import { ExecutableService } from '../common/protocol';
|
||||
import { ExecutableService, Port } from '../common/protocol';
|
||||
import { getExecPath, spawnCommand } from './exec-util';
|
||||
import { ILogger } from '@theia/core/lib/common/logger';
|
||||
import { MonitorManager } from './monitor-manager';
|
||||
|
||||
@injectable()
|
||||
export class ArduinoFirmwareUploaderImpl implements ArduinoFirmwareUploader {
|
||||
@@ -18,6 +19,9 @@ export class ArduinoFirmwareUploaderImpl implements ArduinoFirmwareUploader {
|
||||
@named('fwuploader')
|
||||
protected readonly logger: ILogger;
|
||||
|
||||
@inject(MonitorManager)
|
||||
protected readonly monitorManager: MonitorManager;
|
||||
|
||||
protected onError(error: any): void {
|
||||
this.logger.error(error);
|
||||
}
|
||||
@@ -65,16 +69,29 @@ export class ArduinoFirmwareUploaderImpl implements ArduinoFirmwareUploader {
|
||||
return await this.list(fqbn);
|
||||
}
|
||||
|
||||
async flash(firmware: FirmwareInfo, port: string): Promise<string> {
|
||||
return await this.runCommand([
|
||||
'firmware',
|
||||
'flash',
|
||||
'--fqbn',
|
||||
firmware.board_fqbn,
|
||||
'--address',
|
||||
port,
|
||||
'--module',
|
||||
`${firmware.module}@${firmware.firmware_version}`,
|
||||
]);
|
||||
async flash(firmware: FirmwareInfo, port: Port): Promise<string> {
|
||||
let output;
|
||||
const board = {
|
||||
name: firmware.board_name,
|
||||
fqbn: firmware.board_fqbn,
|
||||
}
|
||||
try {
|
||||
this.monitorManager.notifyUploadStarted(board, port);
|
||||
output = await this.runCommand([
|
||||
'firmware',
|
||||
'flash',
|
||||
'--fqbn',
|
||||
firmware.board_fqbn,
|
||||
'--address',
|
||||
port.address,
|
||||
'--module',
|
||||
`${firmware.module}@${firmware.firmware_version}`,
|
||||
]);
|
||||
} catch (e) {
|
||||
throw e;
|
||||
} finally {
|
||||
this.monitorManager.notifyUploadFinished(board, port);
|
||||
return output;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -40,16 +40,7 @@ import {
|
||||
ArduinoDaemon,
|
||||
ArduinoDaemonPath,
|
||||
} from '../common/protocol/arduino-daemon';
|
||||
import {
|
||||
SerialServiceImpl,
|
||||
SerialServiceName,
|
||||
} from './serial/serial-service-impl';
|
||||
import {
|
||||
SerialService,
|
||||
SerialServicePath,
|
||||
SerialServiceClient,
|
||||
} from '../common/protocol/serial-service';
|
||||
import { MonitorClientProvider } from './serial/monitor-client-provider';
|
||||
|
||||
import { ConfigServiceImpl } from './config-service-impl';
|
||||
import { EnvVariablesServer as TheiaEnvVariablesServer } from '@theia/core/lib/common/env-variables';
|
||||
import { EnvVariablesServer } from './theia/env-variables/env-variables-server';
|
||||
@@ -90,8 +81,24 @@ import {
|
||||
} from '../common/protocol/authentication-service';
|
||||
import { ArduinoFirmwareUploaderImpl } from './arduino-firmware-uploader-impl';
|
||||
import { PlotterBackendContribution } from './plotter/plotter-backend-contribution';
|
||||
import WebSocketServiceImpl from './web-socket/web-socket-service-impl';
|
||||
import { WebSocketService } from './web-socket/web-socket-service';
|
||||
import { ArduinoLocalizationContribution } from './arduino-localization-contribution';
|
||||
import { LocalizationContribution } from '@theia/core/lib/node/i18n/localization-contribution';
|
||||
import { MonitorManagerProxyImpl } from './monitor-manager-proxy-impl';
|
||||
import { MonitorManager, MonitorManagerName } from './monitor-manager';
|
||||
import {
|
||||
MonitorManagerProxy,
|
||||
MonitorManagerProxyClient,
|
||||
MonitorManagerProxyPath,
|
||||
} from '../common/protocol/monitor-service';
|
||||
import { MonitorService, MonitorServiceName } from './monitor-service';
|
||||
import { MonitorSettingsProvider } from './monitor-settings/monitor-settings-provider';
|
||||
import { MonitorSettingsProviderImpl } from './monitor-settings/monitor-settings-provider-impl';
|
||||
import {
|
||||
MonitorServiceFactory,
|
||||
MonitorServiceFactoryOptions,
|
||||
} from './monitor-service-factory';
|
||||
import WebSocketProviderImpl from './web-socket/web-socket-provider-impl';
|
||||
import { WebSocketProvider } from './web-socket/web-socket-provider';
|
||||
|
||||
export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
bind(BackendApplication).toSelf().inSingletonScope();
|
||||
@@ -175,9 +182,6 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
})
|
||||
);
|
||||
|
||||
// Shared WebSocketService for the backend. This will manage all websocket conenctions
|
||||
bind(WebSocketService).to(WebSocketServiceImpl).inSingletonScope();
|
||||
|
||||
// Shared Arduino core client provider service for the backend.
|
||||
bind(CoreClientProvider).toSelf().inSingletonScope();
|
||||
|
||||
@@ -203,19 +207,57 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
|
||||
// #endregion Theia customizations
|
||||
|
||||
// Monitor client provider per connected frontend.
|
||||
// a single MonitorManager is responsible for handling the actual connections to the pluggable monitors
|
||||
bind(MonitorManager).toSelf().inSingletonScope();
|
||||
|
||||
// monitor service & factory bindings
|
||||
bind(MonitorSettingsProviderImpl).toSelf().inSingletonScope();
|
||||
bind(MonitorSettingsProvider).toService(MonitorSettingsProviderImpl);
|
||||
|
||||
bind(WebSocketProviderImpl).toSelf();
|
||||
bind(WebSocketProvider).toService(WebSocketProviderImpl);
|
||||
|
||||
bind(MonitorServiceFactory).toFactory(
|
||||
({ container }) =>
|
||||
(options: MonitorServiceFactoryOptions) => {
|
||||
const logger = container.get<ILogger>(ILogger);
|
||||
|
||||
const monitorSettingsProvider = container.get<MonitorSettingsProvider>(
|
||||
MonitorSettingsProvider
|
||||
);
|
||||
|
||||
const webSocketProvider =
|
||||
container.get<WebSocketProvider>(WebSocketProvider);
|
||||
|
||||
const { board, port, coreClientProvider } = options;
|
||||
|
||||
return new MonitorService(
|
||||
logger,
|
||||
monitorSettingsProvider,
|
||||
webSocketProvider,
|
||||
board,
|
||||
port,
|
||||
coreClientProvider
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
// Serial client provider per connected frontend.
|
||||
bind(ConnectionContainerModule).toConstantValue(
|
||||
ConnectionContainerModule.create(({ bind, bindBackendService }) => {
|
||||
bind(MonitorClientProvider).toSelf().inSingletonScope();
|
||||
bind(SerialServiceImpl).toSelf().inSingletonScope();
|
||||
bind(SerialService).toService(SerialServiceImpl);
|
||||
bindBackendService<SerialService, SerialServiceClient>(
|
||||
SerialServicePath,
|
||||
SerialService,
|
||||
(service, client) => {
|
||||
service.setClient(client);
|
||||
client.onDidCloseConnection(() => service.dispose());
|
||||
return service;
|
||||
bind(MonitorManagerProxyImpl).toSelf().inSingletonScope();
|
||||
bind(MonitorManagerProxy).toService(MonitorManagerProxyImpl);
|
||||
bindBackendService<MonitorManagerProxy, MonitorManagerProxyClient>(
|
||||
MonitorManagerProxyPath,
|
||||
MonitorManagerProxy,
|
||||
(monitorMgrProxy, client) => {
|
||||
monitorMgrProxy.setClient(client);
|
||||
// when the client close the connection, the proxy is disposed.
|
||||
// when the MonitorManagerProxy is disposed, it informs the MonitorManager
|
||||
// telling him that it does not need an address/board anymore.
|
||||
// the MonitorManager will then dispose the actual connection if there are no proxies using it
|
||||
client.onDidCloseConnection(() => monitorMgrProxy.dispose());
|
||||
return monitorMgrProxy;
|
||||
}
|
||||
);
|
||||
})
|
||||
@@ -260,17 +302,14 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
)
|
||||
.inSingletonScope();
|
||||
|
||||
bind(ArduinoFirmwareUploaderImpl).toSelf().inSingletonScope();
|
||||
bind(ArduinoFirmwareUploader).toService(ArduinoFirmwareUploaderImpl);
|
||||
bind(BackendApplicationContribution).toService(ArduinoFirmwareUploaderImpl);
|
||||
bind(ConnectionHandler)
|
||||
.toDynamicValue(
|
||||
(context) =>
|
||||
new JsonRpcConnectionHandler(ArduinoFirmwareUploaderPath, () =>
|
||||
context.container.get(ArduinoFirmwareUploader)
|
||||
)
|
||||
)
|
||||
.inSingletonScope();
|
||||
// Singleton per BE, each FE connection gets its proxy.
|
||||
bind(ConnectionContainerModule).toConstantValue(
|
||||
ConnectionContainerModule.create(({ bind, bindBackendService }) => {
|
||||
bind(ArduinoFirmwareUploaderImpl).toSelf().inSingletonScope();
|
||||
bind(ArduinoFirmwareUploader).toService(ArduinoFirmwareUploaderImpl);
|
||||
bindBackendService(ArduinoFirmwareUploaderPath, ArduinoFirmwareUploader);
|
||||
})
|
||||
);
|
||||
|
||||
// Logger for the Arduino daemon
|
||||
bind(ILogger)
|
||||
@@ -308,14 +347,22 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
.inSingletonScope()
|
||||
.whenTargetNamed('config');
|
||||
|
||||
// Logger for the serial service.
|
||||
// Logger for the monitor manager and its services
|
||||
bind(ILogger)
|
||||
.toDynamicValue((ctx) => {
|
||||
const parentLogger = ctx.container.get<ILogger>(ILogger);
|
||||
return parentLogger.child(SerialServiceName);
|
||||
return parentLogger.child(MonitorManagerName);
|
||||
})
|
||||
.inSingletonScope()
|
||||
.whenTargetNamed(SerialServiceName);
|
||||
.whenTargetNamed(MonitorManagerName);
|
||||
|
||||
bind(ILogger)
|
||||
.toDynamicValue((ctx) => {
|
||||
const parentLogger = ctx.container.get<ILogger>(ILogger);
|
||||
return parentLogger.child(MonitorServiceName);
|
||||
})
|
||||
.inSingletonScope()
|
||||
.whenTargetNamed(MonitorServiceName);
|
||||
|
||||
bind(DefaultGitInit).toSelf();
|
||||
rebind(GitInit).toService(DefaultGitInit);
|
||||
@@ -343,4 +390,6 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
|
||||
bind(PlotterBackendContribution).toSelf().inSingletonScope();
|
||||
bind(BackendApplicationContribution).toService(PlotterBackendContribution);
|
||||
bind(ArduinoLocalizationContribution).toSelf().inSingletonScope();
|
||||
bind(LocalizationContribution).toService(ArduinoLocalizationContribution);
|
||||
});
|
||||
|
@@ -0,0 +1,152 @@
|
||||
import {
|
||||
LocalizationContribution,
|
||||
LocalizationRegistry,
|
||||
} from '@theia/core/lib/node/i18n/localization-contribution';
|
||||
import { injectable } from 'inversify';
|
||||
|
||||
@injectable()
|
||||
export class ArduinoLocalizationContribution
|
||||
implements LocalizationContribution
|
||||
{
|
||||
async registerLocalizations(registry: LocalizationRegistry): Promise<void> {
|
||||
registry.registerLocalizationFromRequire(
|
||||
'af',
|
||||
require('../../build/i18n/af.json')
|
||||
);
|
||||
|
||||
registry.registerLocalizationFromRequire(
|
||||
'en',
|
||||
require('../../build/i18n/en.json')
|
||||
);
|
||||
|
||||
registry.registerLocalizationFromRequire(
|
||||
'fr',
|
||||
require('../../build/i18n/fr.json')
|
||||
);
|
||||
|
||||
registry.registerLocalizationFromRequire(
|
||||
'ko',
|
||||
require('../../build/i18n/ko.json')
|
||||
);
|
||||
|
||||
registry.registerLocalizationFromRequire(
|
||||
'pt',
|
||||
require('../../build/i18n/pt.json')
|
||||
);
|
||||
|
||||
registry.registerLocalizationFromRequire(
|
||||
'uk_UA',
|
||||
require('../../build/i18n/uk_UA.json')
|
||||
);
|
||||
|
||||
registry.registerLocalizationFromRequire(
|
||||
'ar',
|
||||
require('../../build/i18n/ar.json')
|
||||
);
|
||||
|
||||
registry.registerLocalizationFromRequire(
|
||||
'es',
|
||||
require('../../build/i18n/es.json')
|
||||
);
|
||||
|
||||
registry.registerLocalizationFromRequire(
|
||||
'he',
|
||||
require('../../build/i18n/he.json')
|
||||
);
|
||||
|
||||
registry.registerLocalizationFromRequire(
|
||||
'my_MM',
|
||||
require('../../build/i18n/my_MM.json')
|
||||
);
|
||||
|
||||
registry.registerLocalizationFromRequire(
|
||||
'ro',
|
||||
require('../../build/i18n/ro.json')
|
||||
);
|
||||
|
||||
registry.registerLocalizationFromRequire(
|
||||
'zh-cn',
|
||||
require('../../build/i18n/zh.json')
|
||||
);
|
||||
|
||||
registry.registerLocalizationFromRequire(
|
||||
'bg',
|
||||
require('../../build/i18n/bg.json')
|
||||
);
|
||||
|
||||
registry.registerLocalizationFromRequire(
|
||||
'eu',
|
||||
require('../../build/i18n/eu.json')
|
||||
);
|
||||
|
||||
registry.registerLocalizationFromRequire(
|
||||
'hu',
|
||||
require('../../build/i18n/hu.json')
|
||||
);
|
||||
|
||||
registry.registerLocalizationFromRequire(
|
||||
'ne',
|
||||
require('../../build/i18n/ne.json')
|
||||
);
|
||||
|
||||
registry.registerLocalizationFromRequire(
|
||||
'ru',
|
||||
require('../../build/i18n/ru.json')
|
||||
);
|
||||
|
||||
registry.registerLocalizationFromRequire(
|
||||
'zh_TW',
|
||||
require('../../build/i18n/zh_TW.json')
|
||||
);
|
||||
|
||||
registry.registerLocalizationFromRequire(
|
||||
'de',
|
||||
require('../../build/i18n/de.json')
|
||||
);
|
||||
|
||||
registry.registerLocalizationFromRequire(
|
||||
'fa',
|
||||
require('../../build/i18n/fa.json')
|
||||
);
|
||||
|
||||
registry.registerLocalizationFromRequire(
|
||||
'it',
|
||||
require('../../build/i18n/it.json')
|
||||
);
|
||||
|
||||
registry.registerLocalizationFromRequire(
|
||||
'nl',
|
||||
require('../../build/i18n/nl.json')
|
||||
);
|
||||
|
||||
registry.registerLocalizationFromRequire(
|
||||
'sv_SE',
|
||||
require('../../build/i18n/sv_SE.json')
|
||||
);
|
||||
|
||||
registry.registerLocalizationFromRequire(
|
||||
'el',
|
||||
require('../../build/i18n/el.json')
|
||||
);
|
||||
|
||||
registry.registerLocalizationFromRequire(
|
||||
'fil',
|
||||
require('../../build/i18n/fil.json')
|
||||
);
|
||||
|
||||
registry.registerLocalizationFromRequire(
|
||||
'ja',
|
||||
require('../../build/i18n/ja.json')
|
||||
);
|
||||
|
||||
registry.registerLocalizationFromRequire(
|
||||
'pl',
|
||||
require('../../build/i18n/pl.json')
|
||||
);
|
||||
|
||||
registry.registerLocalizationFromRequire(
|
||||
'tr',
|
||||
require('../../build/i18n/tr.json')
|
||||
);
|
||||
}
|
||||
}
|
@@ -124,8 +124,22 @@ export class BoardDiscovery extends CoreClientAware {
|
||||
|
||||
const address = (detectedPort as any).getPort().getAddress();
|
||||
const protocol = (detectedPort as any).getPort().getProtocol();
|
||||
// Different discoveries can detect the same port with different
|
||||
// protocols, so we consider the combination of address and protocol
|
||||
// to be the id of a certain port to distinguish it from others.
|
||||
// If we'd use only the address of a port to store it in a map
|
||||
// we can have conflicts the same port is found with multiple
|
||||
// protocols.
|
||||
const portID = `${address}|${protocol}`;
|
||||
const label = (detectedPort as any).getPort().getLabel();
|
||||
const port = { address, protocol, label };
|
||||
const protocolLabel = (detectedPort as any).getPort().getProtocolLabel();
|
||||
const port = {
|
||||
id: portID,
|
||||
address,
|
||||
addressLabel: label,
|
||||
protocol,
|
||||
protocolLabel,
|
||||
};
|
||||
const boards: Board[] = [];
|
||||
for (const item of detectedPort.getMatchingBoardsList()) {
|
||||
boards.push({
|
||||
@@ -136,23 +150,21 @@ export class BoardDiscovery extends CoreClientAware {
|
||||
}
|
||||
|
||||
if (eventType === 'add') {
|
||||
if (newState[port.address]) {
|
||||
const [, knownBoards] = newState[port.address];
|
||||
if (newState[portID]) {
|
||||
const [, knownBoards] = newState[portID];
|
||||
console.warn(
|
||||
`Port '${
|
||||
port.address
|
||||
}' was already available. Known boards before override: ${JSON.stringify(
|
||||
`Port '${Port.toString(port)}' was already available. Known boards before override: ${JSON.stringify(
|
||||
knownBoards
|
||||
)}`
|
||||
);
|
||||
}
|
||||
newState[port.address] = [port, boards];
|
||||
newState[portID] = [port, boards];
|
||||
} else if (eventType === 'remove') {
|
||||
if (!newState[port.address]) {
|
||||
console.warn(`Port '${port.address}' was not available. Skipping`);
|
||||
if (!newState[portID]) {
|
||||
console.warn(`Port '${Port.toString(port)}' was not available. Skipping`);
|
||||
return;
|
||||
}
|
||||
delete newState[port.address];
|
||||
delete newState[portID];
|
||||
}
|
||||
|
||||
const oldAvailablePorts = this.getAvailablePorts(oldState);
|
||||
@@ -179,8 +191,8 @@ export class BoardDiscovery extends CoreClientAware {
|
||||
|
||||
getAttachedBoards(state: AvailablePorts = this.state): Board[] {
|
||||
const attachedBoards: Board[] = [];
|
||||
for (const address of Object.keys(state)) {
|
||||
const [, boards] = state[address];
|
||||
for (const portID of Object.keys(state)) {
|
||||
const [, boards] = state[portID];
|
||||
attachedBoards.push(...boards);
|
||||
}
|
||||
return attachedBoards;
|
||||
@@ -188,8 +200,8 @@ export class BoardDiscovery extends CoreClientAware {
|
||||
|
||||
getAvailablePorts(state: AvailablePorts = this.state): Port[] {
|
||||
const availablePorts: Port[] = [];
|
||||
for (const address of Object.keys(state)) {
|
||||
const [port] = state[address];
|
||||
for (const portID of Object.keys(state)) {
|
||||
const [port] = state[portID];
|
||||
availablePorts.push(port);
|
||||
}
|
||||
return availablePorts;
|
||||
|
@@ -12,6 +12,7 @@ import * as cc_arduino_cli_commands_v1_common_pb from "../../../../../cc/arduino
|
||||
import * as cc_arduino_cli_commands_v1_board_pb from "../../../../../cc/arduino/cli/commands/v1/board_pb";
|
||||
import * as cc_arduino_cli_commands_v1_compile_pb from "../../../../../cc/arduino/cli/commands/v1/compile_pb";
|
||||
import * as cc_arduino_cli_commands_v1_core_pb from "../../../../../cc/arduino/cli/commands/v1/core_pb";
|
||||
import * as cc_arduino_cli_commands_v1_monitor_pb from "../../../../../cc/arduino/cli/commands/v1/monitor_pb";
|
||||
import * as cc_arduino_cli_commands_v1_upload_pb from "../../../../../cc/arduino/cli/commands/v1/upload_pb";
|
||||
import * as cc_arduino_cli_commands_v1_lib_pb from "../../../../../cc/arduino/cli/commands/v1/lib_pb";
|
||||
|
||||
@@ -25,6 +26,7 @@ interface IArduinoCoreServiceService extends grpc.ServiceDefinition<grpc.Untyped
|
||||
outdated: IArduinoCoreServiceService_IOutdated;
|
||||
upgrade: IArduinoCoreServiceService_IUpgrade;
|
||||
version: IArduinoCoreServiceService_IVersion;
|
||||
newSketch: IArduinoCoreServiceService_INewSketch;
|
||||
loadSketch: IArduinoCoreServiceService_ILoadSketch;
|
||||
archiveSketch: IArduinoCoreServiceService_IArchiveSketch;
|
||||
boardDetails: IArduinoCoreServiceService_IBoardDetails;
|
||||
@@ -54,6 +56,8 @@ interface IArduinoCoreServiceService extends grpc.ServiceDefinition<grpc.Untyped
|
||||
libraryResolveDependencies: IArduinoCoreServiceService_ILibraryResolveDependencies;
|
||||
librarySearch: IArduinoCoreServiceService_ILibrarySearch;
|
||||
libraryList: IArduinoCoreServiceService_ILibraryList;
|
||||
monitor: IArduinoCoreServiceService_IMonitor;
|
||||
enumerateMonitorPortSettings: IArduinoCoreServiceService_IEnumerateMonitorPortSettings;
|
||||
}
|
||||
|
||||
interface IArduinoCoreServiceService_ICreate extends grpc.MethodDefinition<cc_arduino_cli_commands_v1_commands_pb.CreateRequest, cc_arduino_cli_commands_v1_commands_pb.CreateResponse> {
|
||||
@@ -137,6 +141,15 @@ interface IArduinoCoreServiceService_IVersion extends grpc.MethodDefinition<cc_a
|
||||
responseSerialize: grpc.serialize<cc_arduino_cli_commands_v1_commands_pb.VersionResponse>;
|
||||
responseDeserialize: grpc.deserialize<cc_arduino_cli_commands_v1_commands_pb.VersionResponse>;
|
||||
}
|
||||
interface IArduinoCoreServiceService_INewSketch extends grpc.MethodDefinition<cc_arduino_cli_commands_v1_commands_pb.NewSketchRequest, cc_arduino_cli_commands_v1_commands_pb.NewSketchResponse> {
|
||||
path: "/cc.arduino.cli.commands.v1.ArduinoCoreService/NewSketch";
|
||||
requestStream: false;
|
||||
responseStream: false;
|
||||
requestSerialize: grpc.serialize<cc_arduino_cli_commands_v1_commands_pb.NewSketchRequest>;
|
||||
requestDeserialize: grpc.deserialize<cc_arduino_cli_commands_v1_commands_pb.NewSketchRequest>;
|
||||
responseSerialize: grpc.serialize<cc_arduino_cli_commands_v1_commands_pb.NewSketchResponse>;
|
||||
responseDeserialize: grpc.deserialize<cc_arduino_cli_commands_v1_commands_pb.NewSketchResponse>;
|
||||
}
|
||||
interface IArduinoCoreServiceService_ILoadSketch extends grpc.MethodDefinition<cc_arduino_cli_commands_v1_commands_pb.LoadSketchRequest, cc_arduino_cli_commands_v1_commands_pb.LoadSketchResponse> {
|
||||
path: "/cc.arduino.cli.commands.v1.ArduinoCoreService/LoadSketch";
|
||||
requestStream: false;
|
||||
@@ -398,6 +411,24 @@ interface IArduinoCoreServiceService_ILibraryList extends grpc.MethodDefinition<
|
||||
responseSerialize: grpc.serialize<cc_arduino_cli_commands_v1_lib_pb.LibraryListResponse>;
|
||||
responseDeserialize: grpc.deserialize<cc_arduino_cli_commands_v1_lib_pb.LibraryListResponse>;
|
||||
}
|
||||
interface IArduinoCoreServiceService_IMonitor extends grpc.MethodDefinition<cc_arduino_cli_commands_v1_monitor_pb.MonitorRequest, cc_arduino_cli_commands_v1_monitor_pb.MonitorResponse> {
|
||||
path: "/cc.arduino.cli.commands.v1.ArduinoCoreService/Monitor";
|
||||
requestStream: true;
|
||||
responseStream: true;
|
||||
requestSerialize: grpc.serialize<cc_arduino_cli_commands_v1_monitor_pb.MonitorRequest>;
|
||||
requestDeserialize: grpc.deserialize<cc_arduino_cli_commands_v1_monitor_pb.MonitorRequest>;
|
||||
responseSerialize: grpc.serialize<cc_arduino_cli_commands_v1_monitor_pb.MonitorResponse>;
|
||||
responseDeserialize: grpc.deserialize<cc_arduino_cli_commands_v1_monitor_pb.MonitorResponse>;
|
||||
}
|
||||
interface IArduinoCoreServiceService_IEnumerateMonitorPortSettings extends grpc.MethodDefinition<cc_arduino_cli_commands_v1_monitor_pb.EnumerateMonitorPortSettingsRequest, cc_arduino_cli_commands_v1_monitor_pb.EnumerateMonitorPortSettingsResponse> {
|
||||
path: "/cc.arduino.cli.commands.v1.ArduinoCoreService/EnumerateMonitorPortSettings";
|
||||
requestStream: false;
|
||||
responseStream: false;
|
||||
requestSerialize: grpc.serialize<cc_arduino_cli_commands_v1_monitor_pb.EnumerateMonitorPortSettingsRequest>;
|
||||
requestDeserialize: grpc.deserialize<cc_arduino_cli_commands_v1_monitor_pb.EnumerateMonitorPortSettingsRequest>;
|
||||
responseSerialize: grpc.serialize<cc_arduino_cli_commands_v1_monitor_pb.EnumerateMonitorPortSettingsResponse>;
|
||||
responseDeserialize: grpc.deserialize<cc_arduino_cli_commands_v1_monitor_pb.EnumerateMonitorPortSettingsResponse>;
|
||||
}
|
||||
|
||||
export const ArduinoCoreServiceService: IArduinoCoreServiceService;
|
||||
|
||||
@@ -411,6 +442,7 @@ export interface IArduinoCoreServiceServer {
|
||||
outdated: grpc.handleUnaryCall<cc_arduino_cli_commands_v1_commands_pb.OutdatedRequest, cc_arduino_cli_commands_v1_commands_pb.OutdatedResponse>;
|
||||
upgrade: grpc.handleServerStreamingCall<cc_arduino_cli_commands_v1_commands_pb.UpgradeRequest, cc_arduino_cli_commands_v1_commands_pb.UpgradeResponse>;
|
||||
version: grpc.handleUnaryCall<cc_arduino_cli_commands_v1_commands_pb.VersionRequest, cc_arduino_cli_commands_v1_commands_pb.VersionResponse>;
|
||||
newSketch: grpc.handleUnaryCall<cc_arduino_cli_commands_v1_commands_pb.NewSketchRequest, cc_arduino_cli_commands_v1_commands_pb.NewSketchResponse>;
|
||||
loadSketch: grpc.handleUnaryCall<cc_arduino_cli_commands_v1_commands_pb.LoadSketchRequest, cc_arduino_cli_commands_v1_commands_pb.LoadSketchResponse>;
|
||||
archiveSketch: grpc.handleUnaryCall<cc_arduino_cli_commands_v1_commands_pb.ArchiveSketchRequest, cc_arduino_cli_commands_v1_commands_pb.ArchiveSketchResponse>;
|
||||
boardDetails: grpc.handleUnaryCall<cc_arduino_cli_commands_v1_board_pb.BoardDetailsRequest, cc_arduino_cli_commands_v1_board_pb.BoardDetailsResponse>;
|
||||
@@ -440,6 +472,8 @@ export interface IArduinoCoreServiceServer {
|
||||
libraryResolveDependencies: grpc.handleUnaryCall<cc_arduino_cli_commands_v1_lib_pb.LibraryResolveDependenciesRequest, cc_arduino_cli_commands_v1_lib_pb.LibraryResolveDependenciesResponse>;
|
||||
librarySearch: grpc.handleUnaryCall<cc_arduino_cli_commands_v1_lib_pb.LibrarySearchRequest, cc_arduino_cli_commands_v1_lib_pb.LibrarySearchResponse>;
|
||||
libraryList: grpc.handleUnaryCall<cc_arduino_cli_commands_v1_lib_pb.LibraryListRequest, cc_arduino_cli_commands_v1_lib_pb.LibraryListResponse>;
|
||||
monitor: grpc.handleBidiStreamingCall<cc_arduino_cli_commands_v1_monitor_pb.MonitorRequest, cc_arduino_cli_commands_v1_monitor_pb.MonitorResponse>;
|
||||
enumerateMonitorPortSettings: grpc.handleUnaryCall<cc_arduino_cli_commands_v1_monitor_pb.EnumerateMonitorPortSettingsRequest, cc_arduino_cli_commands_v1_monitor_pb.EnumerateMonitorPortSettingsResponse>;
|
||||
}
|
||||
|
||||
export interface IArduinoCoreServiceClient {
|
||||
@@ -465,6 +499,9 @@ export interface IArduinoCoreServiceClient {
|
||||
version(request: cc_arduino_cli_commands_v1_commands_pb.VersionRequest, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_commands_pb.VersionResponse) => void): grpc.ClientUnaryCall;
|
||||
version(request: cc_arduino_cli_commands_v1_commands_pb.VersionRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_commands_pb.VersionResponse) => void): grpc.ClientUnaryCall;
|
||||
version(request: cc_arduino_cli_commands_v1_commands_pb.VersionRequest, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_commands_pb.VersionResponse) => void): grpc.ClientUnaryCall;
|
||||
newSketch(request: cc_arduino_cli_commands_v1_commands_pb.NewSketchRequest, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_commands_pb.NewSketchResponse) => void): grpc.ClientUnaryCall;
|
||||
newSketch(request: cc_arduino_cli_commands_v1_commands_pb.NewSketchRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_commands_pb.NewSketchResponse) => void): grpc.ClientUnaryCall;
|
||||
newSketch(request: cc_arduino_cli_commands_v1_commands_pb.NewSketchRequest, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_commands_pb.NewSketchResponse) => void): grpc.ClientUnaryCall;
|
||||
loadSketch(request: cc_arduino_cli_commands_v1_commands_pb.LoadSketchRequest, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_commands_pb.LoadSketchResponse) => void): grpc.ClientUnaryCall;
|
||||
loadSketch(request: cc_arduino_cli_commands_v1_commands_pb.LoadSketchRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_commands_pb.LoadSketchResponse) => void): grpc.ClientUnaryCall;
|
||||
loadSketch(request: cc_arduino_cli_commands_v1_commands_pb.LoadSketchRequest, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_commands_pb.LoadSketchResponse) => void): grpc.ClientUnaryCall;
|
||||
@@ -537,6 +574,12 @@ export interface IArduinoCoreServiceClient {
|
||||
libraryList(request: cc_arduino_cli_commands_v1_lib_pb.LibraryListRequest, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_lib_pb.LibraryListResponse) => void): grpc.ClientUnaryCall;
|
||||
libraryList(request: cc_arduino_cli_commands_v1_lib_pb.LibraryListRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_lib_pb.LibraryListResponse) => void): grpc.ClientUnaryCall;
|
||||
libraryList(request: cc_arduino_cli_commands_v1_lib_pb.LibraryListRequest, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_lib_pb.LibraryListResponse) => void): grpc.ClientUnaryCall;
|
||||
monitor(): grpc.ClientDuplexStream<cc_arduino_cli_commands_v1_monitor_pb.MonitorRequest, cc_arduino_cli_commands_v1_monitor_pb.MonitorResponse>;
|
||||
monitor(options: Partial<grpc.CallOptions>): grpc.ClientDuplexStream<cc_arduino_cli_commands_v1_monitor_pb.MonitorRequest, cc_arduino_cli_commands_v1_monitor_pb.MonitorResponse>;
|
||||
monitor(metadata: grpc.Metadata, options?: Partial<grpc.CallOptions>): grpc.ClientDuplexStream<cc_arduino_cli_commands_v1_monitor_pb.MonitorRequest, cc_arduino_cli_commands_v1_monitor_pb.MonitorResponse>;
|
||||
enumerateMonitorPortSettings(request: cc_arduino_cli_commands_v1_monitor_pb.EnumerateMonitorPortSettingsRequest, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_monitor_pb.EnumerateMonitorPortSettingsResponse) => void): grpc.ClientUnaryCall;
|
||||
enumerateMonitorPortSettings(request: cc_arduino_cli_commands_v1_monitor_pb.EnumerateMonitorPortSettingsRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_monitor_pb.EnumerateMonitorPortSettingsResponse) => void): grpc.ClientUnaryCall;
|
||||
enumerateMonitorPortSettings(request: cc_arduino_cli_commands_v1_monitor_pb.EnumerateMonitorPortSettingsRequest, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_monitor_pb.EnumerateMonitorPortSettingsResponse) => void): grpc.ClientUnaryCall;
|
||||
}
|
||||
|
||||
export class ArduinoCoreServiceClient extends grpc.Client implements IArduinoCoreServiceClient {
|
||||
@@ -563,6 +606,9 @@ export class ArduinoCoreServiceClient extends grpc.Client implements IArduinoCor
|
||||
public version(request: cc_arduino_cli_commands_v1_commands_pb.VersionRequest, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_commands_pb.VersionResponse) => void): grpc.ClientUnaryCall;
|
||||
public version(request: cc_arduino_cli_commands_v1_commands_pb.VersionRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_commands_pb.VersionResponse) => void): grpc.ClientUnaryCall;
|
||||
public version(request: cc_arduino_cli_commands_v1_commands_pb.VersionRequest, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_commands_pb.VersionResponse) => void): grpc.ClientUnaryCall;
|
||||
public newSketch(request: cc_arduino_cli_commands_v1_commands_pb.NewSketchRequest, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_commands_pb.NewSketchResponse) => void): grpc.ClientUnaryCall;
|
||||
public newSketch(request: cc_arduino_cli_commands_v1_commands_pb.NewSketchRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_commands_pb.NewSketchResponse) => void): grpc.ClientUnaryCall;
|
||||
public newSketch(request: cc_arduino_cli_commands_v1_commands_pb.NewSketchRequest, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_commands_pb.NewSketchResponse) => void): grpc.ClientUnaryCall;
|
||||
public loadSketch(request: cc_arduino_cli_commands_v1_commands_pb.LoadSketchRequest, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_commands_pb.LoadSketchResponse) => void): grpc.ClientUnaryCall;
|
||||
public loadSketch(request: cc_arduino_cli_commands_v1_commands_pb.LoadSketchRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_commands_pb.LoadSketchResponse) => void): grpc.ClientUnaryCall;
|
||||
public loadSketch(request: cc_arduino_cli_commands_v1_commands_pb.LoadSketchRequest, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_commands_pb.LoadSketchResponse) => void): grpc.ClientUnaryCall;
|
||||
@@ -634,4 +680,9 @@ export class ArduinoCoreServiceClient extends grpc.Client implements IArduinoCor
|
||||
public libraryList(request: cc_arduino_cli_commands_v1_lib_pb.LibraryListRequest, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_lib_pb.LibraryListResponse) => void): grpc.ClientUnaryCall;
|
||||
public libraryList(request: cc_arduino_cli_commands_v1_lib_pb.LibraryListRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_lib_pb.LibraryListResponse) => void): grpc.ClientUnaryCall;
|
||||
public libraryList(request: cc_arduino_cli_commands_v1_lib_pb.LibraryListRequest, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_lib_pb.LibraryListResponse) => void): grpc.ClientUnaryCall;
|
||||
public monitor(options?: Partial<grpc.CallOptions>): grpc.ClientDuplexStream<cc_arduino_cli_commands_v1_monitor_pb.MonitorRequest, cc_arduino_cli_commands_v1_monitor_pb.MonitorResponse>;
|
||||
public monitor(metadata?: grpc.Metadata, options?: Partial<grpc.CallOptions>): grpc.ClientDuplexStream<cc_arduino_cli_commands_v1_monitor_pb.MonitorRequest, cc_arduino_cli_commands_v1_monitor_pb.MonitorResponse>;
|
||||
public enumerateMonitorPortSettings(request: cc_arduino_cli_commands_v1_monitor_pb.EnumerateMonitorPortSettingsRequest, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_monitor_pb.EnumerateMonitorPortSettingsResponse) => void): grpc.ClientUnaryCall;
|
||||
public enumerateMonitorPortSettings(request: cc_arduino_cli_commands_v1_monitor_pb.EnumerateMonitorPortSettingsRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_monitor_pb.EnumerateMonitorPortSettingsResponse) => void): grpc.ClientUnaryCall;
|
||||
public enumerateMonitorPortSettings(request: cc_arduino_cli_commands_v1_monitor_pb.EnumerateMonitorPortSettingsRequest, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_monitor_pb.EnumerateMonitorPortSettingsResponse) => void): grpc.ClientUnaryCall;
|
||||
}
|
||||
|
@@ -23,6 +23,7 @@ var cc_arduino_cli_commands_v1_common_pb = require('../../../../../cc/arduino/cl
|
||||
var cc_arduino_cli_commands_v1_board_pb = require('../../../../../cc/arduino/cli/commands/v1/board_pb.js');
|
||||
var cc_arduino_cli_commands_v1_compile_pb = require('../../../../../cc/arduino/cli/commands/v1/compile_pb.js');
|
||||
var cc_arduino_cli_commands_v1_core_pb = require('../../../../../cc/arduino/cli/commands/v1/core_pb.js');
|
||||
var cc_arduino_cli_commands_v1_monitor_pb = require('../../../../../cc/arduino/cli/commands/v1/monitor_pb.js');
|
||||
var cc_arduino_cli_commands_v1_upload_pb = require('../../../../../cc/arduino/cli/commands/v1/upload_pb.js');
|
||||
var cc_arduino_cli_commands_v1_lib_pb = require('../../../../../cc/arduino/cli/commands/v1/lib_pb.js');
|
||||
|
||||
@@ -268,6 +269,28 @@ function deserialize_cc_arduino_cli_commands_v1_DestroyResponse(buffer_arg) {
|
||||
return cc_arduino_cli_commands_v1_commands_pb.DestroyResponse.deserializeBinary(new Uint8Array(buffer_arg));
|
||||
}
|
||||
|
||||
function serialize_cc_arduino_cli_commands_v1_EnumerateMonitorPortSettingsRequest(arg) {
|
||||
if (!(arg instanceof cc_arduino_cli_commands_v1_monitor_pb.EnumerateMonitorPortSettingsRequest)) {
|
||||
throw new Error('Expected argument of type cc.arduino.cli.commands.v1.EnumerateMonitorPortSettingsRequest');
|
||||
}
|
||||
return Buffer.from(arg.serializeBinary());
|
||||
}
|
||||
|
||||
function deserialize_cc_arduino_cli_commands_v1_EnumerateMonitorPortSettingsRequest(buffer_arg) {
|
||||
return cc_arduino_cli_commands_v1_monitor_pb.EnumerateMonitorPortSettingsRequest.deserializeBinary(new Uint8Array(buffer_arg));
|
||||
}
|
||||
|
||||
function serialize_cc_arduino_cli_commands_v1_EnumerateMonitorPortSettingsResponse(arg) {
|
||||
if (!(arg instanceof cc_arduino_cli_commands_v1_monitor_pb.EnumerateMonitorPortSettingsResponse)) {
|
||||
throw new Error('Expected argument of type cc.arduino.cli.commands.v1.EnumerateMonitorPortSettingsResponse');
|
||||
}
|
||||
return Buffer.from(arg.serializeBinary());
|
||||
}
|
||||
|
||||
function deserialize_cc_arduino_cli_commands_v1_EnumerateMonitorPortSettingsResponse(buffer_arg) {
|
||||
return cc_arduino_cli_commands_v1_monitor_pb.EnumerateMonitorPortSettingsResponse.deserializeBinary(new Uint8Array(buffer_arg));
|
||||
}
|
||||
|
||||
function serialize_cc_arduino_cli_commands_v1_GitLibraryInstallRequest(arg) {
|
||||
if (!(arg instanceof cc_arduino_cli_commands_v1_lib_pb.GitLibraryInstallRequest)) {
|
||||
throw new Error('Expected argument of type cc.arduino.cli.commands.v1.GitLibraryInstallRequest');
|
||||
@@ -510,6 +533,50 @@ function deserialize_cc_arduino_cli_commands_v1_LoadSketchResponse(buffer_arg) {
|
||||
return cc_arduino_cli_commands_v1_commands_pb.LoadSketchResponse.deserializeBinary(new Uint8Array(buffer_arg));
|
||||
}
|
||||
|
||||
function serialize_cc_arduino_cli_commands_v1_MonitorRequest(arg) {
|
||||
if (!(arg instanceof cc_arduino_cli_commands_v1_monitor_pb.MonitorRequest)) {
|
||||
throw new Error('Expected argument of type cc.arduino.cli.commands.v1.MonitorRequest');
|
||||
}
|
||||
return Buffer.from(arg.serializeBinary());
|
||||
}
|
||||
|
||||
function deserialize_cc_arduino_cli_commands_v1_MonitorRequest(buffer_arg) {
|
||||
return cc_arduino_cli_commands_v1_monitor_pb.MonitorRequest.deserializeBinary(new Uint8Array(buffer_arg));
|
||||
}
|
||||
|
||||
function serialize_cc_arduino_cli_commands_v1_MonitorResponse(arg) {
|
||||
if (!(arg instanceof cc_arduino_cli_commands_v1_monitor_pb.MonitorResponse)) {
|
||||
throw new Error('Expected argument of type cc.arduino.cli.commands.v1.MonitorResponse');
|
||||
}
|
||||
return Buffer.from(arg.serializeBinary());
|
||||
}
|
||||
|
||||
function deserialize_cc_arduino_cli_commands_v1_MonitorResponse(buffer_arg) {
|
||||
return cc_arduino_cli_commands_v1_monitor_pb.MonitorResponse.deserializeBinary(new Uint8Array(buffer_arg));
|
||||
}
|
||||
|
||||
function serialize_cc_arduino_cli_commands_v1_NewSketchRequest(arg) {
|
||||
if (!(arg instanceof cc_arduino_cli_commands_v1_commands_pb.NewSketchRequest)) {
|
||||
throw new Error('Expected argument of type cc.arduino.cli.commands.v1.NewSketchRequest');
|
||||
}
|
||||
return Buffer.from(arg.serializeBinary());
|
||||
}
|
||||
|
||||
function deserialize_cc_arduino_cli_commands_v1_NewSketchRequest(buffer_arg) {
|
||||
return cc_arduino_cli_commands_v1_commands_pb.NewSketchRequest.deserializeBinary(new Uint8Array(buffer_arg));
|
||||
}
|
||||
|
||||
function serialize_cc_arduino_cli_commands_v1_NewSketchResponse(arg) {
|
||||
if (!(arg instanceof cc_arduino_cli_commands_v1_commands_pb.NewSketchResponse)) {
|
||||
throw new Error('Expected argument of type cc.arduino.cli.commands.v1.NewSketchResponse');
|
||||
}
|
||||
return Buffer.from(arg.serializeBinary());
|
||||
}
|
||||
|
||||
function deserialize_cc_arduino_cli_commands_v1_NewSketchResponse(buffer_arg) {
|
||||
return cc_arduino_cli_commands_v1_commands_pb.NewSketchResponse.deserializeBinary(new Uint8Array(buffer_arg));
|
||||
}
|
||||
|
||||
function serialize_cc_arduino_cli_commands_v1_OutdatedRequest(arg) {
|
||||
if (!(arg instanceof cc_arduino_cli_commands_v1_commands_pb.OutdatedRequest)) {
|
||||
throw new Error('Expected argument of type cc.arduino.cli.commands.v1.OutdatedRequest');
|
||||
@@ -974,6 +1041,18 @@ version: {
|
||||
responseSerialize: serialize_cc_arduino_cli_commands_v1_VersionResponse,
|
||||
responseDeserialize: deserialize_cc_arduino_cli_commands_v1_VersionResponse,
|
||||
},
|
||||
// Create a new Sketch
|
||||
newSketch: {
|
||||
path: '/cc.arduino.cli.commands.v1.ArduinoCoreService/NewSketch',
|
||||
requestStream: false,
|
||||
responseStream: false,
|
||||
requestType: cc_arduino_cli_commands_v1_commands_pb.NewSketchRequest,
|
||||
responseType: cc_arduino_cli_commands_v1_commands_pb.NewSketchResponse,
|
||||
requestSerialize: serialize_cc_arduino_cli_commands_v1_NewSketchRequest,
|
||||
requestDeserialize: deserialize_cc_arduino_cli_commands_v1_NewSketchRequest,
|
||||
responseSerialize: serialize_cc_arduino_cli_commands_v1_NewSketchResponse,
|
||||
responseDeserialize: deserialize_cc_arduino_cli_commands_v1_NewSketchResponse,
|
||||
},
|
||||
// Returns all files composing a Sketch
|
||||
loadSketch: {
|
||||
path: '/cc.arduino.cli.commands.v1.ArduinoCoreService/LoadSketch',
|
||||
@@ -1331,6 +1410,30 @@ libraryList: {
|
||||
responseSerialize: serialize_cc_arduino_cli_commands_v1_LibraryListResponse,
|
||||
responseDeserialize: deserialize_cc_arduino_cli_commands_v1_LibraryListResponse,
|
||||
},
|
||||
// Open a monitor connection to a board port
|
||||
monitor: {
|
||||
path: '/cc.arduino.cli.commands.v1.ArduinoCoreService/Monitor',
|
||||
requestStream: true,
|
||||
responseStream: true,
|
||||
requestType: cc_arduino_cli_commands_v1_monitor_pb.MonitorRequest,
|
||||
responseType: cc_arduino_cli_commands_v1_monitor_pb.MonitorResponse,
|
||||
requestSerialize: serialize_cc_arduino_cli_commands_v1_MonitorRequest,
|
||||
requestDeserialize: deserialize_cc_arduino_cli_commands_v1_MonitorRequest,
|
||||
responseSerialize: serialize_cc_arduino_cli_commands_v1_MonitorResponse,
|
||||
responseDeserialize: deserialize_cc_arduino_cli_commands_v1_MonitorResponse,
|
||||
},
|
||||
// Returns the parameters that can be set in the MonitorRequest calls
|
||||
enumerateMonitorPortSettings: {
|
||||
path: '/cc.arduino.cli.commands.v1.ArduinoCoreService/EnumerateMonitorPortSettings',
|
||||
requestStream: false,
|
||||
responseStream: false,
|
||||
requestType: cc_arduino_cli_commands_v1_monitor_pb.EnumerateMonitorPortSettingsRequest,
|
||||
responseType: cc_arduino_cli_commands_v1_monitor_pb.EnumerateMonitorPortSettingsResponse,
|
||||
requestSerialize: serialize_cc_arduino_cli_commands_v1_EnumerateMonitorPortSettingsRequest,
|
||||
requestDeserialize: deserialize_cc_arduino_cli_commands_v1_EnumerateMonitorPortSettingsRequest,
|
||||
responseSerialize: serialize_cc_arduino_cli_commands_v1_EnumerateMonitorPortSettingsResponse,
|
||||
responseDeserialize: deserialize_cc_arduino_cli_commands_v1_EnumerateMonitorPortSettingsResponse,
|
||||
},
|
||||
};
|
||||
|
||||
// BOOTSTRAP COMMANDS
|
||||
|
@@ -10,6 +10,7 @@ import * as cc_arduino_cli_commands_v1_common_pb from "../../../../../cc/arduino
|
||||
import * as cc_arduino_cli_commands_v1_board_pb from "../../../../../cc/arduino/cli/commands/v1/board_pb";
|
||||
import * as cc_arduino_cli_commands_v1_compile_pb from "../../../../../cc/arduino/cli/commands/v1/compile_pb";
|
||||
import * as cc_arduino_cli_commands_v1_core_pb from "../../../../../cc/arduino/cli/commands/v1/core_pb";
|
||||
import * as cc_arduino_cli_commands_v1_monitor_pb from "../../../../../cc/arduino/cli/commands/v1/monitor_pb";
|
||||
import * as cc_arduino_cli_commands_v1_upload_pb from "../../../../../cc/arduino/cli/commands/v1/upload_pb";
|
||||
import * as cc_arduino_cli_commands_v1_lib_pb from "../../../../../cc/arduino/cli/commands/v1/lib_pb";
|
||||
|
||||
@@ -489,6 +490,59 @@ export namespace VersionResponse {
|
||||
}
|
||||
}
|
||||
|
||||
export class NewSketchRequest 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): NewSketchRequest;
|
||||
|
||||
getSketchName(): string;
|
||||
setSketchName(value: string): NewSketchRequest;
|
||||
|
||||
getSketchDir(): string;
|
||||
setSketchDir(value: string): NewSketchRequest;
|
||||
|
||||
|
||||
serializeBinary(): Uint8Array;
|
||||
toObject(includeInstance?: boolean): NewSketchRequest.AsObject;
|
||||
static toObject(includeInstance: boolean, msg: NewSketchRequest): NewSketchRequest.AsObject;
|
||||
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
|
||||
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
|
||||
static serializeBinaryToWriter(message: NewSketchRequest, writer: jspb.BinaryWriter): void;
|
||||
static deserializeBinary(bytes: Uint8Array): NewSketchRequest;
|
||||
static deserializeBinaryFromReader(message: NewSketchRequest, reader: jspb.BinaryReader): NewSketchRequest;
|
||||
}
|
||||
|
||||
export namespace NewSketchRequest {
|
||||
export type AsObject = {
|
||||
instance?: cc_arduino_cli_commands_v1_common_pb.Instance.AsObject,
|
||||
sketchName: string,
|
||||
sketchDir: string,
|
||||
}
|
||||
}
|
||||
|
||||
export class NewSketchResponse extends jspb.Message {
|
||||
getMainFile(): string;
|
||||
setMainFile(value: string): NewSketchResponse;
|
||||
|
||||
|
||||
serializeBinary(): Uint8Array;
|
||||
toObject(includeInstance?: boolean): NewSketchResponse.AsObject;
|
||||
static toObject(includeInstance: boolean, msg: NewSketchResponse): NewSketchResponse.AsObject;
|
||||
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
|
||||
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
|
||||
static serializeBinaryToWriter(message: NewSketchResponse, writer: jspb.BinaryWriter): void;
|
||||
static deserializeBinary(bytes: Uint8Array): NewSketchResponse;
|
||||
static deserializeBinaryFromReader(message: NewSketchResponse, reader: jspb.BinaryReader): NewSketchResponse;
|
||||
}
|
||||
|
||||
export namespace NewSketchResponse {
|
||||
export type AsObject = {
|
||||
mainFile: string,
|
||||
}
|
||||
}
|
||||
|
||||
export class LoadSketchRequest extends jspb.Message {
|
||||
|
||||
hasInstance(): boolean;
|
||||
|
@@ -25,6 +25,8 @@ var cc_arduino_cli_commands_v1_compile_pb = require('../../../../../cc/arduino/c
|
||||
goog.object.extend(proto, cc_arduino_cli_commands_v1_compile_pb);
|
||||
var cc_arduino_cli_commands_v1_core_pb = require('../../../../../cc/arduino/cli/commands/v1/core_pb.js');
|
||||
goog.object.extend(proto, cc_arduino_cli_commands_v1_core_pb);
|
||||
var cc_arduino_cli_commands_v1_monitor_pb = require('../../../../../cc/arduino/cli/commands/v1/monitor_pb.js');
|
||||
goog.object.extend(proto, cc_arduino_cli_commands_v1_monitor_pb);
|
||||
var cc_arduino_cli_commands_v1_upload_pb = require('../../../../../cc/arduino/cli/commands/v1/upload_pb.js');
|
||||
goog.object.extend(proto, cc_arduino_cli_commands_v1_upload_pb);
|
||||
var cc_arduino_cli_commands_v1_lib_pb = require('../../../../../cc/arduino/cli/commands/v1/lib_pb.js');
|
||||
@@ -41,6 +43,8 @@ goog.exportSymbol('proto.cc.arduino.cli.commands.v1.InitResponse.MessageCase', n
|
||||
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.InitResponse.Progress', null, global);
|
||||
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.LoadSketchRequest', null, global);
|
||||
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.LoadSketchResponse', null, global);
|
||||
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.NewSketchRequest', null, global);
|
||||
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.NewSketchResponse', null, global);
|
||||
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.OutdatedRequest', null, global);
|
||||
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.OutdatedResponse', null, global);
|
||||
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.UpdateCoreLibrariesIndexRequest', null, global);
|
||||
@@ -452,6 +456,48 @@ if (goog.DEBUG && !COMPILED) {
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.VersionResponse.displayName = 'proto.cc.arduino.cli.commands.v1.VersionResponse';
|
||||
}
|
||||
/**
|
||||
* 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.NewSketchRequest = function(opt_data) {
|
||||
jspb.Message.initialize(this, opt_data, 0, -1, null, null);
|
||||
};
|
||||
goog.inherits(proto.cc.arduino.cli.commands.v1.NewSketchRequest, jspb.Message);
|
||||
if (goog.DEBUG && !COMPILED) {
|
||||
/**
|
||||
* @public
|
||||
* @override
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.NewSketchRequest.displayName = 'proto.cc.arduino.cli.commands.v1.NewSketchRequest';
|
||||
}
|
||||
/**
|
||||
* 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.NewSketchResponse = function(opt_data) {
|
||||
jspb.Message.initialize(this, opt_data, 0, -1, null, null);
|
||||
};
|
||||
goog.inherits(proto.cc.arduino.cli.commands.v1.NewSketchResponse, jspb.Message);
|
||||
if (goog.DEBUG && !COMPILED) {
|
||||
/**
|
||||
* @public
|
||||
* @override
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.NewSketchResponse.displayName = 'proto.cc.arduino.cli.commands.v1.NewSketchResponse';
|
||||
}
|
||||
/**
|
||||
* Generated by JsPbCodeGenerator.
|
||||
* @param {Array=} opt_data Optional initial data array, typically from a
|
||||
@@ -3508,6 +3554,347 @@ proto.cc.arduino.cli.commands.v1.VersionResponse.prototype.setVersion = function
|
||||
|
||||
|
||||
|
||||
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.NewSketchRequest.prototype.toObject = function(opt_includeInstance) {
|
||||
return proto.cc.arduino.cli.commands.v1.NewSketchRequest.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.NewSketchRequest} msg The msg instance to transform.
|
||||
* @return {!Object}
|
||||
* @suppress {unusedLocalVariables} f is only used for nested messages
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.NewSketchRequest.toObject = function(includeInstance, msg) {
|
||||
var f, obj = {
|
||||
instance: (f = msg.getInstance()) && cc_arduino_cli_commands_v1_common_pb.Instance.toObject(includeInstance, f),
|
||||
sketchName: jspb.Message.getFieldWithDefault(msg, 2, ""),
|
||||
sketchDir: jspb.Message.getFieldWithDefault(msg, 3, "")
|
||||
};
|
||||
|
||||
if (includeInstance) {
|
||||
obj.$jspbMessageInstance = msg;
|
||||
}
|
||||
return obj;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Deserializes binary data (in protobuf wire format).
|
||||
* @param {jspb.ByteSource} bytes The bytes to deserialize.
|
||||
* @return {!proto.cc.arduino.cli.commands.v1.NewSketchRequest}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.NewSketchRequest.deserializeBinary = function(bytes) {
|
||||
var reader = new jspb.BinaryReader(bytes);
|
||||
var msg = new proto.cc.arduino.cli.commands.v1.NewSketchRequest;
|
||||
return proto.cc.arduino.cli.commands.v1.NewSketchRequest.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.NewSketchRequest} msg The message object to deserialize into.
|
||||
* @param {!jspb.BinaryReader} reader The BinaryReader to use.
|
||||
* @return {!proto.cc.arduino.cli.commands.v1.NewSketchRequest}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.NewSketchRequest.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 = /** @type {string} */ (reader.readString());
|
||||
msg.setSketchName(value);
|
||||
break;
|
||||
case 3:
|
||||
var value = /** @type {string} */ (reader.readString());
|
||||
msg.setSketchDir(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.NewSketchRequest.prototype.serializeBinary = function() {
|
||||
var writer = new jspb.BinaryWriter();
|
||||
proto.cc.arduino.cli.commands.v1.NewSketchRequest.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.NewSketchRequest} message
|
||||
* @param {!jspb.BinaryWriter} writer
|
||||
* @suppress {unusedLocalVariables} f is only used for nested messages
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.NewSketchRequest.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.getSketchName();
|
||||
if (f.length > 0) {
|
||||
writer.writeString(
|
||||
2,
|
||||
f
|
||||
);
|
||||
}
|
||||
f = message.getSketchDir();
|
||||
if (f.length > 0) {
|
||||
writer.writeString(
|
||||
3,
|
||||
f
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional Instance instance = 1;
|
||||
* @return {?proto.cc.arduino.cli.commands.v1.Instance}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.NewSketchRequest.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.NewSketchRequest} returns this
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.NewSketchRequest.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.NewSketchRequest} returns this
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.NewSketchRequest.prototype.clearInstance = function() {
|
||||
return this.setInstance(undefined);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether this field is set.
|
||||
* @return {boolean}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.NewSketchRequest.prototype.hasInstance = function() {
|
||||
return jspb.Message.getField(this, 1) != null;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional string sketch_name = 2;
|
||||
* @return {string}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.NewSketchRequest.prototype.getSketchName = function() {
|
||||
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, ""));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {string} value
|
||||
* @return {!proto.cc.arduino.cli.commands.v1.NewSketchRequest} returns this
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.NewSketchRequest.prototype.setSketchName = function(value) {
|
||||
return jspb.Message.setProto3StringField(this, 2, value);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional string sketch_dir = 3;
|
||||
* @return {string}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.NewSketchRequest.prototype.getSketchDir = function() {
|
||||
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 3, ""));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {string} value
|
||||
* @return {!proto.cc.arduino.cli.commands.v1.NewSketchRequest} returns this
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.NewSketchRequest.prototype.setSketchDir = function(value) {
|
||||
return jspb.Message.setProto3StringField(this, 3, value);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
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.NewSketchResponse.prototype.toObject = function(opt_includeInstance) {
|
||||
return proto.cc.arduino.cli.commands.v1.NewSketchResponse.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.NewSketchResponse} msg The msg instance to transform.
|
||||
* @return {!Object}
|
||||
* @suppress {unusedLocalVariables} f is only used for nested messages
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.NewSketchResponse.toObject = function(includeInstance, msg) {
|
||||
var f, obj = {
|
||||
mainFile: jspb.Message.getFieldWithDefault(msg, 1, "")
|
||||
};
|
||||
|
||||
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.NewSketchResponse}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.NewSketchResponse.deserializeBinary = function(bytes) {
|
||||
var reader = new jspb.BinaryReader(bytes);
|
||||
var msg = new proto.cc.arduino.cli.commands.v1.NewSketchResponse;
|
||||
return proto.cc.arduino.cli.commands.v1.NewSketchResponse.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.NewSketchResponse} msg The message object to deserialize into.
|
||||
* @param {!jspb.BinaryReader} reader The BinaryReader to use.
|
||||
* @return {!proto.cc.arduino.cli.commands.v1.NewSketchResponse}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.NewSketchResponse.deserializeBinaryFromReader = function(msg, reader) {
|
||||
while (reader.nextField()) {
|
||||
if (reader.isEndGroup()) {
|
||||
break;
|
||||
}
|
||||
var field = reader.getFieldNumber();
|
||||
switch (field) {
|
||||
case 1:
|
||||
var value = /** @type {string} */ (reader.readString());
|
||||
msg.setMainFile(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.NewSketchResponse.prototype.serializeBinary = function() {
|
||||
var writer = new jspb.BinaryWriter();
|
||||
proto.cc.arduino.cli.commands.v1.NewSketchResponse.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.NewSketchResponse} message
|
||||
* @param {!jspb.BinaryWriter} writer
|
||||
* @suppress {unusedLocalVariables} f is only used for nested messages
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.NewSketchResponse.serializeBinaryToWriter = function(message, writer) {
|
||||
var f = undefined;
|
||||
f = message.getMainFile();
|
||||
if (f.length > 0) {
|
||||
writer.writeString(
|
||||
1,
|
||||
f
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional string main_file = 1;
|
||||
* @return {string}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.NewSketchResponse.prototype.getMainFile = function() {
|
||||
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, ""));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {string} value
|
||||
* @return {!proto.cc.arduino.cli.commands.v1.NewSketchResponse} returns this
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.NewSketchResponse.prototype.setMainFile = function(value) {
|
||||
return jspb.Message.setProto3StringField(this, 1, value);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if (jspb.Message.GENERATE_TO_OBJECT) {
|
||||
/**
|
||||
* Creates an object representation of this proto.
|
||||
|
@@ -74,6 +74,9 @@ export class TaskProgress extends jspb.Message {
|
||||
getCompleted(): boolean;
|
||||
setCompleted(value: boolean): TaskProgress;
|
||||
|
||||
getPercent(): number;
|
||||
setPercent(value: number): TaskProgress;
|
||||
|
||||
|
||||
serializeBinary(): Uint8Array;
|
||||
toObject(includeInstance?: boolean): TaskProgress.AsObject;
|
||||
@@ -90,6 +93,7 @@ export namespace TaskProgress {
|
||||
name: string,
|
||||
message: string,
|
||||
completed: boolean,
|
||||
percent: number,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,6 +185,31 @@ export namespace Platform {
|
||||
}
|
||||
}
|
||||
|
||||
export class PlatformReference extends jspb.Message {
|
||||
getId(): string;
|
||||
setId(value: string): PlatformReference;
|
||||
|
||||
getVersion(): string;
|
||||
setVersion(value: string): PlatformReference;
|
||||
|
||||
|
||||
serializeBinary(): Uint8Array;
|
||||
toObject(includeInstance?: boolean): PlatformReference.AsObject;
|
||||
static toObject(includeInstance: boolean, msg: PlatformReference): PlatformReference.AsObject;
|
||||
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
|
||||
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
|
||||
static serializeBinaryToWriter(message: PlatformReference, writer: jspb.BinaryWriter): void;
|
||||
static deserializeBinary(bytes: Uint8Array): PlatformReference;
|
||||
static deserializeBinaryFromReader(message: PlatformReference, reader: jspb.BinaryReader): PlatformReference;
|
||||
}
|
||||
|
||||
export namespace PlatformReference {
|
||||
export type AsObject = {
|
||||
id: string,
|
||||
version: string,
|
||||
}
|
||||
}
|
||||
|
||||
export class Board extends jspb.Message {
|
||||
getName(): string;
|
||||
setName(value: string): Board;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user