mirror of
https://github.com/arduino/arduino-ide.git
synced 2025-10-19 16:18:32 +00:00
Compare commits
54 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
fd5154ae93 | ||
![]() |
726628e20c | ||
![]() |
585a82b51a | ||
![]() |
5edccb9c35 | ||
![]() |
555da878f4 | ||
![]() |
df8658eff9 | ||
![]() |
4c55807392 | ||
![]() |
cb50d3a70d | ||
![]() |
eaf14aa1eb | ||
![]() |
a59e0da2af | ||
![]() |
3a3ac6da4e | ||
![]() |
d7809616a4 | ||
![]() |
5b486b1480 | ||
![]() |
5fc30bd33e | ||
![]() |
522a5c6e01 | ||
![]() |
1ae60ec9bc | ||
![]() |
b8c718ce9e | ||
![]() |
b407d0aee0 | ||
![]() |
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 |
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)
|
1
.github/workflows/build.yml
vendored
1
.github/workflows/build.yml
vendored
@@ -78,6 +78,7 @@ jobs:
|
||||
fi
|
||||
fi
|
||||
|
||||
npx node-gyp install
|
||||
yarn --cwd ./electron/packager/
|
||||
yarn --cwd ./electron/packager/ package
|
||||
|
||||
|
14
.github/workflows/compose-full-changelog.yaml
vendored
14
.github/workflows/compose-full-changelog.yaml
vendored
@@ -2,10 +2,13 @@ name: Compose full changelog
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [created, edited]
|
||||
types:
|
||||
- edited
|
||||
|
||||
env:
|
||||
CHANGELOG_ARTIFACTS: changelog
|
||||
# See: https://github.com/actions/setup-node/#readme
|
||||
NODE_VERSION: 14.x
|
||||
|
||||
jobs:
|
||||
create-changelog:
|
||||
@@ -15,6 +18,12 @@ jobs:
|
||||
- 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: |
|
||||
@@ -23,10 +32,11 @@ jobs:
|
||||
- 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 --iso-8601=s).md"
|
||||
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"
|
||||
|
49
.github/workflows/themes-weekly-pull.yml
vendored
Normal file
49
.github/workflows/themes-weekly-pull.yml
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
name: themes-weekly-pull
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# run every friday at 5AM
|
||||
- cron: '0 5 * * 5'
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
NODE_VERSION: 14.x
|
||||
|
||||
jobs:
|
||||
pull-from-jsonbin:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn
|
||||
|
||||
- name: Run themes:pull script
|
||||
run: yarn run themes:pull
|
||||
env:
|
||||
JSONBIN_MASTER_KEY: ${{ secrets.JSONBIN_MASTER_KEY }}
|
||||
JSONBIN_ID: ${{ secrets.JSONBIN_ID }}
|
||||
|
||||
- name: Generate dark tokens
|
||||
run: npx token-transformer scripts/themes/tokens/arduino-tokens.json scripts/themes/tokens/dark.json core,ide-default,ide-dark,theia core,ide-default,ide-dark
|
||||
|
||||
- name: Generate default tokens
|
||||
run: npx token-transformer scripts/themes/tokens/arduino-tokens.json scripts/themes/tokens/default.json core,ide-default,theia core,ide-default
|
||||
|
||||
- name: Run themes:generate script
|
||||
run: yarn run themes:generate
|
||||
|
||||
- name: Create Pull Request
|
||||
uses: peter-evans/create-pull-request@v4
|
||||
with:
|
||||
commit-message: Updated themes
|
||||
title: Update themes
|
||||
branch: themes/themes-update
|
||||
author: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
|
6
.gitignore
vendored
6
.gitignore
vendored
@@ -17,3 +17,9 @@ yarn*.log
|
||||
plugins
|
||||
# the config files for the CLI
|
||||
arduino-ide-extension/data/cli/config
|
||||
# the tokens folder for the themes
|
||||
scripts/themes/tokens
|
||||
# environment variables
|
||||
.env
|
||||
# content trace files for electron
|
||||
electron-app/traces
|
||||
|
@@ -2,5 +2,6 @@
|
||||
"singleQuote": true,
|
||||
"tabWidth": 2,
|
||||
"useTabs": false,
|
||||
"printWidth": 80
|
||||
"printWidth": 80,
|
||||
"endOfLine": "auto"
|
||||
}
|
||||
|
40
.vscode/launch.json
vendored
40
.vscode/launch.json
vendored
@@ -1,6 +1,44 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"name": "App (Electron) [Dev]",
|
||||
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron",
|
||||
"windows": {
|
||||
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron.cmd",
|
||||
},
|
||||
"cwd": "${workspaceFolder}/electron-app",
|
||||
"args": [
|
||||
".",
|
||||
"--log-level=debug",
|
||||
"--hostname=localhost",
|
||||
"--no-cluster",
|
||||
"--app-project-path=${workspaceRoot}/electron-app",
|
||||
"--remote-debugging-port=9222",
|
||||
"--no-app-auto-install",
|
||||
"--plugins=local-dir:../plugins",
|
||||
"--hosted-plugin-inspect=9339",
|
||||
"--nosplash",
|
||||
"--content-trace",
|
||||
"--open-devtools"
|
||||
],
|
||||
"env": {
|
||||
"NODE_ENV": "development"
|
||||
},
|
||||
"sourceMaps": true,
|
||||
"outFiles": [
|
||||
"${workspaceRoot}/electron-app/src-gen/backend/*.js",
|
||||
"${workspaceRoot}/electron-app/src-gen/frontend/*.js",
|
||||
"${workspaceRoot}/electron-app/lib/**/*.js",
|
||||
"${workspaceRoot}/arduino-ide-extension/lib/**/*.js",
|
||||
"${workspaceRoot}/node_modules/@theia/**/*.js"
|
||||
],
|
||||
"smartStep": true,
|
||||
"internalConsoleOptions": "openOnSessionStart",
|
||||
"outputCapture": "std"
|
||||
},
|
||||
{
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
@@ -10,7 +48,6 @@
|
||||
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron.cmd",
|
||||
},
|
||||
"cwd": "${workspaceFolder}/electron-app",
|
||||
"protocol": "inspector",
|
||||
"args": [
|
||||
".",
|
||||
"--log-level=debug",
|
||||
@@ -78,7 +115,6 @@
|
||||
{
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"protocol": "inspector",
|
||||
"name": "Run Test [current]",
|
||||
"program": "${workspaceRoot}/node_modules/mocha/bin/_mocha",
|
||||
"args": [
|
||||
|
@@ -66,6 +66,13 @@ Once you have all the tools installed, you can build the editor following these
|
||||
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
|
||||
|
||||
This project is built on [GitHub Actions](https://github.com/arduino/arduino-ide/actions).
|
||||
@@ -82,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.
|
||||
|
@@ -23,13 +23,14 @@ should be considered unstable:
|
||||
|
||||
| Platform | 32 bit | 64 bit |
|
||||
| --------- | ------------------------ | ------------------------------------------------------------------------------------------------------ |
|
||||
| Linux | | [Nightly Linux 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 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
|
||||
|
@@ -1,15 +1,16 @@
|
||||
{
|
||||
"name": "arduino-ide-extension",
|
||||
"version": "2.0.0-rc4",
|
||||
"version": "2.0.0-rc7",
|
||||
"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,31 +21,30 @@
|
||||
"test:watch": "mocha --watch --watch-files lib \"./lib/test/**/*.test.js\""
|
||||
},
|
||||
"dependencies": {
|
||||
"@grpc/grpc-js": "^1.3.7",
|
||||
"@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",
|
||||
"@grpc/grpc-js": "^1.6.7",
|
||||
"@theia/application-package": "1.25.0",
|
||||
"@theia/core": "1.25.0",
|
||||
"@theia/editor": "1.25.0",
|
||||
"@theia/editor-preview": "1.25.0",
|
||||
"@theia/electron": "1.25.0",
|
||||
"@theia/filesystem": "1.25.0",
|
||||
"@theia/keymaps": "1.25.0",
|
||||
"@theia/markers": "1.25.0",
|
||||
"@theia/monaco": "1.25.0",
|
||||
"@theia/navigator": "1.25.0",
|
||||
"@theia/outline-view": "1.25.0",
|
||||
"@theia/output": "1.25.0",
|
||||
"@theia/preferences": "1.25.0",
|
||||
"@theia/search-in-workspace": "1.25.0",
|
||||
"@theia/terminal": "1.25.0",
|
||||
"@theia/workspace": "1.25.0",
|
||||
"@tippyjs/react": "^4.2.5",
|
||||
"@types/atob": "^2.1.2",
|
||||
"@types/auth0-js": "^9.14.0",
|
||||
"@types/btoa": "^1.2.3",
|
||||
"@types/dateformat": "^3.0.1",
|
||||
"@types/deepmerge": "^2.2.0",
|
||||
"@types/glob": "^5.0.35",
|
||||
"@types/glob": "^7.2.0",
|
||||
"@types/google-protobuf": "^3.7.2",
|
||||
"@types/js-yaml": "^3.12.2",
|
||||
"@types/keytar": "^4.4.0",
|
||||
@@ -57,19 +57,17 @@
|
||||
"@types/temp": "^0.8.34",
|
||||
"@types/which": "^1.3.1",
|
||||
"ajv": "^6.5.3",
|
||||
"arduino-serial-plotter-webapp": "0.0.17",
|
||||
"arduino-serial-plotter-webapp": "0.1.0",
|
||||
"async-mutex": "^0.3.0",
|
||||
"atob": "^2.1.2",
|
||||
"auth0-js": "^9.14.0",
|
||||
"btoa": "^1.2.1",
|
||||
"css-element-queries": "^1.2.0",
|
||||
"dateformat": "^3.0.3",
|
||||
"deepmerge": "2.0.1",
|
||||
"electron-updater": "^4.6.5",
|
||||
"fuzzy": "^0.1.3",
|
||||
"fast-safe-stringify": "^2.1.1",
|
||||
"glob": "^7.1.6",
|
||||
"google-protobuf": "^3.11.4",
|
||||
"grpc": "^1.24.11",
|
||||
"google-protobuf": "^3.20.1",
|
||||
"hash.js": "^1.1.7",
|
||||
"is-valid-path": "^0.1.1",
|
||||
"js-yaml": "^3.13.1",
|
||||
@@ -90,6 +88,7 @@
|
||||
"semver": "^7.3.2",
|
||||
"string-natural-compare": "^2.0.3",
|
||||
"temp": "^0.9.1",
|
||||
"temp-dir": "^2.0.0",
|
||||
"tree-kill": "^1.2.1",
|
||||
"upath": "^1.1.2",
|
||||
"url": "^0.11.0",
|
||||
@@ -156,16 +155,16 @@
|
||||
],
|
||||
"arduino": {
|
||||
"cli": {
|
||||
"version": "0.21.0"
|
||||
"version": "0.23.0"
|
||||
},
|
||||
"fwuploader": {
|
||||
"version": "2.0.0"
|
||||
"version": "2.2.0"
|
||||
},
|
||||
"clangd": {
|
||||
"version": "13.0.0"
|
||||
"version": "14.0.0"
|
||||
},
|
||||
"languageServer": {
|
||||
"version": "0.6.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -4,30 +4,93 @@
|
||||
const version = '1.9.1';
|
||||
|
||||
(async () => {
|
||||
const os = require('os');
|
||||
const { promises: fs } = require('fs');
|
||||
const path = require('path');
|
||||
const shell = require('shelljs');
|
||||
const { v4 } = require('uuid');
|
||||
|
||||
const os = require('os');
|
||||
const path = require('path');
|
||||
const shell = require('shelljs');
|
||||
const { v4 } = require('uuid');
|
||||
const repository = path.join(os.tmpdir(), `${v4()}-arduino-examples`);
|
||||
if (shell.mkdir('-p', repository).code !== 0) {
|
||||
shell.exit(1);
|
||||
}
|
||||
|
||||
const repository = path.join(os.tmpdir(), `${v4()}-arduino-examples`);
|
||||
if (shell.mkdir('-p', repository).code !== 0) {
|
||||
shell.exit(1);
|
||||
process.exit(1);
|
||||
if (
|
||||
shell.exec(
|
||||
`git clone https://github.com/arduino/arduino-examples.git ${repository}`
|
||||
).code !== 0
|
||||
) {
|
||||
shell.exit(1);
|
||||
}
|
||||
|
||||
if (
|
||||
shell.exec(`git -C ${repository} checkout tags/${version} -b ${version}`)
|
||||
.code !== 0
|
||||
) {
|
||||
shell.exit(1);
|
||||
}
|
||||
|
||||
const destination = path.join(__dirname, '..', 'Examples');
|
||||
shell.mkdir('-p', destination);
|
||||
shell.cp('-fR', path.join(repository, 'examples', '*'), destination);
|
||||
|
||||
const isSketch = async (pathLike) => {
|
||||
try {
|
||||
const names = await fs.readdir(pathLike);
|
||||
const dirName = path.basename(pathLike);
|
||||
return names.indexOf(`${dirName}.ino`) !== -1;
|
||||
} catch (e) {
|
||||
if (e.code === 'ENOTDIR') {
|
||||
return false;
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
|
||||
if (shell.exec(`git clone https://github.com/arduino/arduino-examples.git ${repository}`).code !== 0) {
|
||||
shell.exit(1);
|
||||
process.exit(1);
|
||||
};
|
||||
const examples = [];
|
||||
const categories = await fs.readdir(destination);
|
||||
const visit = async (pathLike, container) => {
|
||||
const stat = await fs.lstat(pathLike);
|
||||
if (stat.isDirectory()) {
|
||||
if (await isSketch(pathLike)) {
|
||||
container.sketches.push({
|
||||
name: path.basename(pathLike),
|
||||
relativePath: path.relative(destination, pathLike),
|
||||
});
|
||||
} else {
|
||||
const names = await fs.readdir(pathLike);
|
||||
for (const name of names) {
|
||||
const childPath = path.join(pathLike, name);
|
||||
if (await isSketch(childPath)) {
|
||||
container.sketches.push({
|
||||
name,
|
||||
relativePath: path.relative(destination, childPath),
|
||||
});
|
||||
} else {
|
||||
const child = {
|
||||
label: name,
|
||||
children: [],
|
||||
sketches: [],
|
||||
};
|
||||
container.children.push(child);
|
||||
await visit(childPath, child);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (shell.exec(`git -C ${repository} checkout tags/${version} -b ${version}`).code !== 0) {
|
||||
shell.exit(1);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const destination = path.join(__dirname, '..', 'Examples');
|
||||
shell.mkdir('-p', destination);
|
||||
shell.cp('-fR', path.join(repository, 'examples', '*'), destination);
|
||||
|
||||
};
|
||||
for (const category of categories) {
|
||||
const example = {
|
||||
label: category,
|
||||
children: [],
|
||||
sketches: [],
|
||||
};
|
||||
await visit(path.join(destination, category), example);
|
||||
examples.push(example);
|
||||
}
|
||||
await fs.writeFile(
|
||||
path.join(destination, 'examples.json'),
|
||||
JSON.stringify(examples, null, 2),
|
||||
{ encoding: 'utf8' }
|
||||
);
|
||||
shell.echo(`Generated output to ${path.join(destination, 'examples.json')}`);
|
||||
})();
|
||||
|
@@ -66,21 +66,24 @@
|
||||
build,
|
||||
`arduino-language-server${platform === 'win32' ? '.exe' : ''}`
|
||||
);
|
||||
let clangdExecutablePath, lsSuffix, clangdSuffix;
|
||||
let clangdExecutablePath, clangFormatExecutablePath, lsSuffix, clangdSuffix;
|
||||
|
||||
switch (platformArch) {
|
||||
case 'darwin-x64':
|
||||
clangdExecutablePath = path.join(build, 'clangd');
|
||||
clangFormatExecutablePath = path.join(build, 'clang-format');
|
||||
lsSuffix = 'macOS_64bit.tar.gz';
|
||||
clangdSuffix = 'macOS_64bit';
|
||||
break;
|
||||
case 'linux-x64':
|
||||
clangdExecutablePath = path.join(build, 'clangd');
|
||||
clangFormatExecutablePath = path.join(build, 'clang-format');
|
||||
lsSuffix = 'Linux_64bit.tar.gz';
|
||||
clangdSuffix = 'Linux_64bit';
|
||||
break;
|
||||
case 'win32-x64':
|
||||
clangdExecutablePath = path.join(build, 'clangd.exe');
|
||||
clangFormatExecutablePath = path.join(build, 'clang-format.exe');
|
||||
lsSuffix = 'Windows_64bit.zip';
|
||||
clangdSuffix = 'Windows_64bit';
|
||||
break;
|
||||
@@ -103,4 +106,15 @@
|
||||
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.
|
||||
|
||||
const clangdFormatUrl = `https://downloads.arduino.cc/tools/clang-format_${clangdVersion}_${clangdSuffix}.tar.bz2`;
|
||||
downloader.downloadUnzipAll(
|
||||
clangdFormatUrl,
|
||||
build,
|
||||
clangFormatExecutablePath,
|
||||
force,
|
||||
{
|
||||
strip: 1,
|
||||
}
|
||||
);
|
||||
})();
|
||||
|
@@ -1,5 +1,9 @@
|
||||
import { inject, injectable, postConstruct } from 'inversify';
|
||||
import * as React from 'react';
|
||||
import {
|
||||
inject,
|
||||
injectable,
|
||||
postConstruct,
|
||||
} from '@theia/core/shared/inversify';
|
||||
import * as React from '@theia/core/shared/react';
|
||||
import * as remote from '@theia/core/electron-shared/@electron/remote';
|
||||
import {
|
||||
BoardsService,
|
||||
@@ -7,6 +11,7 @@ import {
|
||||
ExecutableService,
|
||||
Sketch,
|
||||
LibraryService,
|
||||
ArduinoDaemon,
|
||||
} from '../common/protocol';
|
||||
import { Mutex } from 'async-mutex';
|
||||
import {
|
||||
@@ -17,9 +22,12 @@ import {
|
||||
DisposableCollection,
|
||||
} from '@theia/core';
|
||||
import {
|
||||
Dialog,
|
||||
FrontendApplication,
|
||||
FrontendApplicationContribution,
|
||||
LocalStorageService,
|
||||
OnWillStopAction,
|
||||
SaveableWidget,
|
||||
StatusBar,
|
||||
StatusBarAlignment,
|
||||
} from '@theia/core/lib/browser';
|
||||
@@ -38,23 +46,17 @@ 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 { OutlineViewContribution } from '@theia/outline-view/lib/browser/outline-view-contribution';
|
||||
import { OutputContribution } from '@theia/output/lib/browser/output-contribution';
|
||||
import { ScmContribution } from '@theia/scm/lib/browser/scm-contribution';
|
||||
import { SearchInWorkspaceFrontendContribution } from '@theia/search-in-workspace/lib/browser/search-in-workspace-frontend-contribution';
|
||||
import { FileNavigatorCommands } from '@theia/navigator/lib/browser/navigator-contribution';
|
||||
import { TerminalMenus } from '@theia/terminal/lib/browser/terminal-frontend-contribution';
|
||||
import { HostedPluginSupport } from '@theia/plugin-ext/lib/hosted/browser/hosted-plugin';
|
||||
import { FileService } from '@theia/filesystem/lib/browser/file-service';
|
||||
import { FileChangeType } from '@theia/filesystem/lib/browser';
|
||||
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
|
||||
import { ConfigService } from '../common/protocol/config-service';
|
||||
import { ArduinoCommands } from './arduino-commands';
|
||||
import { BoardsConfig } from './boards/boards-config';
|
||||
import { BoardsConfigDialog } from './boards/boards-config-dialog';
|
||||
@@ -65,11 +67,15 @@ import { ArduinoMenus } from './menu/arduino-menus';
|
||||
import { MonitorViewContribution } from './serial/monitor/monitor-view-contribution';
|
||||
import { ArduinoToolbar } from './toolbar/arduino-toolbar';
|
||||
import { ArduinoPreferences } from './arduino-preferences';
|
||||
import { SketchesServiceClientImpl } from '../common/protocol/sketches-service-client-impl';
|
||||
import {
|
||||
CurrentSketch,
|
||||
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';
|
||||
import { HostedPluginEvents } from './hosted-plugin-events';
|
||||
|
||||
const INIT_LIBS_AND_PACKAGES = 'initializedLibsAndPackages';
|
||||
export const SKIP_IDE_VERSION = 'skipIDEVersion';
|
||||
@@ -81,89 +87,73 @@ export class ArduinoFrontendContribution
|
||||
TabBarToolbarContribution,
|
||||
CommandContribution,
|
||||
MenuContribution,
|
||||
ColorContribution {
|
||||
ColorContribution
|
||||
{
|
||||
@inject(ILogger)
|
||||
protected logger: ILogger;
|
||||
private readonly logger: ILogger;
|
||||
|
||||
@inject(MessageService)
|
||||
protected readonly messageService: MessageService;
|
||||
private readonly messageService: MessageService;
|
||||
|
||||
@inject(BoardsService)
|
||||
protected readonly boardsService: BoardsService;
|
||||
private readonly boardsService: BoardsService;
|
||||
|
||||
@inject(LibraryService)
|
||||
protected readonly libraryService: LibraryService;
|
||||
private readonly libraryService: LibraryService;
|
||||
|
||||
@inject(BoardsServiceProvider)
|
||||
protected readonly boardsServiceClientImpl: BoardsServiceProvider;
|
||||
private readonly boardsServiceClientImpl: BoardsServiceProvider;
|
||||
|
||||
@inject(EditorManager)
|
||||
protected readonly editorManager: EditorManager;
|
||||
private readonly editorManager: EditorManager;
|
||||
|
||||
@inject(FileService)
|
||||
protected readonly fileService: FileService;
|
||||
private readonly fileService: FileService;
|
||||
|
||||
@inject(SketchesService)
|
||||
protected readonly sketchService: SketchesService;
|
||||
private readonly sketchService: SketchesService;
|
||||
|
||||
@inject(BoardsConfigDialog)
|
||||
protected readonly boardsConfigDialog: BoardsConfigDialog;
|
||||
private readonly boardsConfigDialog: BoardsConfigDialog;
|
||||
|
||||
@inject(CommandRegistry)
|
||||
protected readonly commandRegistry: CommandRegistry;
|
||||
private readonly commandRegistry: CommandRegistry;
|
||||
|
||||
@inject(StatusBar)
|
||||
protected readonly statusBar: StatusBar;
|
||||
|
||||
@inject(FileNavigatorContribution)
|
||||
protected readonly fileNavigatorContributions: FileNavigatorContribution;
|
||||
|
||||
@inject(OutputContribution)
|
||||
protected readonly outputContribution: OutputContribution;
|
||||
|
||||
@inject(OutlineViewContribution)
|
||||
protected readonly outlineContribution: OutlineViewContribution;
|
||||
|
||||
@inject(ProblemContribution)
|
||||
protected readonly problemContribution: ProblemContribution;
|
||||
|
||||
@inject(ScmContribution)
|
||||
protected readonly scmContribution: ScmContribution;
|
||||
|
||||
@inject(SearchInWorkspaceFrontendContribution)
|
||||
protected readonly siwContribution: SearchInWorkspaceFrontendContribution;
|
||||
|
||||
@inject(SketchbookWidgetContribution)
|
||||
protected readonly sketchbookWidgetContribution: SketchbookWidgetContribution;
|
||||
private readonly statusBar: StatusBar;
|
||||
|
||||
@inject(EditorMode)
|
||||
protected readonly editorMode: EditorMode;
|
||||
private readonly editorMode: EditorMode;
|
||||
|
||||
@inject(ConfigService)
|
||||
protected readonly configService: ConfigService;
|
||||
|
||||
@inject(HostedPluginSupport)
|
||||
protected hostedPluginSupport: HostedPluginSupport;
|
||||
@inject(HostedPluginEvents)
|
||||
private readonly hostedPluginEvents: HostedPluginEvents;
|
||||
|
||||
@inject(ExecutableService)
|
||||
protected executableService: ExecutableService;
|
||||
private readonly executableService: ExecutableService;
|
||||
|
||||
@inject(ArduinoPreferences)
|
||||
protected readonly arduinoPreferences: ArduinoPreferences;
|
||||
private readonly arduinoPreferences: ArduinoPreferences;
|
||||
|
||||
@inject(SketchesServiceClientImpl)
|
||||
protected readonly sketchServiceClient: SketchesServiceClientImpl;
|
||||
private readonly sketchServiceClient: SketchesServiceClientImpl;
|
||||
|
||||
protected readonly appStateService: FrontendApplicationStateService;
|
||||
@inject(FrontendApplicationStateService)
|
||||
private readonly appStateService: FrontendApplicationStateService;
|
||||
|
||||
@inject(LocalStorageService)
|
||||
protected readonly localStorageService: LocalStorageService;
|
||||
private readonly localStorageService: LocalStorageService;
|
||||
|
||||
@inject(FileSystemFrontendContribution)
|
||||
private readonly fileSystemFrontendContribution: FileSystemFrontendContribution;
|
||||
|
||||
@inject(IDEUpdater)
|
||||
protected readonly updater: IDEUpdater;
|
||||
private readonly updater: IDEUpdater;
|
||||
|
||||
@inject(IDEUpdaterDialog)
|
||||
protected readonly updaterDialog: IDEUpdaterDialog;
|
||||
private readonly updaterDialog: IDEUpdaterDialog;
|
||||
|
||||
@inject(ArduinoDaemon)
|
||||
private readonly daemon: ArduinoDaemon;
|
||||
|
||||
protected invalidConfigPopup:
|
||||
| Promise<void | 'No' | 'Yes' | undefined>
|
||||
@@ -234,7 +224,10 @@ export class ArduinoFrontendContribution
|
||||
updateStatusBar(this.boardsServiceClientImpl.boardsConfig);
|
||||
this.appStateService.reachedState('ready').then(async () => {
|
||||
const sketch = await this.sketchServiceClient.currentSketch();
|
||||
if (sketch && !(await this.sketchService.isTemp(sketch))) {
|
||||
if (
|
||||
CurrentSketch.isValid(sketch) &&
|
||||
!(await this.sketchService.isTemp(sketch))
|
||||
) {
|
||||
this.toDisposeOnStop.push(this.fileService.watch(new URI(sketch.uri)));
|
||||
this.toDisposeOnStop.push(
|
||||
this.fileService.onDidFilesChange(async (event) => {
|
||||
@@ -260,21 +253,6 @@ export class ArduinoFrontendContribution
|
||||
}
|
||||
|
||||
async onStart(app: FrontendApplication): Promise<void> {
|
||||
// Initialize all `pro-mode` widgets. This is a NOOP if in normal mode.
|
||||
for (const viewContribution of [
|
||||
this.fileNavigatorContributions,
|
||||
this.outputContribution,
|
||||
this.outlineContribution,
|
||||
this.problemContribution,
|
||||
this.scmContribution,
|
||||
this.siwContribution,
|
||||
this.sketchbookWidgetContribution,
|
||||
] as Array<FrontendApplicationContribution>) {
|
||||
if (viewContribution.initializeLayout) {
|
||||
viewContribution.initializeLayout(app);
|
||||
}
|
||||
}
|
||||
|
||||
this.updater
|
||||
.init(
|
||||
this.arduinoPreferences.get('arduino.ide.updateChannel'),
|
||||
@@ -308,6 +286,12 @@ export class ArduinoFrontendContribution
|
||||
}
|
||||
};
|
||||
this.boardsServiceClientImpl.onBoardsConfigChanged(start);
|
||||
this.hostedPluginEvents.onPluginsDidStart(() =>
|
||||
start(this.boardsServiceClientImpl.boardsConfig)
|
||||
);
|
||||
this.hostedPluginEvents.onPluginsWillUnload(
|
||||
() => (this.languageServerFqbn = undefined)
|
||||
);
|
||||
this.arduinoPreferences.onPreferenceChanged((event) => {
|
||||
if (event.newValue !== event.oldValue) {
|
||||
switch (event.preferenceName) {
|
||||
@@ -337,6 +321,19 @@ export class ArduinoFrontendContribution
|
||||
});
|
||||
|
||||
app.shell.leftPanelHandler.removeBottomMenu('settings-menu');
|
||||
|
||||
this.fileSystemFrontendContribution.onDidChangeEditorFile(
|
||||
({ type, editor }) => {
|
||||
if (type === FileChangeType.DELETED) {
|
||||
const editorWidget = editor;
|
||||
if (SaveableWidget.is(editorWidget)) {
|
||||
editorWidget.closeWithoutSaving();
|
||||
} else {
|
||||
editorWidget.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
onStop(): void {
|
||||
@@ -349,9 +346,13 @@ export class ArduinoFrontendContribution
|
||||
fqbn: string,
|
||||
name: string | undefined
|
||||
): Promise<void> {
|
||||
const port = await this.daemon.tryGetPort();
|
||||
if (!port) {
|
||||
return;
|
||||
}
|
||||
const release = await this.languageServerStartMutex.acquire();
|
||||
try {
|
||||
await this.hostedPluginSupport.didStart;
|
||||
await this.hostedPluginEvents.didStart;
|
||||
const details = await this.boardsService.getBoardDetails({ fqbn });
|
||||
if (!details) {
|
||||
// Core is not installed for the selected board.
|
||||
@@ -386,7 +387,7 @@ export class ArduinoFrontendContribution
|
||||
let currentSketchPath: string | undefined = undefined;
|
||||
if (log) {
|
||||
const currentSketch = await this.sketchServiceClient.currentSketch();
|
||||
if (currentSketch) {
|
||||
if (CurrentSketch.isValid(currentSketch)) {
|
||||
currentSketchPath = await this.fileService.fsPath(
|
||||
new URI(currentSketch.uri)
|
||||
);
|
||||
@@ -398,8 +399,6 @@ export class ArduinoFrontendContribution
|
||||
this.fileService.fsPath(new URI(lsUri)),
|
||||
]);
|
||||
|
||||
const config = await this.configService.getConfiguration();
|
||||
|
||||
this.languageServerFqbn = await Promise.race([
|
||||
new Promise<undefined>((_, reject) =>
|
||||
setTimeout(
|
||||
@@ -411,7 +410,7 @@ export class ArduinoFrontendContribution
|
||||
'arduino.languageserver.start',
|
||||
{
|
||||
lsPath,
|
||||
cliDaemonAddr: `localhost:${config.daemon.port}`, // TODO: verify if this port is coming from the BE
|
||||
cliDaemonAddr: `localhost:${port}`,
|
||||
clangdPath,
|
||||
log: currentSketchPath ? currentSketchPath : log,
|
||||
cliDaemonInstance: '1',
|
||||
@@ -469,9 +468,21 @@ 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) {
|
||||
registerMenus(registry: MenuModelRegistry): void {
|
||||
const menuId = (menuPath: string[]): string => {
|
||||
const index = menuPath.length - 1;
|
||||
const menuId = menuPath[index];
|
||||
@@ -540,12 +551,19 @@ export class ArduinoFrontendContribution
|
||||
uri: string,
|
||||
forceOpen = false,
|
||||
options?: EditorOpenerOptions | undefined
|
||||
): Promise<any> {
|
||||
): Promise<unknown> {
|
||||
const widget = this.editorManager.all.find(
|
||||
(widget) => widget.editor.uri.toString() === uri
|
||||
);
|
||||
if (!widget || forceOpen) {
|
||||
return this.editorManager.open(new URI(uri), options);
|
||||
return this.editorManager.open(
|
||||
new URI(uri),
|
||||
options ?? {
|
||||
mode: 'reveal',
|
||||
preview: false,
|
||||
counter: 0,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -629,4 +647,57 @@ export class ArduinoFrontendContribution
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
onWillStop(): OnWillStopAction {
|
||||
return {
|
||||
reason: 'temp-sketch',
|
||||
action: () => {
|
||||
return this.showTempSketchDialog();
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
private async showTempSketchDialog(): Promise<boolean> {
|
||||
const sketch = await this.sketchServiceClient.currentSketch();
|
||||
if (!CurrentSketch.isValid(sketch)) {
|
||||
return true;
|
||||
}
|
||||
const isTemp = await this.sketchService.isTemp(sketch);
|
||||
if (!isTemp) {
|
||||
return true;
|
||||
}
|
||||
const messageBoxResult = await remote.dialog.showMessageBox(
|
||||
remote.getCurrentWindow(),
|
||||
{
|
||||
message: nls.localize(
|
||||
'arduino/sketch/saveTempSketch',
|
||||
'Save your sketch to open it again later.'
|
||||
),
|
||||
title: nls.localize(
|
||||
'theia/core/quitTitle',
|
||||
'Are you sure you want to quit?'
|
||||
),
|
||||
type: 'question',
|
||||
buttons: [
|
||||
Dialog.CANCEL,
|
||||
nls.localizeByDefault('Save As...'),
|
||||
nls.localizeByDefault("Don't Save"),
|
||||
],
|
||||
}
|
||||
);
|
||||
const result = messageBoxResult.response;
|
||||
if (result === 2) {
|
||||
return true;
|
||||
} else if (result === 1) {
|
||||
return !!(await this.commandRegistry.executeCommand(
|
||||
SaveAsSketch.Commands.SAVE_AS_SKETCH.id,
|
||||
{
|
||||
execOnlyIfTemp: false,
|
||||
openAfterMove: false,
|
||||
wipeOriginal: true,
|
||||
}
|
||||
));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import '../../src/browser/style/index.css';
|
||||
import { ContainerModule } from 'inversify';
|
||||
import { ContainerModule } from '@theia/core/shared/inversify';
|
||||
import { WidgetFactory } from '@theia/core/lib/browser/widget-manager';
|
||||
import { CommandContribution } from '@theia/core/lib/common/command';
|
||||
import { bindViewContribution } from '@theia/core/lib/browser/shell/view-contribution';
|
||||
@@ -50,7 +50,6 @@ import {
|
||||
ApplicationShell as TheiaApplicationShell,
|
||||
ShellLayoutRestorer as TheiaShellLayoutRestorer,
|
||||
CommonFrontendContribution as TheiaCommonFrontendContribution,
|
||||
KeybindingRegistry as TheiaKeybindingRegistry,
|
||||
TabBarRendererFactory,
|
||||
ContextMenuRenderer,
|
||||
createTreeContainer,
|
||||
@@ -69,20 +68,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';
|
||||
@@ -138,7 +129,6 @@ import { PreferencesContribution } from './theia/preferences/preferences-contrib
|
||||
import { QuitApp } from './contributions/quit-app';
|
||||
import { SketchControl } from './contributions/sketch-control';
|
||||
import { Settings } from './contributions/settings';
|
||||
import { KeybindingRegistry } from './theia/core/keybindings';
|
||||
import { WorkspaceCommandContribution } from './theia/workspace/workspace-commands';
|
||||
import { WorkspaceDeleteHandler as TheiaWorkspaceDeleteHandler } from '@theia/workspace/lib/browser/workspace-delete-handler';
|
||||
import { WorkspaceDeleteHandler } from './theia/workspace/workspace-delete-handler';
|
||||
@@ -160,7 +150,14 @@ 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';
|
||||
@@ -275,20 +272,41 @@ import {
|
||||
IDEUpdaterDialogWidget,
|
||||
} from './dialogs/ide-updater/ide-updater-dialog';
|
||||
import { ElectronIpcConnectionProvider } from '@theia/core/lib/electron-browser/messaging/electron-ipc-connection-provider';
|
||||
|
||||
const ElementQueries = require('css-element-queries/src/ElementQueries');
|
||||
import { MonitorModel } from './monitor-model';
|
||||
import { MonitorManagerProxyClientImpl } from './monitor-manager-proxy-client-impl';
|
||||
import { EditorManager as TheiaEditorManager } from '@theia/editor/lib/browser/editor-manager';
|
||||
import { EditorManager } from './theia/editor/editor-manager';
|
||||
import { HostedPluginEvents } from './hosted-plugin-events';
|
||||
import { HostedPluginSupport } from './theia/plugin-ext/hosted-plugin';
|
||||
import { HostedPluginSupport as TheiaHostedPluginSupport } from '@theia/plugin-ext/lib/hosted/browser/hosted-plugin';
|
||||
import { Formatter, FormatterPath } from '../common/protocol/formatter';
|
||||
import { Format } from './contributions/format';
|
||||
import { MonacoFormattingConflictsContribution } from './theia/monaco/monaco-formatting-conflicts';
|
||||
import { MonacoFormattingConflictsContribution as TheiaMonacoFormattingConflictsContribution } from '@theia/monaco/lib/browser/monaco-formatting-conflicts';
|
||||
import { DefaultJsonSchemaContribution } from './theia/core/json-schema-store';
|
||||
import { DefaultJsonSchemaContribution as TheiaDefaultJsonSchemaContribution } from '@theia/core/lib/browser/json-schema-store';
|
||||
import { EditorNavigationContribution } from './theia/editor/editor-navigation-contribution';
|
||||
import { EditorNavigationContribution as TheiaEditorNavigationContribution } from '@theia/editor/lib/browser/editor-navigation-contribution';
|
||||
import { PreferenceTreeGenerator } from './theia/preferences/preference-tree-generator';
|
||||
import { PreferenceTreeGenerator as TheiaPreferenceTreeGenerator } from '@theia/preferences/lib/browser/util/preference-tree-generator';
|
||||
import { AboutDialog } from './theia/core/about-dialog';
|
||||
import { AboutDialog as TheiaAboutDialog } from '@theia/core/lib/browser/about-dialog';
|
||||
|
||||
MonacoThemingService.register({
|
||||
id: 'arduino-theme',
|
||||
label: 'Light (Arduino)',
|
||||
uiTheme: 'vs',
|
||||
json: require('../../src/browser/data/arduino.color-theme.json'),
|
||||
json: require('../../src/browser/data/default.color-theme.json'),
|
||||
});
|
||||
|
||||
MonacoThemingService.register({
|
||||
id: 'arduino-theme-dark',
|
||||
label: 'Dark (Arduino)',
|
||||
uiTheme: 'vs-dark',
|
||||
json: require('../../src/browser/data/dark.color-theme.json'),
|
||||
});
|
||||
|
||||
export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
ElementQueries.listen();
|
||||
ElementQueries.init();
|
||||
|
||||
// Commands and toolbar items
|
||||
bind(ArduinoFrontendContribution).toSelf().inSingletonScope();
|
||||
bind(CommandContribution).toService(ArduinoFrontendContribution);
|
||||
@@ -407,29 +425,44 @@ 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),
|
||||
}));
|
||||
// Frontend binding for the serial service
|
||||
bind(SerialService)
|
||||
.toDynamicValue((context) => {
|
||||
const connection = context.container.get(WebSocketConnectionProvider);
|
||||
const client = context.container.get<SerialServiceClient>(
|
||||
SerialServiceClient
|
||||
createWidget: () => {
|
||||
return new MonitorWidget(
|
||||
context.container.get<MonitorModel>(MonitorModel),
|
||||
context.container.get<MonitorManagerProxyClient>(
|
||||
MonitorManagerProxyClient
|
||||
),
|
||||
context.container.get<BoardsServiceProvider>(BoardsServiceProvider)
|
||||
);
|
||||
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);
|
||||
@@ -477,7 +510,6 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
rebind(TheiaPreferencesContribution)
|
||||
.to(PreferencesContribution)
|
||||
.inSingletonScope();
|
||||
rebind(TheiaKeybindingRegistry).to(KeybindingRegistry).inSingletonScope();
|
||||
rebind(TheiaWorkspaceCommandContribution)
|
||||
.to(WorkspaceCommandContribution)
|
||||
.inSingletonScope();
|
||||
@@ -486,11 +518,12 @@ 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);
|
||||
@@ -508,6 +541,10 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
bind(SearchInWorkspaceWidget).toSelf();
|
||||
rebind(TheiaSearchInWorkspaceWidget).toService(SearchInWorkspaceWidget);
|
||||
|
||||
// Disabled reference counter in the editor manager to avoid opening the same editor (with different opener options) multiple times.
|
||||
bind(EditorManager).toSelf().inSingletonScope();
|
||||
rebind(TheiaEditorManager).to(EditorManager);
|
||||
|
||||
// replace search icon
|
||||
rebind(TheiaSearchInWorkspaceFactory)
|
||||
.to(SearchInWorkspaceFactory)
|
||||
@@ -559,6 +596,12 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
)
|
||||
.inSingletonScope();
|
||||
|
||||
bind(Formatter)
|
||||
.toDynamicValue(({ container }) =>
|
||||
WebSocketConnectionProvider.createProxy(container, FormatterPath)
|
||||
)
|
||||
.inSingletonScope();
|
||||
|
||||
bind(ArduinoFirmwareUploader)
|
||||
.toDynamicValue((context) =>
|
||||
WebSocketConnectionProvider.createProxy(
|
||||
@@ -626,6 +669,14 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
Contribution.configure(bind, ArchiveSketch);
|
||||
Contribution.configure(bind, AddZipLibrary);
|
||||
Contribution.configure(bind, PlotterFrontendContribution);
|
||||
Contribution.configure(bind, Format);
|
||||
|
||||
// Disabled the quick-pick customization from Theia when multiple formatters are available.
|
||||
// Use the default VS Code behavior, and pick the first one. In the IDE2, clang-format has `exclusive` selectors.
|
||||
bind(MonacoFormattingConflictsContribution).toSelf().inSingletonScope();
|
||||
rebind(TheiaMonacoFormattingConflictsContribution).toService(
|
||||
MonacoFormattingConflictsContribution
|
||||
);
|
||||
|
||||
bind(ResponseServiceImpl)
|
||||
.toSelf()
|
||||
@@ -655,15 +706,13 @@ 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,
|
||||
@@ -673,6 +722,8 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
|
||||
// Workaround for https://github.com/eclipse-theia/theia/issues/8722
|
||||
// Do not trigger a save on IDE startup if `"editor.autoSave": "on"` was set as a preference.
|
||||
// Note: `"editor.autoSave" was renamed to `"files.autoSave" and `"on"` was replaced with three
|
||||
// different cases, but we treat `!== 'off'` as auto save enabled. (https://github.com/eclipse-theia/theia/issues/10812)
|
||||
bind(EditorCommandContribution).toSelf().inSingletonScope();
|
||||
rebind(TheiaEditorCommandContribution).toService(EditorCommandContribution);
|
||||
|
||||
@@ -680,6 +731,26 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
bind(NavigatorTabBarDecorator).toSelf().inSingletonScope();
|
||||
rebind(TheiaNavigatorTabBarDecorator).toService(NavigatorTabBarDecorator);
|
||||
|
||||
// Do not fetch the `catalog.json` from Azure on FE load.
|
||||
bind(DefaultJsonSchemaContribution).toSelf().inSingletonScope();
|
||||
rebind(TheiaDefaultJsonSchemaContribution).toService(
|
||||
DefaultJsonSchemaContribution
|
||||
);
|
||||
|
||||
// Do not block the app startup when initializing the editor navigation history.
|
||||
bind(EditorNavigationContribution).toSelf().inSingletonScope();
|
||||
rebind(TheiaEditorNavigationContribution).toService(
|
||||
EditorNavigationContribution
|
||||
);
|
||||
|
||||
// IDE2 does not use the Theia preferences widget, no need to create and sync the underlying tree model.
|
||||
bind(PreferenceTreeGenerator).toSelf().inSingletonScope();
|
||||
rebind(TheiaPreferenceTreeGenerator).toService(PreferenceTreeGenerator);
|
||||
|
||||
// IDE2 has a custom about dialog, so there is no need to load the Theia extensions on FE load
|
||||
bind(AboutDialog).toSelf().inSingletonScope();
|
||||
rebind(TheiaAboutDialog).toService(AboutDialog);
|
||||
|
||||
// To avoid running `Save All` when there are no dirty editors before starting the debug session.
|
||||
bind(DebugSessionManager).toSelf().inSingletonScope();
|
||||
rebind(TheiaDebugSessionManager).toService(DebugSessionManager);
|
||||
@@ -799,4 +870,9 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
);
|
||||
})
|
||||
.inSingletonScope();
|
||||
|
||||
bind(HostedPluginSupport).toSelf().inSingletonScope();
|
||||
rebind(TheiaHostedPluginSupport).toService(HostedPluginSupport);
|
||||
bind(HostedPluginEvents).toSelf().inSingletonScope();
|
||||
bind(FrontendApplicationContribution).toService(HostedPluginEvents);
|
||||
});
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { interfaces } from 'inversify';
|
||||
import { interfaces } from '@theia/core/shared/inversify';
|
||||
import {
|
||||
createPreferenceProxy,
|
||||
PreferenceProxy,
|
||||
@@ -83,7 +83,7 @@ export const ArduinoConfigSchema: PreferenceSchema = {
|
||||
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'`
|
||||
"The base URL where to download updates from. Defaults to 'https://downloads.arduino.cc/arduino-ide'"
|
||||
),
|
||||
},
|
||||
'arduino.board.certificates': {
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { inject, injectable } from 'inversify';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import { Emitter } from '@theia/core/lib/common/event';
|
||||
import { JsonRpcProxy } from '@theia/core/lib/common/messaging/proxy-factory';
|
||||
import { WindowService } from '@theia/core/lib/browser/window/window-service';
|
||||
@@ -43,13 +43,15 @@ export class AuthenticationClientService
|
||||
|
||||
readonly onSessionDidChange = this.onSessionDidChangeEmitter.event;
|
||||
|
||||
onStart(): void {
|
||||
async onStart(): Promise<void> {
|
||||
this.toDispose.push(this.onSessionDidChangeEmitter);
|
||||
this.service.setClient(this);
|
||||
this.service
|
||||
.session()
|
||||
.then((session) => this.notifySessionDidChange(session));
|
||||
this.setOptions();
|
||||
|
||||
this.setOptions().then(() => this.service.initAuthSession());
|
||||
|
||||
this.arduinoPreferences.onPreferenceChanged((event) => {
|
||||
if (event.preferenceName.startsWith('arduino.auth.')) {
|
||||
this.setOptions();
|
||||
@@ -57,8 +59,8 @@ export class AuthenticationClientService
|
||||
});
|
||||
}
|
||||
|
||||
setOptions(): void {
|
||||
this.service.setOptions({
|
||||
setOptions(): Promise<void> {
|
||||
return this.service.setOptions({
|
||||
redirectUri: `http://localhost:${serverPort}/callback`,
|
||||
responseType: 'code',
|
||||
clientID: this.arduinoPreferences['arduino.auth.clientID'],
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { injectable, inject } from 'inversify';
|
||||
import { injectable, inject } from '@theia/core/shared/inversify';
|
||||
import { MessageService } from '@theia/core/lib/common/message-service';
|
||||
import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application';
|
||||
import {
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import * as React from 'react';
|
||||
import { injectable, inject } from 'inversify';
|
||||
import * as React from '@theia/core/shared/react';
|
||||
import { injectable, inject } from '@theia/core/shared/inversify';
|
||||
import { Emitter } from '@theia/core/lib/common/event';
|
||||
import { ReactWidget, Message } from '@theia/core/lib/browser';
|
||||
import { BoardsService } from '../../common/protocol/boards-service';
|
||||
@@ -55,12 +55,13 @@ export class BoardsConfigDialogWidget extends ReactWidget {
|
||||
onConfigChange={this.fireConfigChanged}
|
||||
onFocusNodeSet={this.setFocusNode}
|
||||
onFilteredTextDidChangeEvent={this.onFilterTextDidChangeEmitter.event}
|
||||
onAppStateDidChange={this.notificationCenter.onAppStateDidChange}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
protected onActivateRequest(msg: Message): void {
|
||||
protected override onActivateRequest(msg: Message): void {
|
||||
super.onActivateRequest(msg);
|
||||
if (this.focusNode instanceof HTMLInputElement) {
|
||||
this.focusNode.select();
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { injectable, inject, postConstruct } from 'inversify';
|
||||
import { Message } from '@phosphor/messaging';
|
||||
import { injectable, inject, postConstruct } from '@theia/core/shared/inversify';
|
||||
import { Message } from '@theia/core/shared/@phosphor/messaging';
|
||||
import { DialogProps, Widget, DialogError } from '@theia/core/lib/browser';
|
||||
import { AbstractDialog } from '../theia/dialogs/dialogs';
|
||||
import { BoardsConfig } from './boards-config';
|
||||
@@ -26,7 +26,7 @@ export class BoardsConfigDialog extends AbstractDialog<BoardsConfig.Config> {
|
||||
|
||||
constructor(
|
||||
@inject(BoardsConfigDialogProps)
|
||||
protected readonly props: BoardsConfigDialogProps
|
||||
protected override readonly props: BoardsConfigDialogProps
|
||||
) {
|
||||
super(props);
|
||||
|
||||
@@ -52,7 +52,7 @@ export class BoardsConfigDialog extends AbstractDialog<BoardsConfig.Config> {
|
||||
/**
|
||||
* Pass in an empty string if you want to reset the search term. Using `undefined` has no effect.
|
||||
*/
|
||||
async open(
|
||||
override async open(
|
||||
query: string | undefined = undefined
|
||||
): Promise<BoardsConfig.Config | undefined> {
|
||||
if (typeof query === 'string') {
|
||||
@@ -95,7 +95,7 @@ export class BoardsConfigDialog extends AbstractDialog<BoardsConfig.Config> {
|
||||
return head;
|
||||
}
|
||||
|
||||
protected onAfterAttach(msg: Message): void {
|
||||
protected override onAfterAttach(msg: Message): void {
|
||||
if (this.widget.isAttached) {
|
||||
Widget.detach(this.widget);
|
||||
}
|
||||
@@ -110,23 +110,23 @@ export class BoardsConfigDialog extends AbstractDialog<BoardsConfig.Config> {
|
||||
this.update();
|
||||
}
|
||||
|
||||
protected onUpdateRequest(msg: Message) {
|
||||
protected override onUpdateRequest(msg: Message): void {
|
||||
super.onUpdateRequest(msg);
|
||||
this.widget.update();
|
||||
}
|
||||
|
||||
protected onActivateRequest(msg: Message): void {
|
||||
protected override onActivateRequest(msg: Message): void {
|
||||
super.onActivateRequest(msg);
|
||||
this.widget.activate();
|
||||
}
|
||||
|
||||
protected handleEnter(event: KeyboardEvent): boolean | void {
|
||||
protected override handleEnter(event: KeyboardEvent): boolean | void {
|
||||
if (event.target instanceof HTMLTextAreaElement) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected isValid(value: BoardsConfig.Config): DialogError {
|
||||
protected override isValid(value: BoardsConfig.Config): DialogError {
|
||||
if (!value.selectedBoard) {
|
||||
if (value.selectedPort) {
|
||||
return nls.localize(
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import * as React from 'react';
|
||||
import * as React from '@theia/core/shared/react';
|
||||
import { Event } from '@theia/core/lib/common/event';
|
||||
import { notEmpty } from '@theia/core/lib/common/objects';
|
||||
import { MaybePromise } from '@theia/core/lib/common/types';
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
} from './boards-service-provider';
|
||||
import { naturalCompare } from '../../common/utils';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
import { FrontendApplicationState } from '@theia/core/lib/common/frontend-application-state';
|
||||
|
||||
export namespace BoardsConfig {
|
||||
export interface Config {
|
||||
@@ -29,6 +30,7 @@ export namespace BoardsConfig {
|
||||
readonly onConfigChange: (config: Config) => void;
|
||||
readonly onFocusNodeSet: (element: HTMLElement | undefined) => void;
|
||||
readonly onFilteredTextDidChangeEvent: Event<string>;
|
||||
readonly onAppStateDidChange: Event<FrontendApplicationState>;
|
||||
}
|
||||
|
||||
export interface State extends Config {
|
||||
@@ -47,7 +49,7 @@ export abstract class Item<T> extends React.Component<{
|
||||
missing?: boolean;
|
||||
details?: string;
|
||||
}> {
|
||||
render(): React.ReactNode {
|
||||
override render(): React.ReactNode {
|
||||
const { selected, label, missing, details } = this.props;
|
||||
const classNames = ['item'];
|
||||
if (selected) {
|
||||
@@ -99,14 +101,18 @@ export class BoardsConfig extends React.Component<
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.updateBoards();
|
||||
this.updatePorts(
|
||||
this.props.boardsServiceProvider.availableBoards
|
||||
.map(({ port }) => port)
|
||||
.filter(notEmpty)
|
||||
);
|
||||
override componentDidMount(): void {
|
||||
this.toDispose.pushAll([
|
||||
this.props.onAppStateDidChange((state) => {
|
||||
if (state === 'ready') {
|
||||
this.updateBoards();
|
||||
this.updatePorts(
|
||||
this.props.boardsServiceProvider.availableBoards
|
||||
.map(({ port }) => port)
|
||||
.filter(notEmpty)
|
||||
);
|
||||
}
|
||||
}),
|
||||
this.props.notificationCenter.onAttachedBoardsChanged((event) =>
|
||||
this.updatePorts(
|
||||
event.newState.ports,
|
||||
@@ -141,11 +147,11 @@ export class BoardsConfig extends React.Component<
|
||||
]);
|
||||
}
|
||||
|
||||
componentWillUnmount(): void {
|
||||
override componentWillUnmount(): void {
|
||||
this.toDispose.dispose();
|
||||
}
|
||||
|
||||
protected fireConfigChanged() {
|
||||
protected fireConfigChanged(): void {
|
||||
const { selectedBoard, selectedPort } = this.state;
|
||||
this.props.onConfigChange({ selectedBoard, selectedPort });
|
||||
}
|
||||
@@ -250,7 +256,7 @@ export class BoardsConfig extends React.Component<
|
||||
this.props.onFocusNodeSet(element || undefined);
|
||||
};
|
||||
|
||||
render(): React.ReactNode {
|
||||
override render(): React.ReactNode {
|
||||
return (
|
||||
<div className="body">
|
||||
{this.renderContainer('boards', this.renderBoards.bind(this))}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import * as PQueue from 'p-queue';
|
||||
import { inject, injectable } from 'inversify';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import { CommandRegistry } from '@theia/core/lib/common/command';
|
||||
import { MenuModelRegistry } from '@theia/core/lib/common/menu';
|
||||
import {
|
||||
@@ -13,6 +13,7 @@ import { BoardsDataStore } from './boards-data-store';
|
||||
import { MainMenuManager } from '../../common/main-menu-manager';
|
||||
import { ArduinoMenus, unregisterSubmenu } from '../menu/arduino-menus';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
|
||||
|
||||
@injectable()
|
||||
export class BoardsDataMenuUpdater implements FrontendApplicationContribution {
|
||||
@@ -31,11 +32,20 @@ export class BoardsDataMenuUpdater implements FrontendApplicationContribution {
|
||||
@inject(BoardsServiceProvider)
|
||||
protected readonly boardsServiceClient: BoardsServiceProvider;
|
||||
|
||||
@inject(FrontendApplicationStateService)
|
||||
private readonly appStateService: FrontendApplicationStateService;
|
||||
|
||||
protected readonly queue = new PQueue({ autoStart: true, concurrency: 1 });
|
||||
protected readonly toDisposeOnBoardChange = new DisposableCollection();
|
||||
|
||||
async onStart(): Promise<void> {
|
||||
this.updateMenuActions(this.boardsServiceClient.boardsConfig.selectedBoard);
|
||||
this.appStateService
|
||||
.reachedState('ready')
|
||||
.then(() =>
|
||||
this.updateMenuActions(
|
||||
this.boardsServiceClient.boardsConfig.selectedBoard
|
||||
)
|
||||
);
|
||||
this.boardsDataStore.onChanged(() =>
|
||||
this.updateMenuActions(
|
||||
this.boardsServiceClient.boardsConfig.selectedBoard
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { injectable, inject, named } from 'inversify';
|
||||
import { injectable, inject, named } from '@theia/core/shared/inversify';
|
||||
import { ILogger } from '@theia/core/lib/common/logger';
|
||||
import { deepClone } from '@theia/core/lib/common/objects';
|
||||
import { Event, Emitter } from '@theia/core/lib/common/event';
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { inject, injectable, postConstruct } from 'inversify';
|
||||
import { inject, injectable, postConstruct } from '@theia/core/shared/inversify';
|
||||
import {
|
||||
BoardsPackage,
|
||||
BoardsService,
|
||||
@@ -30,7 +30,7 @@ export class BoardsListWidget extends ListWidget<BoardsPackage> {
|
||||
}
|
||||
|
||||
@postConstruct()
|
||||
protected init(): void {
|
||||
protected override init(): void {
|
||||
super.init();
|
||||
this.toDispose.pushAll([
|
||||
this.notificationCenter.onPlatformInstalled(() =>
|
||||
@@ -42,7 +42,7 @@ export class BoardsListWidget extends ListWidget<BoardsPackage> {
|
||||
]);
|
||||
}
|
||||
|
||||
protected async install({
|
||||
protected override async install({
|
||||
item,
|
||||
progressId,
|
||||
version,
|
||||
@@ -63,7 +63,7 @@ export class BoardsListWidget extends ListWidget<BoardsPackage> {
|
||||
);
|
||||
}
|
||||
|
||||
protected async uninstall({
|
||||
protected override async uninstall({
|
||||
item,
|
||||
progressId,
|
||||
}: {
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { injectable, inject } from 'inversify';
|
||||
import { injectable, inject } from '@theia/core/shared/inversify';
|
||||
import { Emitter } from '@theia/core/lib/common/event';
|
||||
import { ILogger } from '@theia/core/lib/common/logger';
|
||||
import { CommandService } from '@theia/core/lib/common/command';
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import * as React from 'react';
|
||||
import * as ReactDOM from 'react-dom';
|
||||
import * as React from '@theia/core/shared/react';
|
||||
import * as ReactDOM from '@theia/core/shared/react-dom';
|
||||
import { CommandRegistry } from '@theia/core/lib/common/command';
|
||||
import { DisposableCollection } from '@theia/core/lib/common/disposable';
|
||||
import { Port } from '../../common/protocol';
|
||||
@@ -41,7 +41,7 @@ export class BoardsDropDown extends React.Component<BoardsDropDown.Props> {
|
||||
}
|
||||
}
|
||||
|
||||
render(): React.ReactNode {
|
||||
override render(): React.ReactNode {
|
||||
return ReactDOM.createPortal(this.renderNode(), this.dropdownElement);
|
||||
}
|
||||
|
||||
@@ -130,13 +130,13 @@ export class BoardsToolBarItem extends React.Component<
|
||||
});
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
override componentDidMount(): void {
|
||||
this.props.boardsServiceClient.onAvailableBoardsChanged((availableBoards) =>
|
||||
this.setState({ availableBoards })
|
||||
);
|
||||
}
|
||||
|
||||
componentWillUnmount(): void {
|
||||
override componentWillUnmount(): void {
|
||||
this.toDispose.dispose();
|
||||
}
|
||||
|
||||
@@ -161,7 +161,7 @@ export class BoardsToolBarItem extends React.Component<
|
||||
event.nativeEvent.stopImmediatePropagation();
|
||||
};
|
||||
|
||||
render(): React.ReactNode {
|
||||
override render(): React.ReactNode {
|
||||
const { coords, availableBoards } = this.state;
|
||||
const boardsConfig = this.props.boardsServiceClient.boardsConfig;
|
||||
const title = BoardsConfig.Config.toString(boardsConfig, {
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { injectable } from 'inversify';
|
||||
import { injectable } from '@theia/core/shared/inversify';
|
||||
import { BoardsListWidget } from './boards-list-widget';
|
||||
import { BoardsPackage } from '../../common/protocol/boards-service';
|
||||
import { ListWidgetFrontendContribution } from '../widgets/component-list/list-widget-frontend-contribution';
|
||||
@@ -18,7 +18,7 @@ export class BoardsListWidgetFrontendContribution extends ListWidgetFrontendCont
|
||||
});
|
||||
}
|
||||
|
||||
async initializeLayout(): Promise<void> {
|
||||
override async initializeLayout(): Promise<void> {
|
||||
this.openView();
|
||||
}
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import * as React from 'react';
|
||||
import * as React from '@theia/core/shared/react';
|
||||
|
||||
export type ProgressBarProps = {
|
||||
percent?: number;
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { inject, injectable } from 'inversify';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import * as moment from 'moment';
|
||||
import * as remote from '@theia/core/electron-shared/@electron/remote';
|
||||
import { isOSX, isWindows } from '@theia/core/lib/common/os';
|
||||
@@ -22,13 +22,13 @@ export class About extends Contribution {
|
||||
@inject(ConfigService)
|
||||
protected readonly configService: ConfigService;
|
||||
|
||||
registerCommands(registry: CommandRegistry): void {
|
||||
override registerCommands(registry: CommandRegistry): void {
|
||||
registry.registerCommand(About.Commands.ABOUT_APP, {
|
||||
execute: () => this.showAbout(),
|
||||
});
|
||||
}
|
||||
|
||||
registerMenus(registry: MenuModelRegistry): void {
|
||||
override registerMenus(registry: MenuModelRegistry): void {
|
||||
registry.registerMenuAction(ArduinoMenus.HELP__ABOUT_GROUP, {
|
||||
commandId: About.Commands.ABOUT_APP.id,
|
||||
label: nls.localize(
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { inject, injectable } from 'inversify';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import * as remote from '@theia/core/electron-shared/@electron/remote';
|
||||
import { ArduinoMenus } from '../menu/arduino-menus';
|
||||
import {
|
||||
@@ -10,19 +10,20 @@ import {
|
||||
} from './contribution';
|
||||
import { FileDialogService } from '@theia/filesystem/lib/browser';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
import { CurrentSketch } from '../../common/protocol/sketches-service-client-impl';
|
||||
|
||||
@injectable()
|
||||
export class AddFile extends SketchContribution {
|
||||
@inject(FileDialogService)
|
||||
protected readonly fileDialogService: FileDialogService;
|
||||
|
||||
registerCommands(registry: CommandRegistry): void {
|
||||
override registerCommands(registry: CommandRegistry): void {
|
||||
registry.registerCommand(AddFile.Commands.ADD_FILE, {
|
||||
execute: () => this.addFile(),
|
||||
});
|
||||
}
|
||||
|
||||
registerMenus(registry: MenuModelRegistry): void {
|
||||
override registerMenus(registry: MenuModelRegistry): void {
|
||||
registry.registerMenuAction(ArduinoMenus.SKETCH__UTILS_GROUP, {
|
||||
commandId: AddFile.Commands.ADD_FILE.id,
|
||||
label: nls.localize('arduino/contributions/addFile', 'Add File') + '...',
|
||||
@@ -32,7 +33,7 @@ export class AddFile extends SketchContribution {
|
||||
|
||||
protected async addFile(): Promise<void> {
|
||||
const sketch = await this.sketchServiceClient.currentSketch();
|
||||
if (!sketch) {
|
||||
if (!CurrentSketch.isValid(sketch)) {
|
||||
return;
|
||||
}
|
||||
const toAddUri = await this.fileDialogService.showOpenDialog({
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { inject, injectable } from 'inversify';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import * as remote from '@theia/core/electron-shared/@electron/remote';
|
||||
import URI from '@theia/core/lib/common/uri';
|
||||
import { ConfirmDialog } from '@theia/core/lib/browser/dialogs';
|
||||
@@ -28,21 +28,17 @@ export class AddZipLibrary extends SketchContribution {
|
||||
@inject(LibraryService)
|
||||
protected readonly libraryService: LibraryService;
|
||||
|
||||
registerCommands(registry: CommandRegistry): void {
|
||||
override registerCommands(registry: CommandRegistry): void {
|
||||
registry.registerCommand(AddZipLibrary.Commands.ADD_ZIP_LIBRARY, {
|
||||
execute: () => this.addZipLibrary(),
|
||||
});
|
||||
}
|
||||
|
||||
registerMenus(registry: MenuModelRegistry): void {
|
||||
override registerMenus(registry: MenuModelRegistry): void {
|
||||
const includeLibMenuPath = [
|
||||
...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,4 +1,4 @@
|
||||
import { injectable } from 'inversify';
|
||||
import { injectable } from '@theia/core/shared/inversify';
|
||||
import * as remote from '@theia/core/electron-shared/@electron/remote';
|
||||
import * as dateFormat from 'dateformat';
|
||||
import URI from '@theia/core/lib/common/uri';
|
||||
@@ -10,16 +10,17 @@ import {
|
||||
MenuModelRegistry,
|
||||
} from './contribution';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
import { CurrentSketch } from '../../common/protocol/sketches-service-client-impl';
|
||||
|
||||
@injectable()
|
||||
export class ArchiveSketch extends SketchContribution {
|
||||
registerCommands(registry: CommandRegistry): void {
|
||||
override registerCommands(registry: CommandRegistry): void {
|
||||
registry.registerCommand(ArchiveSketch.Commands.ARCHIVE_SKETCH, {
|
||||
execute: () => this.archiveSketch(),
|
||||
});
|
||||
}
|
||||
|
||||
registerMenus(registry: MenuModelRegistry): void {
|
||||
override registerMenus(registry: MenuModelRegistry): void {
|
||||
registry.registerMenuAction(ArduinoMenus.TOOLS__MAIN_GROUP, {
|
||||
commandId: ArchiveSketch.Commands.ARCHIVE_SKETCH.id,
|
||||
label: nls.localize('arduino/sketch/archiveSketch', 'Archive Sketch'),
|
||||
@@ -32,7 +33,7 @@ export class ArchiveSketch extends SketchContribution {
|
||||
this.sketchServiceClient.currentSketch(),
|
||||
this.configService.getConfiguration(),
|
||||
]);
|
||||
if (!sketch) {
|
||||
if (!CurrentSketch.isValid(sketch)) {
|
||||
return;
|
||||
}
|
||||
const archiveBasename = `${sketch.name}-${dateFormat(
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { inject, injectable } from 'inversify';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import * as remote from '@theia/core/electron-shared/@electron/remote';
|
||||
import { MenuModelRegistry } from '@theia/core/lib/common/menu';
|
||||
import {
|
||||
@@ -47,7 +47,7 @@ export class BoardSelection extends SketchContribution {
|
||||
|
||||
protected readonly toDisposeBeforeMenuRebuild = new DisposableCollection();
|
||||
|
||||
registerCommands(registry: CommandRegistry): void {
|
||||
override registerCommands(registry: CommandRegistry): void {
|
||||
registry.registerCommand(BoardSelection.Commands.GET_BOARD_INFO, {
|
||||
execute: async () => {
|
||||
const { selectedBoard, selectedPort } =
|
||||
@@ -100,19 +100,20 @@ PID: ${PID}`;
|
||||
});
|
||||
}
|
||||
|
||||
onStart(): void {
|
||||
override onStart(): void {
|
||||
this.notificationCenter.onPlatformInstalled(() => this.updateMenus());
|
||||
this.notificationCenter.onPlatformUninstalled(() => this.updateMenus());
|
||||
this.boardsServiceProvider.onBoardsConfigChanged(() => this.updateMenus());
|
||||
this.boardsServiceProvider.onAvailableBoardsChanged(() =>
|
||||
this.updateMenus()
|
||||
);
|
||||
this.boardsServiceProvider.onAvailablePortsChanged(() =>
|
||||
this.updateMenus()
|
||||
);
|
||||
}
|
||||
|
||||
override async onReady(): Promise<void> {
|
||||
this.updateMenus();
|
||||
this.notificationCenter.onPlatformInstalled(this.updateMenus.bind(this));
|
||||
this.notificationCenter.onPlatformUninstalled(this.updateMenus.bind(this));
|
||||
this.boardsServiceProvider.onBoardsConfigChanged(
|
||||
this.updateMenus.bind(this)
|
||||
);
|
||||
this.boardsServiceProvider.onAvailableBoardsChanged(
|
||||
this.updateMenus.bind(this)
|
||||
);
|
||||
this.boardsServiceProvider.onAvailablePortsChanged(
|
||||
this.updateMenus.bind(this)
|
||||
);
|
||||
}
|
||||
|
||||
protected async updateMenus(): Promise<void> {
|
||||
|
@@ -1,9 +1,8 @@
|
||||
import { inject, injectable } from 'inversify';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
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;
|
||||
@@ -28,15 +25,15 @@ export class BurnBootloader extends SketchContribution {
|
||||
protected readonly boardsServiceClientImpl: BoardsServiceProvider;
|
||||
|
||||
@inject(OutputChannelManager)
|
||||
protected readonly outputChannelManager: OutputChannelManager;
|
||||
protected override readonly outputChannelManager: OutputChannelManager;
|
||||
|
||||
registerCommands(registry: CommandRegistry): void {
|
||||
override registerCommands(registry: CommandRegistry): void {
|
||||
registry.registerCommand(BurnBootloader.Commands.BURN_BOOTLOADER, {
|
||||
execute: () => this.burnBootloader(),
|
||||
});
|
||||
}
|
||||
|
||||
registerMenus(registry: MenuModelRegistry): void {
|
||||
override registerMenus(registry: MenuModelRegistry): void {
|
||||
registry.registerMenuAction(ArduinoMenus.TOOLS__BOARD_SETTINGS_GROUP, {
|
||||
commandId: BurnBootloader.Commands.BURN_BOOTLOADER.id,
|
||||
label: nls.localize(
|
||||
@@ -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,12 +1,10 @@
|
||||
import { inject, injectable } from 'inversify';
|
||||
import { toArray } from '@phosphor/algorithm';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
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';
|
||||
import { FrontendApplication } from '@theia/core/lib/browser/frontend-application';
|
||||
import { ArduinoMenus } from '../menu/arduino-menus';
|
||||
import { SaveAsSketch } from './save-as-sketch';
|
||||
import {
|
||||
SketchContribution,
|
||||
Command,
|
||||
@@ -23,90 +21,21 @@ import { nls } from '@theia/core/lib/common';
|
||||
@injectable()
|
||||
export class Close extends SketchContribution {
|
||||
@inject(EditorManager)
|
||||
protected readonly editorManager: EditorManager;
|
||||
protected override readonly editorManager: EditorManager;
|
||||
|
||||
protected shell: ApplicationShell;
|
||||
|
||||
onStart(app: FrontendApplication): void {
|
||||
override onStart(app: FrontendApplication): void {
|
||||
this.shell = app.shell;
|
||||
}
|
||||
|
||||
registerCommands(registry: CommandRegistry): void {
|
||||
override registerCommands(registry: CommandRegistry): void {
|
||||
registry.registerCommand(Close.Commands.CLOSE, {
|
||||
execute: async () => {
|
||||
// Close current editor if closeable.
|
||||
const { currentEditor } = this.editorManager;
|
||||
if (currentEditor && currentEditor.title.closable) {
|
||||
currentEditor.close();
|
||||
return;
|
||||
}
|
||||
|
||||
// Close current widget from the main area if possible.
|
||||
const { currentWidget } = this.shell;
|
||||
if (currentWidget) {
|
||||
const currentWidgetInMain = toArray(
|
||||
this.shell.mainPanel.widgets()
|
||||
).find((widget) => widget === currentWidget);
|
||||
if (currentWidgetInMain && currentWidgetInMain.title.closable) {
|
||||
return currentWidgetInMain.close();
|
||||
}
|
||||
}
|
||||
|
||||
// Close the sketch (window).
|
||||
const sketch = await this.sketchServiceClient.currentSketch();
|
||||
if (!sketch) {
|
||||
return;
|
||||
}
|
||||
const isTemp = await this.sketchService.isTemp(sketch);
|
||||
const uri = await this.sketchServiceClient.currentSketchFile();
|
||||
if (!uri) {
|
||||
return;
|
||||
}
|
||||
if (isTemp && (await this.wasTouched(uri))) {
|
||||
const { response } = await remote.dialog.showMessageBox({
|
||||
type: 'question',
|
||||
buttons: [
|
||||
nls.localize(
|
||||
'vscode/abstractTaskService/saveBeforeRun.dontSave',
|
||||
"Don't Save"
|
||||
),
|
||||
nls.localize('vscode/issueMainService/cancel', 'Cancel'),
|
||||
nls.localize(
|
||||
'vscode/abstractTaskService/saveBeforeRun.save',
|
||||
'Save'
|
||||
),
|
||||
],
|
||||
message: nls.localize(
|
||||
'arduino/common/saveChangesToSketch',
|
||||
'Do you want to save changes to this sketch before closing?'
|
||||
),
|
||||
detail: nls.localize(
|
||||
'arduino/common/loseChanges',
|
||||
"If you don't save, your changes will be lost."
|
||||
),
|
||||
});
|
||||
if (response === 1) {
|
||||
// Cancel
|
||||
return;
|
||||
}
|
||||
if (response === 2) {
|
||||
// Save
|
||||
const saved = await this.commandService.executeCommand(
|
||||
SaveAsSketch.Commands.SAVE_AS_SKETCH.id,
|
||||
{ openAfterMove: false, execOnlyIfTemp: true }
|
||||
);
|
||||
if (!saved) {
|
||||
// If it was not saved, do bail the close.
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
window.close();
|
||||
},
|
||||
execute: () => remote.getCurrentWindow().close()
|
||||
});
|
||||
}
|
||||
|
||||
registerMenus(registry: MenuModelRegistry): void {
|
||||
override registerMenus(registry: MenuModelRegistry): void {
|
||||
registry.registerMenuAction(ArduinoMenus.FILE__SKETCH_GROUP, {
|
||||
commandId: Close.Commands.CLOSE.id,
|
||||
label: nls.localize('vscode/editor.contribution/close', 'Close'),
|
||||
@@ -114,7 +43,7 @@ export class Close extends SketchContribution {
|
||||
});
|
||||
}
|
||||
|
||||
registerKeybindings(registry: KeybindingRegistry): void {
|
||||
override registerKeybindings(registry: KeybindingRegistry): void {
|
||||
registry.registerKeybinding({
|
||||
command: Close.Commands.CLOSE.id,
|
||||
keybinding: 'CtrlCmd+W',
|
||||
|
@@ -1,4 +1,9 @@
|
||||
import { inject, injectable, interfaces } from 'inversify';
|
||||
import {
|
||||
inject,
|
||||
injectable,
|
||||
interfaces,
|
||||
postConstruct,
|
||||
} from '@theia/core/shared/inversify';
|
||||
import URI from '@theia/core/lib/common/uri';
|
||||
import { ILogger } from '@theia/core/lib/common/logger';
|
||||
import { Saveable } from '@theia/core/lib/browser/saveable';
|
||||
@@ -34,7 +39,10 @@ import {
|
||||
} from '@theia/core/lib/common/command';
|
||||
import { EditorMode } from '../editor-mode';
|
||||
import { SettingsService } from '../dialogs/settings/settings';
|
||||
import { SketchesServiceClientImpl } from '../../common/protocol/sketches-service-client-impl';
|
||||
import {
|
||||
CurrentSketch,
|
||||
SketchesServiceClientImpl,
|
||||
} from '../../common/protocol/sketches-service-client-impl';
|
||||
import {
|
||||
SketchesService,
|
||||
ConfigService,
|
||||
@@ -42,6 +50,7 @@ import {
|
||||
Sketch,
|
||||
} from '../../common/protocol';
|
||||
import { ArduinoPreferences } from '../arduino-preferences';
|
||||
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
|
||||
|
||||
export {
|
||||
Command,
|
||||
@@ -84,15 +93,31 @@ export abstract class Contribution
|
||||
@inject(SettingsService)
|
||||
protected readonly settingsService: SettingsService;
|
||||
|
||||
@inject(FrontendApplicationStateService)
|
||||
protected readonly appStateService: FrontendApplicationStateService;
|
||||
|
||||
@postConstruct()
|
||||
protected init(): void {
|
||||
this.appStateService.reachedState('ready').then(() => this.onReady());
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function, unused-imports/no-unused-vars
|
||||
onStart(app: FrontendApplication): MaybePromise<void> {}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function, unused-imports/no-unused-vars
|
||||
registerCommands(registry: CommandRegistry): void {}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function, unused-imports/no-unused-vars
|
||||
registerMenus(registry: MenuModelRegistry): void {}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function, unused-imports/no-unused-vars
|
||||
registerKeybindings(registry: KeybindingRegistry): void {}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function, unused-imports/no-unused-vars
|
||||
registerToolbarItems(registry: TabBarToolbarRegistry): void {}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
onReady(): MaybePromise<void> {}
|
||||
}
|
||||
|
||||
@injectable()
|
||||
@@ -127,7 +152,7 @@ export abstract class SketchContribution extends Contribution {
|
||||
protected async sourceOverride(): Promise<Record<string, string>> {
|
||||
const override: Record<string, string> = {};
|
||||
const sketch = await this.sketchServiceClient.currentSketch();
|
||||
if (sketch) {
|
||||
if (CurrentSketch.isValid(sketch)) {
|
||||
for (const editor of this.editorManager.all) {
|
||||
const uri = editor.editor.uri;
|
||||
if (Saveable.isDirty(editor) && Sketch.isInSketch(uri, sketch)) {
|
||||
@@ -140,7 +165,7 @@ export abstract class SketchContribution extends Contribution {
|
||||
}
|
||||
|
||||
export namespace Contribution {
|
||||
export function configure<T>(
|
||||
export function configure(
|
||||
bind: interfaces.Bind,
|
||||
serviceIdentifier: typeof Contribution
|
||||
): void {
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { inject, injectable } from 'inversify';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import { Event, Emitter } from '@theia/core/lib/common/event';
|
||||
import { HostedPluginSupport } from '@theia/plugin-ext/lib/hosted/browser/hosted-plugin';
|
||||
import { ArduinoToolbar } from '../toolbar/arduino-toolbar';
|
||||
@@ -12,7 +12,8 @@ import {
|
||||
SketchContribution,
|
||||
TabBarToolbarRegistry,
|
||||
} from './contribution';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
import { MaybePromise, nls } from '@theia/core/lib/common';
|
||||
import { CurrentSketch } from '../../common/protocol/sketches-service-client-impl';
|
||||
|
||||
@injectable()
|
||||
export class Debug extends SketchContribution {
|
||||
@@ -66,7 +67,7 @@ export class Debug extends SketchContribution {
|
||||
onDidChange: this.onDisabledMessageDidChange as Event<void>,
|
||||
};
|
||||
|
||||
onStart(): void {
|
||||
override onStart(): void {
|
||||
this.onDisabledMessageDidChange(
|
||||
() =>
|
||||
(this.debugToolbarItem.tooltip = `${
|
||||
@@ -79,55 +80,18 @@ export class Debug extends SketchContribution {
|
||||
: Debug.Commands.START_DEBUGGING.label
|
||||
}`)
|
||||
);
|
||||
const refreshState = async (
|
||||
board: Board | undefined = this.boardsServiceProvider.boardsConfig
|
||||
.selectedBoard
|
||||
) => {
|
||||
if (!board) {
|
||||
this.disabledMessage = nls.localize(
|
||||
'arduino/common/noBoardSelected',
|
||||
'No board selected'
|
||||
);
|
||||
return;
|
||||
}
|
||||
const fqbn = board.fqbn;
|
||||
if (!fqbn) {
|
||||
this.disabledMessage = nls.localize(
|
||||
'arduino/debug/noPlatformInstalledFor',
|
||||
"Platform is not installed for '{0}'",
|
||||
board.name
|
||||
);
|
||||
return;
|
||||
}
|
||||
const details = await this.boardService.getBoardDetails({ fqbn });
|
||||
if (!details) {
|
||||
this.disabledMessage = nls.localize(
|
||||
'arduino/debug/noPlatformInstalledFor',
|
||||
"Platform is not installed for '{0}'",
|
||||
board.name
|
||||
);
|
||||
return;
|
||||
}
|
||||
const { debuggingSupported } = details;
|
||||
if (!debuggingSupported) {
|
||||
this.disabledMessage = nls.localize(
|
||||
'arduino/debug/debuggingNotSupported',
|
||||
"Debugging is not supported by '{0}'",
|
||||
board.name
|
||||
);
|
||||
} else {
|
||||
this.disabledMessage = undefined;
|
||||
}
|
||||
};
|
||||
this.boardsServiceProvider.onBoardsConfigChanged(({ selectedBoard }) =>
|
||||
refreshState(selectedBoard)
|
||||
this.refreshState(selectedBoard)
|
||||
);
|
||||
this.notificationCenter.onPlatformInstalled(() => refreshState());
|
||||
this.notificationCenter.onPlatformUninstalled(() => refreshState());
|
||||
refreshState();
|
||||
this.notificationCenter.onPlatformInstalled(() => this.refreshState());
|
||||
this.notificationCenter.onPlatformUninstalled(() => this.refreshState());
|
||||
}
|
||||
|
||||
registerCommands(registry: CommandRegistry): void {
|
||||
override onReady(): MaybePromise<void> {
|
||||
this.refreshState();
|
||||
}
|
||||
|
||||
override registerCommands(registry: CommandRegistry): void {
|
||||
registry.registerCommand(Debug.Commands.START_DEBUGGING, {
|
||||
execute: () => this.startDebug(),
|
||||
isVisible: (widget) =>
|
||||
@@ -136,10 +100,51 @@ export class Debug extends SketchContribution {
|
||||
});
|
||||
}
|
||||
|
||||
registerToolbarItems(registry: TabBarToolbarRegistry): void {
|
||||
override registerToolbarItems(registry: TabBarToolbarRegistry): void {
|
||||
registry.registerItem(this.debugToolbarItem);
|
||||
}
|
||||
|
||||
private async refreshState(
|
||||
board: Board | undefined = this.boardsServiceProvider.boardsConfig
|
||||
.selectedBoard
|
||||
): Promise<void> {
|
||||
if (!board) {
|
||||
this.disabledMessage = nls.localize(
|
||||
'arduino/common/noBoardSelected',
|
||||
'No board selected'
|
||||
);
|
||||
return;
|
||||
}
|
||||
const fqbn = board.fqbn;
|
||||
if (!fqbn) {
|
||||
this.disabledMessage = nls.localize(
|
||||
'arduino/debug/noPlatformInstalledFor',
|
||||
"Platform is not installed for '{0}'",
|
||||
board.name
|
||||
);
|
||||
return;
|
||||
}
|
||||
const details = await this.boardService.getBoardDetails({ fqbn });
|
||||
if (!details) {
|
||||
this.disabledMessage = nls.localize(
|
||||
'arduino/debug/noPlatformInstalledFor',
|
||||
"Platform is not installed for '{0}'",
|
||||
board.name
|
||||
);
|
||||
return;
|
||||
}
|
||||
const { debuggingSupported } = details;
|
||||
if (!debuggingSupported) {
|
||||
this.disabledMessage = nls.localize(
|
||||
'arduino/debug/debuggingNotSupported',
|
||||
"Debugging is not supported by '{0}'",
|
||||
board.name
|
||||
);
|
||||
} else {
|
||||
this.disabledMessage = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
protected async startDebug(
|
||||
board: Board | undefined = this.boardsServiceProvider.boardsConfig
|
||||
.selectedBoard
|
||||
@@ -156,7 +161,7 @@ export class Debug extends SketchContribution {
|
||||
this.sketchServiceClient.currentSketch(),
|
||||
this.executableService.list(),
|
||||
]);
|
||||
if (!sketch) {
|
||||
if (!CurrentSketch.isValid(sketch)) {
|
||||
return;
|
||||
}
|
||||
const ideTempFolderUri = await this.sketchService.getIdeTempFolderUri(
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { inject, injectable } from 'inversify';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import { CommonCommands } from '@theia/core/lib/browser/common-frontend-contribution';
|
||||
import { ClipboardService } from '@theia/core/lib/browser/clipboard-service';
|
||||
import { PreferenceService } from '@theia/core/lib/browser/preferences/preference-service';
|
||||
@@ -12,6 +12,8 @@ import {
|
||||
} from './contribution';
|
||||
import { ArduinoMenus } from '../menu/arduino-menus';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
import type { ICodeEditor } from '@theia/monaco-editor-core/esm/vs/editor/browser/editorBrowser';
|
||||
import type { StandaloneCodeEditor } from '@theia/monaco-editor-core/esm/vs/editor/standalone/browser/standaloneCodeEditor';
|
||||
|
||||
// TODO: [macOS]: to remove `Start Dictation...` and `Emoji & Symbol` see this thread: https://github.com/electron/electron/issues/8283#issuecomment-269522072
|
||||
// Depends on https://github.com/eclipse-theia/theia/pull/7964
|
||||
@@ -26,7 +28,7 @@ export class EditContributions extends Contribution {
|
||||
@inject(PreferenceService)
|
||||
protected readonly preferences: PreferenceService;
|
||||
|
||||
registerCommands(registry: CommandRegistry): void {
|
||||
override registerCommands(registry: CommandRegistry): void {
|
||||
registry.registerCommand(EditContributions.Commands.GO_TO_LINE, {
|
||||
execute: () => this.run('editor.action.gotoLine'),
|
||||
});
|
||||
@@ -43,10 +45,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'),
|
||||
@@ -91,7 +93,7 @@ ${value}
|
||||
});
|
||||
}
|
||||
|
||||
registerMenus(registry: MenuModelRegistry): void {
|
||||
override registerMenus(registry: MenuModelRegistry): void {
|
||||
registry.registerMenuAction(ArduinoMenus.EDIT__TEXT_CONTROL_GROUP, {
|
||||
commandId: CommonCommands.CUT.id,
|
||||
order: '0',
|
||||
@@ -199,7 +201,7 @@ ${value}
|
||||
});
|
||||
}
|
||||
|
||||
registerKeybindings(registry: KeybindingRegistry): void {
|
||||
override registerKeybindings(registry: KeybindingRegistry): void {
|
||||
registry.registerKeybinding({
|
||||
command: EditContributions.Commands.COPY_FOR_FORUM.id,
|
||||
keybinding: 'CtrlCmd+Shift+C',
|
||||
@@ -250,10 +252,10 @@ ${value}
|
||||
});
|
||||
}
|
||||
|
||||
protected async current(): Promise<monaco.editor.ICodeEditor | undefined> {
|
||||
protected async current(): Promise<ICodeEditor | StandaloneCodeEditor | undefined> {
|
||||
return (
|
||||
this.codeEditorService.getFocusedCodeEditor() ||
|
||||
this.codeEditorService.getActiveCodeEditor()
|
||||
this.codeEditorService.getActiveCodeEditor() || undefined
|
||||
);
|
||||
}
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import * as PQueue from 'p-queue';
|
||||
import { inject, injectable, postConstruct } from 'inversify';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import { CommandHandler } from '@theia/core/lib/common/command';
|
||||
import {
|
||||
MenuPath,
|
||||
@@ -21,7 +21,7 @@ import {
|
||||
MenuModelRegistry,
|
||||
} from './contribution';
|
||||
import { NotificationCenter } from '../notification-center';
|
||||
import { Board, Sketch, SketchContainer } from '../../common/protocol';
|
||||
import { Board, SketchRef, SketchContainer } from '../../common/protocol';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
|
||||
@injectable()
|
||||
@@ -43,8 +43,8 @@ export abstract class Examples extends SketchContribution {
|
||||
|
||||
protected readonly toDispose = new DisposableCollection();
|
||||
|
||||
@postConstruct()
|
||||
init(): void {
|
||||
protected override init(): void {
|
||||
super.init();
|
||||
this.boardsServiceClient.onBoardsConfigChanged(({ selectedBoard }) =>
|
||||
this.handleBoardChanged(selectedBoard)
|
||||
);
|
||||
@@ -54,7 +54,7 @@ export abstract class Examples extends SketchContribution {
|
||||
// NOOP
|
||||
}
|
||||
|
||||
registerMenus(registry: MenuModelRegistry): void {
|
||||
override registerMenus(registry: MenuModelRegistry): void {
|
||||
try {
|
||||
// This is a hack the ensures the desired menu ordering! We cannot use https://github.com/eclipse-theia/theia/pull/8377 due to ATL-222.
|
||||
const index = ArduinoMenus.FILE__EXAMPLES_SUBMENU.length - 1;
|
||||
@@ -82,7 +82,7 @@ export abstract class Examples extends SketchContribution {
|
||||
registerRecursively(
|
||||
sketchContainerOrPlaceholder:
|
||||
| SketchContainer
|
||||
| (Sketch | SketchContainer)[]
|
||||
| (SketchRef | SketchContainer)[]
|
||||
| string,
|
||||
menuPath: MenuPath,
|
||||
pushToDispose: DisposableCollection = new DisposableCollection(),
|
||||
@@ -100,7 +100,7 @@ export abstract class Examples extends SketchContribution {
|
||||
)
|
||||
);
|
||||
} else {
|
||||
const sketches: Sketch[] = [];
|
||||
const sketches: SketchRef[] = [];
|
||||
const children: SketchContainer[] = [];
|
||||
let submenuPath = menuPath;
|
||||
|
||||
@@ -161,7 +161,7 @@ export abstract class Examples extends SketchContribution {
|
||||
|
||||
@injectable()
|
||||
export class BuiltInExamples extends Examples {
|
||||
onStart(): void {
|
||||
override async onReady(): Promise<void> {
|
||||
this.register(); // no `await`
|
||||
}
|
||||
|
||||
@@ -201,13 +201,16 @@ export class LibraryExamples extends Examples {
|
||||
|
||||
protected readonly queue = new PQueue({ autoStart: true, concurrency: 1 });
|
||||
|
||||
onStart(): void {
|
||||
this.register(); // no `await`
|
||||
override onStart(): void {
|
||||
this.notificationCenter.onLibraryInstalled(() => this.register());
|
||||
this.notificationCenter.onLibraryUninstalled(() => this.register());
|
||||
}
|
||||
|
||||
protected handleBoardChanged(board: Board | undefined): void {
|
||||
override async onReady(): Promise<void> {
|
||||
this.register(); // no `await`
|
||||
}
|
||||
|
||||
protected override handleBoardChanged(board: Board | undefined): void {
|
||||
this.register(board);
|
||||
}
|
||||
|
||||
|
94
arduino-ide-extension/src/browser/contributions/format.ts
Normal file
94
arduino-ide-extension/src/browser/contributions/format.ts
Normal file
@@ -0,0 +1,94 @@
|
||||
import { MaybePromise } from '@theia/core';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import * as monaco from '@theia/monaco-editor-core';
|
||||
import { Formatter } from '../../common/protocol/formatter';
|
||||
import { Contribution, URI } from './contribution';
|
||||
|
||||
@injectable()
|
||||
export class Format
|
||||
extends Contribution
|
||||
implements
|
||||
monaco.languages.DocumentRangeFormattingEditProvider,
|
||||
monaco.languages.DocumentFormattingEditProvider
|
||||
{
|
||||
@inject(Formatter)
|
||||
private readonly formatter: Formatter;
|
||||
|
||||
override onStart(): MaybePromise<void> {
|
||||
const selector = this.selectorOf('ino', 'c', 'cpp', 'h', 'hpp', 'pde');
|
||||
monaco.languages.registerDocumentRangeFormattingEditProvider(
|
||||
selector,
|
||||
this
|
||||
);
|
||||
monaco.languages.registerDocumentFormattingEditProvider(selector, this);
|
||||
}
|
||||
async provideDocumentRangeFormattingEdits(
|
||||
model: monaco.editor.ITextModel,
|
||||
range: monaco.Range,
|
||||
options: monaco.languages.FormattingOptions,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
_token: monaco.CancellationToken
|
||||
): Promise<monaco.languages.TextEdit[]> {
|
||||
const text = await this.format(model, range, options);
|
||||
return [{ range, text }];
|
||||
}
|
||||
|
||||
async provideDocumentFormattingEdits(
|
||||
model: monaco.editor.ITextModel,
|
||||
options: monaco.languages.FormattingOptions,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
_token: monaco.CancellationToken
|
||||
): Promise<monaco.languages.TextEdit[]> {
|
||||
const range = this.fullRange(model);
|
||||
const text = await this.format(model, range, options);
|
||||
return [{ range, text }];
|
||||
}
|
||||
|
||||
private fullRange(model: monaco.editor.ITextModel): monaco.Range {
|
||||
const lastLine = model.getLineCount();
|
||||
const lastLineMaxColumn = model.getLineMaxColumn(lastLine);
|
||||
const end = new monaco.Position(lastLine, lastLineMaxColumn);
|
||||
return monaco.Range.fromPositions(new monaco.Position(1, 1), end);
|
||||
}
|
||||
|
||||
/**
|
||||
* From the currently opened workspaces (IDE2 has always one), it calculates all possible
|
||||
* folder locations where the `.clang-format` file could be.
|
||||
*/
|
||||
private formatterConfigFolderUris(model: monaco.editor.ITextModel): string[] {
|
||||
const editorUri = new URI(model.uri.toString());
|
||||
return this.workspaceService
|
||||
.tryGetRoots()
|
||||
.map(({ resource }) => resource)
|
||||
.filter((workspaceUri) => workspaceUri.isEqualOrParent(editorUri))
|
||||
.map((uri) => uri.toString());
|
||||
}
|
||||
|
||||
private format(
|
||||
model: monaco.editor.ITextModel,
|
||||
range: monaco.Range,
|
||||
options: monaco.languages.FormattingOptions
|
||||
): Promise<string> {
|
||||
console.info(
|
||||
`Formatting ${model.uri.toString()} [Range: ${JSON.stringify(
|
||||
range.toJSON()
|
||||
)}]`
|
||||
);
|
||||
const content = model.getValueInRange(range);
|
||||
const formatterConfigFolderUris = this.formatterConfigFolderUris(model);
|
||||
return this.formatter.format({
|
||||
content,
|
||||
formatterConfigFolderUris,
|
||||
options,
|
||||
});
|
||||
}
|
||||
|
||||
private selectorOf(
|
||||
...languageId: string[]
|
||||
): monaco.languages.LanguageSelector {
|
||||
return languageId.map((language) => ({
|
||||
language,
|
||||
exclusive: true, // <-- this should make sure the custom formatter has higher precedence over the LS formatter.
|
||||
}));
|
||||
}
|
||||
}
|
@@ -1,4 +1,4 @@
|
||||
import { inject, injectable } from 'inversify';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import { MonacoEditor } from '@theia/monaco/lib/browser/monaco-editor';
|
||||
import { EditorManager } from '@theia/editor/lib/browser/editor-manager';
|
||||
import { WindowService } from '@theia/core/lib/browser/window/window-service';
|
||||
@@ -14,6 +14,8 @@ import {
|
||||
} 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';
|
||||
import * as monaco from '@theia/monaco-editor-core';
|
||||
|
||||
@injectable()
|
||||
export class Help extends Contribution {
|
||||
@@ -26,7 +28,7 @@ export class Help extends Contribution {
|
||||
@inject(QuickInputService)
|
||||
protected readonly quickInputService: QuickInputService;
|
||||
|
||||
registerCommands(registry: CommandRegistry): void {
|
||||
override registerCommands(registry: CommandRegistry): void {
|
||||
const open = (url: string) =>
|
||||
this.windowService.openNewWindow(url, { external: true });
|
||||
const createOpenHandler = (url: string) =>
|
||||
@@ -84,9 +86,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 {
|
||||
override 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',
|
||||
@@ -117,12 +127,16 @@ export class Help extends Contribution {
|
||||
order: '6',
|
||||
});
|
||||
registry.registerMenuAction(ArduinoMenus.HELP__FIND_GROUP, {
|
||||
commandId: IDEUpdaterCommands.CHECK_FOR_UPDATES.id,
|
||||
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 {
|
||||
override registerKeybindings(registry: KeybindingRegistry): void {
|
||||
registry.registerKeybinding({
|
||||
command: Help.Commands.FIND_IN_REFERENCE.id,
|
||||
keybinding: 'CtrlCmd+Shift+F',
|
||||
@@ -167,5 +181,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 * as PQueue from 'p-queue';
|
||||
import { inject, injectable } from 'inversify';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import URI from '@theia/core/lib/common/uri';
|
||||
import { MonacoEditor } from '@theia/monaco/lib/browser/monaco-editor';
|
||||
import { EditorManager } from '@theia/editor/lib/browser';
|
||||
@@ -16,6 +16,8 @@ import { BoardsServiceProvider } from '../boards/boards-service-provider';
|
||||
import { SketchContribution, Command, CommandRegistry } from './contribution';
|
||||
import { NotificationCenter } from '../notification-center';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
import * as monaco from '@theia/monaco-editor-core';
|
||||
import { CurrentSketch } from '../../common/protocol/sketches-service-client-impl';
|
||||
|
||||
@injectable()
|
||||
export class IncludeLibrary extends SketchContribution {
|
||||
@@ -29,7 +31,7 @@ export class IncludeLibrary extends SketchContribution {
|
||||
protected readonly mainMenuManager: MainMenuManager;
|
||||
|
||||
@inject(EditorManager)
|
||||
protected readonly editorManager: EditorManager;
|
||||
protected override readonly editorManager: EditorManager;
|
||||
|
||||
@inject(NotificationCenter)
|
||||
protected readonly notificationCenter: NotificationCenter;
|
||||
@@ -43,8 +45,7 @@ export class IncludeLibrary extends SketchContribution {
|
||||
protected readonly queue = new PQueue({ autoStart: true, concurrency: 1 });
|
||||
protected readonly toDispose = new DisposableCollection();
|
||||
|
||||
onStart(): void {
|
||||
this.updateMenuActions();
|
||||
override onStart(): void {
|
||||
this.boardsServiceClient.onBoardsConfigChanged(() =>
|
||||
this.updateMenuActions()
|
||||
);
|
||||
@@ -54,7 +55,11 @@ export class IncludeLibrary extends SketchContribution {
|
||||
);
|
||||
}
|
||||
|
||||
registerMenus(registry: MenuModelRegistry): void {
|
||||
override async onReady(): Promise<void> {
|
||||
this.updateMenuActions();
|
||||
}
|
||||
|
||||
override registerMenus(registry: MenuModelRegistry): void {
|
||||
// `Include Library` submenu
|
||||
const includeLibMenuPath = [
|
||||
...ArduinoMenus.SKETCH__UTILS_GROUP,
|
||||
@@ -77,7 +82,7 @@ export class IncludeLibrary extends SketchContribution {
|
||||
});
|
||||
}
|
||||
|
||||
registerCommands(registry: CommandRegistry): void {
|
||||
override registerCommands(registry: CommandRegistry): void {
|
||||
registry.registerCommand(IncludeLibrary.Commands.INCLUDE_LIBRARY, {
|
||||
execute: async (arg) => {
|
||||
if (LibraryPackage.is(arg)) {
|
||||
@@ -168,7 +173,7 @@ export class IncludeLibrary extends SketchContribution {
|
||||
|
||||
protected async includeLibrary(library: LibraryPackage): Promise<void> {
|
||||
const sketch = await this.sketchServiceClient.currentSketch();
|
||||
if (!sketch) {
|
||||
if (!CurrentSketch.isValid(sketch)) {
|
||||
return;
|
||||
}
|
||||
// If the current editor is one of the additional files from the sketch, we use that.
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
import { injectable } from 'inversify';
|
||||
import { injectable } from '@theia/core/shared/inversify';
|
||||
import { ArduinoMenus } from '../menu/arduino-menus';
|
||||
import { ArduinoToolbar } from '../toolbar/arduino-toolbar';
|
||||
import {
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
|
||||
@injectable()
|
||||
export class NewSketch extends SketchContribution {
|
||||
registerCommands(registry: CommandRegistry): void {
|
||||
override registerCommands(registry: CommandRegistry): void {
|
||||
registry.registerCommand(NewSketch.Commands.NEW_SKETCH, {
|
||||
execute: () => this.newSketch(),
|
||||
});
|
||||
@@ -25,7 +25,7 @@ export class NewSketch extends SketchContribution {
|
||||
});
|
||||
}
|
||||
|
||||
registerMenus(registry: MenuModelRegistry): void {
|
||||
override registerMenus(registry: MenuModelRegistry): void {
|
||||
registry.registerMenuAction(ArduinoMenus.FILE__SKETCH_GROUP, {
|
||||
commandId: NewSketch.Commands.NEW_SKETCH.id,
|
||||
label: nls.localize('arduino/sketch/new', 'New'),
|
||||
@@ -33,14 +33,14 @@ export class NewSketch extends SketchContribution {
|
||||
});
|
||||
}
|
||||
|
||||
registerKeybindings(registry: KeybindingRegistry): void {
|
||||
override registerKeybindings(registry: KeybindingRegistry): void {
|
||||
registry.registerKeybinding({
|
||||
command: NewSketch.Commands.NEW_SKETCH.id,
|
||||
keybinding: 'CtrlCmd+N',
|
||||
});
|
||||
}
|
||||
|
||||
registerToolbarItems(registry: TabBarToolbarRegistry): void {
|
||||
override registerToolbarItems(registry: TabBarToolbarRegistry): void {
|
||||
registry.registerItem({
|
||||
id: NewSketch.Commands.NEW_SKETCH__TOOLBAR.id,
|
||||
command: NewSketch.Commands.NEW_SKETCH__TOOLBAR.id,
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { inject, injectable } from 'inversify';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import { WorkspaceServer } from '@theia/workspace/lib/common/workspace-protocol';
|
||||
import {
|
||||
Disposable,
|
||||
@@ -35,18 +35,19 @@ export class OpenRecentSketch extends SketchContribution {
|
||||
|
||||
protected toDisposeBeforeRegister = new Map<string, DisposableCollection>();
|
||||
|
||||
onStart(): void {
|
||||
const refreshMenu = (sketches: Sketch[]) => {
|
||||
this.register(sketches);
|
||||
this.mainMenuManager.update();
|
||||
};
|
||||
override onStart(): void {
|
||||
this.notificationCenter.onRecentSketchesChanged(({ sketches }) =>
|
||||
refreshMenu(sketches)
|
||||
this.refreshMenu(sketches)
|
||||
);
|
||||
this.sketchService.recentlyOpenedSketches().then(refreshMenu);
|
||||
}
|
||||
|
||||
registerMenus(registry: MenuModelRegistry): void {
|
||||
override async onReady(): Promise<void> {
|
||||
this.sketchService
|
||||
.recentlyOpenedSketches()
|
||||
.then((sketches) => this.refreshMenu(sketches));
|
||||
}
|
||||
|
||||
override registerMenus(registry: MenuModelRegistry): void {
|
||||
registry.registerSubmenu(
|
||||
ArduinoMenus.FILE__OPEN_RECENT_SUBMENU,
|
||||
nls.localize('arduino/sketch/openRecent', 'Open Recent'),
|
||||
@@ -54,6 +55,11 @@ export class OpenRecentSketch extends SketchContribution {
|
||||
);
|
||||
}
|
||||
|
||||
private refreshMenu(sketches: Sketch[]): void {
|
||||
this.register(sketches);
|
||||
this.mainMenuManager.update();
|
||||
}
|
||||
|
||||
protected register(sketches: Sketch[]): void {
|
||||
const order = 0;
|
||||
for (const sketch of sketches) {
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { injectable } from 'inversify';
|
||||
import { injectable } from '@theia/core/shared/inversify';
|
||||
import * as remote from '@theia/core/electron-shared/@electron/remote';
|
||||
import URI from '@theia/core/lib/common/uri';
|
||||
import { ArduinoMenus } from '../menu/arduino-menus';
|
||||
@@ -13,13 +13,13 @@ import { nls } from '@theia/core/lib/common';
|
||||
|
||||
@injectable()
|
||||
export class OpenSketchExternal extends SketchContribution {
|
||||
registerCommands(registry: CommandRegistry): void {
|
||||
override registerCommands(registry: CommandRegistry): void {
|
||||
registry.registerCommand(OpenSketchExternal.Commands.OPEN_EXTERNAL, {
|
||||
execute: () => this.openExternal(),
|
||||
});
|
||||
}
|
||||
|
||||
registerMenus(registry: MenuModelRegistry): void {
|
||||
override registerMenus(registry: MenuModelRegistry): void {
|
||||
registry.registerMenuAction(ArduinoMenus.SKETCH__UTILS_GROUP, {
|
||||
commandId: OpenSketchExternal.Commands.OPEN_EXTERNAL.id,
|
||||
label: nls.localize('arduino/sketch/showFolder', 'Show Sketch Folder'),
|
||||
@@ -27,7 +27,7 @@ export class OpenSketchExternal extends SketchContribution {
|
||||
});
|
||||
}
|
||||
|
||||
registerKeybindings(registry: KeybindingRegistry): void {
|
||||
override registerKeybindings(registry: KeybindingRegistry): void {
|
||||
registry.registerKeybinding({
|
||||
command: OpenSketchExternal.Commands.OPEN_EXTERNAL.id,
|
||||
keybinding: 'CtrlCmd+Alt+K',
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { inject, injectable } from 'inversify';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
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';
|
||||
@@ -43,7 +43,7 @@ export class OpenSketch extends SketchContribution {
|
||||
|
||||
protected readonly toDispose = new DisposableCollection();
|
||||
|
||||
registerCommands(registry: CommandRegistry): void {
|
||||
override registerCommands(registry: CommandRegistry): void {
|
||||
registry.registerCommand(OpenSketch.Commands.OPEN_SKETCH, {
|
||||
execute: (arg) =>
|
||||
Sketch.is(arg) ? this.openSketch(arg) : this.openSketch(),
|
||||
@@ -116,7 +116,7 @@ export class OpenSketch extends SketchContribution {
|
||||
});
|
||||
}
|
||||
|
||||
registerMenus(registry: MenuModelRegistry): void {
|
||||
override registerMenus(registry: MenuModelRegistry): void {
|
||||
registry.registerMenuAction(ArduinoMenus.FILE__SKETCH_GROUP, {
|
||||
commandId: OpenSketch.Commands.OPEN_SKETCH.id,
|
||||
label: nls.localize('vscode/workspaceActions/openFileFolder', 'Open...'),
|
||||
@@ -124,14 +124,14 @@ export class OpenSketch extends SketchContribution {
|
||||
});
|
||||
}
|
||||
|
||||
registerKeybindings(registry: KeybindingRegistry): void {
|
||||
override registerKeybindings(registry: KeybindingRegistry): void {
|
||||
registry.registerKeybinding({
|
||||
command: OpenSketch.Commands.OPEN_SKETCH.id,
|
||||
keybinding: 'CtrlCmd+O',
|
||||
});
|
||||
}
|
||||
|
||||
registerToolbarItems(registry: TabBarToolbarRegistry): void {
|
||||
override registerToolbarItems(registry: TabBarToolbarRegistry): void {
|
||||
registry.registerItem({
|
||||
id: OpenSketch.Commands.OPEN_SKETCH__TOOLBAR.id,
|
||||
command: OpenSketch.Commands.OPEN_SKETCH__TOOLBAR.id,
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { injectable } from 'inversify';
|
||||
import { injectable } from '@theia/core/shared/inversify';
|
||||
import * as remote from '@theia/core/electron-shared/@electron/remote';
|
||||
import { isOSX } from '@theia/core/lib/common/os';
|
||||
import {
|
||||
@@ -13,7 +13,7 @@ import { nls } from '@theia/core/lib/common';
|
||||
|
||||
@injectable()
|
||||
export class QuitApp extends Contribution {
|
||||
registerCommands(registry: CommandRegistry): void {
|
||||
override registerCommands(registry: CommandRegistry): void {
|
||||
if (!isOSX) {
|
||||
registry.registerCommand(QuitApp.Commands.QUIT_APP, {
|
||||
execute: () => remote.app.quit(),
|
||||
@@ -21,7 +21,7 @@ export class QuitApp extends Contribution {
|
||||
}
|
||||
}
|
||||
|
||||
registerMenus(registry: MenuModelRegistry): void {
|
||||
override registerMenus(registry: MenuModelRegistry): void {
|
||||
// On macOS we will get the `Quit ${YOUR_APP_NAME}` menu item natively, no need to duplicate it.
|
||||
if (!isOSX) {
|
||||
registry.registerMenuAction(ArduinoMenus.FILE__QUIT_GROUP, {
|
||||
@@ -32,7 +32,7 @@ export class QuitApp extends Contribution {
|
||||
}
|
||||
}
|
||||
|
||||
registerKeybindings(registry: KeybindingRegistry): void {
|
||||
override registerKeybindings(registry: KeybindingRegistry): void {
|
||||
if (!isOSX) {
|
||||
registry.registerKeybinding({
|
||||
command: QuitApp.Commands.QUIT_APP.id,
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { injectable } from 'inversify';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import * as remote from '@theia/core/electron-shared/@electron/remote';
|
||||
import * as dateFormat from 'dateformat';
|
||||
import { ArduinoMenus } from '../menu/arduino-menus';
|
||||
@@ -11,16 +11,30 @@ 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';
|
||||
import { CurrentSketch } from '../../common/protocol/sketches-service-client-impl';
|
||||
|
||||
@injectable()
|
||||
export class SaveAsSketch extends SketchContribution {
|
||||
registerCommands(registry: CommandRegistry): void {
|
||||
|
||||
@inject(ApplicationShell)
|
||||
protected readonly applicationShell: ApplicationShell;
|
||||
|
||||
@inject(EditorManager)
|
||||
protected override readonly editorManager: EditorManager;
|
||||
|
||||
@inject(WindowService)
|
||||
protected readonly windowService: WindowService;
|
||||
|
||||
override registerCommands(registry: CommandRegistry): void {
|
||||
registry.registerCommand(SaveAsSketch.Commands.SAVE_AS_SKETCH, {
|
||||
execute: (args) => this.saveAs(args),
|
||||
});
|
||||
}
|
||||
|
||||
registerMenus(registry: MenuModelRegistry): void {
|
||||
override registerMenus(registry: MenuModelRegistry): void {
|
||||
registry.registerMenuAction(ArduinoMenus.FILE__SKETCH_GROUP, {
|
||||
commandId: SaveAsSketch.Commands.SAVE_AS_SKETCH.id,
|
||||
label: nls.localize('vscode/fileCommands/saveAs', 'Save As...'),
|
||||
@@ -28,7 +42,7 @@ export class SaveAsSketch extends SketchContribution {
|
||||
});
|
||||
}
|
||||
|
||||
registerKeybindings(registry: KeybindingRegistry): void {
|
||||
override registerKeybindings(registry: KeybindingRegistry): void {
|
||||
registry.registerKeybinding({
|
||||
command: SaveAsSketch.Commands.SAVE_AS_SKETCH.id,
|
||||
keybinding: 'CtrlCmd+Shift+S',
|
||||
@@ -46,7 +60,7 @@ export class SaveAsSketch extends SketchContribution {
|
||||
}: SaveAsSketch.Options = SaveAsSketch.Options.DEFAULT
|
||||
): Promise<boolean> {
|
||||
const sketch = await this.sketchServiceClient.currentSketch();
|
||||
if (!sketch) {
|
||||
if (!CurrentSketch.isValid(sketch)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -90,6 +104,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 +117,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 {
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { injectable } from 'inversify';
|
||||
import { injectable } from '@theia/core/shared/inversify';
|
||||
import { CommonCommands } from '@theia/core/lib/browser/common-frontend-contribution';
|
||||
import { ArduinoMenus } from '../menu/arduino-menus';
|
||||
import { ArduinoToolbar } from '../toolbar/arduino-toolbar';
|
||||
@@ -12,10 +12,11 @@ import {
|
||||
TabBarToolbarRegistry,
|
||||
} from './contribution';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
import { CurrentSketch } from '../../common/protocol/sketches-service-client-impl';
|
||||
|
||||
@injectable()
|
||||
export class SaveSketch extends SketchContribution {
|
||||
registerCommands(registry: CommandRegistry): void {
|
||||
override registerCommands(registry: CommandRegistry): void {
|
||||
registry.registerCommand(SaveSketch.Commands.SAVE_SKETCH, {
|
||||
execute: () => this.saveSketch(),
|
||||
});
|
||||
@@ -27,7 +28,7 @@ export class SaveSketch extends SketchContribution {
|
||||
});
|
||||
}
|
||||
|
||||
registerMenus(registry: MenuModelRegistry): void {
|
||||
override registerMenus(registry: MenuModelRegistry): void {
|
||||
registry.registerMenuAction(ArduinoMenus.FILE__SKETCH_GROUP, {
|
||||
commandId: SaveSketch.Commands.SAVE_SKETCH.id,
|
||||
label: nls.localize('vscode/fileCommands/save', 'Save'),
|
||||
@@ -35,14 +36,14 @@ export class SaveSketch extends SketchContribution {
|
||||
});
|
||||
}
|
||||
|
||||
registerKeybindings(registry: KeybindingRegistry): void {
|
||||
override registerKeybindings(registry: KeybindingRegistry): void {
|
||||
registry.registerKeybinding({
|
||||
command: SaveSketch.Commands.SAVE_SKETCH.id,
|
||||
keybinding: 'CtrlCmd+S',
|
||||
});
|
||||
}
|
||||
|
||||
registerToolbarItems(registry: TabBarToolbarRegistry): void {
|
||||
override registerToolbarItems(registry: TabBarToolbarRegistry): void {
|
||||
registry.registerItem({
|
||||
id: SaveSketch.Commands.SAVE_SKETCH__TOOLBAR.id,
|
||||
command: SaveSketch.Commands.SAVE_SKETCH__TOOLBAR.id,
|
||||
@@ -53,7 +54,7 @@ export class SaveSketch extends SketchContribution {
|
||||
|
||||
async saveSketch(): Promise<void> {
|
||||
const sketch = await this.sketchServiceClient.currentSketch();
|
||||
if (!sketch) {
|
||||
if (!CurrentSketch.isValid(sketch)) {
|
||||
return;
|
||||
}
|
||||
const isTemp = await this.sketchService.isTemp(sketch);
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { inject, injectable } from 'inversify';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import {
|
||||
Command,
|
||||
MenuModelRegistry,
|
||||
@@ -18,7 +18,7 @@ export class Settings extends SketchContribution {
|
||||
|
||||
protected settingsOpened = false;
|
||||
|
||||
registerCommands(registry: CommandRegistry): void {
|
||||
override registerCommands(registry: CommandRegistry): void {
|
||||
registry.registerCommand(Settings.Commands.OPEN, {
|
||||
execute: async () => {
|
||||
let settings: Preferences | undefined = undefined;
|
||||
@@ -39,7 +39,7 @@ export class Settings extends SketchContribution {
|
||||
});
|
||||
}
|
||||
|
||||
registerMenus(registry: MenuModelRegistry): void {
|
||||
override registerMenus(registry: MenuModelRegistry): void {
|
||||
registry.registerMenuAction(ArduinoMenus.FILE__PREFERENCES_GROUP, {
|
||||
commandId: Settings.Commands.OPEN.id,
|
||||
label:
|
||||
@@ -52,7 +52,7 @@ export class Settings extends SketchContribution {
|
||||
registry.registerSubmenu(ArduinoMenus.FILE__ADVANCED_SUBMENU, 'Advanced');
|
||||
}
|
||||
|
||||
registerKeybindings(registry: KeybindingRegistry): void {
|
||||
override registerKeybindings(registry: KeybindingRegistry): void {
|
||||
registry.registerKeybinding({
|
||||
command: Settings.Commands.OPEN.id,
|
||||
keybinding: 'CtrlCmd+,',
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { inject, injectable } from 'inversify';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import { CommonCommands } from '@theia/core/lib/browser/common-frontend-contribution';
|
||||
import { ApplicationShell } from '@theia/core/lib/browser/shell/application-shell';
|
||||
import { WorkspaceCommands } from '@theia/workspace/lib/browser';
|
||||
@@ -19,7 +19,10 @@ import {
|
||||
} from './contribution';
|
||||
import { ArduinoMenus, PlaceholderMenuNode } from '../menu/arduino-menus';
|
||||
import { EditorManager } from '@theia/editor/lib/browser/editor-manager';
|
||||
import { SketchesServiceClientImpl } from '../../common/protocol/sketches-service-client-impl';
|
||||
import {
|
||||
CurrentSketch,
|
||||
SketchesServiceClientImpl,
|
||||
} from '../../common/protocol/sketches-service-client-impl';
|
||||
import { LocalCacheFsProvider } from '../local-cache/local-cache-fs-provider';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
|
||||
@@ -35,7 +38,7 @@ export class SketchControl extends SketchContribution {
|
||||
protected readonly contextMenuRenderer: ContextMenuRenderer;
|
||||
|
||||
@inject(EditorManager)
|
||||
protected readonly editorManager: EditorManager;
|
||||
protected override readonly editorManager: EditorManager;
|
||||
|
||||
@inject(SketchesServiceClientImpl)
|
||||
protected readonly sketchesServiceClient: SketchesServiceClientImpl;
|
||||
@@ -46,7 +49,7 @@ export class SketchControl extends SketchContribution {
|
||||
protected readonly toDisposeBeforeCreateNewContextMenu =
|
||||
new DisposableCollection();
|
||||
|
||||
registerCommands(registry: CommandRegistry): void {
|
||||
override registerCommands(registry: CommandRegistry): void {
|
||||
registry.registerCommand(
|
||||
SketchControl.Commands.OPEN_SKETCH_CONTROL__TOOLBAR,
|
||||
{
|
||||
@@ -55,7 +58,7 @@ export class SketchControl extends SketchContribution {
|
||||
execute: async () => {
|
||||
this.toDisposeBeforeCreateNewContextMenu.dispose();
|
||||
const sketch = await this.sketchServiceClient.currentSketch();
|
||||
if (!sketch) {
|
||||
if (!CurrentSketch.isValid(sketch)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -70,25 +73,22 @@ export class SketchControl extends SketchContribution {
|
||||
return;
|
||||
}
|
||||
|
||||
const { mainFileUri, rootFolderFileUris } =
|
||||
await this.sketchService.loadSketch(sketch.uri);
|
||||
const { mainFileUri, rootFolderFileUris } = sketch;
|
||||
const uris = [mainFileUri, ...rootFolderFileUris];
|
||||
|
||||
const currentSketch =
|
||||
await this.sketchesServiceClient.currentSketch();
|
||||
const parentsketchUri = this.editorManager.currentEditor
|
||||
const parentSketchUri = this.editorManager.currentEditor
|
||||
?.getResourceUri()
|
||||
?.toString();
|
||||
const parentsketch = await this.sketchService.getSketchFolder(
|
||||
parentsketchUri || ''
|
||||
const parentSketch = await this.sketchService.getSketchFolder(
|
||||
parentSketchUri || ''
|
||||
);
|
||||
|
||||
// if the current file is in the current opened sketch, show extra menus
|
||||
if (
|
||||
currentSketch &&
|
||||
parentsketch &&
|
||||
parentsketch.uri === currentSketch.uri &&
|
||||
this.allowRename(parentsketch.uri)
|
||||
sketch &&
|
||||
parentSketch &&
|
||||
parentSketch.uri === sketch.uri &&
|
||||
this.allowRename(parentSketch.uri)
|
||||
) {
|
||||
this.menuRegistry.registerMenuAction(
|
||||
ArduinoMenus.SKETCH_CONTROL__CONTEXT__MAIN_GROUP,
|
||||
@@ -122,10 +122,10 @@ export class SketchControl extends SketchContribution {
|
||||
}
|
||||
|
||||
if (
|
||||
currentSketch &&
|
||||
parentsketch &&
|
||||
parentsketch.uri === currentSketch.uri &&
|
||||
this.allowDelete(parentsketch.uri)
|
||||
sketch &&
|
||||
parentSketch &&
|
||||
parentSketch.uri === sketch.uri &&
|
||||
this.allowDelete(parentSketch.uri)
|
||||
) {
|
||||
this.menuRegistry.registerMenuAction(
|
||||
ArduinoMenus.SKETCH_CONTROL__CONTEXT__MAIN_GROUP,
|
||||
@@ -200,7 +200,7 @@ export class SketchControl extends SketchContribution {
|
||||
);
|
||||
}
|
||||
|
||||
registerMenus(registry: MenuModelRegistry): void {
|
||||
override registerMenus(registry: MenuModelRegistry): void {
|
||||
registry.registerMenuAction(
|
||||
ArduinoMenus.SKETCH_CONTROL__CONTEXT__MAIN_GROUP,
|
||||
{
|
||||
@@ -228,7 +228,7 @@ export class SketchControl extends SketchContribution {
|
||||
);
|
||||
}
|
||||
|
||||
registerKeybindings(registry: KeybindingRegistry): void {
|
||||
override registerKeybindings(registry: KeybindingRegistry): void {
|
||||
registry.registerKeybinding({
|
||||
command: WorkspaceCommands.NEW_FILE.id,
|
||||
keybinding: 'CtrlCmd+Shift+N',
|
||||
@@ -243,7 +243,7 @@ export class SketchControl extends SketchContribution {
|
||||
});
|
||||
}
|
||||
|
||||
registerToolbarItems(registry: TabBarToolbarRegistry): void {
|
||||
override registerToolbarItems(registry: TabBarToolbarRegistry): void {
|
||||
registry.registerItem({
|
||||
id: SketchControl.Commands.OPEN_SKETCH_CONTROL__TOOLBAR.id,
|
||||
command: SketchControl.Commands.OPEN_SKETCH_CONTROL__TOOLBAR.id,
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { inject, injectable } from 'inversify';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import { CommandHandler } from '@theia/core/lib/common/command';
|
||||
import { CommandRegistry, MenuModelRegistry } from './contribution';
|
||||
import { ArduinoMenus } from '../menu/arduino-menus';
|
||||
@@ -12,10 +12,10 @@ import { nls } from '@theia/core/lib/common';
|
||||
@injectable()
|
||||
export class Sketchbook extends Examples {
|
||||
@inject(CommandRegistry)
|
||||
protected readonly commandRegistry: CommandRegistry;
|
||||
protected override readonly commandRegistry: CommandRegistry;
|
||||
|
||||
@inject(MenuModelRegistry)
|
||||
protected readonly menuRegistry: MenuModelRegistry;
|
||||
protected override readonly menuRegistry: MenuModelRegistry;
|
||||
|
||||
@inject(MainMenuManager)
|
||||
protected readonly mainMenuManager: MainMenuManager;
|
||||
@@ -23,11 +23,7 @@ export class Sketchbook extends Examples {
|
||||
@inject(NotificationCenter)
|
||||
protected readonly notificationCenter: NotificationCenter;
|
||||
|
||||
onStart(): void {
|
||||
this.sketchService.getSketches({}).then((container) => {
|
||||
this.register(container);
|
||||
this.mainMenuManager.update();
|
||||
});
|
||||
override onStart(): void {
|
||||
this.sketchServiceClient.onSketchbookDidChange(() => {
|
||||
this.sketchService.getSketches({}).then((container) => {
|
||||
this.register(container);
|
||||
@@ -36,7 +32,14 @@ export class Sketchbook extends Examples {
|
||||
});
|
||||
}
|
||||
|
||||
registerMenus(registry: MenuModelRegistry): void {
|
||||
override async onReady(): Promise<void> {
|
||||
this.sketchService.getSketches({}).then((container) => {
|
||||
this.register(container);
|
||||
this.mainMenuManager.update();
|
||||
});
|
||||
}
|
||||
|
||||
override registerMenus(registry: MenuModelRegistry): void {
|
||||
registry.registerSubmenu(
|
||||
ArduinoMenus.FILE__SKETCHBOOK_SUBMENU,
|
||||
nls.localize('arduino/sketch/sketchbook', 'Sketchbook'),
|
||||
@@ -53,7 +56,7 @@ export class Sketchbook extends Examples {
|
||||
);
|
||||
}
|
||||
|
||||
protected createHandler(uri: string): CommandHandler {
|
||||
protected override createHandler(uri: string): CommandHandler {
|
||||
return {
|
||||
execute: async () => {
|
||||
const sketch = await this.sketchService.loadSketch(uri);
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { inject, injectable } from 'inversify';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import {
|
||||
Command,
|
||||
MenuModelRegistry,
|
||||
@@ -39,7 +39,7 @@ export class UploadCertificate extends Contribution {
|
||||
|
||||
protected dialogOpened = false;
|
||||
|
||||
registerCommands(registry: CommandRegistry): void {
|
||||
override registerCommands(registry: CommandRegistry): void {
|
||||
registry.registerCommand(UploadCertificate.Commands.OPEN, {
|
||||
execute: async () => {
|
||||
try {
|
||||
@@ -93,7 +93,7 @@ export class UploadCertificate extends Contribution {
|
||||
});
|
||||
}
|
||||
|
||||
registerMenus(registry: MenuModelRegistry): void {
|
||||
override registerMenus(registry: MenuModelRegistry): void {
|
||||
registry.registerMenuAction(ArduinoMenus.TOOLS__FIRMWARE_UPLOADER_GROUP, {
|
||||
commandId: UploadCertificate.Commands.OPEN.id,
|
||||
label: UploadCertificate.Commands.OPEN.label,
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { inject, injectable } from 'inversify';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import {
|
||||
Command,
|
||||
MenuModelRegistry,
|
||||
@@ -16,7 +16,7 @@ export class UploadFirmware extends Contribution {
|
||||
|
||||
protected dialogOpened = false;
|
||||
|
||||
registerCommands(registry: CommandRegistry): void {
|
||||
override registerCommands(registry: CommandRegistry): void {
|
||||
registry.registerCommand(UploadFirmware.Commands.OPEN, {
|
||||
execute: async () => {
|
||||
try {
|
||||
@@ -30,7 +30,7 @@ export class UploadFirmware extends Contribution {
|
||||
});
|
||||
}
|
||||
|
||||
registerMenus(registry: MenuModelRegistry): void {
|
||||
override registerMenus(registry: MenuModelRegistry): void {
|
||||
registry.registerMenuAction(ArduinoMenus.TOOLS__FIRMWARE_UPLOADER_GROUP, {
|
||||
commandId: UploadFirmware.Commands.OPEN.id,
|
||||
label: UploadFirmware.Commands.OPEN.label,
|
||||
|
@@ -1,10 +1,9 @@
|
||||
import { inject, injectable, postConstruct } from 'inversify';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import { Emitter } from '@theia/core/lib/common/event';
|
||||
import { BoardUserField, CoreService } from '../../common/protocol';
|
||||
import { ArduinoMenus, PlaceholderMenuNode } from '../menu/arduino-menus';
|
||||
import { ArduinoToolbar } from '../toolbar/arduino-toolbar';
|
||||
import { BoardsDataStore } from '../boards/boards-data-store';
|
||||
import { SerialConnectionManager } from '../serial/serial-connection-manager';
|
||||
import { BoardsServiceProvider } from '../boards/boards-service-provider';
|
||||
import {
|
||||
SketchContribution,
|
||||
@@ -16,15 +15,13 @@ import {
|
||||
} from './contribution';
|
||||
import { UserFieldsDialog } from '../dialogs/user-fields/user-fields-dialog';
|
||||
import { DisposableCollection, nls } from '@theia/core/lib/common';
|
||||
import { CurrentSketch } from '../../common/protocol/sketches-service-client-impl';
|
||||
|
||||
@injectable()
|
||||
export class UploadSketch extends SketchContribution {
|
||||
@inject(CoreService)
|
||||
protected readonly coreService: CoreService;
|
||||
|
||||
@inject(SerialConnectionManager)
|
||||
protected readonly serialConnection: SerialConnectionManager;
|
||||
|
||||
@inject(MenuModelRegistry)
|
||||
protected readonly menuRegistry: MenuModelRegistry;
|
||||
|
||||
@@ -47,8 +44,8 @@ export class UploadSketch extends SketchContribution {
|
||||
|
||||
protected readonly menuActionsDisposables = new DisposableCollection();
|
||||
|
||||
@postConstruct()
|
||||
protected init(): void {
|
||||
protected override init(): void {
|
||||
super.init();
|
||||
this.boardsServiceClientImpl.onBoardsConfigChanged(async () => {
|
||||
const userFields =
|
||||
await this.boardsServiceClientImpl.selectedBoardUserFields();
|
||||
@@ -72,7 +69,7 @@ export class UploadSketch extends SketchContribution {
|
||||
return fqbn + '|' + address;
|
||||
}
|
||||
|
||||
registerCommands(registry: CommandRegistry): void {
|
||||
override registerCommands(registry: CommandRegistry): void {
|
||||
registry.registerCommand(UploadSketch.Commands.UPLOAD_SKETCH, {
|
||||
execute: async () => {
|
||||
const key = this.selectedFqbnAddress();
|
||||
@@ -134,7 +131,7 @@ export class UploadSketch extends SketchContribution {
|
||||
});
|
||||
}
|
||||
|
||||
registerMenus(registry: MenuModelRegistry): void {
|
||||
override registerMenus(registry: MenuModelRegistry): void {
|
||||
this.menuActionsDisposables.dispose();
|
||||
|
||||
this.menuActionsDisposables.push(
|
||||
@@ -177,7 +174,7 @@ export class UploadSketch extends SketchContribution {
|
||||
);
|
||||
}
|
||||
|
||||
registerKeybindings(registry: KeybindingRegistry): void {
|
||||
override registerKeybindings(registry: KeybindingRegistry): void {
|
||||
registry.registerKeybinding({
|
||||
command: UploadSketch.Commands.UPLOAD_SKETCH.id,
|
||||
keybinding: 'CtrlCmd+U',
|
||||
@@ -188,7 +185,7 @@ export class UploadSketch extends SketchContribution {
|
||||
});
|
||||
}
|
||||
|
||||
registerToolbarItems(registry: TabBarToolbarRegistry): void {
|
||||
override registerToolbarItems(registry: TabBarToolbarRegistry): void {
|
||||
registry.registerItem({
|
||||
id: UploadSketch.Commands.UPLOAD_SKETCH_TOOLBAR.id,
|
||||
command: UploadSketch.Commands.UPLOAD_SKETCH_TOOLBAR.id,
|
||||
@@ -209,7 +206,7 @@ export class UploadSketch extends SketchContribution {
|
||||
this.uploadInProgress = true;
|
||||
this.onDidChangeEmitter.fire();
|
||||
const sketch = await this.sketchServiceClient.currentSketch();
|
||||
if (!sketch) {
|
||||
if (!CurrentSketch.isValid(sketch)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -226,6 +223,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 +249,7 @@ export class UploadSketch extends SketchContribution {
|
||||
const programmer = selectedProgrammer;
|
||||
options = {
|
||||
sketchUri,
|
||||
fqbn,
|
||||
board,
|
||||
optimizeForDebug,
|
||||
programmer,
|
||||
port,
|
||||
@@ -259,7 +261,7 @@ export class UploadSketch extends SketchContribution {
|
||||
} else {
|
||||
options = {
|
||||
sketchUri,
|
||||
fqbn,
|
||||
board,
|
||||
optimizeForDebug,
|
||||
port,
|
||||
verbose,
|
||||
@@ -289,8 +291,6 @@ export class UploadSketch extends SketchContribution {
|
||||
} finally {
|
||||
this.uploadInProgress = false;
|
||||
this.onDidChangeEmitter.fire();
|
||||
|
||||
setTimeout(() => this.serialConnection.reconnectAfterUpload(), 5000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { inject, injectable } from 'inversify';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import { Emitter } from '@theia/core/lib/common/event';
|
||||
import { CoreService } from '../../common/protocol';
|
||||
import { ArduinoMenus } from '../menu/arduino-menus';
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
TabBarToolbarRegistry,
|
||||
} from './contribution';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
import { CurrentSketch } from '../../common/protocol/sketches-service-client-impl';
|
||||
|
||||
@injectable()
|
||||
export class VerifySketch extends SketchContribution {
|
||||
@@ -31,7 +32,7 @@ export class VerifySketch extends SketchContribution {
|
||||
|
||||
protected verifyInProgress = false;
|
||||
|
||||
registerCommands(registry: CommandRegistry): void {
|
||||
override registerCommands(registry: CommandRegistry): void {
|
||||
registry.registerCommand(VerifySketch.Commands.VERIFY_SKETCH, {
|
||||
execute: () => this.verifySketch(),
|
||||
isEnabled: () => !this.verifyInProgress,
|
||||
@@ -50,7 +51,7 @@ export class VerifySketch extends SketchContribution {
|
||||
});
|
||||
}
|
||||
|
||||
registerMenus(registry: MenuModelRegistry): void {
|
||||
override registerMenus(registry: MenuModelRegistry): void {
|
||||
registry.registerMenuAction(ArduinoMenus.SKETCH__MAIN_GROUP, {
|
||||
commandId: VerifySketch.Commands.VERIFY_SKETCH.id,
|
||||
label: nls.localize('arduino/sketch/verifyOrCompile', 'Verify/Compile'),
|
||||
@@ -66,7 +67,7 @@ export class VerifySketch extends SketchContribution {
|
||||
});
|
||||
}
|
||||
|
||||
registerKeybindings(registry: KeybindingRegistry): void {
|
||||
override registerKeybindings(registry: KeybindingRegistry): void {
|
||||
registry.registerKeybinding({
|
||||
command: VerifySketch.Commands.VERIFY_SKETCH.id,
|
||||
keybinding: 'CtrlCmd+R',
|
||||
@@ -77,7 +78,7 @@ export class VerifySketch extends SketchContribution {
|
||||
});
|
||||
}
|
||||
|
||||
registerToolbarItems(registry: TabBarToolbarRegistry): void {
|
||||
override registerToolbarItems(registry: TabBarToolbarRegistry): void {
|
||||
registry.registerItem({
|
||||
id: VerifySketch.Commands.VERIFY_SKETCH_TOOLBAR.id,
|
||||
command: VerifySketch.Commands.VERIFY_SKETCH_TOOLBAR.id,
|
||||
@@ -99,7 +100,7 @@ export class VerifySketch extends SketchContribution {
|
||||
this.onDidChangeEmitter.fire();
|
||||
const sketch = await this.sketchServiceClient.currentSketch();
|
||||
|
||||
if (!sketch) {
|
||||
if (!CurrentSketch.isValid(sketch)) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
@@ -110,12 +111,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,
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { injectable, inject } from 'inversify';
|
||||
import { injectable, inject } from '@theia/core/shared/inversify';
|
||||
import * as createPaths from './create-paths';
|
||||
import { posix } from './create-paths';
|
||||
import { AuthenticationClientService } from '../auth/authentication-client-service';
|
||||
@@ -100,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;
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { inject, injectable } from 'inversify';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import URI from '@theia/core/lib/common/uri';
|
||||
import { Event } from '@theia/core/lib/common/event';
|
||||
import {
|
||||
|
149
arduino-ide-extension/src/browser/data/dark.color-theme.json
Normal file
149
arduino-ide-extension/src/browser/data/dark.color-theme.json
Normal file
@@ -0,0 +1,149 @@
|
||||
{
|
||||
"name": "Arduino dark",
|
||||
"type": "dark",
|
||||
"colors": {
|
||||
"list.highlightForeground": "#0ca1a6",
|
||||
"list.activeSelectionForeground": "#dae3e3",
|
||||
"list.activeSelectionBackground": "#434f54",
|
||||
"list.inactiveSelectionForeground": "#dae3e3",
|
||||
"list.inactiveSelectionBackground": "#434f54",
|
||||
"list.hoverBackground": "#1f272a",
|
||||
"progressBar.background": "#005c5f",
|
||||
"editor.background": "#1f272a",
|
||||
"editor.foreground": "#dae3e3",
|
||||
"editor.lineHighlightBackground": "#434f5410",
|
||||
"editor.selectionBackground": "#f1c40f",
|
||||
"editorCursor.foreground": "#434f54",
|
||||
"editorWhitespace.foreground": "#bfbfbf",
|
||||
"editorWidget.background": "#171e21",
|
||||
"focusBorder": "#dae3e3",
|
||||
"menubar.selectionBackground": "#ffffff",
|
||||
"menubar.selectionForeground": "#212121",
|
||||
"menu.selectionBackground": "#dae3e3",
|
||||
"menu.selectionForeground": "#212121",
|
||||
"editorGroupHeader.tabsBackground": "#171e21",
|
||||
"button.background": "#0ca1a6",
|
||||
"titleBar.activeBackground": "#171e21",
|
||||
"titleBar.activeForeground": "#dae3e3",
|
||||
"terminal.background": "#000000",
|
||||
"terminal.foreground": "#e0e0e0",
|
||||
"dropdown.border": "#7fcbcd",
|
||||
"dropdown.background": "#2c353a",
|
||||
"dropdown.foreground": "#dae3e3",
|
||||
"activityBar.background": "#171e21",
|
||||
"activityBar.foreground": "#dae3e3",
|
||||
"activityBar.inactiveForeground": "#4e5b61",
|
||||
"activityBar.activeBorder": "#0ca1a6",
|
||||
"statusBar.background": "#171e21",
|
||||
"secondaryButton.background": "#ff000000",
|
||||
"secondaryButton.foreground": "#dae3e3",
|
||||
"secondaryButton.hoverBackground": "#434f54",
|
||||
"arduino.branding.primary": "#0ca1a6",
|
||||
"arduino.branding.secondary": "#b5c8c9",
|
||||
"arduino.foreground": "#edf1f1",
|
||||
"arduino.output.foreground": "#ffffff",
|
||||
"arduino.output.background": "#000000",
|
||||
"arduino.toolbar.hoverBackground": "#dae3e3",
|
||||
"sideBar.background": "#101618",
|
||||
"input.background": "#000000",
|
||||
"foreground": "#dae3e3",
|
||||
"settings.headerForeground": "#dae3e3",
|
||||
"tree.indentGuidesStroke": "#374146",
|
||||
"tab.unfocusedActiveForeground": "#dae3e3",
|
||||
"tab.inactiveBackground": "#171e21"
|
||||
},
|
||||
"tokenColors": [
|
||||
{
|
||||
"name": "",
|
||||
"settings": {
|
||||
"foreground": "#dae3e3"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Comments",
|
||||
"scope": "comment",
|
||||
"settings": {
|
||||
"foreground": "#7f8c8d"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Keywords Attributes",
|
||||
"scope": [
|
||||
"storage",
|
||||
"support",
|
||||
"string.quoted.single.c"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#0ca1a6"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "literal",
|
||||
"scope": [
|
||||
"meta.function.c",
|
||||
"entity.name.function",
|
||||
"meta.function-call.c",
|
||||
"variable.other"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#F39C12"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "punctuation",
|
||||
"scope": [
|
||||
"punctuation.section",
|
||||
"meta.function-call.c",
|
||||
"meta.block.c",
|
||||
"meta.function.c",
|
||||
"variable",
|
||||
"variable.name"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#dae3e3"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "function preprocessor",
|
||||
"scope": [
|
||||
"entity.name.function.preprocessor.c",
|
||||
"meta.preprocessor.macro.c"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#569CD6"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "constants",
|
||||
"scope": [
|
||||
"string.quoted.double",
|
||||
"string.quoted.other.lt-gt",
|
||||
"constant"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#7fcbcd"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "meta keywords",
|
||||
"scope": [
|
||||
"keyword.control",
|
||||
"meta.preprocessor.c"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#C586C0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "numeric preprocessor",
|
||||
"scope": [
|
||||
"meta.preprocessor.macro.c",
|
||||
"constant.numeric.preprocessor.c",
|
||||
"meta.preprocessor.c"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#434f54"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
149
arduino-ide-extension/src/browser/data/default.color-theme.json
Normal file
149
arduino-ide-extension/src/browser/data/default.color-theme.json
Normal file
@@ -0,0 +1,149 @@
|
||||
{
|
||||
"name": "Arduino default",
|
||||
"type": "default",
|
||||
"colors": {
|
||||
"list.highlightForeground": "#008184",
|
||||
"list.activeSelectionForeground": "#4e5b61",
|
||||
"list.activeSelectionBackground": "#dae3e3",
|
||||
"list.inactiveSelectionForeground": "#4e5b61",
|
||||
"list.inactiveSelectionBackground": "#dae3e3",
|
||||
"list.hoverBackground": "#ecf1f1",
|
||||
"progressBar.background": "#005c5f",
|
||||
"editor.background": "#ffffff",
|
||||
"editor.foreground": "#4e5b61",
|
||||
"editor.lineHighlightBackground": "#434f5410",
|
||||
"editor.selectionBackground": "#f1c40f",
|
||||
"editorCursor.foreground": "#434f54",
|
||||
"editorWhitespace.foreground": "#bfbfbf",
|
||||
"editorWidget.background": "#f7f9f9",
|
||||
"focusBorder": "#7fcbcd",
|
||||
"menubar.selectionBackground": "#ffffff",
|
||||
"menubar.selectionForeground": "#212121",
|
||||
"menu.selectionBackground": "#dae3e3",
|
||||
"menu.selectionForeground": "#212121",
|
||||
"editorGroupHeader.tabsBackground": "#ecf1f1",
|
||||
"button.background": "#7fcbcd",
|
||||
"titleBar.activeBackground": "#006d70",
|
||||
"titleBar.activeForeground": "#f7f9f9",
|
||||
"terminal.background": "#000000",
|
||||
"terminal.foreground": "#e0e0e0",
|
||||
"dropdown.border": "#f7f9f9",
|
||||
"dropdown.background": "#ffffff",
|
||||
"dropdown.foreground": "#4e5b61",
|
||||
"activityBar.background": "#ecf1f1",
|
||||
"activityBar.foreground": "#4e5b61",
|
||||
"activityBar.inactiveForeground": "#bdc7c7",
|
||||
"activityBar.activeBorder": "#008184",
|
||||
"statusBar.background": "#006d70",
|
||||
"secondaryButton.background": "#ff000000",
|
||||
"secondaryButton.foreground": "#008184",
|
||||
"secondaryButton.hoverBackground": "#dae3e3",
|
||||
"arduino.branding.primary": "#008184",
|
||||
"arduino.branding.secondary": "#b5c8c9",
|
||||
"arduino.foreground": "#edf1f1",
|
||||
"arduino.output.foreground": "#ffffff",
|
||||
"arduino.output.background": "#000000",
|
||||
"arduino.toolbar.hoverBackground": "#f7f9f9",
|
||||
"sideBar.background": "#f7f9f9",
|
||||
"input.background": "#ffffff",
|
||||
"foreground": "#4e5b61",
|
||||
"settings.headerForeground": "#4e5b61",
|
||||
"tree.indentGuidesStroke": "#dae3e3",
|
||||
"tab.unfocusedActiveForeground": "#4e5b61",
|
||||
"tab.inactiveBackground": "#ecf1f1"
|
||||
},
|
||||
"tokenColors": [
|
||||
{
|
||||
"name": "",
|
||||
"settings": {
|
||||
"foreground": "#434f54"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Comments",
|
||||
"scope": "comment",
|
||||
"settings": {
|
||||
"foreground": "#95a5a6cc"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Keywords Attributes",
|
||||
"scope": [
|
||||
"storage",
|
||||
"support",
|
||||
"string.quoted.single.c"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#00979D"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "literal",
|
||||
"scope": [
|
||||
"meta.function.c",
|
||||
"entity.name.function",
|
||||
"meta.function-call.c",
|
||||
"variable.other"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#D35400"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "punctuation",
|
||||
"scope": [
|
||||
"punctuation.section",
|
||||
"meta.function-call.c",
|
||||
"meta.block.c",
|
||||
"meta.function.c",
|
||||
"variable",
|
||||
"variable.name"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#434f54"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "function preprocessor",
|
||||
"scope": [
|
||||
"entity.name.function.preprocessor.c",
|
||||
"meta.preprocessor.macro.c"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#9e846d"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "constants",
|
||||
"scope": [
|
||||
"string.quoted.double",
|
||||
"string.quoted.other.lt-gt",
|
||||
"constant"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#005C5F"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "meta keywords",
|
||||
"scope": [
|
||||
"keyword.control",
|
||||
"meta.preprocessor.c"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#728E00"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "numeric preprocessor",
|
||||
"scope": [
|
||||
"meta.preprocessor.macro.c",
|
||||
"constant.numeric.preprocessor.c",
|
||||
"meta.preprocessor.c"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#434f54"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
@@ -1,5 +1,5 @@
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
import * as React from 'react';
|
||||
import * as React from '@theia/core/shared/react';
|
||||
|
||||
export const CertificateAddComponent = ({
|
||||
addCertificate,
|
||||
@@ -8,9 +8,12 @@ export const CertificateAddComponent = ({
|
||||
}): React.ReactElement => {
|
||||
const [value, setValue] = React.useState('');
|
||||
|
||||
const handleChange = React.useCallback((event) => {
|
||||
setValue(event.target.value);
|
||||
}, []);
|
||||
const handleChange = React.useCallback(
|
||||
(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setValue(event.target.value);
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
return (
|
||||
<form
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import * as React from 'react';
|
||||
import * as React from '@theia/core/shared/react';
|
||||
|
||||
export const CertificateListComponent = ({
|
||||
certificates,
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import * as React from 'react';
|
||||
import * as React from '@theia/core/shared/react';
|
||||
import Tippy from '@tippyjs/react';
|
||||
import { AvailableBoard } from '../../boards/boards-service-provider';
|
||||
import { CertificateListComponent } from './certificate-list';
|
||||
|
@@ -1,9 +1,9 @@
|
||||
import * as React from 'react';
|
||||
import { inject, injectable, postConstruct } from 'inversify';
|
||||
import * as React from '@theia/core/shared/react';
|
||||
import { inject, injectable, postConstruct } from '@theia/core/shared/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 { Widget } from '@theia/core/shared/@phosphor/widgets';
|
||||
import { Message } from '@theia/core/shared/@phosphor/messaging';
|
||||
import { ReactWidget } from '@theia/core/lib/browser/widgets/react-widget';
|
||||
import {
|
||||
AvailableBoard,
|
||||
@@ -139,7 +139,7 @@ export class UploadCertificateDialog extends AbstractDialog<void> {
|
||||
|
||||
constructor(
|
||||
@inject(UploadCertificateDialogProps)
|
||||
protected readonly props: UploadCertificateDialogProps
|
||||
protected override readonly props: UploadCertificateDialogProps
|
||||
) {
|
||||
super({
|
||||
title: nls.localize(
|
||||
@@ -155,7 +155,7 @@ export class UploadCertificateDialog extends AbstractDialog<void> {
|
||||
return;
|
||||
}
|
||||
|
||||
protected onAfterAttach(msg: Message): void {
|
||||
protected override onAfterAttach(msg: Message): void {
|
||||
if (this.widget.isAttached) {
|
||||
Widget.detach(this.widget);
|
||||
}
|
||||
@@ -165,21 +165,21 @@ export class UploadCertificateDialog extends AbstractDialog<void> {
|
||||
this.update();
|
||||
}
|
||||
|
||||
protected onUpdateRequest(msg: Message): void {
|
||||
protected override onUpdateRequest(msg: Message): void {
|
||||
super.onUpdateRequest(msg);
|
||||
this.widget.update();
|
||||
}
|
||||
|
||||
protected onActivateRequest(msg: Message): void {
|
||||
protected override onActivateRequest(msg: Message): void {
|
||||
super.onActivateRequest(msg);
|
||||
this.widget.activate();
|
||||
}
|
||||
|
||||
protected handleEnter(event: KeyboardEvent): boolean | void {
|
||||
protected override handleEnter(event: KeyboardEvent): boolean | void {
|
||||
return false;
|
||||
}
|
||||
|
||||
close(): void {
|
||||
override close(): void {
|
||||
if (this.busy) {
|
||||
return;
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
import * as React from 'react';
|
||||
import * as React from '@theia/core/shared/react';
|
||||
import { AvailableBoard } from '../../boards/boards-service-provider';
|
||||
import { ArduinoSelect } from '../../widgets/arduino-select';
|
||||
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import * as React from 'react';
|
||||
import { inject, injectable } from 'inversify';
|
||||
import { Widget } from '@phosphor/widgets';
|
||||
import { Message } from '@phosphor/messaging';
|
||||
import * as React from '@theia/core/shared/react';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import { Widget } from '@theia/core/shared/@phosphor/widgets';
|
||||
import { Message } from '@theia/core/shared/@phosphor/messaging';
|
||||
import { clipboard } from 'electron';
|
||||
import { ReactWidget, DialogProps } from '@theia/core/lib/browser';
|
||||
import { AbstractDialog } from '../theia/dialogs/dialogs';
|
||||
@@ -149,7 +149,7 @@ export class ShareSketchDialog extends AbstractDialog<void> {
|
||||
|
||||
constructor(
|
||||
@inject(ShareSketchDialogProps)
|
||||
protected readonly props: ShareSketchDialogProps
|
||||
protected override readonly props: ShareSketchDialogProps
|
||||
) {
|
||||
super({ title: props.title });
|
||||
this.contentNode.classList.add('arduino-share-sketch-dialog');
|
||||
@@ -159,7 +159,7 @@ export class ShareSketchDialog extends AbstractDialog<void> {
|
||||
get value(): void {
|
||||
return;
|
||||
}
|
||||
protected onAfterAttach(msg: Message): void {
|
||||
protected override onAfterAttach(msg: Message): void {
|
||||
if (this.widget.isAttached) {
|
||||
Widget.detach(this.widget);
|
||||
}
|
||||
@@ -168,12 +168,12 @@ export class ShareSketchDialog extends AbstractDialog<void> {
|
||||
this.update();
|
||||
}
|
||||
|
||||
protected onUpdateRequest(msg: Message): void {
|
||||
protected override onUpdateRequest(msg: Message): void {
|
||||
super.onUpdateRequest(msg);
|
||||
this.widget.update();
|
||||
}
|
||||
|
||||
protected onActivateRequest(msg: Message): void {
|
||||
protected override onActivateRequest(msg: Message): void {
|
||||
super.onActivateRequest(msg);
|
||||
this.widget.activate();
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { inject, injectable } from 'inversify';
|
||||
import { Widget } from '@phosphor/widgets';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import { Widget } from '@theia/core/shared/@phosphor/widgets';
|
||||
import { CancellationTokenSource } from '@theia/core/lib/common/cancellation';
|
||||
import {
|
||||
ConfirmDialog,
|
||||
@@ -19,7 +19,7 @@ export class DoNotAskAgainConfirmDialog extends ConfirmDialog {
|
||||
|
||||
constructor(
|
||||
@inject(DoNotAskAgainDialogProps)
|
||||
protected readonly props: DoNotAskAgainDialogProps
|
||||
protected override readonly props: DoNotAskAgainDialogProps
|
||||
) {
|
||||
super(props);
|
||||
this.controlPanel.removeChild(this.errorMessageNode);
|
||||
@@ -42,7 +42,7 @@ export class DoNotAskAgainConfirmDialog extends ConfirmDialog {
|
||||
this.doNotAskAgainCheckbox.type = 'checkbox';
|
||||
}
|
||||
|
||||
protected async accept(): Promise<void> {
|
||||
protected override async accept(): Promise<void> {
|
||||
if (!this.resolve) {
|
||||
return;
|
||||
}
|
||||
@@ -65,7 +65,7 @@ export class DoNotAskAgainConfirmDialog extends ConfirmDialog {
|
||||
}
|
||||
}
|
||||
|
||||
protected setErrorMessage(error: DialogError): void {
|
||||
protected override setErrorMessage(error: DialogError): void {
|
||||
if (this.acceptButton) {
|
||||
this.acceptButton.disabled = !DialogError.getResult(error);
|
||||
}
|
||||
|
@@ -1,5 +1,6 @@
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
import * as React from 'react';
|
||||
import * as React from '@theia/core/shared/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 {
|
||||
|
@@ -1,9 +1,13 @@
|
||||
import * as React from 'react';
|
||||
import { inject, injectable, postConstruct } from 'inversify';
|
||||
import * as React from '@theia/core/shared/react';
|
||||
import {
|
||||
inject,
|
||||
injectable,
|
||||
postConstruct,
|
||||
} from '@theia/core/shared/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 { Widget } from '@theia/core/shared/@phosphor/widgets';
|
||||
import { Message } from '@theia/core/shared/@phosphor/messaging';
|
||||
import { ReactWidget } from '@theia/core/lib/browser/widgets/react-widget';
|
||||
import {
|
||||
AvailableBoard,
|
||||
@@ -15,6 +19,8 @@ import {
|
||||
} from '../../../common/protocol/arduino-firmware-uploader';
|
||||
import { FirmwareUploaderComponent } from './firmware-uploader-component';
|
||||
import { UploadFirmware } from '../../contributions/upload-firmware';
|
||||
import { Port } from '../../../common/protocol';
|
||||
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
|
||||
|
||||
@injectable()
|
||||
export class UploadFirmwareDialogWidget extends ReactWidget {
|
||||
@@ -24,6 +30,9 @@ export class UploadFirmwareDialogWidget extends ReactWidget {
|
||||
@inject(ArduinoFirmwareUploader)
|
||||
protected readonly arduinoFirmwareUploader: ArduinoFirmwareUploader;
|
||||
|
||||
@inject(FrontendApplicationStateService)
|
||||
private readonly appStatusService: FrontendApplicationStateService;
|
||||
|
||||
protected updatableFqbns: string[] = [];
|
||||
protected availableBoards: AvailableBoard[] = [];
|
||||
protected isOpen = new Object();
|
||||
@@ -38,7 +47,8 @@ export class UploadFirmwareDialogWidget extends ReactWidget {
|
||||
|
||||
@postConstruct()
|
||||
protected init(): void {
|
||||
this.arduinoFirmwareUploader.updatableBoards().then((fqbns) => {
|
||||
this.appStatusService.reachedState('ready').then(async () => {
|
||||
const fqbns = await this.arduinoFirmwareUploader.updatableBoards();
|
||||
this.updatableFqbns = fqbns;
|
||||
this.update();
|
||||
});
|
||||
@@ -49,14 +59,14 @@ 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)
|
||||
.finally(() => this.busyCallback(false));
|
||||
}
|
||||
|
||||
onCloseRequest(msg: Message): void {
|
||||
protected override onCloseRequest(msg: Message): void {
|
||||
super.onCloseRequest(msg);
|
||||
this.isOpen = new Object();
|
||||
}
|
||||
@@ -88,7 +98,7 @@ export class UploadFirmwareDialog extends AbstractDialog<void> {
|
||||
|
||||
constructor(
|
||||
@inject(UploadFirmwareDialogProps)
|
||||
protected readonly props: UploadFirmwareDialogProps
|
||||
protected override readonly props: UploadFirmwareDialogProps
|
||||
) {
|
||||
super({ title: UploadFirmware.Commands.OPEN.label || '' });
|
||||
this.contentNode.classList.add('firmware-uploader-dialog');
|
||||
@@ -99,7 +109,7 @@ export class UploadFirmwareDialog extends AbstractDialog<void> {
|
||||
return;
|
||||
}
|
||||
|
||||
protected onAfterAttach(msg: Message): void {
|
||||
protected override onAfterAttach(msg: Message): void {
|
||||
if (this.widget.isAttached) {
|
||||
Widget.detach(this.widget);
|
||||
}
|
||||
@@ -109,21 +119,21 @@ export class UploadFirmwareDialog extends AbstractDialog<void> {
|
||||
this.update();
|
||||
}
|
||||
|
||||
protected onUpdateRequest(msg: Message): void {
|
||||
protected override onUpdateRequest(msg: Message): void {
|
||||
super.onUpdateRequest(msg);
|
||||
this.widget.update();
|
||||
}
|
||||
|
||||
protected onActivateRequest(msg: Message): void {
|
||||
protected override onActivateRequest(msg: Message): void {
|
||||
super.onActivateRequest(msg);
|
||||
this.widget.activate();
|
||||
}
|
||||
|
||||
protected handleEnter(event: KeyboardEvent): boolean | void {
|
||||
protected override handleEnter(event: KeyboardEvent): boolean | void {
|
||||
return false;
|
||||
}
|
||||
|
||||
close(): void {
|
||||
override close(): void {
|
||||
if (this.busy) {
|
||||
return;
|
||||
}
|
||||
|
@@ -1,8 +1,8 @@
|
||||
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 * as React from '@theia/core/shared/react';
|
||||
import * as ReactDOM from '@theia/core/shared/react-dom';
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import { ProgressInfo, UpdateInfo } from '../../../common/protocol/ide-updater';
|
||||
import ProgressBar from '../../components/ProgressBar';
|
||||
|
@@ -1,9 +1,9 @@
|
||||
import * as React from 'react';
|
||||
import { inject, injectable } from 'inversify';
|
||||
import * as React from '@theia/core/shared/react';
|
||||
import { inject, injectable } from '@theia/core/shared/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 { Widget } from '@theia/core/shared/@phosphor/widgets';
|
||||
import { Message } from '@theia/core/shared/@phosphor/messaging';
|
||||
import { ReactWidget } from '@theia/core/lib/browser/widgets/react-widget';
|
||||
import { nls } from '@theia/core';
|
||||
import { IDEUpdaterComponent } from './ide-updater-component';
|
||||
@@ -70,7 +70,7 @@ export class IDEUpdaterDialogWidget extends ReactWidget {
|
||||
this.close();
|
||||
}
|
||||
|
||||
close(): void {
|
||||
override close(): void {
|
||||
super.close();
|
||||
this.onClose();
|
||||
}
|
||||
@@ -122,7 +122,7 @@ export class IDEUpdaterDialog extends AbstractDialog<UpdateInfo> {
|
||||
|
||||
constructor(
|
||||
@inject(IDEUpdaterDialogProps)
|
||||
protected readonly props: IDEUpdaterDialogProps
|
||||
protected override readonly props: IDEUpdaterDialogProps
|
||||
) {
|
||||
super({
|
||||
title: nls.localize(
|
||||
@@ -138,7 +138,7 @@ export class IDEUpdaterDialog extends AbstractDialog<UpdateInfo> {
|
||||
return this.widget.updateInfo;
|
||||
}
|
||||
|
||||
protected onAfterAttach(msg: Message): void {
|
||||
protected override onAfterAttach(msg: Message): void {
|
||||
if (this.widget.isAttached) {
|
||||
Widget.detach(this.widget);
|
||||
}
|
||||
@@ -147,7 +147,7 @@ export class IDEUpdaterDialog extends AbstractDialog<UpdateInfo> {
|
||||
this.update();
|
||||
}
|
||||
|
||||
async open(
|
||||
override async open(
|
||||
data: UpdateInfo | undefined = undefined
|
||||
): Promise<UpdateInfo | undefined> {
|
||||
if (data && data.version) {
|
||||
@@ -156,17 +156,17 @@ export class IDEUpdaterDialog extends AbstractDialog<UpdateInfo> {
|
||||
}
|
||||
}
|
||||
|
||||
protected onUpdateRequest(msg: Message): void {
|
||||
protected override onUpdateRequest(msg: Message): void {
|
||||
super.onUpdateRequest(msg);
|
||||
this.widget.update();
|
||||
}
|
||||
|
||||
protected onActivateRequest(msg: Message): void {
|
||||
protected override onActivateRequest(msg: Message): void {
|
||||
super.onActivateRequest(msg);
|
||||
this.widget.activate();
|
||||
}
|
||||
|
||||
close(): void {
|
||||
override close(): void {
|
||||
this.widget.dispose();
|
||||
super.close();
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import * as React from 'react';
|
||||
import * as React from '@theia/core/shared/react';
|
||||
import { Tab, Tabs, TabList, TabPanel } from 'react-tabs';
|
||||
import 'react-tabs/style/react-tabs.css';
|
||||
import { Disable } from 'react-disable';
|
||||
@@ -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,
|
||||
@@ -16,7 +17,10 @@ import {
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
import { Settings, SettingsService } from './settings';
|
||||
import { AdditionalUrlsDialog } from './settings-dialog';
|
||||
import { AsyncLocalizationProvider } from '@theia/core/lib/common/i18n/localization';
|
||||
import {
|
||||
AsyncLocalizationProvider,
|
||||
LanguageInfo,
|
||||
} from '@theia/core/lib/common/i18n/localization';
|
||||
|
||||
export class SettingsComponent extends React.Component<
|
||||
SettingsComponent.Props,
|
||||
@@ -28,35 +32,46 @@ export class SettingsComponent extends React.Component<
|
||||
super(props);
|
||||
}
|
||||
|
||||
componentDidUpdate(
|
||||
override componentDidUpdate(
|
||||
_: SettingsComponent.Props,
|
||||
prevState: SettingsComponent.State
|
||||
): void {
|
||||
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 {
|
||||
override 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 {
|
||||
override componentWillUnmount(): void {
|
||||
this.toDispose.dispose();
|
||||
}
|
||||
|
||||
render(): React.ReactNode {
|
||||
override render(): React.ReactNode {
|
||||
if (!this.state) {
|
||||
return <div />;
|
||||
}
|
||||
@@ -201,11 +216,9 @@ export class SettingsComponent extends React.Component<
|
||||
value={this.state.currentLanguage}
|
||||
onChange={this.languageDidChange}
|
||||
>
|
||||
{this.state.languages.map((label) => (
|
||||
<option key={label} value={label}>
|
||||
{label}
|
||||
</option>
|
||||
))}
|
||||
{this.state.languages.map((label) =>
|
||||
this.toSelectOptions(label)
|
||||
)}
|
||||
</select>
|
||||
<span style={{ marginLeft: '5px' }}>
|
||||
(
|
||||
@@ -263,7 +276,7 @@ export class SettingsComponent extends React.Component<
|
||||
<label className="flex-line">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={this.state.autoSave === 'on'}
|
||||
checked={this.state.autoSave !== 'off'}
|
||||
onChange={this.autoSaveDidChange}
|
||||
/>
|
||||
{nls.localize(
|
||||
@@ -290,8 +303,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"
|
||||
@@ -302,6 +315,24 @@ export class SettingsComponent extends React.Component<
|
||||
);
|
||||
}
|
||||
|
||||
private toSelectOptions(language: string | LanguageInfo): JSX.Element {
|
||||
const plain = typeof language === 'string';
|
||||
const key = plain ? language : language.languageId;
|
||||
const value = plain ? language : language.languageId;
|
||||
const label = plain
|
||||
? language === 'en'
|
||||
? 'English'
|
||||
: language
|
||||
: language.localizedLanguageName ||
|
||||
language.languageName ||
|
||||
language.languageId;
|
||||
return (
|
||||
<option key={key} value={value}>
|
||||
{label}
|
||||
</option>
|
||||
);
|
||||
}
|
||||
|
||||
protected renderNetwork(): React.ReactNode {
|
||||
return (
|
||||
<div className="content noselect">
|
||||
@@ -475,11 +506,13 @@ export class SettingsComponent extends React.Component<
|
||||
|
||||
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),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -492,11 +525,11 @@ export class SettingsComponent extends React.Component<
|
||||
}
|
||||
};
|
||||
|
||||
protected additionalUrlsDidChange = (
|
||||
protected rawAdditionalUrlsValueDidChange = (
|
||||
event: React.ChangeEvent<HTMLInputElement>
|
||||
): void => {
|
||||
this.setState({
|
||||
additionalUrls: event.target.value.split(',').map((url) => url.trim()),
|
||||
rawAdditionalUrlsValue: event.target.value,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -535,7 +568,9 @@ export class SettingsComponent extends React.Component<
|
||||
protected autoSaveDidChange = (
|
||||
event: React.ChangeEvent<HTMLInputElement>
|
||||
): void => {
|
||||
this.setState({ autoSave: event.target.checked ? 'on' : 'off' });
|
||||
this.setState({
|
||||
autoSave: event.target.checked ? Settings.AutoSave.DEFAULT_ON : 'off',
|
||||
});
|
||||
};
|
||||
|
||||
protected quickSuggestionsOtherDidChange = (
|
||||
@@ -699,5 +734,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,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import * as React from 'react';
|
||||
import { injectable, inject, postConstruct } from 'inversify';
|
||||
import { Widget } from '@phosphor/widgets';
|
||||
import { Message } from '@phosphor/messaging';
|
||||
import * as React from '@theia/core/shared/react';
|
||||
import { injectable, inject, postConstruct } from '@theia/core/shared/inversify';
|
||||
import { Widget } from '@theia/core/shared/@phosphor/widgets';
|
||||
import { Message } from '@theia/core/shared/@phosphor/messaging';
|
||||
import { DialogError, ReactWidget } from '@theia/core/lib/browser';
|
||||
import { AbstractDialog, DialogProps } from '@theia/core/lib/browser';
|
||||
import { Settings, SettingsService } from './settings';
|
||||
@@ -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 {
|
||||
@@ -55,7 +56,7 @@ export class SettingsDialog extends AbstractDialog<Promise<Settings>> {
|
||||
|
||||
constructor(
|
||||
@inject(SettingsDialogProps)
|
||||
protected readonly props: SettingsDialogProps
|
||||
protected override readonly props: SettingsDialogProps
|
||||
) {
|
||||
super(props);
|
||||
this.contentNode.classList.add('arduino-settings-dialog');
|
||||
@@ -72,7 +73,7 @@ export class SettingsDialog extends AbstractDialog<Promise<Settings>> {
|
||||
);
|
||||
}
|
||||
|
||||
protected async isValid(settings: Promise<Settings>): Promise<DialogError> {
|
||||
protected override async isValid(settings: Promise<Settings>): Promise<DialogError> {
|
||||
const result = await this.settingsService.validate(settings);
|
||||
if (typeof result === 'string') {
|
||||
return result;
|
||||
@@ -84,7 +85,7 @@ export class SettingsDialog extends AbstractDialog<Promise<Settings>> {
|
||||
return this.settingsService.settings();
|
||||
}
|
||||
|
||||
protected onAfterAttach(msg: Message): void {
|
||||
protected override onAfterAttach(msg: Message): void {
|
||||
if (this.widget.isAttached) {
|
||||
Widget.detach(this.widget);
|
||||
}
|
||||
@@ -96,16 +97,16 @@ export class SettingsDialog extends AbstractDialog<Promise<Settings>> {
|
||||
this.update();
|
||||
}
|
||||
|
||||
protected onUpdateRequest(msg: Message) {
|
||||
protected override onUpdateRequest(msg: Message): void {
|
||||
super.onUpdateRequest(msg);
|
||||
this.widget.update();
|
||||
}
|
||||
|
||||
protected onActivateRequest(msg: Message): void {
|
||||
protected override onActivateRequest(msg: Message): void {
|
||||
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,23 +169,20 @@ 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 {
|
||||
protected override onAfterAttach(message: Message): void {
|
||||
super.onAfterAttach(message);
|
||||
this.addUpdateListener(this.textArea, 'input');
|
||||
}
|
||||
|
||||
protected onActivateRequest(message: Message): void {
|
||||
protected override onActivateRequest(message: Message): void {
|
||||
super.onActivateRequest(message);
|
||||
this.textArea.focus();
|
||||
}
|
||||
|
||||
protected handleEnter(event: KeyboardEvent): boolean | void {
|
||||
protected override handleEnter(event: KeyboardEvent): boolean | void {
|
||||
if (event.target instanceof HTMLInputElement) {
|
||||
return super.handleEnter(event);
|
||||
}
|
||||
|
@@ -1,4 +1,8 @@
|
||||
import { injectable, inject, postConstruct } from 'inversify';
|
||||
import {
|
||||
injectable,
|
||||
inject,
|
||||
postConstruct,
|
||||
} from '@theia/core/shared/inversify';
|
||||
import URI from '@theia/core/lib/common/uri';
|
||||
import { Emitter } from '@theia/core/lib/common/event';
|
||||
import { Deferred, timeout } from '@theia/core/lib/common/promise-util';
|
||||
@@ -8,19 +12,23 @@ 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 { AsyncLocalizationProvider } from '@theia/core/lib/common/i18n/localization';
|
||||
import { CommandService, nls } from '@theia/core/lib/common';
|
||||
import {
|
||||
AsyncLocalizationProvider,
|
||||
LanguageInfo,
|
||||
} from '@theia/core/lib/common/i18n/localization';
|
||||
import { ElectronCommands } from '@theia/core/lib/electron-browser/menu/electron-menu-contribution';
|
||||
|
||||
export const EDITOR_SETTING = 'editor';
|
||||
export const FONT_SIZE_SETTING = `${EDITOR_SETTING}.fontSize`;
|
||||
export const AUTO_SAVE_SETTING = `${EDITOR_SETTING}.autoSave`;
|
||||
export const AUTO_SAVE_SETTING = `files.autoSave`;
|
||||
export const QUICK_SUGGESTIONS_SETTING = `${EDITOR_SETTING}.quickSuggestions`;
|
||||
export const ARDUINO_SETTING = 'arduino';
|
||||
export const WINDOW_SETTING = `${ARDUINO_SETTING}.window`;
|
||||
@@ -35,13 +43,13 @@ 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`
|
||||
autoSave: Settings.AutoSave; // `files.autoSave`
|
||||
quickSuggestions: Record<'other' | 'comments' | 'strings', boolean>; // `editor.quickSuggestions`
|
||||
|
||||
languages: string[]; // `languages from the plugins`
|
||||
languages: (string | LanguageInfo)[]; // `languages from the plugins`
|
||||
currentLanguage: string;
|
||||
|
||||
autoScaleInterface: boolean; // `arduino.window.autoScale`
|
||||
@@ -53,13 +61,21 @@ 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 {
|
||||
export function belongsToCli<K extends keyof Settings>(key: K): boolean {
|
||||
return key === 'sketchbookPath' || key === 'additionalUrls';
|
||||
}
|
||||
export type AutoSave =
|
||||
| 'off'
|
||||
| 'afterDelay'
|
||||
| 'onFocusChange'
|
||||
| 'onWindowChange';
|
||||
export namespace AutoSave {
|
||||
export const DEFAULT_ON: AutoSave = 'afterDelay'; // https://github.com/eclipse-theia/theia/issues/10812
|
||||
}
|
||||
}
|
||||
|
||||
@injectable()
|
||||
@@ -82,8 +98,13 @@ 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;
|
||||
@@ -120,7 +141,10 @@ export class SettingsService {
|
||||
'workbench.colorTheme',
|
||||
'arduino-theme'
|
||||
),
|
||||
this.preferenceService.get<'on' | 'off'>(AUTO_SAVE_SETTING, 'on'),
|
||||
this.preferenceService.get<Settings.AutoSave>(
|
||||
AUTO_SAVE_SETTING,
|
||||
Settings.AutoSave.DEFAULT_ON
|
||||
),
|
||||
this.preferenceService.get<
|
||||
Record<'other' | 'comments' | 'strings', boolean>
|
||||
>(QUICK_SUGGESTIONS_SETTING, {
|
||||
@@ -167,7 +191,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);
|
||||
@@ -176,7 +203,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(
|
||||
@@ -252,7 +280,7 @@ export class SettingsService {
|
||||
|
||||
await this.savePreference('editor.fontSize', editorFontSize);
|
||||
await this.savePreference('workbench.colorTheme', themeId);
|
||||
await this.savePreference('editor.autoSave', autoSave);
|
||||
await this.savePreference(AUTO_SAVE_SETTING, autoSave);
|
||||
await this.savePreference('editor.quickSuggestions', quickSuggestions);
|
||||
await this.savePreference(AUTO_SCALE_SETTING, autoScaleInterface);
|
||||
await this.savePreference(ZOOM_LEVEL_SETTING, interfaceScale);
|
||||
@@ -267,13 +295,16 @@ export class SettingsService {
|
||||
|
||||
// after saving all the settings, if we need to change the language we need to perform a reload
|
||||
// Only reload if the language differs from the current locale. `nls.locale === undefined` signals english as well
|
||||
if (currentLanguage !== nls.locale && !(currentLanguage === 'en' && nls.locale === undefined)) {
|
||||
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);
|
||||
}
|
||||
window.location.reload();
|
||||
this.commandService.executeCommand(ElectronCommands.RELOAD.id);
|
||||
}
|
||||
|
||||
return true;
|
@@ -1,4 +1,4 @@
|
||||
import * as React from 'react';
|
||||
import * as React from '@theia/core/shared/react';
|
||||
import { BoardUserField } from '../../../common/protocol';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
|
||||
|
@@ -1,12 +1,12 @@
|
||||
import * as React from 'react';
|
||||
import { inject, injectable } from 'inversify';
|
||||
import * as React from '@theia/core/shared/react';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import {
|
||||
AbstractDialog,
|
||||
DialogProps,
|
||||
ReactWidget,
|
||||
} from '@theia/core/lib/browser';
|
||||
import { Widget } from '@phosphor/widgets';
|
||||
import { Message } from '@phosphor/messaging';
|
||||
import { Widget } from '@theia/core/shared/@phosphor/widgets';
|
||||
import { Message } from '@theia/core/shared/@phosphor/messaging';
|
||||
import { UploadSketch } from '../../contributions/upload-sketch';
|
||||
import { UserFieldsComponent } from './user-fields-component';
|
||||
import { BoardUserField } from '../../../common/protocol';
|
||||
@@ -61,7 +61,7 @@ export class UserFieldsDialog extends AbstractDialog<BoardUserField[]> {
|
||||
|
||||
constructor(
|
||||
@inject(UserFieldsDialogProps)
|
||||
protected readonly props: UserFieldsDialogProps
|
||||
protected override readonly props: UserFieldsDialogProps
|
||||
) {
|
||||
super({
|
||||
title: UploadSketch.Commands.UPLOAD_WITH_CONFIGURATION.label || '',
|
||||
@@ -83,7 +83,7 @@ export class UserFieldsDialog extends AbstractDialog<BoardUserField[]> {
|
||||
return this.widget.currentUserFields;
|
||||
}
|
||||
|
||||
protected onAfterAttach(msg: Message): void {
|
||||
protected override onAfterAttach(msg: Message): void {
|
||||
if (this.widget.isAttached) {
|
||||
Widget.detach(this.widget);
|
||||
}
|
||||
@@ -92,17 +92,17 @@ export class UserFieldsDialog extends AbstractDialog<BoardUserField[]> {
|
||||
this.update();
|
||||
}
|
||||
|
||||
protected onUpdateRequest(msg: Message): void {
|
||||
protected override onUpdateRequest(msg: Message): void {
|
||||
super.onUpdateRequest(msg);
|
||||
this.widget.update();
|
||||
}
|
||||
|
||||
protected onActivateRequest(msg: Message): void {
|
||||
protected override onActivateRequest(msg: Message): void {
|
||||
super.onActivateRequest(msg);
|
||||
this.widget.activate();
|
||||
}
|
||||
|
||||
protected async accept(): Promise<void> {
|
||||
protected override async accept(): Promise<void> {
|
||||
// If the user presses enter and at least
|
||||
// a field is empty don't accept the input
|
||||
for (const field of this.value) {
|
||||
@@ -113,7 +113,7 @@ export class UserFieldsDialog extends AbstractDialog<BoardUserField[]> {
|
||||
return super.accept();
|
||||
}
|
||||
|
||||
close(): void {
|
||||
override close(): void {
|
||||
this.widget.resetUserFieldsValue();
|
||||
this.widget.close();
|
||||
super.close();
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { injectable, inject } from 'inversify';
|
||||
import { injectable, inject } from '@theia/core/shared/inversify';
|
||||
import {
|
||||
FrontendApplicationContribution,
|
||||
FrontendApplication,
|
||||
|
74
arduino-ide-extension/src/browser/hosted-plugin-events.ts
Normal file
74
arduino-ide-extension/src/browser/hosted-plugin-events.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
import { DisposableCollection, Emitter, Event } from '@theia/core';
|
||||
import { FrontendApplicationContribution } from '@theia/core/lib/browser';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import { HostedPluginSupport } from './theia/plugin-ext/hosted-plugin';
|
||||
|
||||
/**
|
||||
* Frontend contribution to watch VS Code extension start/stop events from Theia.
|
||||
*
|
||||
* In Theia, there are no events when a VS Code extension is loaded, started, unloaded, and stopped.
|
||||
* Currently, it's possible to `@inject` the `HostedPluginSupport` service from Theia and `await`
|
||||
* for the `didStart` promise to resolve. But if the OS goes to sleep, the VS Code extensions will
|
||||
* be unloaded and loaded and started again when the OS awakes. Theia reloads the VS Code extensions
|
||||
* after the OS awake event, but the `didStart` promise was already resolved, so IDE2 cannot restart the LS.
|
||||
* This service is meant to work around the limitation of Theia and fire an event every time the VS Code extensions
|
||||
* loaded and started.
|
||||
*/
|
||||
@injectable()
|
||||
export class HostedPluginEvents implements FrontendApplicationContribution {
|
||||
@inject(HostedPluginSupport)
|
||||
private readonly hostedPluginSupport: HostedPluginSupport;
|
||||
|
||||
private firstStart = true;
|
||||
private readonly onPluginsDidStartEmitter = new Emitter<void>();
|
||||
private readonly onPluginsWillUnloadEmitter = new Emitter<void>();
|
||||
private readonly toDispose = new DisposableCollection(
|
||||
this.onPluginsDidStartEmitter,
|
||||
this.onPluginsWillUnloadEmitter
|
||||
);
|
||||
|
||||
onStart(): void {
|
||||
this.hostedPluginSupport.onDidLoad(() => {
|
||||
// Fire the first event, when `didStart` resolves.
|
||||
if (!this.firstStart) {
|
||||
console.debug('HostedPluginEvents', "Received 'onDidLoad' event.");
|
||||
this.onPluginsDidStartEmitter.fire();
|
||||
} else {
|
||||
console.debug(
|
||||
'HostedPluginEvents',
|
||||
"Received 'onDidLoad' event before the first start. Skipping."
|
||||
);
|
||||
}
|
||||
});
|
||||
this.hostedPluginSupport.didStart.then(() => {
|
||||
console.debug('HostedPluginEvents', "Hosted plugins 'didStart'.");
|
||||
if (!this.firstStart) {
|
||||
throw new Error(
|
||||
'Unexpectedly received a `didStart` event after the first start.'
|
||||
);
|
||||
}
|
||||
this.firstStart = false;
|
||||
this.onPluginsDidStartEmitter.fire();
|
||||
});
|
||||
this.hostedPluginSupport.onDidCloseConnection(() => {
|
||||
console.debug('HostedPluginEvents', "Received 'onDidCloseConnection'.");
|
||||
this.onPluginsWillUnloadEmitter.fire();
|
||||
});
|
||||
}
|
||||
|
||||
onStop(): void {
|
||||
this.toDispose.dispose();
|
||||
}
|
||||
|
||||
get onPluginsDidStart(): Event<void> {
|
||||
return this.onPluginsDidStartEmitter.event;
|
||||
}
|
||||
|
||||
get onPluginsWillUnload(): Event<void> {
|
||||
return this.onPluginsWillUnloadEmitter.event;
|
||||
}
|
||||
|
||||
get didStart(): Promise<void> {
|
||||
return this.hostedPluginSupport.didStart;
|
||||
}
|
||||
}
|
@@ -5,7 +5,7 @@ import {
|
||||
MessageService,
|
||||
nls,
|
||||
} from '@theia/core';
|
||||
import { injectable, inject } from 'inversify';
|
||||
import { injectable, inject } from '@theia/core/shared/inversify';
|
||||
import { IDEUpdater, UpdateInfo } from '../../common/protocol/ide-updater';
|
||||
import { IDEUpdaterDialog } from '../dialogs/ide-updater/ide-updater-dialog';
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { injectable, postConstruct, inject } from 'inversify';
|
||||
import { Message } from '@phosphor/messaging';
|
||||
import { injectable, postConstruct, inject } from '@theia/core/shared/inversify';
|
||||
import { Message } from '@theia/core/shared/@phosphor/messaging';
|
||||
import { addEventListener } from '@theia/core/lib/browser/widgets/widget';
|
||||
import { DialogProps } from '@theia/core/lib/browser/dialogs';
|
||||
import { AbstractDialog } from '../theia/dialogs/dialogs';
|
||||
@@ -38,7 +38,7 @@ export class LibraryListWidget extends ListWidget<LibraryPackage> {
|
||||
}
|
||||
|
||||
@postConstruct()
|
||||
protected init(): void {
|
||||
protected override init(): void {
|
||||
super.init();
|
||||
this.toDispose.pushAll([
|
||||
this.notificationCenter.onLibraryInstalled(() => this.refresh(undefined)),
|
||||
@@ -48,7 +48,7 @@ export class LibraryListWidget extends ListWidget<LibraryPackage> {
|
||||
]);
|
||||
}
|
||||
|
||||
protected async install({
|
||||
protected override async install({
|
||||
item,
|
||||
progressId,
|
||||
version,
|
||||
@@ -158,7 +158,7 @@ export class LibraryListWidget extends ListWidget<LibraryPackage> {
|
||||
}
|
||||
}
|
||||
|
||||
protected async uninstall({
|
||||
protected override async uninstall({
|
||||
item,
|
||||
progressId,
|
||||
}: {
|
||||
@@ -199,7 +199,7 @@ class MessageBoxDialog extends AbstractDialog<MessageBoxDialog.Result> {
|
||||
});
|
||||
}
|
||||
|
||||
protected onCloseRequest(message: Message): void {
|
||||
protected override onCloseRequest(message: Message): void {
|
||||
super.onCloseRequest(message);
|
||||
this.accept();
|
||||
}
|
||||
@@ -217,7 +217,7 @@ class MessageBoxDialog extends AbstractDialog<MessageBoxDialog.Result> {
|
||||
return message;
|
||||
}
|
||||
|
||||
protected handleEnter(event: KeyboardEvent): boolean | void {
|
||||
protected override handleEnter(event: KeyboardEvent): boolean | void {
|
||||
this.response = 0;
|
||||
super.handleEnter(event);
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { injectable } from 'inversify';
|
||||
import { injectable } from '@theia/core/shared/inversify';
|
||||
import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application';
|
||||
import { AbstractViewContribution } from '@theia/core/lib/browser/shell/view-contribution';
|
||||
import { MenuModelRegistry } from '@theia/core';
|
||||
@@ -28,7 +28,7 @@ export class LibraryListWidgetFrontendContribution
|
||||
this.openView();
|
||||
}
|
||||
|
||||
registerMenus(menus: MenuModelRegistry): void {
|
||||
override registerMenus(menus: MenuModelRegistry): void {
|
||||
if (this.toggleCommand) {
|
||||
menus.registerMenuAction(ArduinoMenus.TOOLS__MAIN_GROUP, {
|
||||
commandId: this.toggleCommand.id,
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { inject, injectable } from 'inversify';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import { URI as Uri } from 'vscode-uri';
|
||||
import URI from '@theia/core/lib/common/uri';
|
||||
import { Deferred } from '@theia/core/lib/common/promise-util';
|
||||
|
@@ -0,0 +1,199 @@
|
||||
import {
|
||||
CommandRegistry,
|
||||
Disposable,
|
||||
Emitter,
|
||||
MessageService,
|
||||
nls,
|
||||
} 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';
|
||||
import { BoardsConfig } from './boards/boards-config';
|
||||
import { BoardsServiceProvider } from './boards/boards-service-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 onMonitorSettingsDidChangeEmitter =
|
||||
new Emitter<MonitorSettings>();
|
||||
readonly onMonitorSettingsDidChange =
|
||||
this.onMonitorSettingsDidChangeEmitter.event;
|
||||
|
||||
protected readonly onMonitorShouldResetEmitter = new Emitter();
|
||||
readonly onMonitorShouldReset = this.onMonitorShouldResetEmitter.event;
|
||||
|
||||
// WebSocket used to handle pluggable monitor communication between
|
||||
// frontend and backend.
|
||||
private webSocket?: WebSocket;
|
||||
private wsPort?: number;
|
||||
private lastConnectedBoard: BoardsConfig.Config;
|
||||
private onBoardsConfigChanged: Disposable | undefined;
|
||||
|
||||
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,
|
||||
|
||||
@inject(CommandRegistry)
|
||||
protected readonly commandRegistry: CommandRegistry,
|
||||
|
||||
@inject(BoardsServiceProvider)
|
||||
protected readonly boardsServiceProvider: BoardsServiceProvider
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Connects a localhost WebSocket using the specified port.
|
||||
* @param addressPort port of the WebSocket
|
||||
*/
|
||||
async connect(addressPort: number): Promise<void> {
|
||||
if (!!this.webSocket) {
|
||||
if (this.wsPort === addressPort) return;
|
||||
else this.disconnect();
|
||||
}
|
||||
try {
|
||||
this.webSocket = new WebSocket(`ws://localhost:${addressPort}`);
|
||||
} catch {
|
||||
this.messageService.error(
|
||||
nls.localize(
|
||||
'arduino/monitor/unableToConnectToWebSocket',
|
||||
'Unable to connect to websocket'
|
||||
)
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
this.webSocket.onmessage = (message) => {
|
||||
const parsedMessage = JSON.parse(message.data);
|
||||
if (Array.isArray(parsedMessage))
|
||||
this.onMessagesReceivedEmitter.fire({ messages: parsedMessage });
|
||||
else if (
|
||||
parsedMessage.command ===
|
||||
Monitor.MiddlewareCommand.ON_SETTINGS_DID_CHANGE
|
||||
) {
|
||||
this.onMonitorSettingsDidChangeEmitter.fire(parsedMessage.data);
|
||||
}
|
||||
};
|
||||
this.wsPort = addressPort;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disconnects the WebSocket if connected.
|
||||
*/
|
||||
disconnect(): void {
|
||||
if (!this.webSocket) return;
|
||||
this.onBoardsConfigChanged?.dispose();
|
||||
this.onBoardsConfigChanged = undefined;
|
||||
try {
|
||||
this.webSocket?.close();
|
||||
this.webSocket = undefined;
|
||||
} catch {
|
||||
this.messageService.error(
|
||||
nls.localize(
|
||||
'arduino/monitor/unableToCloseWebSocket',
|
||||
'Unable to close websocket'
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async isWSConnected(): Promise<boolean> {
|
||||
return !!this.webSocket;
|
||||
}
|
||||
|
||||
async startMonitor(settings?: PluggableMonitorSettings): Promise<void> {
|
||||
this.lastConnectedBoard = {
|
||||
selectedBoard: this.boardsServiceProvider.boardsConfig.selectedBoard,
|
||||
selectedPort: this.boardsServiceProvider.boardsConfig.selectedPort,
|
||||
};
|
||||
|
||||
if (!this.onBoardsConfigChanged) {
|
||||
this.onBoardsConfigChanged =
|
||||
this.boardsServiceProvider.onBoardsConfigChanged(
|
||||
async ({ selectedBoard, selectedPort }) => {
|
||||
if (
|
||||
typeof selectedBoard === 'undefined' ||
|
||||
typeof selectedPort === 'undefined'
|
||||
)
|
||||
return;
|
||||
|
||||
// a board is plugged and it's different from the old connected board
|
||||
if (
|
||||
selectedBoard?.fqbn !==
|
||||
this.lastConnectedBoard?.selectedBoard?.fqbn ||
|
||||
selectedPort?.id !== this.lastConnectedBoard?.selectedPort?.id
|
||||
) {
|
||||
this.onMonitorShouldResetEmitter.fire(null);
|
||||
this.lastConnectedBoard = {
|
||||
selectedBoard: selectedBoard,
|
||||
selectedPort: selectedPort,
|
||||
};
|
||||
} else {
|
||||
// a board is plugged and it's the same as prev, rerun "this.startMonitor" to
|
||||
// recreate the listener callback
|
||||
this.startMonitor();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const { selectedBoard, selectedPort } =
|
||||
this.boardsServiceProvider.boardsConfig;
|
||||
if (!selectedBoard || !selectedBoard.fqbn || !selectedPort) return;
|
||||
await this.server().startMonitor(selectedBoard, selectedPort, settings);
|
||||
}
|
||||
|
||||
getCurrentSettings(board: Board, port: Port): Promise<MonitorSettings> {
|
||||
return this.server().getCurrentSettings(board, port);
|
||||
}
|
||||
|
||||
send(message: string): void {
|
||||
if (!this.webSocket) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.webSocket.send(
|
||||
JSON.stringify({
|
||||
command: Monitor.ClientCommand.SEND_MESSAGE,
|
||||
data: message,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
changeSettings(settings: MonitorSettings): void {
|
||||
if (!this.webSocket) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.webSocket.send(
|
||||
JSON.stringify({
|
||||
command: Monitor.ClientCommand.CHANGE_SETTINGS,
|
||||
data: settings,
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
278
arduino-ide-extension/src/browser/monitor-model.ts
Normal file
278
arduino-ide-extension/src/browser/monitor-model.ts
Normal file
@@ -0,0 +1,278 @@
|
||||
import { Emitter, Event } from '@theia/core';
|
||||
import {
|
||||
FrontendApplicationContribution,
|
||||
LocalStorageService,
|
||||
} from '@theia/core/lib/browser';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import { MonitorManagerProxyClient } from '../common/protocol';
|
||||
import { isNullOrUndefined } from '../common/utils';
|
||||
import { MonitorSettings } from '../node/monitor-settings/monitor-settings-provider';
|
||||
|
||||
@injectable()
|
||||
export class MonitorModel implements FrontendApplicationContribution {
|
||||
protected static STORAGE_ID = 'arduino-monitor-model';
|
||||
|
||||
@inject(LocalStorageService)
|
||||
protected readonly localStorageService: LocalStorageService;
|
||||
|
||||
@inject(MonitorManagerProxyClient)
|
||||
protected readonly monitorManagerProxy: MonitorManagerProxyClient;
|
||||
|
||||
protected readonly onChangeEmitter: Emitter<
|
||||
MonitorModel.State.Change<keyof MonitorModel.State>
|
||||
>;
|
||||
|
||||
protected _autoscroll: boolean;
|
||||
protected _timestamp: boolean;
|
||||
protected _lineEnding: MonitorModel.EOL;
|
||||
protected _interpolate: boolean;
|
||||
protected _darkTheme: boolean;
|
||||
protected _wsPort: number;
|
||||
protected _serialPort: string;
|
||||
protected _connected: boolean;
|
||||
|
||||
constructor() {
|
||||
this._autoscroll = true;
|
||||
this._timestamp = false;
|
||||
this._interpolate = false;
|
||||
this._lineEnding = MonitorModel.EOL.DEFAULT;
|
||||
this._darkTheme = false;
|
||||
this._wsPort = 0;
|
||||
this._serialPort = '';
|
||||
this._connected = true;
|
||||
|
||||
this.onChangeEmitter = new Emitter<
|
||||
MonitorModel.State.Change<keyof MonitorModel.State>
|
||||
>();
|
||||
}
|
||||
|
||||
onStart(): void {
|
||||
this.localStorageService
|
||||
.getData<MonitorModel.State>(MonitorModel.STORAGE_ID)
|
||||
.then(this.restoreState.bind(this));
|
||||
|
||||
this.monitorManagerProxy.onMonitorSettingsDidChange(
|
||||
this.onMonitorSettingsDidChange.bind(this)
|
||||
);
|
||||
}
|
||||
|
||||
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;
|
||||
this._serialPort = state.serialPort;
|
||||
}
|
||||
|
||||
protected async storeState(): Promise<void> {
|
||||
return this.localStorageService.setData(MonitorModel.STORAGE_ID, {
|
||||
autoscroll: this._autoscroll,
|
||||
timestamp: this._timestamp,
|
||||
lineEnding: this._lineEnding,
|
||||
interpolate: this._interpolate,
|
||||
serialPort: this._serialPort,
|
||||
});
|
||||
}
|
||||
|
||||
get autoscroll(): boolean {
|
||||
return this._autoscroll;
|
||||
}
|
||||
|
||||
set autoscroll(autoscroll: boolean) {
|
||||
if (autoscroll === this._autoscroll) return;
|
||||
this._autoscroll = autoscroll;
|
||||
this.monitorManagerProxy.changeSettings({
|
||||
monitorUISettings: { autoscroll },
|
||||
});
|
||||
this.storeState().then(() => {
|
||||
this.onChangeEmitter.fire({
|
||||
property: 'autoscroll',
|
||||
value: this._autoscroll,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
toggleAutoscroll(): void {
|
||||
this.autoscroll = !this._autoscroll;
|
||||
}
|
||||
|
||||
get timestamp(): boolean {
|
||||
return this._timestamp;
|
||||
}
|
||||
|
||||
set timestamp(timestamp: boolean) {
|
||||
if (timestamp === this._timestamp) return;
|
||||
this._timestamp = timestamp;
|
||||
this.monitorManagerProxy.changeSettings({
|
||||
monitorUISettings: { timestamp },
|
||||
});
|
||||
this.storeState().then(() =>
|
||||
this.onChangeEmitter.fire({
|
||||
property: 'timestamp',
|
||||
value: this._timestamp,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
toggleTimestamp(): void {
|
||||
this.timestamp = !this._timestamp;
|
||||
}
|
||||
|
||||
get lineEnding(): MonitorModel.EOL {
|
||||
return this._lineEnding;
|
||||
}
|
||||
|
||||
set lineEnding(lineEnding: MonitorModel.EOL) {
|
||||
if (lineEnding === this._lineEnding) return;
|
||||
this._lineEnding = lineEnding;
|
||||
this.monitorManagerProxy.changeSettings({
|
||||
monitorUISettings: { lineEnding },
|
||||
});
|
||||
this.storeState().then(() =>
|
||||
this.onChangeEmitter.fire({
|
||||
property: 'lineEnding',
|
||||
value: this._lineEnding,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
get interpolate(): boolean {
|
||||
return this._interpolate;
|
||||
}
|
||||
|
||||
set interpolate(interpolate: boolean) {
|
||||
if (interpolate === this._interpolate) return;
|
||||
this._interpolate = interpolate;
|
||||
this.monitorManagerProxy.changeSettings({
|
||||
monitorUISettings: { interpolate },
|
||||
});
|
||||
this.storeState().then(() =>
|
||||
this.onChangeEmitter.fire({
|
||||
property: 'interpolate',
|
||||
value: this._interpolate,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
get darkTheme(): boolean {
|
||||
return this._darkTheme;
|
||||
}
|
||||
|
||||
set darkTheme(darkTheme: boolean) {
|
||||
if (darkTheme === this._darkTheme) return;
|
||||
this._darkTheme = darkTheme;
|
||||
this.monitorManagerProxy.changeSettings({
|
||||
monitorUISettings: { darkTheme },
|
||||
});
|
||||
this.onChangeEmitter.fire({
|
||||
property: 'darkTheme',
|
||||
value: this._darkTheme,
|
||||
});
|
||||
}
|
||||
|
||||
get wsPort(): number {
|
||||
return this._wsPort;
|
||||
}
|
||||
|
||||
set wsPort(wsPort: number) {
|
||||
if (wsPort === this._wsPort) return;
|
||||
this._wsPort = wsPort;
|
||||
this.monitorManagerProxy.changeSettings({
|
||||
monitorUISettings: { wsPort },
|
||||
});
|
||||
this.onChangeEmitter.fire({
|
||||
property: 'wsPort',
|
||||
value: this._wsPort,
|
||||
});
|
||||
}
|
||||
|
||||
get serialPort(): string {
|
||||
return this._serialPort;
|
||||
}
|
||||
|
||||
set serialPort(serialPort: string) {
|
||||
if (serialPort === this._serialPort) return;
|
||||
this._serialPort = serialPort;
|
||||
this.monitorManagerProxy.changeSettings({
|
||||
monitorUISettings: { serialPort },
|
||||
});
|
||||
this.storeState().then(() =>
|
||||
this.onChangeEmitter.fire({
|
||||
property: 'serialPort',
|
||||
value: this._serialPort,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
get connected(): boolean {
|
||||
return this._connected;
|
||||
}
|
||||
|
||||
set connected(connected: boolean) {
|
||||
if (connected === this._connected) return;
|
||||
this._connected = connected;
|
||||
this.monitorManagerProxy.changeSettings({
|
||||
monitorUISettings: { connected },
|
||||
});
|
||||
this.onChangeEmitter.fire({
|
||||
property: 'connected',
|
||||
value: this._connected,
|
||||
});
|
||||
}
|
||||
|
||||
protected onMonitorSettingsDidChange = (settings: MonitorSettings): void => {
|
||||
const { monitorUISettings } = settings;
|
||||
if (!monitorUISettings) return;
|
||||
const {
|
||||
autoscroll,
|
||||
interpolate,
|
||||
lineEnding,
|
||||
timestamp,
|
||||
darkTheme,
|
||||
wsPort,
|
||||
serialPort,
|
||||
connected,
|
||||
} = monitorUISettings;
|
||||
|
||||
if (!isNullOrUndefined(autoscroll)) this.autoscroll = autoscroll;
|
||||
if (!isNullOrUndefined(interpolate)) this.interpolate = interpolate;
|
||||
if (!isNullOrUndefined(lineEnding)) this.lineEnding = lineEnding;
|
||||
if (!isNullOrUndefined(timestamp)) this.timestamp = timestamp;
|
||||
if (!isNullOrUndefined(darkTheme)) this.darkTheme = darkTheme;
|
||||
if (!isNullOrUndefined(wsPort)) this.wsPort = wsPort;
|
||||
if (!isNullOrUndefined(serialPort)) this.serialPort = serialPort;
|
||||
if (!isNullOrUndefined(connected)) this.connected = connected;
|
||||
};
|
||||
}
|
||||
|
||||
// TODO: Move this to /common
|
||||
export namespace MonitorModel {
|
||||
export interface State {
|
||||
autoscroll: boolean;
|
||||
timestamp: boolean;
|
||||
lineEnding: EOL;
|
||||
interpolate: boolean;
|
||||
darkTheme: boolean;
|
||||
wsPort: number;
|
||||
serialPort: string;
|
||||
connected: boolean;
|
||||
}
|
||||
export namespace State {
|
||||
export interface Change<K extends keyof State> {
|
||||
readonly property: K;
|
||||
readonly value: State[K];
|
||||
}
|
||||
}
|
||||
|
||||
export type EOL = '' | '\n' | '\r' | '\r\n';
|
||||
export namespace EOL {
|
||||
export const DEFAULT: EOL = '\n';
|
||||
}
|
||||
}
|
@@ -1,4 +1,8 @@
|
||||
import { inject, injectable, postConstruct } from 'inversify';
|
||||
import {
|
||||
inject,
|
||||
injectable,
|
||||
postConstruct,
|
||||
} from '@theia/core/shared/inversify';
|
||||
import { Emitter } from '@theia/core/lib/common/event';
|
||||
import { JsonRpcProxy } from '@theia/core/lib/common/messaging/proxy-factory';
|
||||
import { DisposableCollection } from '@theia/core/lib/common/disposable';
|
||||
@@ -14,6 +18,10 @@ import {
|
||||
Config,
|
||||
Sketch,
|
||||
} from '../common/protocol';
|
||||
import {
|
||||
FrontendApplicationStateService,
|
||||
FrontendApplicationState,
|
||||
} from '@theia/core/lib/browser/frontend-application-state';
|
||||
|
||||
@injectable()
|
||||
export class NotificationCenter
|
||||
@@ -22,8 +30,11 @@ export class NotificationCenter
|
||||
@inject(NotificationServiceServer)
|
||||
protected readonly server: JsonRpcProxy<NotificationServiceServer>;
|
||||
|
||||
@inject(FrontendApplicationStateService)
|
||||
private readonly appStateService: FrontendApplicationStateService;
|
||||
|
||||
protected readonly indexUpdatedEmitter = new Emitter<void>();
|
||||
protected readonly daemonStartedEmitter = new Emitter<void>();
|
||||
protected readonly daemonStartedEmitter = new Emitter<string>();
|
||||
protected readonly daemonStoppedEmitter = new Emitter<void>();
|
||||
protected readonly configChangedEmitter = new Emitter<{
|
||||
config: Config | undefined;
|
||||
@@ -45,6 +56,8 @@ export class NotificationCenter
|
||||
protected readonly recentSketchesChangedEmitter = new Emitter<{
|
||||
sketches: Sketch[];
|
||||
}>();
|
||||
private readonly onAppStateDidChangeEmitter =
|
||||
new Emitter<FrontendApplicationState>();
|
||||
|
||||
protected readonly toDispose = new DisposableCollection(
|
||||
this.indexUpdatedEmitter,
|
||||
@@ -68,10 +81,16 @@ export class NotificationCenter
|
||||
readonly onLibraryUninstalled = this.libraryUninstalledEmitter.event;
|
||||
readonly onAttachedBoardsChanged = this.attachedBoardsChangedEmitter.event;
|
||||
readonly onRecentSketchesChanged = this.recentSketchesChangedEmitter.event;
|
||||
readonly onAppStateDidChange = this.onAppStateDidChangeEmitter.event;
|
||||
|
||||
@postConstruct()
|
||||
protected init(): void {
|
||||
this.server.setClient(this);
|
||||
this.toDispose.push(
|
||||
this.appStateService.onStateChanged((state) =>
|
||||
this.onAppStateDidChangeEmitter.fire(state)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
onStop(): void {
|
||||
@@ -82,8 +101,8 @@ export class NotificationCenter
|
||||
this.indexUpdatedEmitter.fire();
|
||||
}
|
||||
|
||||
notifyDaemonStarted(): void {
|
||||
this.daemonStartedEmitter.fire();
|
||||
notifyDaemonStarted(port: string): void {
|
||||
this.daemonStartedEmitter.fire(port);
|
||||
}
|
||||
|
||||
notifyDaemonStopped(): void {
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { inject, injectable } from 'inversify';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import { Emitter } from '@theia/core/lib/common/event';
|
||||
import { OutputContribution } from '@theia/output/lib/browser/output-contribution';
|
||||
import { OutputChannelManager } from '@theia/output/lib/browser/output-channel';
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import * as React from 'react';
|
||||
import { injectable, inject } from 'inversify';
|
||||
import * as React from '@theia/core/shared/react';
|
||||
import { injectable, inject } from '@theia/core/shared/inversify';
|
||||
import { AbstractViewContribution, codicon } from '@theia/core/lib/browser';
|
||||
import { MonitorWidget } from './monitor-widget';
|
||||
import { MenuModelRegistry, Command, CommandRegistry } from '@theia/core';
|
||||
@@ -8,9 +8,10 @@ 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';
|
||||
import { MonitorManagerProxyClient } from '../../../common/protocol';
|
||||
|
||||
export namespace SerialMonitor {
|
||||
export namespace Commands {
|
||||
@@ -47,10 +48,15 @@ export class MonitorViewContribution
|
||||
static readonly TOGGLE_SERIAL_MONITOR = MonitorWidget.ID + ':toggle';
|
||||
static readonly TOGGLE_SERIAL_MONITOR_TOOLBAR =
|
||||
MonitorWidget.ID + ':toggle-toolbar';
|
||||
static readonly RESET_SERIAL_MONITOR = MonitorWidget.ID + ':reset';
|
||||
|
||||
@inject(SerialModel) protected readonly model: SerialModel;
|
||||
constructor(
|
||||
@inject(MonitorModel)
|
||||
protected readonly model: MonitorModel,
|
||||
|
||||
constructor() {
|
||||
@inject(MonitorManagerProxyClient)
|
||||
protected readonly monitorManagerProxy: MonitorManagerProxyClient
|
||||
) {
|
||||
super({
|
||||
widgetId: MonitorWidget.ID,
|
||||
widgetName: MonitorWidget.LABEL,
|
||||
@@ -60,9 +66,10 @@ export class MonitorViewContribution
|
||||
toggleCommandId: MonitorViewContribution.TOGGLE_SERIAL_MONITOR,
|
||||
toggleKeybinding: 'CtrlCmd+Shift+M',
|
||||
});
|
||||
this.monitorManagerProxy.onMonitorShouldReset(() => this.reset());
|
||||
}
|
||||
|
||||
registerMenus(menus: MenuModelRegistry): void {
|
||||
override registerMenus(menus: MenuModelRegistry): void {
|
||||
if (this.toggleCommand) {
|
||||
menus.registerMenuAction(ArduinoMenus.TOOLS__MAIN_GROUP, {
|
||||
commandId: this.toggleCommand.id,
|
||||
@@ -95,7 +102,7 @@ export class MonitorViewContribution
|
||||
});
|
||||
}
|
||||
|
||||
registerCommands(commands: CommandRegistry): void {
|
||||
override registerCommands(commands: CommandRegistry): void {
|
||||
commands.registerCommand(SerialMonitor.Commands.CLEAR_OUTPUT, {
|
||||
isEnabled: (widget) => widget instanceof MonitorWidget,
|
||||
isVisible: (widget) => widget instanceof MonitorWidget,
|
||||
@@ -118,6 +125,10 @@ export class MonitorViewContribution
|
||||
}
|
||||
);
|
||||
}
|
||||
commands.registerCommand(
|
||||
{ id: MonitorViewContribution.RESET_SERIAL_MONITOR },
|
||||
{ execute: () => this.reset() }
|
||||
);
|
||||
}
|
||||
|
||||
protected async toggle(): Promise<void> {
|
||||
@@ -129,6 +140,14 @@ export class MonitorViewContribution
|
||||
}
|
||||
}
|
||||
|
||||
protected async reset(): Promise<void> {
|
||||
const widget = this.tryGetWidget();
|
||||
if (widget) {
|
||||
widget.dispose();
|
||||
await this.openView({ activate: true, reveal: true });
|
||||
}
|
||||
}
|
||||
|
||||
protected renderAutoScrollButton(): React.ReactNode {
|
||||
return (
|
||||
<React.Fragment key="autoscroll-toolbar-item">
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import * as React from 'react';
|
||||
import { postConstruct, injectable, inject } from 'inversify';
|
||||
import * as React from '@theia/core/shared/react';
|
||||
import { injectable, inject } from '@theia/core/shared/inversify';
|
||||
import { OptionsType } from 'react-select/src/types';
|
||||
import { Emitter } from '@theia/core/lib/common/event';
|
||||
import { Disposable } from '@theia/core/lib/common/disposable';
|
||||
@@ -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,14 +26,7 @@ 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 settings: MonitorSettings = {};
|
||||
|
||||
protected widgetHeight: number;
|
||||
|
||||
@@ -48,7 +41,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 +59,30 @@ 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())
|
||||
);
|
||||
}
|
||||
|
||||
@postConstruct()
|
||||
protected init(): void {
|
||||
protected override onBeforeAttach(msg: Message): void {
|
||||
this.update();
|
||||
this.toDispose.push(
|
||||
this.serialConnection.onConnectionChanged(() => this.clearConsole())
|
||||
this.toDispose.push(this.monitorModel.onChange(() => this.update()));
|
||||
this.getCurrentSettings().then(this.onMonitorSettingsDidChange.bind(this));
|
||||
this.monitorManagerProxy.onMonitorSettingsDidChange(
|
||||
this.onMonitorSettingsDidChange.bind(this)
|
||||
);
|
||||
this.toDispose.push(this.serialModel.onChange(() => this.update()));
|
||||
|
||||
this.monitorManagerProxy.startMonitor();
|
||||
}
|
||||
|
||||
onMonitorSettingsDidChange(settings: MonitorSettings): void {
|
||||
this.settings = {
|
||||
...this.settings,
|
||||
pluggableMonitorSettings: {
|
||||
...this.settings.pluggableMonitorSettings,
|
||||
...settings.pluggableMonitorSettings,
|
||||
},
|
||||
};
|
||||
this.update();
|
||||
}
|
||||
|
||||
clearConsole(): void {
|
||||
@@ -75,21 +90,16 @@ export class MonitorWidget extends ReactWidget {
|
||||
this.update();
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
override dispose(): void {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
protected onAfterAttach(msg: Message): void {
|
||||
super.onAfterAttach(msg);
|
||||
this.serialConnection.openWSToBE();
|
||||
}
|
||||
|
||||
onCloseRequest(msg: Message): void {
|
||||
protected override onCloseRequest(msg: Message): void {
|
||||
this.closing = true;
|
||||
super.onCloseRequest(msg);
|
||||
}
|
||||
|
||||
protected onUpdateRequest(msg: Message): void {
|
||||
protected override onUpdateRequest(msg: Message): void {
|
||||
// TODO: `this.isAttached`
|
||||
// See: https://github.com/eclipse-theia/theia/issues/6704#issuecomment-562574713
|
||||
if (!this.closing && this.isAttached) {
|
||||
@@ -97,13 +107,13 @@ export class MonitorWidget extends ReactWidget {
|
||||
}
|
||||
}
|
||||
|
||||
protected onResize(msg: Widget.ResizeMessage): void {
|
||||
protected override onResize(msg: Widget.ResizeMessage): void {
|
||||
super.onResize(msg);
|
||||
this.widgetHeight = msg.height;
|
||||
this.update();
|
||||
}
|
||||
|
||||
protected onActivateRequest(msg: Message): void {
|
||||
protected override onActivateRequest(msg: Message): void {
|
||||
super.onActivateRequest(msg);
|
||||
(this.focusNode || this.node).focus();
|
||||
}
|
||||
@@ -119,7 +129,7 @@ export class MonitorWidget extends ReactWidget {
|
||||
};
|
||||
|
||||
protected get lineEndings(): OptionsType<
|
||||
SerialMonitorOutput.SelectOption<SerialModel.EOL>
|
||||
SerialMonitorOutput.SelectOption<MonitorModel.EOL>
|
||||
> {
|
||||
return [
|
||||
{
|
||||
@@ -144,32 +154,40 @@ 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(): Promise<MonitorSettings> {
|
||||
const board = this.boardsServiceProvider.boardsConfig.selectedBoard;
|
||||
const port = this.boardsServiceProvider.boardsConfig.selectedPort;
|
||||
if (!board || !port) {
|
||||
return Promise.resolve(this.settings || {});
|
||||
}
|
||||
return this.monitorManagerProxy.getCurrentSettings(board, port);
|
||||
}
|
||||
|
||||
protected render(): React.ReactNode {
|
||||
const { baudRates, lineEndings } = this;
|
||||
const baudrate = this.settings?.pluggableMonitorSettings
|
||||
? this.settings.pluggableMonitorSettings.baudrate
|
||||
: undefined;
|
||||
|
||||
const baudrateOptions = baudrate?.values.map((b) => ({
|
||||
label: b + ' baud',
|
||||
value: b,
|
||||
}));
|
||||
const baudrateSelectedOption = baudrateOptions?.find(
|
||||
(b) => b.value === baudrate?.selectedValue
|
||||
);
|
||||
|
||||
const lineEnding =
|
||||
lineEndings.find((item) => item.value === this.serialModel.lineEnding) ||
|
||||
lineEndings[1]; // Defaults to `\n`.
|
||||
const baudRate =
|
||||
baudRates.find((item) => item.value === this.serialModel.baudRate) ||
|
||||
baudRates[4]; // Defaults to `9600`.
|
||||
this.lineEndings.find(
|
||||
(item) => item.value === this.monitorModel.lineEnding
|
||||
) || this.lineEndings[1]; // Defaults to `\n`.
|
||||
|
||||
return (
|
||||
<div className="serial-monitor">
|
||||
<div className="head">
|
||||
<div className="send">
|
||||
<SerialMonitorSendInput
|
||||
serialConnection={this.serialConnection}
|
||||
boardsServiceProvider={this.boardsServiceProvider}
|
||||
monitorModel={this.monitorModel}
|
||||
resolveFocus={this.onFocusResolved}
|
||||
onSend={this.onSend}
|
||||
/>
|
||||
@@ -178,26 +196,28 @@ export class MonitorWidget extends ReactWidget {
|
||||
<div className="select">
|
||||
<ArduinoSelect
|
||||
maxMenuHeight={this.widgetHeight - 40}
|
||||
options={lineEndings}
|
||||
options={this.lineEndings}
|
||||
value={lineEnding}
|
||||
onChange={this.onChangeLineEnding}
|
||||
/>
|
||||
</div>
|
||||
<div className="select">
|
||||
<ArduinoSelect
|
||||
className="select"
|
||||
maxMenuHeight={this.widgetHeight - 40}
|
||||
options={baudRates}
|
||||
value={baudRate}
|
||||
onChange={this.onChangeBaudRate}
|
||||
/>
|
||||
</div>
|
||||
{baudrateOptions && baudrateSelectedOption && (
|
||||
<div className="select">
|
||||
<ArduinoSelect
|
||||
className="select"
|
||||
maxMenuHeight={this.widgetHeight - 40}
|
||||
options={baudrateOptions}
|
||||
value={baudrateSelectedOption}
|
||||
onChange={this.onChangeBaudRate}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</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 +228,26 @@ 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>
|
||||
) => {
|
||||
this.serialModel.lineEnding = option.value;
|
||||
option: SerialMonitorOutput.SelectOption<MonitorModel.EOL>
|
||||
): void => {
|
||||
this.monitorModel.lineEnding = option.value;
|
||||
};
|
||||
|
||||
protected readonly onChangeBaudRate = (
|
||||
option: SerialMonitorOutput.SelectOption<SerialConfig.BaudRate>
|
||||
) => {
|
||||
this.serialModel.baudRate = option.value;
|
||||
protected readonly onChangeBaudRate = ({
|
||||
value,
|
||||
}: {
|
||||
value: string;
|
||||
}): void => {
|
||||
this.getCurrentSettings().then(({ pluggableMonitorSettings }) => {
|
||||
if (!pluggableMonitorSettings || !pluggableMonitorSettings['baudrate'])
|
||||
return;
|
||||
const baudRateSettings = pluggableMonitorSettings['baudrate'];
|
||||
baudRateSettings.selectedValue = value;
|
||||
this.monitorManagerProxy.changeSettings({ pluggableMonitorSettings });
|
||||
});
|
||||
};
|
||||
}
|
||||
|
@@ -1,14 +1,15 @@
|
||||
import * as React from 'react';
|
||||
import * as React from '@theia/core/shared/react';
|
||||
import { Key, KeyCode } from '@theia/core/lib/browser/keys';
|
||||
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 { BoardsServiceProvider } from '../../boards/boards-service-provider';
|
||||
import { MonitorModel } from '../../monitor-model';
|
||||
|
||||
export namespace SerialMonitorSendInput {
|
||||
export interface Props {
|
||||
readonly serialConnection: SerialConnectionManager;
|
||||
readonly boardsServiceProvider: BoardsServiceProvider;
|
||||
readonly monitorModel: MonitorModel;
|
||||
readonly onSend: (text: string) => void;
|
||||
readonly resolveFocus: (element: HTMLElement | undefined) => void;
|
||||
}
|
||||
@@ -26,41 +27,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.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 });
|
||||
}
|
||||
}),
|
||||
]);
|
||||
override componentDidMount(): void {
|
||||
this.setState({ connected: this.props.monitorModel.connected });
|
||||
this.toDisposeBeforeUnmount.push(
|
||||
this.props.monitorModel.onChange(({ property }) => {
|
||||
if (property === 'connected')
|
||||
this.setState({ connected: this.props.monitorModel.connected });
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
componentWillUnmount(): void {
|
||||
override componentWillUnmount(): void {
|
||||
// TODO: "Your preferred browser's local storage is almost full." Discard `content` before saving layout?
|
||||
this.toDisposeBeforeUnmount.dispose();
|
||||
}
|
||||
|
||||
render(): React.ReactNode {
|
||||
override render(): React.ReactNode {
|
||||
return (
|
||||
<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,15 +62,22 @@ 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}')",
|
||||
@@ -102,7 +102,7 @@ export class SerialMonitorSendInput extends React.Component<
|
||||
}
|
||||
|
||||
protected onSend(): void {
|
||||
this.props.onSend(this.state.text);
|
||||
this.props.onSend(this.state.text + this.props.monitorModel.lineEnding);
|
||||
this.setState({ text: '' });
|
||||
}
|
||||
|
||||
|
@@ -1,11 +1,11 @@
|
||||
import * as React from 'react';
|
||||
import * as React from '@theia/core/shared/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,12 +24,12 @@ 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,
|
||||
};
|
||||
}
|
||||
|
||||
render(): React.ReactNode {
|
||||
override render(): React.ReactNode {
|
||||
return (
|
||||
<List
|
||||
className="serial-monitor-messages"
|
||||
@@ -51,21 +51,20 @@ export class SerialMonitorOutput extends React.Component<
|
||||
);
|
||||
}
|
||||
|
||||
shouldComponentUpdate(): boolean {
|
||||
override shouldComponentUpdate(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
componentDidMount(): void {
|
||||
override 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,
|
||||
@@ -75,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') {
|
||||
@@ -87,13 +86,13 @@ export class SerialMonitorOutput extends React.Component<
|
||||
]);
|
||||
}
|
||||
|
||||
componentWillUnmount(): void {
|
||||
override componentWillUnmount(): void {
|
||||
// TODO: "Your preferred browser's local storage is almost full." Discard `content` before saving layout?
|
||||
this.toDisposeBeforeUnmount.dispose();
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -115,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
|
||||
@@ -126,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;
|
||||
}
|
||||
|
@@ -1,20 +1,19 @@
|
||||
import { ThemeService } from '@theia/core/lib/browser/theming';
|
||||
import { injectable, inject } from 'inversify';
|
||||
import { injectable, inject } from '@theia/core/shared/inversify';
|
||||
import {
|
||||
Command,
|
||||
CommandRegistry,
|
||||
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/electron/shared/electron';
|
||||
import { SerialConfig } from '../../../common/protocol';
|
||||
import { SerialConnectionManager } from '../serial-connection-manager';
|
||||
import { SerialPlotter } from './protocol';
|
||||
import { MonitorManagerProxyClient } from '../../../common/protocol';
|
||||
import { BoardsServiceProvider } from '../../boards/boards-service-provider';
|
||||
import { MonitorModel } from '../../monitor-model';
|
||||
|
||||
const queryString = require('query-string');
|
||||
|
||||
export namespace SerialPlotterContribution {
|
||||
@@ -24,6 +23,11 @@ export namespace SerialPlotterContribution {
|
||||
label: 'Serial Plotter',
|
||||
category: 'Arduino',
|
||||
};
|
||||
export const RESET: Command = {
|
||||
id: 'serial-plotter-reset',
|
||||
label: 'Reset Serial Plotter',
|
||||
category: 'Arduino',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,19 +37,19 @@ 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;
|
||||
|
||||
onStart(app: FrontendApplication): MaybePromise<void> {
|
||||
override onStart(app: FrontendApplication): MaybePromise<void> {
|
||||
this.url = new Endpoint({ path: '/plotter' }).getRestUrl().toString();
|
||||
|
||||
ipcRenderer.on('CLOSE_CHILD_WINDOW', async () => {
|
||||
@@ -53,16 +57,21 @@ export class PlotterFrontendContribution extends Contribution {
|
||||
this.window = null;
|
||||
}
|
||||
});
|
||||
this.monitorManagerProxy.onMonitorShouldReset(() => this.reset());
|
||||
|
||||
return super.onStart(app);
|
||||
}
|
||||
|
||||
registerCommands(registry: CommandRegistry): void {
|
||||
override registerCommands(registry: CommandRegistry): void {
|
||||
registry.registerCommand(SerialPlotterContribution.Commands.OPEN, {
|
||||
execute: this.connect.bind(this),
|
||||
execute: this.startPlotter.bind(this),
|
||||
});
|
||||
registry.registerCommand(SerialPlotterContribution.Commands.RESET, {
|
||||
execute: () => this.reset(),
|
||||
});
|
||||
}
|
||||
|
||||
registerMenus(menus: MenuModelRegistry): void {
|
||||
override registerMenus(menus: MenuModelRegistry): void {
|
||||
menus.registerMenuAction(ArduinoMenus.TOOLS__MAIN_GROUP, {
|
||||
commandId: SerialPlotterContribution.Commands.OPEN.id,
|
||||
label: SerialPlotterContribution.Commands.OPEN.label,
|
||||
@@ -70,12 +79,13 @@ export class PlotterFrontendContribution extends Contribution {
|
||||
});
|
||||
}
|
||||
|
||||
async connect(): Promise<void> {
|
||||
async startPlotter(): Promise<void> {
|
||||
await this.monitorManagerProxy.startMonitor();
|
||||
if (!!this.window) {
|
||||
this.window.focus();
|
||||
return;
|
||||
}
|
||||
const wsPort = this.serialConnection.getWsPort();
|
||||
const wsPort = this.monitorManagerProxy.getWebSocketPort();
|
||||
if (wsPort) {
|
||||
this.open(wsPort);
|
||||
} else {
|
||||
@@ -84,15 +94,10 @@ export class PlotterFrontendContribution extends Contribution {
|
||||
}
|
||||
|
||||
protected async open(wsPort: number): Promise<void> {
|
||||
const initConfig: Partial<SerialPlotter.Config> = {
|
||||
baudrates: SerialConfig.BaudRates.map((b) => b),
|
||||
currentBaudrate: this.model.baudRate,
|
||||
currentLineEnding: this.model.lineEnding,
|
||||
const initConfig = {
|
||||
darkTheme: this.themeService.getCurrentTheme().type === 'dark',
|
||||
wsPort,
|
||||
interpolate: this.model.interpolate,
|
||||
connected: await this.serialConnection.isBESerialConnected(),
|
||||
serialPort: this.boardsServiceProvider.boardsConfig.selectedPort?.address,
|
||||
serialPort: this.model.serialPort,
|
||||
};
|
||||
const urlWithParams = queryString.stringifyUrl(
|
||||
{
|
||||
@@ -103,4 +108,11 @@ export class PlotterFrontendContribution extends Contribution {
|
||||
);
|
||||
this.window = window.open(urlWithParams, 'serialPlotter');
|
||||
}
|
||||
|
||||
protected async reset(): Promise<void> {
|
||||
if (!!this.window) {
|
||||
this.window.close();
|
||||
await this.startPlotter();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,26 +0,0 @@
|
||||
export namespace SerialPlotter {
|
||||
export type Config = {
|
||||
currentBaudrate: number;
|
||||
baudrates: number[];
|
||||
currentLineEnding: string;
|
||||
darkTheme: boolean;
|
||||
wsPort: number;
|
||||
interpolate: boolean;
|
||||
serialPort: string;
|
||||
connected: boolean;
|
||||
generate?: boolean;
|
||||
};
|
||||
export namespace Protocol {
|
||||
export enum Command {
|
||||
PLOTTER_SET_BAUDRATE = 'PLOTTER_SET_BAUDRATE',
|
||||
PLOTTER_SET_LINE_ENDING = 'PLOTTER_SET_LINE_ENDING',
|
||||
PLOTTER_SET_INTERPOLATE = 'PLOTTER_SET_INTERPOLATE',
|
||||
PLOTTER_SEND_MESSAGE = 'PLOTTER_SEND_MESSAGE',
|
||||
MIDDLEWARE_CONFIG_CHANGED = 'MIDDLEWARE_CONFIG_CHANGED',
|
||||
}
|
||||
export type Message = {
|
||||
command: SerialPlotter.Protocol.Command;
|
||||
data?: any;
|
||||
};
|
||||
}
|
||||
}
|
@@ -1,360 +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 {
|
||||
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.address
|
||||
),
|
||||
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.address
|
||||
),
|
||||
options
|
||||
);
|
||||
break;
|
||||
}
|
||||
case undefined: {
|
||||
this.messageService.error(
|
||||
nls.localize(
|
||||
'arduino/serial/unexpectedError',
|
||||
'Unexpected error. Reconnecting {0} on port {1}.',
|
||||
Board.toString(board),
|
||||
port.address
|
||||
),
|
||||
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.address
|
||||
)
|
||||
);
|
||||
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.address,
|
||||
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.address}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isSerialConfig(config: Partial<SerialConfig>): config is SerialConfig {
|
||||
return !!config.board && !!config.baudRate && !!config.port;
|
||||
}
|
@@ -1,163 +0,0 @@
|
||||
import { injectable, inject } from 'inversify';
|
||||
import { Emitter, Event } from '@theia/core/lib/common/event';
|
||||
import { SerialConfig } from '../../common/protocol';
|
||||
import {
|
||||
FrontendApplicationContribution,
|
||||
LocalStorageService,
|
||||
} from '@theia/core/lib/browser';
|
||||
import { BoardsServiceProvider } from '../boards/boards-service-provider';
|
||||
|
||||
@injectable()
|
||||
export class SerialModel implements FrontendApplicationContribution {
|
||||
protected static STORAGE_ID = 'arduino-serial-model';
|
||||
|
||||
@inject(LocalStorageService)
|
||||
protected readonly localStorageService: LocalStorageService;
|
||||
|
||||
@inject(BoardsServiceProvider)
|
||||
protected readonly boardsServiceClient: BoardsServiceProvider;
|
||||
|
||||
protected readonly onChangeEmitter: Emitter<
|
||||
SerialModel.State.Change<keyof SerialModel.State>
|
||||
>;
|
||||
protected _autoscroll: boolean;
|
||||
protected _timestamp: boolean;
|
||||
protected _baudRate: SerialConfig.BaudRate;
|
||||
protected _lineEnding: SerialModel.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.onChangeEmitter = new Emitter<
|
||||
SerialModel.State.Change<keyof SerialModel.State>
|
||||
>();
|
||||
}
|
||||
|
||||
onStart(): void {
|
||||
this.localStorageService
|
||||
.getData<SerialModel.State>(SerialModel.STORAGE_ID)
|
||||
.then((state) => {
|
||||
if (state) {
|
||||
this.restoreState(state);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
get onChange(): Event<SerialModel.State.Change<keyof SerialModel.State>> {
|
||||
return this.onChangeEmitter.event;
|
||||
}
|
||||
|
||||
get autoscroll(): boolean {
|
||||
return this._autoscroll;
|
||||
}
|
||||
|
||||
toggleAutoscroll(): void {
|
||||
this._autoscroll = !this._autoscroll;
|
||||
this.storeState();
|
||||
this.storeState().then(() =>
|
||||
this.onChangeEmitter.fire({
|
||||
property: 'autoscroll',
|
||||
value: this._autoscroll,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
get timestamp(): boolean {
|
||||
return this._timestamp;
|
||||
}
|
||||
|
||||
toggleTimestamp(): void {
|
||||
this._timestamp = !this._timestamp;
|
||||
this.storeState().then(() =>
|
||||
this.onChangeEmitter.fire({
|
||||
property: 'timestamp',
|
||||
value: this._timestamp,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
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 {
|
||||
return this._lineEnding;
|
||||
}
|
||||
|
||||
set lineEnding(lineEnding: SerialModel.EOL) {
|
||||
this._lineEnding = lineEnding;
|
||||
this.storeState().then(() =>
|
||||
this.onChangeEmitter.fire({
|
||||
property: 'lineEnding',
|
||||
value: this._lineEnding,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
get interpolate(): boolean {
|
||||
return this._interpolate;
|
||||
}
|
||||
|
||||
set interpolate(i: boolean) {
|
||||
this._interpolate = i;
|
||||
this.storeState().then(() =>
|
||||
this.onChangeEmitter.fire({
|
||||
property: 'interpolate',
|
||||
value: this._interpolate,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
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 {
|
||||
export interface State {
|
||||
autoscroll: boolean;
|
||||
timestamp: boolean;
|
||||
baudRate: SerialConfig.BaudRate;
|
||||
lineEnding: EOL;
|
||||
interpolate: boolean;
|
||||
}
|
||||
export namespace State {
|
||||
export interface Change<K extends keyof State> {
|
||||
readonly property: K;
|
||||
readonly value: State[K];
|
||||
}
|
||||
}
|
||||
|
||||
export type EOL = '' | '\n' | '\r' | '\r\n';
|
||||
export namespace EOL {
|
||||
export const DEFAULT: EOL = '\n';
|
||||
}
|
||||
}
|
@@ -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);
|
||||
}
|
||||
}
|
@@ -1,4 +1,4 @@
|
||||
import { injectable, inject } from 'inversify';
|
||||
import { injectable, inject } from '@theia/core/shared/inversify';
|
||||
import { StorageService } from '@theia/core/lib/browser/storage-service';
|
||||
import {
|
||||
Command,
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user