mirror of
https://github.com/arduino/arduino-ide.git
synced 2025-10-14 05:48:31 +00:00
Compare commits
43 Commits
armv7-sign
...
2.0.3
Author | SHA1 | Date | |
---|---|---|---|
![]() |
1104467329 | ||
![]() |
5695fd8afb | ||
![]() |
d0e383853f | ||
![]() |
3bc412b42f | ||
![]() |
f553d6919d | ||
![]() |
d6a4b0f910 | ||
![]() |
c0488d1f64 | ||
![]() |
81195431b0 | ||
![]() |
87109e6559 | ||
![]() |
c0af1e62e8 | ||
![]() |
ac9cce16f7 | ||
![]() |
3ad660927f | ||
![]() |
8778d70ad7 | ||
![]() |
fe3fbb189c | ||
![]() |
23c7f5f848 | ||
![]() |
f1144efb93 | ||
![]() |
9cec643cab | ||
![]() |
1a7784a540 | ||
![]() |
d24a3911f8 | ||
![]() |
3735553003 | ||
![]() |
f6d112e1f6 | ||
![]() |
cc2d557706 | ||
![]() |
103acc4b7e | ||
![]() |
c3dc7c6307 | ||
![]() |
7d6a2d5e33 | ||
![]() |
6984c52b92 | ||
![]() |
3a70547770 | ||
![]() |
8a85b5c3d8 | ||
![]() |
b998d35524 | ||
![]() |
ddec64c4a5 | ||
![]() |
8fed08003e | ||
![]() |
8454c625f7 | ||
![]() |
60df322f09 | ||
![]() |
8bfb140e7c | ||
![]() |
260227e79a | ||
![]() |
cc310bf1a5 | ||
![]() |
dbd52e2f34 | ||
![]() |
9cd03bec46 | ||
![]() |
c29452a858 | ||
![]() |
7d91f2d8cb | ||
![]() |
f6275f9f62 | ||
![]() |
0d0550974a | ||
![]() |
4e882d25d9 |
4
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
4
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
@@ -30,7 +30,7 @@ body:
|
||||
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).
|
||||
This should be the latest [nightly build](https://www.arduino.cc/en/software#nightly-builds).
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
@@ -68,7 +68,7 @@ body:
|
||||
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)
|
||||
- label: I verified the problem still occurs when using the latest [nightly build](https://www.arduino.cc/en/software#nightly-builds)
|
||||
required: true
|
||||
- label: My report contains all necessary details
|
||||
required: true
|
||||
|
4
.github/ISSUE_TEMPLATE/feature-request.yml
vendored
4
.github/ISSUE_TEMPLATE/feature-request.yml
vendored
@@ -25,7 +25,7 @@ body:
|
||||
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).
|
||||
This should be the latest [nightly build](https://www.arduino.cc/en/software#nightly-builds).
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
@@ -63,7 +63,7 @@ body:
|
||||
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)
|
||||
- label: I verified the feature was still missing when using the latest [nightly build](https://www.arduino.cc/en/software#nightly-builds)
|
||||
required: true
|
||||
- label: My request contains all necessary details
|
||||
required: true
|
||||
|
15
.github/dependabot.yml
vendored
Normal file
15
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
# See: https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#about-the-dependabotyml-file
|
||||
version: 2
|
||||
|
||||
updates:
|
||||
# Configure check for outdated GitHub Actions actions in workflows.
|
||||
# Source: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/assets/dependabot/README.md
|
||||
# See: https://docs.github.com/code-security/dependabot/working-with-dependabot/keeping-your-actions-up-to-date-with-dependabot
|
||||
- package-ecosystem: github-actions
|
||||
directory: / # Check the repository's workflows under /.github/workflows/
|
||||
assignees:
|
||||
- per1234
|
||||
schedule:
|
||||
interval: daily
|
||||
labels:
|
||||
- "topic: infrastructure"
|
131
.github/tools/fetch_athena_stats.py
vendored
131
.github/tools/fetch_athena_stats.py
vendored
@@ -1,131 +0,0 @@
|
||||
import boto3
|
||||
import semver
|
||||
import os
|
||||
import logging
|
||||
import uuid
|
||||
import time
|
||||
|
||||
|
||||
# logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)
|
||||
log = logging.getLogger()
|
||||
logging.getLogger("boto3").setLevel(logging.CRITICAL)
|
||||
logging.getLogger("botocore").setLevel(logging.CRITICAL)
|
||||
logging.getLogger("urllib3").setLevel(logging.CRITICAL)
|
||||
|
||||
|
||||
def execute(client, statement, dest_s3_output_location):
|
||||
log.info("execute query: {} dumping in {}".format(statement, dest_s3_output_location))
|
||||
result = client.start_query_execution(
|
||||
QueryString=statement,
|
||||
ClientRequestToken=str(uuid.uuid4()),
|
||||
ResultConfiguration={
|
||||
"OutputLocation": dest_s3_output_location,
|
||||
},
|
||||
)
|
||||
execution_id = result["QueryExecutionId"]
|
||||
log.info("wait for query {} completion".format(execution_id))
|
||||
wait_for_query_execution_completion(client, execution_id)
|
||||
log.info("operation successful")
|
||||
return execution_id
|
||||
|
||||
|
||||
def wait_for_query_execution_completion(client, query_execution_id):
|
||||
query_ended = False
|
||||
while not query_ended:
|
||||
query_execution = client.get_query_execution(QueryExecutionId=query_execution_id)
|
||||
state = query_execution["QueryExecution"]["Status"]["State"]
|
||||
if state == "SUCCEEDED":
|
||||
query_ended = True
|
||||
elif state in ["FAILED", "CANCELLED"]:
|
||||
raise BaseException(
|
||||
"query failed or canceled: {}".format(query_execution["QueryExecution"]["Status"]["StateChangeReason"])
|
||||
)
|
||||
else:
|
||||
time.sleep(1)
|
||||
|
||||
|
||||
def valid(key):
|
||||
split = key.split("_")
|
||||
if len(split) < 1:
|
||||
return False
|
||||
try:
|
||||
semver.parse(split[0])
|
||||
except ValueError:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def get_results(client, execution_id):
|
||||
results_paginator = client.get_paginator("get_query_results")
|
||||
results_iter = results_paginator.paginate(QueryExecutionId=execution_id, PaginationConfig={"PageSize": 1000})
|
||||
res = {}
|
||||
for results_page in results_iter:
|
||||
for row in results_page["ResultSet"]["Rows"][1:]:
|
||||
# Loop through the JSON objects
|
||||
key = row["Data"][0]["VarCharValue"]
|
||||
if valid(key):
|
||||
res[key] = row["Data"][1]["VarCharValue"]
|
||||
|
||||
return res
|
||||
|
||||
|
||||
def convert_data(data):
|
||||
result = []
|
||||
for key, value in data.items():
|
||||
# 0.18.0_macOS_64bit.tar.gz
|
||||
split_key = key.split("_")
|
||||
if len(split_key) != 3:
|
||||
continue
|
||||
(version, os_version, arch) = split_key
|
||||
arch_split = arch.split(".")
|
||||
if len(arch_split) < 1:
|
||||
continue
|
||||
arch = arch_split[0]
|
||||
if len(arch) > 10:
|
||||
# This can't be an architecture really.
|
||||
# It's an ugly solution but works for now so deal with it.
|
||||
continue
|
||||
repo = os.environ["GITHUB_REPOSITORY"].split("/")[1]
|
||||
result.append(
|
||||
{
|
||||
"type": "gauge",
|
||||
"name": "arduino.downloads.total",
|
||||
"value": value,
|
||||
"host": os.environ["GITHUB_REPOSITORY"],
|
||||
"tags": [
|
||||
f"version:{version}",
|
||||
f"os:{os_version}",
|
||||
f"arch:{arch}",
|
||||
"cdn:downloads.arduino.cc",
|
||||
f"project:{repo}",
|
||||
],
|
||||
}
|
||||
)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
DEST_S3_OUTPUT = os.environ["AWS_ATHENA_OUTPUT_LOCATION"]
|
||||
AWS_ATHENA_SOURCE_TABLE = os.environ["AWS_ATHENA_SOURCE_TABLE"]
|
||||
|
||||
session = boto3.session.Session(region_name="us-east-1")
|
||||
athena_client = session.client("athena")
|
||||
|
||||
# Load all partitions before querying downloads
|
||||
execute(athena_client, f"MSCK REPAIR TABLE {AWS_ATHENA_SOURCE_TABLE};", DEST_S3_OUTPUT)
|
||||
|
||||
query = f"""SELECT replace(json_extract_scalar(url_decode(url_decode(querystring)),
|
||||
'$.data.url'), 'https://downloads.arduino.cc/arduino-ide/arduino-ide_', '')
|
||||
AS flavor, count(json_extract(url_decode(url_decode(querystring)),'$')) AS gauge
|
||||
FROM {AWS_ATHENA_SOURCE_TABLE}
|
||||
WHERE json_extract_scalar(url_decode(url_decode(querystring)),'$.data.url')
|
||||
LIKE 'https://downloads.arduino.cc/arduino-ide/arduino-ide_%'
|
||||
AND json_extract_scalar(url_decode(url_decode(querystring)),'$.data.url')
|
||||
NOT LIKE '%latest%' -- exclude latest redirect
|
||||
group by 1 ;"""
|
||||
exec_id = execute(athena_client, query, DEST_S3_OUTPUT)
|
||||
results = get_results(athena_client, exec_id)
|
||||
result_json = convert_data(results)
|
||||
|
||||
print(f"::set-output name=result::{result_json}")
|
57
.github/workflows/arduino-stats.yaml
vendored
57
.github/workflows/arduino-stats.yaml
vendored
@@ -1,57 +0,0 @@
|
||||
name: arduino-stats
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# run every day at 07:00 AM, 03:00 PM and 11:00 PM
|
||||
- cron: "0 7,15,23 * * *"
|
||||
workflow_dispatch:
|
||||
repository_dispatch:
|
||||
|
||||
jobs:
|
||||
push-stats:
|
||||
# This workflow is only of value to the arduino/arduino-ide repository and
|
||||
# would always fail in forks
|
||||
if: github.repository == 'arduino/arduino-ide'
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.x'
|
||||
|
||||
- name: Fetch downloads count form Arduino CDN using AWS Athena
|
||||
id: fetch
|
||||
env:
|
||||
AWS_ACCESS_KEY_ID: ${{ secrets.STATS_AWS_ACCESS_KEY_ID }}
|
||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.STATS_AWS_SECRET_ACCESS_KEY }}
|
||||
AWS_ATHENA_SOURCE_TABLE: ${{ secrets.STATS_AWS_ATHENA_SOURCE_TABLE }}
|
||||
AWS_ATHENA_OUTPUT_LOCATION: ${{ secrets.STATS_AWS_ATHENA_OUTPUT_LOCATION }}
|
||||
GITHUB_REPOSITORY: ${{ github.repository }}
|
||||
run: |
|
||||
pip install boto3 semver
|
||||
python .github/tools/fetch_athena_stats.py
|
||||
|
||||
- name: Send metrics
|
||||
uses: masci/datadog@v1
|
||||
with:
|
||||
api-key: ${{ secrets.DD_API_KEY }}
|
||||
# Metrics input expects YAML but JSON will work just right.
|
||||
metrics: ${{steps.fetch.outputs.result}}
|
||||
|
||||
- name: Report failure
|
||||
if: failure()
|
||||
uses: masci/datadog@v1
|
||||
with:
|
||||
api-key: ${{ secrets.DD_API_KEY }}
|
||||
events: |
|
||||
- title: "Arduino IDE stats failing"
|
||||
text: "Stats collection failed"
|
||||
alert_type: "error"
|
||||
host: ${{ github.repository }}
|
||||
tags:
|
||||
- "project:arduino-ide"
|
||||
- "cdn:downloads.arduino.cc"
|
||||
- "workflow:${{ github.workflow }}"
|
32
.github/workflows/build.yml
vendored
32
.github/workflows/build.yml
vendored
@@ -55,16 +55,16 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install Node.js 14.x
|
||||
uses: actions/setup-node@v1
|
||||
- name: Install Node.js 16.x
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '14.x'
|
||||
node-version: '16.x'
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
|
||||
- name: Install Python 3.x
|
||||
uses: actions/setup-python@v2
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.x'
|
||||
|
||||
@@ -109,7 +109,7 @@ jobs:
|
||||
yarn --cwd ./electron/packager/ package
|
||||
|
||||
- name: Upload [GitHub Actions]
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ${{ env.JOB_TRANSFER_ARTIFACT }}
|
||||
path: electron/build/dist/build-artifacts/
|
||||
@@ -140,13 +140,13 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Download job transfer artifact
|
||||
uses: actions/download-artifact@v2
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: ${{ env.JOB_TRANSFER_ARTIFACT }}
|
||||
path: ${{ env.JOB_TRANSFER_ARTIFACT }}
|
||||
|
||||
- name: Upload tester build artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ${{ matrix.artifact.name }}
|
||||
path: ${{ env.JOB_TRANSFER_ARTIFACT }}/${{ matrix.artifact.path }}
|
||||
@@ -158,7 +158,7 @@ jobs:
|
||||
BODY: ${{ steps.changelog.outputs.BODY }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0 # To fetch all history for all branches and tags.
|
||||
|
||||
@@ -183,12 +183,12 @@ jobs:
|
||||
OUTPUT_SAFE_BODY="${BODY//'%'/'%25'}"
|
||||
OUTPUT_SAFE_BODY="${OUTPUT_SAFE_BODY//$'\n'/'%0A'}"
|
||||
OUTPUT_SAFE_BODY="${OUTPUT_SAFE_BODY//$'\r'/'%0D'}"
|
||||
echo "::set-output name=BODY::$OUTPUT_SAFE_BODY"
|
||||
echo "BODY=$OUTPUT_SAFE_BODY" >> $GITHUB_OUTPUT
|
||||
echo "$BODY" > CHANGELOG.txt
|
||||
|
||||
- name: Upload Changelog [GitHub Actions]
|
||||
if: github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && github.ref == 'refs/heads/main')
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ${{ env.JOB_TRANSFER_ARTIFACT }}
|
||||
path: CHANGELOG.txt
|
||||
@@ -199,7 +199,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Download [GitHub Actions]
|
||||
uses: actions/download-artifact@v2
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: ${{ env.JOB_TRANSFER_ARTIFACT }}
|
||||
path: ${{ env.JOB_TRANSFER_ARTIFACT }}
|
||||
@@ -220,7 +220,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Download [GitHub Actions]
|
||||
uses: actions/download-artifact@v2
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: ${{ env.JOB_TRANSFER_ARTIFACT }}
|
||||
path: ${{ env.JOB_TRANSFER_ARTIFACT }}
|
||||
@@ -228,10 +228,10 @@ jobs:
|
||||
- name: Get Tag
|
||||
id: tag_name
|
||||
run: |
|
||||
echo ::set-output name=TAG_NAME::${GITHUB_REF#refs/tags/}
|
||||
echo "TAG_NAME=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Publish Release [GitHub]
|
||||
uses: svenstaro/upload-release-action@2.2.0
|
||||
uses: svenstaro/upload-release-action@2.3.0
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
release_name: ${{ steps.tag_name.outputs.TAG_NAME }}
|
||||
@@ -263,6 +263,6 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Remove unneeded job transfer artifact
|
||||
uses: geekyeggo/delete-artifact@v1
|
||||
uses: geekyeggo/delete-artifact@v2
|
||||
with:
|
||||
name: ${{ env.JOB_TRANSFER_ARTIFACT }}
|
||||
|
2
.github/workflows/check-certificates.yml
vendored
2
.github/workflows/check-certificates.yml
vendored
@@ -108,7 +108,7 @@ jobs:
|
||||
echo "Certificate expiration date: $EXPIRATION_DATE"
|
||||
echo "Days remaining before expiration: $DAYS_BEFORE_EXPIRATION"
|
||||
|
||||
echo "::set-output name=days::$DAYS_BEFORE_EXPIRATION"
|
||||
echo "days=$DAYS_BEFORE_EXPIRATION" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Check if expiration notification period has been reached
|
||||
id: check-expiration
|
||||
|
8
.github/workflows/check-i18n-task.yml
vendored
8
.github/workflows/check-i18n-task.yml
vendored
@@ -27,12 +27,12 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install Node.js 14.x
|
||||
uses: actions/setup-node@v2
|
||||
- name: Install Node.js 16.x
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '14.x'
|
||||
node-version: '16.x'
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
|
||||
- name: Install Go
|
||||
|
@@ -8,7 +8,7 @@ on:
|
||||
env:
|
||||
CHANGELOG_ARTIFACTS: changelog
|
||||
# See: https://github.com/actions/setup-node/#readme
|
||||
NODE_VERSION: 14.x
|
||||
NODE_VERSION: 16.x
|
||||
|
||||
jobs:
|
||||
create-changelog:
|
||||
@@ -16,7 +16,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v3
|
||||
@@ -27,7 +27,7 @@ jobs:
|
||||
- name: Get Tag
|
||||
id: tag_name
|
||||
run: |
|
||||
echo ::set-output name=TAG_NAME::${GITHUB_REF#refs/tags/}
|
||||
echo "TAG_NAME=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Create full changelog
|
||||
id: full-changelog
|
96
.github/workflows/github-stats.yaml
vendored
96
.github/workflows/github-stats.yaml
vendored
@@ -1,96 +0,0 @@
|
||||
name: github-stats
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# run every 30 minutes
|
||||
- cron: "*/30 * * * *"
|
||||
workflow_dispatch:
|
||||
repository_dispatch:
|
||||
|
||||
jobs:
|
||||
push-stats:
|
||||
# This workflow is only of value to the arduino/arduino-ide repository and
|
||||
# would always fail in forks
|
||||
if: github.repository == 'arduino/arduino-ide'
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Fetch downloads count
|
||||
id: fetch
|
||||
uses: actions/github-script@v4
|
||||
with:
|
||||
github-token: ${{github.token}}
|
||||
script: |
|
||||
let metrics = []
|
||||
|
||||
// Get a list of releases
|
||||
const opts = github.repos.listReleases.endpoint.merge({
|
||||
...context.repo
|
||||
})
|
||||
const releases = await github.paginate(opts)
|
||||
|
||||
// Get download stats for every release
|
||||
for (const rel of releases) {
|
||||
// Names for assets are like `arduino-ide_2.0.0-beta.12_Linux_64bit.zip`,
|
||||
// we'll use this later to split the asset file name more easily
|
||||
const baseName = `arduino-ide_${rel.name}_`
|
||||
|
||||
// Get a list of assets for this release
|
||||
const opts = github.repos.listReleaseAssets.endpoint.merge({
|
||||
...context.repo,
|
||||
release_id: rel.id
|
||||
})
|
||||
const assets = await github.paginate(opts)
|
||||
|
||||
for (const asset of assets) {
|
||||
// Ignore files that are not arduino-ide packages
|
||||
if (!asset.name.startsWith(baseName)) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Strip the base and remove file extension to get `Linux_32bit`
|
||||
systemArch = asset.name.replace(baseName, "").split(".")[0].split("_")
|
||||
|
||||
// Add a metric object to the list of gathered metrics
|
||||
metrics.push({
|
||||
"type": "gauge",
|
||||
"name": "arduino.downloads.total",
|
||||
"value": asset.download_count,
|
||||
"host": "${{ github.repository }}",
|
||||
"tags": [
|
||||
`version:${rel.name}`,
|
||||
`os:${systemArch[0]}`,
|
||||
`arch:${systemArch[1]}`,
|
||||
"cdn:github.com",
|
||||
"project:arduino-ide"
|
||||
]
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// The action will put whatever we return from this function in
|
||||
// `outputs.result`, JSON encoded. So we just return the array
|
||||
// of objects and GitHub will do the rest.
|
||||
return metrics
|
||||
|
||||
- name: Send metrics
|
||||
uses: masci/datadog@v1
|
||||
with:
|
||||
api-key: ${{ secrets.DD_API_KEY }}
|
||||
# Metrics input expects YAML but JSON will work just right.
|
||||
metrics: ${{steps.fetch.outputs.result}}
|
||||
|
||||
- name: Report failure
|
||||
if: failure()
|
||||
uses: masci/datadog@v1
|
||||
with:
|
||||
api-key: ${{ secrets.DD_API_KEY }}
|
||||
events: |
|
||||
- title: "Arduino IDE stats failing"
|
||||
text: "Stats collection failed"
|
||||
alert_type: "error"
|
||||
host: ${{ github.repository }}
|
||||
tags:
|
||||
- "project:arduino-ide"
|
||||
- "cdn:github.com"
|
||||
- "workflow:${{ github.workflow }}"
|
8
.github/workflows/i18n-nightly-push.yml
vendored
8
.github/workflows/i18n-nightly-push.yml
vendored
@@ -14,12 +14,12 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install Node.js 14.x
|
||||
uses: actions/setup-node@v2
|
||||
- name: Install Node.js 16.x
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '14.x'
|
||||
node-version: '16.x'
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
|
||||
- name: Install Go
|
||||
|
10
.github/workflows/i18n-weekly-pull.yml
vendored
10
.github/workflows/i18n-weekly-pull.yml
vendored
@@ -14,12 +14,12 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install Node.js 14.x
|
||||
uses: actions/setup-node@v2
|
||||
- name: Install Node.js 16.x
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '14.x'
|
||||
node-version: '16.x'
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
|
||||
- name: Install Go
|
||||
@@ -45,7 +45,7 @@ jobs:
|
||||
TRANSIFEX_API_KEY: ${{ secrets.TRANSIFEX_API_KEY }}
|
||||
|
||||
- name: Create Pull Request
|
||||
uses: peter-evans/create-pull-request@v3
|
||||
uses: peter-evans/create-pull-request@v4
|
||||
with:
|
||||
commit-message: Updated translation files
|
||||
title: Update translation files
|
||||
|
16
.github/workflows/sync-labels.yml
vendored
16
.github/workflows/sync-labels.yml
vendored
@@ -27,11 +27,11 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Download JSON schema for labels configuration file
|
||||
id: download-schema
|
||||
uses: carlosperate/download-file-action@v1
|
||||
uses: carlosperate/download-file-action@v2
|
||||
with:
|
||||
file-url: https://raw.githubusercontent.com/arduino/tooling-project-assets/main/workflow-templates/assets/sync-labels/arduino-tooling-gh-label-configuration-schema.json
|
||||
location: ${{ runner.temp }}/label-configuration-schema
|
||||
@@ -66,12 +66,12 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Download
|
||||
uses: carlosperate/download-file-action@v1
|
||||
uses: carlosperate/download-file-action@v2
|
||||
with:
|
||||
file-url: https://raw.githubusercontent.com/arduino/tooling-project-assets/main/workflow-templates/assets/sync-labels/${{ matrix.filename }}
|
||||
|
||||
- name: Pass configuration files to next job via workflow artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
path: |
|
||||
*.yaml
|
||||
@@ -103,19 +103,19 @@ jobs:
|
||||
run: |
|
||||
# Use of this flag in the github-label-sync command will cause it to only check the validity of the
|
||||
# configuration.
|
||||
echo "::set-output name=flag::--dry-run"
|
||||
echo "flag=--dry-run" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Download configuration files artifact
|
||||
uses: actions/download-artifact@v2
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: ${{ env.CONFIGURATIONS_ARTIFACT }}
|
||||
path: ${{ env.CONFIGURATIONS_FOLDER }}
|
||||
|
||||
- name: Remove unneeded artifact
|
||||
uses: geekyeggo/delete-artifact@v1
|
||||
uses: geekyeggo/delete-artifact@v2
|
||||
with:
|
||||
name: ${{ env.CONFIGURATIONS_ARTIFACT }}
|
||||
|
||||
|
2
.github/workflows/themes-weekly-pull.yml
vendored
2
.github/workflows/themes-weekly-pull.yml
vendored
@@ -9,7 +9,7 @@ on:
|
||||
env:
|
||||
# See vars.GO_VERSION field of https://github.com/arduino/arduino-cli/blob/master/DistTasks.yml
|
||||
GO_VERSION: "1.17"
|
||||
NODE_VERSION: 14.x
|
||||
NODE_VERSION: 16.x
|
||||
|
||||
jobs:
|
||||
pull-from-jsonbin:
|
||||
|
4
.gitignore
vendored
4
.gitignore
vendored
@@ -7,7 +7,7 @@ build/
|
||||
Examples/
|
||||
!electron/build/
|
||||
src-gen/
|
||||
!webpack.config.js
|
||||
webpack.config.js
|
||||
gen-webpack.config.js
|
||||
.DS_Store
|
||||
# switching from `electron` to `browser` in dev mode.
|
||||
@@ -15,8 +15,6 @@ gen-webpack.config.js
|
||||
yarn*.log
|
||||
# For the VS Code extensions used by Theia.
|
||||
plugins
|
||||
# the config files for the CLI
|
||||
arduino-ide-extension/data/cli/config
|
||||
# the tokens folder for the themes
|
||||
scripts/themes/tokens
|
||||
# environment variables
|
||||
|
6
.vscode/launch.json
vendored
6
.vscode/launch.json
vendored
@@ -21,7 +21,8 @@
|
||||
"--plugins=local-dir:../plugins",
|
||||
"--hosted-plugin-inspect=9339",
|
||||
"--content-trace",
|
||||
"--open-devtools"
|
||||
"--open-devtools",
|
||||
"--no-ping-timeout",
|
||||
],
|
||||
"env": {
|
||||
"NODE_ENV": "development"
|
||||
@@ -56,7 +57,8 @@
|
||||
"--remote-debugging-port=9222",
|
||||
"--no-app-auto-install",
|
||||
"--plugins=local-dir:../plugins",
|
||||
"--hosted-plugin-inspect=9339"
|
||||
"--hosted-plugin-inspect=9339",
|
||||
"--no-ping-timeout",
|
||||
],
|
||||
"env": {
|
||||
"NODE_ENV": "development"
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "arduino-ide-extension",
|
||||
"version": "2.0.1",
|
||||
"version": "2.0.3",
|
||||
"description": "An extension for Theia building the Arduino IDE",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"scripts": {
|
||||
@@ -21,79 +21,83 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@grpc/grpc-js": "^1.6.7",
|
||||
"@theia/application-package": "1.25.0",
|
||||
"@theia/core": "1.25.0",
|
||||
"@theia/editor": "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",
|
||||
"@theia/application-package": "1.31.1",
|
||||
"@theia/core": "1.31.1",
|
||||
"@theia/debug": "1.31.1",
|
||||
"@theia/editor": "1.31.1",
|
||||
"@theia/electron": "1.31.1",
|
||||
"@theia/filesystem": "1.31.1",
|
||||
"@theia/keymaps": "1.31.1",
|
||||
"@theia/markers": "1.31.1",
|
||||
"@theia/messages": "1.31.1",
|
||||
"@theia/monaco": "1.31.1",
|
||||
"@theia/monaco-editor-core": "1.67.2",
|
||||
"@theia/navigator": "1.31.1",
|
||||
"@theia/outline-view": "1.31.1",
|
||||
"@theia/output": "1.31.1",
|
||||
"@theia/plugin-ext": "1.31.1",
|
||||
"@theia/preferences": "1.31.1",
|
||||
"@theia/scm": "1.31.1",
|
||||
"@theia/search-in-workspace": "1.31.1",
|
||||
"@theia/terminal": "1.31.1",
|
||||
"@theia/typehierarchy": "1.31.1",
|
||||
"@theia/workspace": "1.31.1",
|
||||
"@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/deep-equal": "^1.0.1",
|
||||
"@types/deepmerge": "^2.2.0",
|
||||
"@types/glob": "^7.2.0",
|
||||
"@types/google-protobuf": "^3.7.2",
|
||||
"@types/is-valid-path": "^0.1.0",
|
||||
"@types/js-yaml": "^3.12.2",
|
||||
"@types/keytar": "^4.4.0",
|
||||
"@types/lodash.debounce": "^4.0.6",
|
||||
"@types/ncp": "^2.0.4",
|
||||
"@types/node-fetch": "^2.5.7",
|
||||
"@types/p-queue": "^2.3.1",
|
||||
"@types/ps-tree": "^1.1.0",
|
||||
"@types/react-select": "^3.0.0",
|
||||
"@types/react-tabs": "^2.3.2",
|
||||
"@types/react-virtualized": "^9.21.21",
|
||||
"@types/temp": "^0.8.34",
|
||||
"@types/which": "^1.3.1",
|
||||
"ajv": "^6.5.3",
|
||||
"@vscode/debugprotocol": "^1.51.0",
|
||||
"arduino-serial-plotter-webapp": "0.2.0",
|
||||
"async-mutex": "^0.3.0",
|
||||
"atob": "^2.1.2",
|
||||
"auth0-js": "^9.14.0",
|
||||
"btoa": "^1.2.1",
|
||||
"classnames": "^2.3.1",
|
||||
"dateformat": "^3.0.3",
|
||||
"deep-equal": "^2.0.5",
|
||||
"deepmerge": "2.0.1",
|
||||
"electron-updater": "^4.6.5",
|
||||
"fast-safe-stringify": "^2.1.1",
|
||||
"glob": "^7.1.6",
|
||||
"google-protobuf": "^3.20.1",
|
||||
"hash.js": "^1.1.7",
|
||||
"is-valid-path": "^0.1.1",
|
||||
"js-yaml": "^3.13.1",
|
||||
"just-diff": "^5.1.1",
|
||||
"jwt-decode": "^3.1.2",
|
||||
"keytar": "7.2.0",
|
||||
"lodash.debounce": "^4.0.8",
|
||||
"minimatch": "^3.1.2",
|
||||
"ncp": "^2.0.0",
|
||||
"node-fetch": "^2.6.1",
|
||||
"open": "^8.0.6",
|
||||
"p-queue": "^5.0.0",
|
||||
"p-debounce": "^2.1.0",
|
||||
"p-queue": "^2.4.2",
|
||||
"ps-tree": "^1.2.0",
|
||||
"query-string": "^7.0.1",
|
||||
"react-disable": "^0.1.0",
|
||||
"react-disable": "^0.1.1",
|
||||
"react-markdown": "^8.0.0",
|
||||
"react-select": "^3.0.4",
|
||||
"react-perfect-scrollbar": "^1.5.8",
|
||||
"react-select": "^5.6.0",
|
||||
"react-tabs": "^3.1.2",
|
||||
"react-virtualized": "^9.22.3",
|
||||
"react-window": "^1.8.6",
|
||||
"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",
|
||||
"which": "^1.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -102,11 +106,10 @@
|
||||
"@types/chai-string": "^1.4.2",
|
||||
"@types/mocha": "^5.2.7",
|
||||
"@types/react-window": "^1.8.5",
|
||||
"@types/sinon": "^10.0.6",
|
||||
"@types/sinon-chai": "^3.2.6",
|
||||
"chai": "^4.2.0",
|
||||
"chai-string": "^1.5.0",
|
||||
"decompress": "^4.2.0",
|
||||
"decompress-tarbz2": "^4.1.1",
|
||||
"decompress-targz": "^4.1.1",
|
||||
"decompress-unzip": "^4.0.1",
|
||||
"download": "^7.1.0",
|
||||
@@ -116,9 +119,6 @@
|
||||
"moment": "^2.24.0",
|
||||
"protoc": "^1.0.4",
|
||||
"shelljs": "^0.8.3",
|
||||
"sinon": "^12.0.1",
|
||||
"sinon-chai": "^3.7.0",
|
||||
"typemoq": "^2.1.0",
|
||||
"uuid": "^3.2.1",
|
||||
"yargs": "^11.1.0"
|
||||
},
|
||||
@@ -159,16 +159,16 @@
|
||||
],
|
||||
"arduino": {
|
||||
"cli": {
|
||||
"version": "0.28.0"
|
||||
"version": "0.29.0"
|
||||
},
|
||||
"fwuploader": {
|
||||
"version": "2.2.0"
|
||||
"version": "2.2.2"
|
||||
},
|
||||
"clangd": {
|
||||
"version": "14.0.0"
|
||||
},
|
||||
"languageServer": {
|
||||
"version": "0.7.1"
|
||||
"version": "0.7.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -42,6 +42,9 @@
|
||||
const suffix = (() => {
|
||||
switch (platform) {
|
||||
case 'darwin':
|
||||
if (arch === 'arm64') {
|
||||
return 'macOS_ARM64.tar.gz';
|
||||
}
|
||||
return 'macOS_64bit.tar.gz';
|
||||
case 'win32':
|
||||
return 'Windows_64bit.zip';
|
||||
|
@@ -76,6 +76,12 @@
|
||||
lsSuffix = 'macOS_64bit.tar.gz';
|
||||
clangdSuffix = 'macOS_64bit';
|
||||
break;
|
||||
case 'darwin-arm64':
|
||||
clangdExecutablePath = path.join(build, 'clangd');
|
||||
clangFormatExecutablePath = path.join(build, 'clang-format');
|
||||
lsSuffix = 'macOS_ARM64.tar.gz';
|
||||
clangdSuffix = 'macOS_ARM64';
|
||||
break;
|
||||
case 'linux-x64':
|
||||
clangdExecutablePath = path.join(build, 'clangd');
|
||||
clangFormatExecutablePath = path.join(build, 'clang-format');
|
||||
|
@@ -31,7 +31,7 @@ import { EditorCommands, EditorMainMenu } from '@theia/editor/lib/browser';
|
||||
import { MonacoMenus } from '@theia/monaco/lib/browser/monaco-menu';
|
||||
import { FileNavigatorCommands } from '@theia/navigator/lib/browser/navigator-contribution';
|
||||
import { TerminalMenus } from '@theia/terminal/lib/browser/terminal-frontend-contribution';
|
||||
import { ArduinoPreferences } from './arduino-preferences';
|
||||
import { ElectronWindowPreferences } from '@theia/core/lib/electron-browser/window/electron-window-preferences';
|
||||
import { BoardsServiceProvider } from './boards/boards-service-provider';
|
||||
import { BoardsToolBarItem } from './boards/boards-toolbar-item';
|
||||
import { ArduinoMenus } from './menu/arduino-menus';
|
||||
@@ -58,8 +58,8 @@ export class ArduinoFrontendContribution
|
||||
@inject(CommandRegistry)
|
||||
private readonly commandRegistry: CommandRegistry;
|
||||
|
||||
@inject(ArduinoPreferences)
|
||||
private readonly arduinoPreferences: ArduinoPreferences;
|
||||
@inject(ElectronWindowPreferences)
|
||||
private readonly electronWindowPreferences: ElectronWindowPreferences;
|
||||
|
||||
@inject(FrontendApplicationStateService)
|
||||
private readonly appStateService: FrontendApplicationStateService;
|
||||
@@ -78,10 +78,10 @@ export class ArduinoFrontendContribution
|
||||
}
|
||||
|
||||
onStart(app: FrontendApplication): void {
|
||||
this.arduinoPreferences.onPreferenceChanged((event) => {
|
||||
this.electronWindowPreferences.onPreferenceChanged((event) => {
|
||||
if (event.newValue !== event.oldValue) {
|
||||
switch (event.preferenceName) {
|
||||
case 'arduino.window.zoomLevel':
|
||||
case 'window.zoomLevel':
|
||||
if (typeof event.newValue === 'number') {
|
||||
const webContents = remote.getCurrentWebContents();
|
||||
webContents.setZoomLevel(event.newValue || 0);
|
||||
@@ -91,11 +91,10 @@ export class ArduinoFrontendContribution
|
||||
}
|
||||
});
|
||||
this.appStateService.reachedState('ready').then(() =>
|
||||
this.arduinoPreferences.ready.then(() => {
|
||||
this.electronWindowPreferences.ready.then(() => {
|
||||
const webContents = remote.getCurrentWebContents();
|
||||
const zoomLevel = this.arduinoPreferences.get(
|
||||
'arduino.window.zoomLevel'
|
||||
);
|
||||
const zoomLevel =
|
||||
this.electronWindowPreferences.get('window.zoomLevel');
|
||||
webContents.setZoomLevel(zoomLevel);
|
||||
})
|
||||
);
|
||||
|
@@ -1,12 +1,9 @@
|
||||
import '../../src/browser/style/index.css';
|
||||
import { ContainerModule } from '@theia/core/shared/inversify';
|
||||
import { Container, ContainerModule } from '@theia/core/shared/inversify';
|
||||
import { WidgetFactory } from '@theia/core/lib/browser/widget-manager';
|
||||
import { CommandContribution } from '@theia/core/lib/common/command';
|
||||
import { bindViewContribution } from '@theia/core/lib/browser/shell/view-contribution';
|
||||
import {
|
||||
TabBarToolbarContribution,
|
||||
TabBarToolbarFactory,
|
||||
} from '@theia/core/lib/browser/shell/tab-bar-toolbar';
|
||||
import { TabBarToolbarContribution } from '@theia/core/lib/browser/shell/tab-bar-toolbar';
|
||||
import { WebSocketConnectionProvider } from '@theia/core/lib/browser/messaging/ws-connection-provider';
|
||||
import {
|
||||
FrontendApplicationContribution,
|
||||
@@ -84,10 +81,7 @@ import { BoardsAutoInstaller } from './boards/boards-auto-installer';
|
||||
import { ShellLayoutRestorer } from './theia/core/shell-layout-restorer';
|
||||
import { ListItemRenderer } from './widgets/component-list/list-item-renderer';
|
||||
import { ColorContribution } from '@theia/core/lib/browser/color-application-contribution';
|
||||
import {
|
||||
MonacoThemeJson,
|
||||
MonacoThemingService,
|
||||
} from '@theia/monaco/lib/browser/monaco-theming-service';
|
||||
|
||||
import {
|
||||
ArduinoDaemonPath,
|
||||
ArduinoDaemon,
|
||||
@@ -137,7 +131,6 @@ import { Settings } from './contributions/settings';
|
||||
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';
|
||||
import { TabBarToolbar } from './theia/core/tab-bar-toolbar';
|
||||
import { EditorWidgetFactory as TheiaEditorWidgetFactory } from '@theia/editor/lib/browser/editor-widget-factory';
|
||||
import { EditorWidgetFactory } from './theia/editor/editor-widget-factory';
|
||||
import { BurnBootloader } from './contributions/burn-bootloader';
|
||||
@@ -181,8 +174,6 @@ import { EditorCommandContribution } from './theia/editor/editor-command';
|
||||
import { NavigatorTabBarDecorator as TheiaNavigatorTabBarDecorator } from '@theia/navigator/lib/browser/navigator-tab-bar-decorator';
|
||||
import { NavigatorTabBarDecorator } from './theia/navigator/navigator-tab-bar-decorator';
|
||||
import { Debug } from './contributions/debug';
|
||||
import { DebugSessionManager } from './theia/debug/debug-session-manager';
|
||||
import { DebugSessionManager as TheiaDebugSessionManager } from '@theia/debug/lib/browser/debug-session-manager';
|
||||
import { Sketchbook } from './contributions/sketchbook';
|
||||
import { DebugFrontendApplicationContribution } from './theia/debug/debug-frontend-application-contribution';
|
||||
import { DebugFrontendApplicationContribution as TheiaDebugFrontendApplicationContribution } from '@theia/debug/lib/browser/debug-frontend-application-contribution';
|
||||
@@ -241,7 +232,6 @@ import { UploadFirmware } from './contributions/upload-firmware';
|
||||
import {
|
||||
UploadFirmwareDialog,
|
||||
UploadFirmwareDialogProps,
|
||||
UploadFirmwareDialogWidget,
|
||||
} from './dialogs/firmware-uploader/firmware-uploader-dialog';
|
||||
|
||||
import { UploadCertificate } from './contributions/upload-certificate';
|
||||
@@ -258,7 +248,6 @@ import { PlotterFrontendContribution } from './serial/plotter/plotter-frontend-c
|
||||
import {
|
||||
UserFieldsDialog,
|
||||
UserFieldsDialogProps,
|
||||
UserFieldsDialogWidget,
|
||||
} from './dialogs/user-fields/user-fields-dialog';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
import { IDEUpdaterCommands } from './ide-updater/ide-updater-commands';
|
||||
@@ -271,7 +260,6 @@ import { IDEUpdaterClientImpl } from './ide-updater/ide-updater-client-impl';
|
||||
import {
|
||||
IDEUpdaterDialog,
|
||||
IDEUpdaterDialogProps,
|
||||
IDEUpdaterDialogWidget,
|
||||
} from './dialogs/ide-updater/ide-updater-dialog';
|
||||
import { ElectronIpcConnectionProvider } from '@theia/core/lib/electron-browser/messaging/electron-ipc-connection-provider';
|
||||
import { MonitorModel } from './monitor-model';
|
||||
@@ -313,10 +301,6 @@ import { SelectedBoard } from './contributions/selected-board';
|
||||
import { CheckForIDEUpdates } from './contributions/check-for-ide-updates';
|
||||
import { OpenBoardsConfig } from './contributions/open-boards-config';
|
||||
import { SketchFilesTracker } from './contributions/sketch-files-tracker';
|
||||
import { MonacoThemeServiceIsReady } from './utils/window';
|
||||
import { Deferred } from '@theia/core/lib/common/promise-util';
|
||||
import { StatusBarImpl } from './theia/core/status-bar';
|
||||
import { StatusBarImpl as TheiaStatusBarImpl } from '@theia/core/lib/browser';
|
||||
import { EditorMenuContribution } from './theia/editor/editor-file';
|
||||
import { EditorMenuContribution as TheiaEditorMenuContribution } from '@theia/editor/lib/browser/editor-menu';
|
||||
import { PreferencesEditorWidget as TheiaPreferencesEditorWidget } from '@theia/preferences/lib/browser/views/preference-editor-widget';
|
||||
@@ -335,32 +319,30 @@ import { UserFields } from './contributions/user-fields';
|
||||
import { UpdateIndexes } from './contributions/update-indexes';
|
||||
import { InterfaceScale } from './contributions/interface-scale';
|
||||
import { OpenHandler } from '@theia/core/lib/browser/opener-service';
|
||||
|
||||
const registerArduinoThemes = () => {
|
||||
const themes: MonacoThemeJson[] = [
|
||||
{
|
||||
id: 'arduino-theme',
|
||||
label: 'Light (Arduino)',
|
||||
uiTheme: 'vs',
|
||||
json: require('../../src/browser/data/default.color-theme.json'),
|
||||
},
|
||||
{
|
||||
id: 'arduino-theme-dark',
|
||||
label: 'Dark (Arduino)',
|
||||
uiTheme: 'vs-dark',
|
||||
json: require('../../src/browser/data/dark.color-theme.json'),
|
||||
},
|
||||
];
|
||||
themes.forEach((theme) => MonacoThemingService.register(theme));
|
||||
};
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const global = window as any;
|
||||
const ready = global[MonacoThemeServiceIsReady] as Deferred;
|
||||
if (ready) {
|
||||
ready.promise.then(registerArduinoThemes);
|
||||
} else {
|
||||
registerArduinoThemes();
|
||||
}
|
||||
import { NewCloudSketch } from './contributions/new-cloud-sketch';
|
||||
import { SketchbookCompositeWidget } from './widgets/sketchbook/sketchbook-composite-widget';
|
||||
import { WindowTitleUpdater } from './theia/core/window-title-updater';
|
||||
import { WindowTitleUpdater as TheiaWindowTitleUpdater } from '@theia/core/lib/browser/window/window-title-updater';
|
||||
import { ThemeService } from './theia/core/theming';
|
||||
import { ThemeService as TheiaThemeService } from '@theia/core/lib/browser/theming';
|
||||
import { MonacoThemingService } from './theia/monaco/monaco-theming-service';
|
||||
import { MonacoThemingService as TheiaMonacoThemingService } from '@theia/monaco/lib/browser/monaco-theming-service';
|
||||
import { TypeHierarchyServiceProvider } from './theia/typehierarchy/type-hierarchy-service';
|
||||
import { TypeHierarchyServiceProvider as TheiaTypeHierarchyServiceProvider } from '@theia/typehierarchy/lib/browser/typehierarchy-service';
|
||||
import { TypeHierarchyContribution } from './theia/typehierarchy/type-hierarchy-contribution';
|
||||
import { TypeHierarchyContribution as TheiaTypeHierarchyContribution } from '@theia/typehierarchy/lib/browser/typehierarchy-contribution';
|
||||
import { DefaultDebugSessionFactory } from './theia/debug/debug-session-contribution';
|
||||
import { DebugSessionFactory } from '@theia/debug/lib/browser/debug-session-contribution';
|
||||
import { DebugToolbar } from './theia/debug/debug-toolbar-widget';
|
||||
import { DebugToolBar as TheiaDebugToolbar } from '@theia/debug/lib/browser/view/debug-toolbar-widget';
|
||||
import { PluginMenuCommandAdapter } from './theia/plugin-ext/plugin-menu-command-adapter';
|
||||
import { PluginMenuCommandAdapter as TheiaPluginMenuCommandAdapter } from '@theia/plugin-ext/lib/main/browser/menus/plugin-menu-command-adapter';
|
||||
import { DebugSessionManager } from './theia/debug/debug-session-manager';
|
||||
import { DebugSessionManager as TheiaDebugSessionManager } from '@theia/debug/lib/browser/debug-session-manager';
|
||||
import { DebugWidget } from '@theia/debug/lib/browser/view/debug-widget';
|
||||
import { DebugViewModel } from '@theia/debug/lib/browser/view/debug-view-model';
|
||||
import { DebugSessionWidget } from '@theia/debug/lib/browser/view/debug-session-widget';
|
||||
import { DebugConfigurationWidget } from '@theia/debug/lib/browser/view/debug-configuration-widget';
|
||||
|
||||
export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
// Commands and toolbar items
|
||||
@@ -585,14 +567,6 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
.to(WorkspaceDeleteHandler)
|
||||
.inSingletonScope();
|
||||
rebind(TheiaEditorWidgetFactory).to(EditorWidgetFactory).inSingletonScope();
|
||||
rebind(TabBarToolbarFactory).toFactory(
|
||||
({ container: parentContainer }) =>
|
||||
() => {
|
||||
const container = parentContainer.createChild();
|
||||
container.bind(TabBarToolbar).toSelf().inSingletonScope();
|
||||
return container.get(TabBarToolbar);
|
||||
}
|
||||
);
|
||||
bind(OutputChannelManager).toSelf().inSingletonScope();
|
||||
rebind(TheiaOutputChannelManager).toService(OutputChannelManager);
|
||||
bind(OutputChannelRegistryMainImpl).toSelf().inTransientScope();
|
||||
@@ -751,6 +725,7 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
Contribution.configure(bind, DeleteSketch);
|
||||
Contribution.configure(bind, UpdateIndexes);
|
||||
Contribution.configure(bind, InterfaceScale);
|
||||
Contribution.configure(bind, NewCloudSketch);
|
||||
|
||||
bindContributionProvider(bind, StartupTaskProvider);
|
||||
bind(StartupTaskProvider).toService(BoardsServiceProvider); // to reuse the boards config in another window
|
||||
@@ -835,9 +810,6 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
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);
|
||||
// To remove the `Run` menu item from the application menu.
|
||||
bind(DebugFrontendApplicationContribution).toSelf().inSingletonScope();
|
||||
rebind(TheiaDebugFrontendApplicationContribution).toService(
|
||||
@@ -851,10 +823,6 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
bind(WidgetManager).toSelf().inSingletonScope();
|
||||
rebind(TheiaWidgetManager).toService(WidgetManager);
|
||||
|
||||
// To avoid running a status bar update on every single `keypress` event from the editor.
|
||||
bind(StatusBarImpl).toSelf().inSingletonScope();
|
||||
rebind(TheiaStatusBarImpl).toService(StatusBarImpl);
|
||||
|
||||
// Debounced update for the tab-bar toolbar when typing in the editor.
|
||||
bind(DockPanelRenderer).toSelf();
|
||||
rebind(TheiaDockPanelRenderer).toService(DockPanelRenderer);
|
||||
@@ -905,6 +873,11 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
id: 'arduino-sketchbook-widget',
|
||||
createWidget: () => container.get(SketchbookWidget),
|
||||
}));
|
||||
bind(SketchbookCompositeWidget).toSelf();
|
||||
bind<WidgetFactory>(WidgetFactory).toDynamicValue((ctx) => ({
|
||||
id: 'sketchbook-composite-widget',
|
||||
createWidget: () => ctx.container.get(SketchbookCompositeWidget),
|
||||
}));
|
||||
|
||||
bind(CloudSketchbookWidget).toSelf();
|
||||
rebind(SketchbookWidget).toService(CloudSketchbookWidget);
|
||||
@@ -934,12 +907,11 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
bind(LocalCacheFsProvider).toSelf().inSingletonScope();
|
||||
bind(FileServiceContribution).toService(LocalCacheFsProvider);
|
||||
bind(CloudSketchbookCompositeWidget).toSelf();
|
||||
bind<WidgetFactory>(WidgetFactory).toDynamicValue((ctx) => ({
|
||||
bind(WidgetFactory).toDynamicValue((ctx) => ({
|
||||
id: 'cloud-sketchbook-composite-widget',
|
||||
createWidget: () => ctx.container.get(CloudSketchbookCompositeWidget),
|
||||
}));
|
||||
|
||||
bind(UploadFirmwareDialogWidget).toSelf().inSingletonScope();
|
||||
bind(UploadFirmwareDialog).toSelf().inSingletonScope();
|
||||
bind(UploadFirmwareDialogProps).toConstantValue({
|
||||
title: 'UploadFirmware',
|
||||
@@ -950,13 +922,11 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
title: 'UploadCertificate',
|
||||
});
|
||||
|
||||
bind(IDEUpdaterDialogWidget).toSelf().inSingletonScope();
|
||||
bind(IDEUpdaterDialog).toSelf().inSingletonScope();
|
||||
bind(IDEUpdaterDialogProps).toConstantValue({
|
||||
title: 'IDEUpdater',
|
||||
});
|
||||
|
||||
bind(UserFieldsDialogWidget).toSelf().inSingletonScope();
|
||||
bind(UserFieldsDialog).toSelf().inSingletonScope();
|
||||
bind(UserFieldsDialogProps).toConstantValue({
|
||||
title: 'UserFields',
|
||||
@@ -983,4 +953,55 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
rebind(TheiaHostedPluginSupport).toService(HostedPluginSupport);
|
||||
bind(HostedPluginEvents).toSelf().inSingletonScope();
|
||||
bind(FrontendApplicationContribution).toService(HostedPluginEvents);
|
||||
|
||||
// custom window titles
|
||||
bind(WindowTitleUpdater).toSelf().inSingletonScope();
|
||||
rebind(TheiaWindowTitleUpdater).toService(WindowTitleUpdater);
|
||||
|
||||
// register Arduino themes
|
||||
bind(ThemeService).toSelf().inSingletonScope();
|
||||
rebind(TheiaThemeService).toService(ThemeService);
|
||||
bind(MonacoThemingService).toSelf().inSingletonScope();
|
||||
rebind(TheiaMonacoThemingService).toService(MonacoThemingService);
|
||||
|
||||
// disable type-hierarchy support
|
||||
// https://github.com/eclipse-theia/theia/commit/16c88a584bac37f5cf3cc5eb92ffdaa541bda5be
|
||||
bind(TypeHierarchyServiceProvider).toSelf().inSingletonScope();
|
||||
rebind(TheiaTypeHierarchyServiceProvider).toService(
|
||||
TypeHierarchyServiceProvider
|
||||
);
|
||||
bind(TypeHierarchyContribution).toSelf().inSingletonScope();
|
||||
rebind(TheiaTypeHierarchyContribution).toService(TypeHierarchyContribution);
|
||||
|
||||
// patched the debugger for `cortex-debug@1.5.1`
|
||||
// https://github.com/eclipse-theia/theia/issues/11871
|
||||
// https://github.com/eclipse-theia/theia/issues/11879
|
||||
// https://github.com/eclipse-theia/theia/issues/11880
|
||||
// https://github.com/eclipse-theia/theia/issues/11885
|
||||
// https://github.com/eclipse-theia/theia/issues/11886
|
||||
// https://github.com/eclipse-theia/theia/issues/11916
|
||||
// based on: https://github.com/eclipse-theia/theia/compare/master...kittaakos:theia:%2311871
|
||||
bind(DefaultDebugSessionFactory).toSelf().inSingletonScope();
|
||||
rebind(DebugSessionFactory).toService(DefaultDebugSessionFactory);
|
||||
bind(DebugSessionManager).toSelf().inSingletonScope();
|
||||
rebind(TheiaDebugSessionManager).toService(DebugSessionManager);
|
||||
bind(DebugToolbar).toSelf().inSingletonScope();
|
||||
rebind(TheiaDebugToolbar).toService(DebugToolbar);
|
||||
bind(PluginMenuCommandAdapter).toSelf().inSingletonScope();
|
||||
rebind(TheiaPluginMenuCommandAdapter).toService(PluginMenuCommandAdapter);
|
||||
bind(WidgetFactory)
|
||||
.toDynamicValue(({ container }) => ({
|
||||
id: DebugWidget.ID,
|
||||
createWidget: () => {
|
||||
const child = new Container({ defaultScope: 'Singleton' });
|
||||
child.parent = container;
|
||||
child.bind(DebugViewModel).toSelf();
|
||||
child.bind(DebugToolbar).toSelf(); // patched toolbar
|
||||
child.bind(DebugSessionWidget).toSelf();
|
||||
child.bind(DebugConfigurationWidget).toSelf();
|
||||
child.bind(DebugWidget).toSelf();
|
||||
return child.get(DebugWidget);
|
||||
},
|
||||
}))
|
||||
.inSingletonScope();
|
||||
});
|
||||
|
@@ -114,11 +114,12 @@ export const ArduinoConfigSchema: PreferenceSchema = {
|
||||
},
|
||||
'arduino.window.zoomLevel': {
|
||||
type: 'number',
|
||||
description: nls.localize(
|
||||
'arduino/preferences/window.zoomLevel',
|
||||
'Adjust the zoom level of the window. The original size is 0 and each increment above (e.g. 1) or below (e.g. -1) represents zooming 20% larger or smaller. You can also enter decimals to adjust the zoom level with a finer granularity.'
|
||||
),
|
||||
description: '',
|
||||
default: 0,
|
||||
deprecationMessage: nls.localize(
|
||||
'arduino/preferences/window.zoomLevel/deprecationMessage',
|
||||
"Deprecated. Use 'window.zoomLevel' instead."
|
||||
),
|
||||
},
|
||||
'arduino.ide.updateChannel': {
|
||||
type: 'string',
|
||||
@@ -270,7 +271,6 @@ export interface ArduinoConfiguration {
|
||||
'arduino.upload.verbose': boolean;
|
||||
'arduino.upload.verify': boolean;
|
||||
'arduino.window.autoScale': boolean;
|
||||
'arduino.window.zoomLevel': number;
|
||||
'arduino.ide.updateChannel': UpdateChannel;
|
||||
'arduino.ide.updateBaseUrl': string;
|
||||
'arduino.board.certificates': string;
|
||||
|
@@ -65,7 +65,7 @@ export class Close extends SketchContribution {
|
||||
registry.registerMenuAction(ArduinoMenus.FILE__SKETCH_GROUP, {
|
||||
commandId: Close.Commands.CLOSE.id,
|
||||
label: nls.localize('vscode/editor.contribution/close', 'Close'),
|
||||
order: '5',
|
||||
order: '6',
|
||||
});
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,373 @@
|
||||
import { DialogError } from '@theia/core/lib/browser/dialogs';
|
||||
import { KeybindingRegistry } from '@theia/core/lib/browser/keybinding';
|
||||
import { LabelProvider } from '@theia/core/lib/browser/label-provider';
|
||||
import { CompositeTreeNode } from '@theia/core/lib/browser/tree';
|
||||
import { Widget } from '@theia/core/lib/browser/widgets/widget';
|
||||
import { CancellationTokenSource } from '@theia/core/lib/common/cancellation';
|
||||
import {
|
||||
Disposable,
|
||||
DisposableCollection,
|
||||
} from '@theia/core/lib/common/disposable';
|
||||
import { MenuModelRegistry } from '@theia/core/lib/common/menu';
|
||||
import {
|
||||
Progress,
|
||||
ProgressUpdate,
|
||||
} from '@theia/core/lib/common/message-service-protocol';
|
||||
import { nls } from '@theia/core/lib/common/nls';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import { WorkspaceInputDialogProps } from '@theia/workspace/lib/browser/workspace-input-dialog';
|
||||
import { v4 } from 'uuid';
|
||||
import { MainMenuManager } from '../../common/main-menu-manager';
|
||||
import type { AuthenticationSession } from '../../node/auth/types';
|
||||
import { AuthenticationClientService } from '../auth/authentication-client-service';
|
||||
import { CreateApi } from '../create/create-api';
|
||||
import { CreateUri } from '../create/create-uri';
|
||||
import { Create } from '../create/typings';
|
||||
import { ArduinoMenus } from '../menu/arduino-menus';
|
||||
import { WorkspaceInputDialog } from '../theia/workspace/workspace-input-dialog';
|
||||
import { CloudSketchbookTree } from '../widgets/cloud-sketchbook/cloud-sketchbook-tree';
|
||||
import { CloudSketchbookTreeModel } from '../widgets/cloud-sketchbook/cloud-sketchbook-tree-model';
|
||||
import { CloudSketchbookTreeWidget } from '../widgets/cloud-sketchbook/cloud-sketchbook-tree-widget';
|
||||
import { SketchbookCommands } from '../widgets/sketchbook/sketchbook-commands';
|
||||
import { SketchbookWidget } from '../widgets/sketchbook/sketchbook-widget';
|
||||
import { SketchbookWidgetContribution } from '../widgets/sketchbook/sketchbook-widget-contribution';
|
||||
import { Command, CommandRegistry, Contribution, URI } from './contribution';
|
||||
|
||||
@injectable()
|
||||
export class NewCloudSketch extends Contribution {
|
||||
@inject(CreateApi)
|
||||
private readonly createApi: CreateApi;
|
||||
@inject(SketchbookWidgetContribution)
|
||||
private readonly widgetContribution: SketchbookWidgetContribution;
|
||||
@inject(AuthenticationClientService)
|
||||
private readonly authenticationService: AuthenticationClientService;
|
||||
@inject(MainMenuManager)
|
||||
private readonly mainMenuManager: MainMenuManager;
|
||||
|
||||
private readonly toDispose = new DisposableCollection();
|
||||
private _session: AuthenticationSession | undefined;
|
||||
private _enabled: boolean;
|
||||
|
||||
override onReady(): void {
|
||||
this.toDispose.pushAll([
|
||||
this.authenticationService.onSessionDidChange((session) => {
|
||||
const oldSession = this._session;
|
||||
this._session = session;
|
||||
if (!!oldSession !== !!this._session) {
|
||||
this.mainMenuManager.update();
|
||||
}
|
||||
}),
|
||||
this.preferences.onPreferenceChanged(({ preferenceName, newValue }) => {
|
||||
if (preferenceName === 'arduino.cloud.enabled') {
|
||||
const oldEnabled = this._enabled;
|
||||
this._enabled = Boolean(newValue);
|
||||
if (this._enabled !== oldEnabled) {
|
||||
this.mainMenuManager.update();
|
||||
}
|
||||
}
|
||||
}),
|
||||
]);
|
||||
this._enabled = this.preferences['arduino.cloud.enabled'];
|
||||
this._session = this.authenticationService.session;
|
||||
if (this._session) {
|
||||
this.mainMenuManager.update();
|
||||
}
|
||||
}
|
||||
|
||||
onStop(): void {
|
||||
this.toDispose.dispose();
|
||||
}
|
||||
|
||||
override registerCommands(registry: CommandRegistry): void {
|
||||
registry.registerCommand(NewCloudSketch.Commands.NEW_CLOUD_SKETCH, {
|
||||
execute: () => this.createNewSketch(),
|
||||
isEnabled: () => !!this._session,
|
||||
isVisible: () => this._enabled,
|
||||
});
|
||||
}
|
||||
|
||||
override registerMenus(registry: MenuModelRegistry): void {
|
||||
registry.registerMenuAction(ArduinoMenus.FILE__SKETCH_GROUP, {
|
||||
commandId: NewCloudSketch.Commands.NEW_CLOUD_SKETCH.id,
|
||||
label: nls.localize('arduino/cloudSketch/new', 'New Remote Sketch'),
|
||||
order: '1',
|
||||
});
|
||||
}
|
||||
|
||||
override registerKeybindings(registry: KeybindingRegistry): void {
|
||||
registry.registerKeybinding({
|
||||
command: NewCloudSketch.Commands.NEW_CLOUD_SKETCH.id,
|
||||
keybinding: 'CtrlCmd+Alt+N',
|
||||
});
|
||||
}
|
||||
|
||||
private async createNewSketch(
|
||||
initialValue?: string | undefined
|
||||
): Promise<unknown> {
|
||||
const widget = await this.widgetContribution.widget;
|
||||
const treeModel = this.treeModelFrom(widget);
|
||||
if (!treeModel) {
|
||||
return undefined;
|
||||
}
|
||||
const rootNode = CompositeTreeNode.is(treeModel.root)
|
||||
? treeModel.root
|
||||
: undefined;
|
||||
if (!rootNode) {
|
||||
return undefined;
|
||||
}
|
||||
return this.openWizard(rootNode, treeModel, initialValue);
|
||||
}
|
||||
|
||||
private withProgress(
|
||||
value: string,
|
||||
treeModel: CloudSketchbookTreeModel
|
||||
): (progress: Progress) => Promise<unknown> {
|
||||
return async (progress: Progress) => {
|
||||
let result: Create.Sketch | undefined | 'conflict';
|
||||
try {
|
||||
progress.report({
|
||||
message: nls.localize(
|
||||
'arduino/cloudSketch/creating',
|
||||
"Creating remote sketch '{0}'...",
|
||||
value
|
||||
),
|
||||
});
|
||||
result = await this.createApi.createSketch(value);
|
||||
} catch (err) {
|
||||
if (isConflict(err)) {
|
||||
result = 'conflict';
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
} finally {
|
||||
if (result) {
|
||||
progress.report({
|
||||
message: nls.localize(
|
||||
'arduino/cloudSketch/synchronizing',
|
||||
"Synchronizing sketchbook, pulling '{0}'...",
|
||||
value
|
||||
),
|
||||
});
|
||||
await treeModel.refresh();
|
||||
}
|
||||
}
|
||||
if (result === 'conflict') {
|
||||
return this.createNewSketch(value);
|
||||
}
|
||||
if (result) {
|
||||
return this.open(treeModel, result);
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
}
|
||||
|
||||
private async open(
|
||||
treeModel: CloudSketchbookTreeModel,
|
||||
newSketch: Create.Sketch
|
||||
): Promise<URI | undefined> {
|
||||
const id = CreateUri.toUri(newSketch).path.toString();
|
||||
const node = treeModel.getNode(id);
|
||||
if (!node) {
|
||||
throw new Error(
|
||||
`Could not find remote sketchbook tree node with Tree node ID: ${id}.`
|
||||
);
|
||||
}
|
||||
if (!CloudSketchbookTree.CloudSketchDirNode.is(node)) {
|
||||
throw new Error(
|
||||
`Remote sketchbook tree node expected to represent a directory but it did not. Tree node ID: ${id}.`
|
||||
);
|
||||
}
|
||||
try {
|
||||
await treeModel.sketchbookTree().pull({ node });
|
||||
} catch (err) {
|
||||
if (isNotFound(err)) {
|
||||
await treeModel.refresh();
|
||||
this.messageService.error(
|
||||
nls.localize(
|
||||
'arduino/newCloudSketch/notFound',
|
||||
"Could not pull the remote sketch '{0}'. It does not exist.",
|
||||
newSketch.name
|
||||
)
|
||||
);
|
||||
return undefined;
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
return this.commandService.executeCommand(
|
||||
SketchbookCommands.OPEN_NEW_WINDOW.id,
|
||||
{ node }
|
||||
);
|
||||
}
|
||||
|
||||
private treeModelFrom(
|
||||
widget: SketchbookWidget
|
||||
): CloudSketchbookTreeModel | undefined {
|
||||
for (const treeWidget of widget.getTreeWidgets()) {
|
||||
if (treeWidget instanceof CloudSketchbookTreeWidget) {
|
||||
const model = treeWidget.model;
|
||||
if (model instanceof CloudSketchbookTreeModel) {
|
||||
return model;
|
||||
}
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private async openWizard(
|
||||
rootNode: CompositeTreeNode,
|
||||
treeModel: CloudSketchbookTreeModel,
|
||||
initialValue?: string | undefined
|
||||
): Promise<unknown> {
|
||||
const existingNames = rootNode.children
|
||||
.filter(CloudSketchbookTree.CloudSketchDirNode.is)
|
||||
.map(({ fileStat }) => fileStat.name);
|
||||
return new NewCloudSketchDialog(
|
||||
{
|
||||
title: nls.localize(
|
||||
'arduino/newCloudSketch/newSketchTitle',
|
||||
'Name of a new Remote Sketch'
|
||||
),
|
||||
parentUri: CreateUri.root,
|
||||
initialValue,
|
||||
validate: (input) => {
|
||||
if (existingNames.includes(input)) {
|
||||
return nls.localize(
|
||||
'arduino/newCloudSketch/sketchAlreadyExists',
|
||||
"Remote sketch '{0}' already exists.",
|
||||
input
|
||||
);
|
||||
}
|
||||
// This is how https://create.arduino.cc/editor/ works when renaming a sketch.
|
||||
if (/^[0-9a-zA-Z_]{1,36}$/.test(input)) {
|
||||
return '';
|
||||
}
|
||||
return nls.localize(
|
||||
'arduino/newCloudSketch/invalidSketchName',
|
||||
'The name must consist of basic letters, numbers, or underscores. The maximum length is 36 characters.'
|
||||
);
|
||||
},
|
||||
},
|
||||
this.labelProvider,
|
||||
(value) => this.withProgress(value, treeModel)
|
||||
).open();
|
||||
}
|
||||
}
|
||||
export namespace NewCloudSketch {
|
||||
export namespace Commands {
|
||||
export const NEW_CLOUD_SKETCH: Command = {
|
||||
id: 'arduino-new-cloud-sketch',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function isConflict(err: unknown): boolean {
|
||||
return isErrorWithStatusOf(err, 409);
|
||||
}
|
||||
function isNotFound(err: unknown): boolean {
|
||||
return isErrorWithStatusOf(err, 404);
|
||||
}
|
||||
function isErrorWithStatusOf(
|
||||
err: unknown,
|
||||
status: number
|
||||
): err is Error & { status: number } {
|
||||
if (err instanceof Error) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const object = err as any;
|
||||
return 'status' in object && object.status === status;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@injectable()
|
||||
class NewCloudSketchDialog extends WorkspaceInputDialog {
|
||||
constructor(
|
||||
@inject(WorkspaceInputDialogProps)
|
||||
protected override readonly props: WorkspaceInputDialogProps,
|
||||
@inject(LabelProvider)
|
||||
protected override readonly labelProvider: LabelProvider,
|
||||
private readonly withProgress: (
|
||||
value: string
|
||||
) => (progress: Progress) => Promise<unknown>
|
||||
) {
|
||||
super(props, labelProvider);
|
||||
}
|
||||
protected override async accept(): Promise<void> {
|
||||
if (!this.resolve) {
|
||||
return;
|
||||
}
|
||||
this.acceptCancellationSource.cancel();
|
||||
this.acceptCancellationSource = new CancellationTokenSource();
|
||||
const token = this.acceptCancellationSource.token;
|
||||
const value = this.value;
|
||||
const error = await this.isValid(value, 'open');
|
||||
if (token.isCancellationRequested) {
|
||||
return;
|
||||
}
|
||||
if (!DialogError.getResult(error)) {
|
||||
this.setErrorMessage(error);
|
||||
} else {
|
||||
const spinner = document.createElement('div');
|
||||
spinner.classList.add('spinner');
|
||||
const disposables = new DisposableCollection();
|
||||
try {
|
||||
this.toggleButtons(true);
|
||||
disposables.push(Disposable.create(() => this.toggleButtons(false)));
|
||||
|
||||
const closeParent = this.closeCrossNode.parentNode;
|
||||
closeParent?.removeChild(this.closeCrossNode);
|
||||
disposables.push(
|
||||
Disposable.create(() => {
|
||||
closeParent?.appendChild(this.closeCrossNode);
|
||||
})
|
||||
);
|
||||
|
||||
this.errorMessageNode.classList.add('progress');
|
||||
disposables.push(
|
||||
Disposable.create(() =>
|
||||
this.errorMessageNode.classList.remove('progress')
|
||||
)
|
||||
);
|
||||
|
||||
const errorParent = this.errorMessageNode.parentNode;
|
||||
errorParent?.insertBefore(spinner, this.errorMessageNode);
|
||||
disposables.push(
|
||||
Disposable.create(() => errorParent?.removeChild(spinner))
|
||||
);
|
||||
|
||||
const cancellationSource = new CancellationTokenSource();
|
||||
const progress: Progress = {
|
||||
id: v4(),
|
||||
cancel: () => cancellationSource.cancel(),
|
||||
report: (update: ProgressUpdate) => {
|
||||
this.setProgressMessage(update);
|
||||
},
|
||||
result: Promise.resolve(value),
|
||||
};
|
||||
await this.withProgress(value)(progress);
|
||||
} finally {
|
||||
disposables.dispose();
|
||||
}
|
||||
this.resolve(value);
|
||||
Widget.detach(this);
|
||||
}
|
||||
}
|
||||
|
||||
private toggleButtons(disabled: boolean): void {
|
||||
if (this.acceptButton) {
|
||||
this.acceptButton.disabled = disabled;
|
||||
}
|
||||
if (this.closeButton) {
|
||||
this.closeButton.disabled = disabled;
|
||||
}
|
||||
}
|
||||
|
||||
private setProgressMessage(update: ProgressUpdate): void {
|
||||
if (update.work && update.work.done === update.work.total) {
|
||||
this.errorMessageNode.innerText = '';
|
||||
} else {
|
||||
if (update.message) {
|
||||
this.errorMessageNode.innerText = update.message;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -21,7 +21,7 @@ export class NewSketch extends SketchContribution {
|
||||
override registerMenus(registry: MenuModelRegistry): void {
|
||||
registry.registerMenuAction(ArduinoMenus.FILE__SKETCH_GROUP, {
|
||||
commandId: NewSketch.Commands.NEW_SKETCH.id,
|
||||
label: nls.localize('arduino/sketch/new', 'New'),
|
||||
label: nls.localize('arduino/sketch/new', 'New Sketch'),
|
||||
order: '0',
|
||||
});
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { nls } from '@theia/core/lib/common/nls';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import { injectable } from '@theia/core/shared/inversify';
|
||||
import type { EditorOpenerOptions } from '@theia/editor/lib/browser/editor-manager';
|
||||
import { Later } from '../../common/nls';
|
||||
import { Sketch, SketchesError } from '../../common/protocol';
|
||||
@@ -15,17 +15,13 @@ import { ApplicationError } from '@theia/core/lib/common/application-error';
|
||||
import { Deferred, wait } from '@theia/core/lib/common/promise-util';
|
||||
import { EditorWidget } from '@theia/editor/lib/browser/editor-widget';
|
||||
import { DisposableCollection } from '@theia/core/lib/common/disposable';
|
||||
import { MonacoEditor } from '@theia/monaco/lib/browser/monaco-editor';
|
||||
import { ContextKeyService as VSCodeContextKeyService } from '@theia/monaco-editor-core/esm/vs/platform/contextkey/browser/contextKeyService';
|
||||
|
||||
@injectable()
|
||||
export class OpenSketchFiles extends SketchContribution {
|
||||
@inject(VSCodeContextKeyService)
|
||||
private readonly contextKeyService: VSCodeContextKeyService;
|
||||
|
||||
override registerCommands(registry: CommandRegistry): void {
|
||||
registry.registerCommand(OpenSketchFiles.Commands.OPEN_SKETCH_FILES, {
|
||||
execute: (uri: URI) => this.openSketchFiles(uri),
|
||||
execute: (uri: URI, focusMainSketchFile) =>
|
||||
this.openSketchFiles(uri, focusMainSketchFile),
|
||||
});
|
||||
registry.registerCommand(OpenSketchFiles.Commands.ENSURE_OPENED, {
|
||||
execute: (
|
||||
@@ -38,13 +34,19 @@ export class OpenSketchFiles extends SketchContribution {
|
||||
});
|
||||
}
|
||||
|
||||
private async openSketchFiles(uri: URI): Promise<void> {
|
||||
private async openSketchFiles(
|
||||
uri: URI,
|
||||
focusMainSketchFile = false
|
||||
): Promise<void> {
|
||||
try {
|
||||
const sketch = await this.sketchService.loadSketch(uri.toString());
|
||||
const { mainFileUri, rootFolderFileUris } = sketch;
|
||||
for (const uri of [mainFileUri, ...rootFolderFileUris]) {
|
||||
await this.ensureOpened(uri);
|
||||
}
|
||||
if (focusMainSketchFile) {
|
||||
await this.ensureOpened(mainFileUri, true, { mode: 'activate' });
|
||||
}
|
||||
if (mainFileUri.endsWith('.pde')) {
|
||||
const message = nls.localize(
|
||||
'arduino/common/oldFormat',
|
||||
@@ -107,7 +109,7 @@ export class OpenSketchFiles extends SketchContribution {
|
||||
): Promise<Sketch | undefined> {
|
||||
const { invalidMainSketchUri } = err.data;
|
||||
requestAnimationFrame(() => this.messageService.error(err.message));
|
||||
await wait(10); // let IDE2 toast the error message.
|
||||
await wait(250); // let IDE2 open the editor and toast the error message, then open the modal dialog
|
||||
const movedSketch = await promptMoveSketch(invalidMainSketchUri, {
|
||||
fileService: this.fileService,
|
||||
sketchService: this.sketchService,
|
||||
@@ -131,43 +133,40 @@ export class OpenSketchFiles extends SketchContribution {
|
||||
uri: string,
|
||||
forceOpen = false,
|
||||
options?: EditorOpenerOptions
|
||||
): Promise<unknown> {
|
||||
): Promise<EditorWidget | undefined> {
|
||||
const widget = this.editorManager.all.find(
|
||||
(widget) => widget.editor.uri.toString() === uri
|
||||
);
|
||||
if (widget && !forceOpen) {
|
||||
return widget;
|
||||
}
|
||||
|
||||
const disposables = new DisposableCollection();
|
||||
if (!widget || forceOpen) {
|
||||
const deferred = new Deferred<EditorWidget>();
|
||||
const deferred = new Deferred<EditorWidget>();
|
||||
// An editor can be in two primary states:
|
||||
// - The editor is not yet opened. The `widget` is `undefined`. With `editorManager#open`, Theia will create an editor and fire an `editorManager#onCreated` event.
|
||||
// - The editor is opened. Can be active, current, or open.
|
||||
// - If the editor has the focus (the cursor blinks in the editor): it's the active editor.
|
||||
// - If the editor does not have the focus (the focus is on a different widget or the context menu is opened in the editor): it's the current editor.
|
||||
// - If the editor is not the top editor in the main area, it's opened.
|
||||
if (!widget) {
|
||||
// If the widget is `undefined`, IDE2 expects one `onCreate` event. Subscribe to the `onCreated` event
|
||||
// and resolve the promise with the editor only when the new editor's visibility changes.
|
||||
disposables.push(
|
||||
this.editorManager.onCreated((editor) => {
|
||||
if (editor.editor.uri.toString() === uri) {
|
||||
if (editor.isVisible) {
|
||||
disposables.dispose();
|
||||
if (editor.isAttached && editor.isVisible) {
|
||||
deferred.resolve(editor);
|
||||
} else {
|
||||
// In Theia, the promise resolves after opening the editor, but the editor is neither attached to the DOM, nor visible.
|
||||
// This is a hack to first get an event from monaco after the widget update request, then IDE2 waits for the next monaco context key event.
|
||||
// Here, the monaco context key event is not used, but this is the first event after the editor is visible in the UI.
|
||||
disposables.push(
|
||||
(editor.editor as MonacoEditor).onDidResize((dimension) => {
|
||||
if (dimension) {
|
||||
const isKeyOwner = (
|
||||
arg: unknown
|
||||
): arg is { key: string } => {
|
||||
if (typeof arg === 'object') {
|
||||
const object = arg as Record<string, unknown>;
|
||||
return typeof object['key'] === 'string';
|
||||
}
|
||||
return false;
|
||||
};
|
||||
disposables.push(
|
||||
this.contextKeyService.onDidChangeContext((e) => {
|
||||
// `commentIsEmpty` is the first context key change event received from monaco after the editor is for real visible in the UI.
|
||||
if (isKeyOwner(e) && e.key === 'commentIsEmpty') {
|
||||
deferred.resolve(editor);
|
||||
disposables.dispose();
|
||||
}
|
||||
})
|
||||
editor.onDidChangeVisibility((visible) => {
|
||||
if (visible) {
|
||||
// wait an animation frame. although the visible and attached props are true the editor is not there.
|
||||
// let the browser render the widget
|
||||
setTimeout(
|
||||
() =>
|
||||
requestAnimationFrame(() => deferred.resolve(editor)),
|
||||
0
|
||||
);
|
||||
}
|
||||
})
|
||||
@@ -176,29 +175,42 @@ export class OpenSketchFiles extends SketchContribution {
|
||||
}
|
||||
})
|
||||
);
|
||||
this.editorManager.open(
|
||||
}
|
||||
|
||||
this.editorManager
|
||||
.open(
|
||||
new URI(uri),
|
||||
options ?? {
|
||||
mode: 'reveal',
|
||||
preview: false,
|
||||
counter: 0,
|
||||
}
|
||||
)
|
||||
.then((editorWidget) => {
|
||||
// If the widget was defined, it was already opened.
|
||||
// The editor is expected to be attached to the shell and visible in the UI.
|
||||
// The deferred promise does not have to wait for the `editorManager#onCreated` event.
|
||||
// It can resolve earlier.
|
||||
if (widget) {
|
||||
deferred.resolve(editorWidget);
|
||||
}
|
||||
});
|
||||
|
||||
const timeout = 5_000; // number of ms IDE2 waits for the editor to show up in the UI
|
||||
const result: EditorWidget | undefined | 'timeout' = await Promise.race([
|
||||
deferred.promise,
|
||||
wait(timeout).then(() => {
|
||||
disposables.dispose();
|
||||
return 'timeout' as const;
|
||||
}),
|
||||
]);
|
||||
if (result === 'timeout') {
|
||||
console.warn(
|
||||
`Timeout after ${timeout} millis. The editor has not shown up in time. URI: ${uri}`
|
||||
);
|
||||
const timeout = 5_000; // number of ms IDE2 waits for the editor to show up in the UI
|
||||
const result = await Promise.race([
|
||||
deferred.promise,
|
||||
wait(timeout).then(() => {
|
||||
disposables.dispose();
|
||||
return 'timeout';
|
||||
}),
|
||||
]);
|
||||
if (result === 'timeout') {
|
||||
console.warn(
|
||||
`Timeout after ${timeout} millis. The editor has not shown up in time. URI: ${uri}`
|
||||
);
|
||||
}
|
||||
return result;
|
||||
return undefined;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
export namespace OpenSketchFiles {
|
||||
|
@@ -54,7 +54,7 @@ export class OpenSketch extends SketchContribution {
|
||||
registry.registerMenuAction(ArduinoMenus.FILE__SKETCH_GROUP, {
|
||||
commandId: OpenSketch.Commands.OPEN_SKETCH.id,
|
||||
label: nls.localize('vscode/workspaceActions/openFileFolder', 'Open...'),
|
||||
order: '1',
|
||||
order: '2',
|
||||
});
|
||||
}
|
||||
|
||||
|
@@ -24,7 +24,7 @@ export class SaveSketch extends SketchContribution {
|
||||
registry.registerMenuAction(ArduinoMenus.FILE__SKETCH_GROUP, {
|
||||
commandId: SaveSketch.Commands.SAVE_SKETCH.id,
|
||||
label: nls.localize('vscode/fileCommands/save', 'Save'),
|
||||
order: '6',
|
||||
order: '7',
|
||||
});
|
||||
}
|
||||
|
||||
|
@@ -235,7 +235,7 @@ export class SketchControl extends SketchContribution {
|
||||
});
|
||||
registry.registerKeybinding({
|
||||
command: CommonCommands.PREVIOUS_TAB.id,
|
||||
keybinding: 'CtrlCmd+Alt+Left', // TODO: check why electron does not show the keybindings in the UI.
|
||||
keybinding: 'CtrlCmd+Alt+Left',
|
||||
});
|
||||
registry.registerKeybinding({
|
||||
command: CommonCommands.NEXT_TAB.id,
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { URI as Uri } from 'vscode-uri';
|
||||
import { URI as Uri } from '@theia/core/shared/vscode-uri';
|
||||
import URI from '@theia/core/lib/common/uri';
|
||||
import { toPosixPath, parentPosix, posix } from './create-paths';
|
||||
import { Create } from './typings';
|
||||
@@ -7,7 +7,9 @@ export namespace CreateUri {
|
||||
export const scheme = 'arduino-create';
|
||||
export const root = toUri(posix.sep);
|
||||
|
||||
export function toUri(posixPathOrResource: string | Create.Resource): URI {
|
||||
export function toUri(
|
||||
posixPathOrResource: string | Create.Resource | Create.Sketch
|
||||
): URI {
|
||||
const posixPath =
|
||||
typeof posixPathOrResource === 'string'
|
||||
? posixPathOrResource
|
||||
|
@@ -5,10 +5,8 @@ import {
|
||||
postConstruct,
|
||||
} from '@theia/core/shared/inversify';
|
||||
import { DialogProps } from '@theia/core/lib/browser/dialogs';
|
||||
import { AbstractDialog } from '../../theia/dialogs/dialogs';
|
||||
import { Widget } from '@theia/core/shared/@phosphor/widgets';
|
||||
import { ReactDialog } from '../../theia/dialogs/dialogs';
|
||||
import { Message } from '@theia/core/shared/@phosphor/messaging';
|
||||
import { ReactWidget } from '@theia/core/lib/browser/widgets/react-widget';
|
||||
import {
|
||||
AvailableBoard,
|
||||
BoardsServiceProvider,
|
||||
@@ -23,26 +21,30 @@ import { Port } from '../../../common/protocol';
|
||||
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
|
||||
|
||||
@injectable()
|
||||
export class UploadFirmwareDialogWidget extends ReactWidget {
|
||||
export class UploadFirmwareDialogProps extends DialogProps {}
|
||||
|
||||
@injectable()
|
||||
export class UploadFirmwareDialog extends ReactDialog<void> {
|
||||
@inject(BoardsServiceProvider)
|
||||
protected readonly boardsServiceClient: BoardsServiceProvider;
|
||||
|
||||
private readonly boardsServiceClient: BoardsServiceProvider;
|
||||
@inject(ArduinoFirmwareUploader)
|
||||
protected readonly arduinoFirmwareUploader: ArduinoFirmwareUploader;
|
||||
|
||||
private readonly arduinoFirmwareUploader: ArduinoFirmwareUploader;
|
||||
@inject(FrontendApplicationStateService)
|
||||
private readonly appStatusService: FrontendApplicationStateService;
|
||||
|
||||
protected updatableFqbns: string[] = [];
|
||||
protected availableBoards: AvailableBoard[] = [];
|
||||
protected isOpen = new Object();
|
||||
private updatableFqbns: string[] = [];
|
||||
private availableBoards: AvailableBoard[] = [];
|
||||
private isOpen = new Object();
|
||||
private busy = false;
|
||||
|
||||
public busyCallback = (busy: boolean) => {
|
||||
return;
|
||||
};
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
constructor(
|
||||
@inject(UploadFirmwareDialogProps)
|
||||
protected override readonly props: UploadFirmwareDialogProps
|
||||
) {
|
||||
super({ title: UploadFirmware.Commands.OPEN.label || '' });
|
||||
this.node.id = 'firmware-uploader-dialog-container';
|
||||
this.contentNode.classList.add('firmware-uploader-dialog');
|
||||
this.acceptButton = undefined;
|
||||
}
|
||||
|
||||
@postConstruct()
|
||||
@@ -59,79 +61,34 @@ export class UploadFirmwareDialogWidget extends ReactWidget {
|
||||
});
|
||||
}
|
||||
|
||||
protected flashFirmware(firmware: FirmwareInfo, port: Port): Promise<any> {
|
||||
this.busyCallback(true);
|
||||
return this.arduinoFirmwareUploader
|
||||
.flash(firmware, port)
|
||||
.finally(() => this.busyCallback(false));
|
||||
}
|
||||
|
||||
protected override onCloseRequest(msg: Message): void {
|
||||
super.onCloseRequest(msg);
|
||||
this.isOpen = new Object();
|
||||
}
|
||||
|
||||
protected render(): React.ReactNode {
|
||||
return (
|
||||
<form>
|
||||
<FirmwareUploaderComponent
|
||||
availableBoards={this.availableBoards}
|
||||
firmwareUploader={this.arduinoFirmwareUploader}
|
||||
flashFirmware={this.flashFirmware.bind(this)}
|
||||
updatableFqbns={this.updatableFqbns}
|
||||
isOpen={this.isOpen}
|
||||
/>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@injectable()
|
||||
export class UploadFirmwareDialogProps extends DialogProps {}
|
||||
|
||||
@injectable()
|
||||
export class UploadFirmwareDialog extends AbstractDialog<void> {
|
||||
@inject(UploadFirmwareDialogWidget)
|
||||
protected readonly widget: UploadFirmwareDialogWidget;
|
||||
|
||||
private busy = false;
|
||||
|
||||
constructor(
|
||||
@inject(UploadFirmwareDialogProps)
|
||||
protected override readonly props: UploadFirmwareDialogProps
|
||||
) {
|
||||
super({ title: UploadFirmware.Commands.OPEN.label || '' });
|
||||
this.node.id = 'firmware-uploader-dialog-container';
|
||||
this.contentNode.classList.add('firmware-uploader-dialog');
|
||||
this.acceptButton = undefined;
|
||||
}
|
||||
|
||||
get value(): void {
|
||||
return;
|
||||
}
|
||||
|
||||
protected override render(): React.ReactNode {
|
||||
return (
|
||||
<div>
|
||||
<form>
|
||||
<FirmwareUploaderComponent
|
||||
availableBoards={this.availableBoards}
|
||||
firmwareUploader={this.arduinoFirmwareUploader}
|
||||
flashFirmware={this.flashFirmware.bind(this)}
|
||||
updatableFqbns={this.updatableFqbns}
|
||||
isOpen={this.isOpen}
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
protected override onAfterAttach(msg: Message): void {
|
||||
if (this.widget.isAttached) {
|
||||
Widget.detach(this.widget);
|
||||
}
|
||||
Widget.attach(this.widget, this.contentNode);
|
||||
const firstButton = this.widget.node.querySelector('button');
|
||||
const firstButton = this.node.querySelector('button');
|
||||
firstButton?.focus();
|
||||
this.widget.busyCallback = this.busyCallback.bind(this);
|
||||
super.onAfterAttach(msg);
|
||||
this.update();
|
||||
}
|
||||
|
||||
protected override onUpdateRequest(msg: Message): void {
|
||||
super.onUpdateRequest(msg);
|
||||
this.widget.update();
|
||||
}
|
||||
|
||||
protected override onActivateRequest(msg: Message): void {
|
||||
super.onActivateRequest(msg);
|
||||
this.widget.activate();
|
||||
}
|
||||
|
||||
// eslint-disable-next-line unused-imports/no-unused-vars, @typescript-eslint/no-unused-vars
|
||||
protected override handleEnter(event: KeyboardEvent): boolean | void {
|
||||
return false;
|
||||
}
|
||||
@@ -140,11 +97,11 @@ export class UploadFirmwareDialog extends AbstractDialog<void> {
|
||||
if (this.busy) {
|
||||
return;
|
||||
}
|
||||
this.widget.close();
|
||||
super.close();
|
||||
this.isOpen = new Object();
|
||||
}
|
||||
|
||||
busyCallback(busy: boolean): void {
|
||||
private busyCallback(busy: boolean): void {
|
||||
this.busy = busy;
|
||||
if (busy) {
|
||||
this.closeCrossNode.classList.add('disabled');
|
||||
@@ -152,4 +109,11 @@ export class UploadFirmwareDialog extends AbstractDialog<void> {
|
||||
this.closeCrossNode.classList.remove('disabled');
|
||||
}
|
||||
}
|
||||
|
||||
private flashFirmware(firmware: FirmwareInfo, port: Port): Promise<any> {
|
||||
this.busyCallback(true);
|
||||
return this.arduinoFirmwareUploader
|
||||
.flash(firmware, port)
|
||||
.finally(() => this.busyCallback(false));
|
||||
}
|
||||
}
|
||||
|
@@ -1,7 +1,6 @@
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
import { shell } from 'electron';
|
||||
import { shell } from '@theia/core/electron-shared/@electron/remote';
|
||||
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';
|
||||
@@ -28,32 +27,19 @@ export const IDEUpdaterComponent = ({
|
||||
},
|
||||
}: IDEUpdaterComponentProps): React.ReactElement => {
|
||||
const { version, releaseNotes } = updateInfo;
|
||||
const changelogDivRef =
|
||||
React.useRef() as React.MutableRefObject<HTMLDivElement>;
|
||||
const [changelog, setChangelog] = React.useState<string>('');
|
||||
React.useEffect(() => {
|
||||
if (!!releaseNotes && changelogDivRef.current) {
|
||||
let changelog: string;
|
||||
if (typeof releaseNotes === 'string') changelog = releaseNotes;
|
||||
else
|
||||
changelog = releaseNotes.reduce((acc, item) => {
|
||||
return item.note ? (acc += `${item.note}\n\n`) : acc;
|
||||
}, '');
|
||||
ReactDOM.render(
|
||||
<ReactMarkdown
|
||||
components={{
|
||||
a: ({ href, children, ...props }) => (
|
||||
<a onClick={() => href && shell.openExternal(href)} {...props}>
|
||||
{children}
|
||||
</a>
|
||||
),
|
||||
}}
|
||||
>
|
||||
{changelog}
|
||||
</ReactMarkdown>,
|
||||
changelogDivRef.current
|
||||
if (releaseNotes) {
|
||||
setChangelog(
|
||||
typeof releaseNotes === 'string'
|
||||
? releaseNotes
|
||||
: releaseNotes.reduce(
|
||||
(acc, item) => (item.note ? (acc += `${item.note}\n\n`) : acc),
|
||||
''
|
||||
)
|
||||
);
|
||||
}
|
||||
}, [updateInfo]);
|
||||
}, [releaseNotes, changelog]);
|
||||
|
||||
const DownloadCompleted: () => React.ReactElement = () => (
|
||||
<div className="ide-updater-dialog--downloaded">
|
||||
@@ -106,9 +92,24 @@ export const IDEUpdaterComponent = ({
|
||||
version
|
||||
)}
|
||||
</div>
|
||||
{releaseNotes && (
|
||||
{changelog && (
|
||||
<div className="dialogRow changelog-container">
|
||||
<div className="changelog" ref={changelogDivRef} />
|
||||
<div className="changelog">
|
||||
<ReactMarkdown
|
||||
components={{
|
||||
a: ({ href, children, ...props }) => (
|
||||
<a
|
||||
onClick={() => href && shell.openExternal(href)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</a>
|
||||
),
|
||||
}}
|
||||
>
|
||||
{changelog}
|
||||
</ReactMarkdown>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
@@ -5,10 +5,8 @@ import {
|
||||
postConstruct,
|
||||
} from '@theia/core/shared/inversify';
|
||||
import { DialogProps } from '@theia/core/lib/browser/dialogs';
|
||||
import { AbstractDialog } from '../../theia/dialogs/dialogs';
|
||||
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 { ReactDialog } from '../../theia/dialogs/dialogs';
|
||||
import { nls } from '@theia/core';
|
||||
import { IDEUpdaterComponent, UpdateProgress } from './ide-updater-component';
|
||||
import {
|
||||
@@ -22,47 +20,11 @@ import { WindowService } from '@theia/core/lib/browser/window/window-service';
|
||||
|
||||
const DOWNLOAD_PAGE_URL = 'https://www.arduino.cc/en/software';
|
||||
|
||||
@injectable()
|
||||
export class IDEUpdaterDialogWidget extends ReactWidget {
|
||||
private _updateInfo: UpdateInfo;
|
||||
private _updateProgress: UpdateProgress = {};
|
||||
|
||||
setUpdateInfo(updateInfo: UpdateInfo): void {
|
||||
this._updateInfo = updateInfo;
|
||||
this.update();
|
||||
}
|
||||
|
||||
mergeUpdateProgress(updateProgress: UpdateProgress): void {
|
||||
this._updateProgress = { ...this._updateProgress, ...updateProgress };
|
||||
this.update();
|
||||
}
|
||||
|
||||
get updateInfo(): UpdateInfo {
|
||||
return this._updateInfo;
|
||||
}
|
||||
|
||||
get updateProgress(): UpdateProgress {
|
||||
return this._updateProgress;
|
||||
}
|
||||
|
||||
protected render(): React.ReactNode {
|
||||
return !!this._updateInfo ? (
|
||||
<IDEUpdaterComponent
|
||||
updateInfo={this._updateInfo}
|
||||
updateProgress={this._updateProgress}
|
||||
/>
|
||||
) : null;
|
||||
}
|
||||
}
|
||||
|
||||
@injectable()
|
||||
export class IDEUpdaterDialogProps extends DialogProps {}
|
||||
|
||||
@injectable()
|
||||
export class IDEUpdaterDialog extends AbstractDialog<UpdateInfo> {
|
||||
@inject(IDEUpdaterDialogWidget)
|
||||
private readonly widget: IDEUpdaterDialogWidget;
|
||||
|
||||
export class IDEUpdaterDialog extends ReactDialog<UpdateInfo | undefined> {
|
||||
@inject(IDEUpdater)
|
||||
private readonly updater: IDEUpdater;
|
||||
|
||||
@@ -75,6 +37,9 @@ export class IDEUpdaterDialog extends AbstractDialog<UpdateInfo> {
|
||||
@inject(WindowService)
|
||||
private readonly windowService: WindowService;
|
||||
|
||||
private _updateInfo: UpdateInfo | undefined;
|
||||
private _updateProgress: UpdateProgress = {};
|
||||
|
||||
constructor(
|
||||
@inject(IDEUpdaterDialogProps)
|
||||
protected override readonly props: IDEUpdaterDialogProps
|
||||
@@ -94,26 +59,34 @@ export class IDEUpdaterDialog extends AbstractDialog<UpdateInfo> {
|
||||
protected init(): void {
|
||||
this.updaterClient.onUpdaterDidFail((error) => {
|
||||
this.appendErrorButtons();
|
||||
this.widget.mergeUpdateProgress({ error });
|
||||
this.mergeUpdateProgress({ error });
|
||||
});
|
||||
this.updaterClient.onDownloadProgressDidChange((progressInfo) => {
|
||||
this.widget.mergeUpdateProgress({ progressInfo });
|
||||
this.mergeUpdateProgress({ progressInfo });
|
||||
});
|
||||
this.updaterClient.onDownloadDidFinish(() => {
|
||||
this.appendInstallButtons();
|
||||
this.widget.mergeUpdateProgress({ downloadFinished: true });
|
||||
this.mergeUpdateProgress({ downloadFinished: true });
|
||||
});
|
||||
}
|
||||
|
||||
get value(): UpdateInfo {
|
||||
return this.widget.updateInfo;
|
||||
protected render(): React.ReactNode {
|
||||
return (
|
||||
this.updateInfo && (
|
||||
<IDEUpdaterComponent
|
||||
updateInfo={this.updateInfo}
|
||||
updateProgress={this.updateProgress}
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
get value(): UpdateInfo | undefined {
|
||||
return this.updateInfo;
|
||||
}
|
||||
|
||||
protected override onAfterAttach(msg: Message): void {
|
||||
if (this.widget.isAttached) {
|
||||
Widget.detach(this.widget);
|
||||
}
|
||||
Widget.attach(this.widget, this.contentNode);
|
||||
this.update();
|
||||
this.appendInitialButtons();
|
||||
super.onAfterAttach(msg);
|
||||
}
|
||||
@@ -196,15 +169,19 @@ export class IDEUpdaterDialog extends AbstractDialog<UpdateInfo> {
|
||||
}
|
||||
|
||||
private skipVersion(): void {
|
||||
if (!this.updateInfo) {
|
||||
console.warn(`Nothing to skip. No update info is available`);
|
||||
return;
|
||||
}
|
||||
this.localStorageService.setData<string>(
|
||||
SKIP_IDE_VERSION,
|
||||
this.widget.updateInfo.version
|
||||
this.updateInfo.version
|
||||
);
|
||||
this.close();
|
||||
}
|
||||
|
||||
private startDownload(): void {
|
||||
this.widget.mergeUpdateProgress({
|
||||
this.mergeUpdateProgress({
|
||||
downloadStarted: true,
|
||||
});
|
||||
this.clearButtons();
|
||||
@@ -216,31 +193,48 @@ export class IDEUpdaterDialog extends AbstractDialog<UpdateInfo> {
|
||||
this.close();
|
||||
}
|
||||
|
||||
private set updateInfo(updateInfo: UpdateInfo | undefined) {
|
||||
this._updateInfo = updateInfo;
|
||||
this.update();
|
||||
}
|
||||
|
||||
private get updateInfo(): UpdateInfo | undefined {
|
||||
return this._updateInfo;
|
||||
}
|
||||
|
||||
private get updateProgress(): UpdateProgress {
|
||||
return this._updateProgress;
|
||||
}
|
||||
|
||||
private mergeUpdateProgress(updateProgress: UpdateProgress): void {
|
||||
this._updateProgress = { ...this._updateProgress, ...updateProgress };
|
||||
this.update();
|
||||
}
|
||||
|
||||
override async open(
|
||||
data: UpdateInfo | undefined = undefined
|
||||
): Promise<UpdateInfo | undefined> {
|
||||
if (data && data.version) {
|
||||
this.widget.mergeUpdateProgress({
|
||||
this.mergeUpdateProgress({
|
||||
progressInfo: undefined,
|
||||
downloadStarted: false,
|
||||
downloadFinished: false,
|
||||
error: undefined,
|
||||
});
|
||||
this.widget.setUpdateInfo(data);
|
||||
this.updateInfo = data;
|
||||
return super.open();
|
||||
}
|
||||
}
|
||||
|
||||
protected override onActivateRequest(msg: Message): void {
|
||||
super.onActivateRequest(msg);
|
||||
this.widget.activate();
|
||||
this.update();
|
||||
}
|
||||
|
||||
override close(): void {
|
||||
this.widget.dispose();
|
||||
if (
|
||||
this.widget.updateProgress?.downloadStarted &&
|
||||
!this.widget.updateProgress?.downloadFinished
|
||||
this.updateProgress?.downloadStarted &&
|
||||
!this.updateProgress?.downloadFinished
|
||||
) {
|
||||
this.updater.stopDownload();
|
||||
}
|
||||
|
@@ -218,16 +218,14 @@ export class SettingsComponent extends React.Component<
|
||||
<div className="flex-line">
|
||||
<select
|
||||
className="theia-select"
|
||||
value={ThemeService.get().getCurrentTheme().label}
|
||||
value={this.props.themeService.getCurrentTheme().label}
|
||||
onChange={this.themeDidChange}
|
||||
>
|
||||
{ThemeService.get()
|
||||
.getThemes()
|
||||
.map(({ id, label }) => (
|
||||
<option key={id} value={label}>
|
||||
{label}
|
||||
</option>
|
||||
))}
|
||||
{this.props.themeService.getThemes().map(({ id, label }) => (
|
||||
<option key={id} value={label}>
|
||||
{label}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
<div className="flex-line">
|
||||
@@ -612,11 +610,11 @@ export class SettingsComponent extends React.Component<
|
||||
event: React.ChangeEvent<HTMLSelectElement>
|
||||
): void => {
|
||||
const { selectedIndex } = event.target.options;
|
||||
const theme = ThemeService.get().getThemes()[selectedIndex];
|
||||
const theme = this.props.themeService.getThemes()[selectedIndex];
|
||||
if (theme) {
|
||||
this.setState({ themeId: theme.id });
|
||||
if (ThemeService.get().getCurrentTheme().id !== theme.id) {
|
||||
ThemeService.get().setCurrentTheme(theme.id);
|
||||
if (this.props.themeService.getCurrentTheme().id !== theme.id) {
|
||||
this.props.themeService.setCurrentTheme(theme.id);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -755,6 +753,7 @@ export namespace SettingsComponent {
|
||||
readonly fileDialogService: FileDialogService;
|
||||
readonly windowService: WindowService;
|
||||
readonly localizationProvider: AsyncLocalizationProvider;
|
||||
readonly themeService: ThemeService;
|
||||
}
|
||||
export type State = Settings & {
|
||||
rawAdditionalUrlsValue: string;
|
||||
|
@@ -35,6 +35,9 @@ export class SettingsWidget extends ReactWidget {
|
||||
@inject(AsyncLocalizationProvider)
|
||||
protected readonly localizationProvider: AsyncLocalizationProvider;
|
||||
|
||||
@inject(ThemeService)
|
||||
private readonly themeService: ThemeService;
|
||||
|
||||
protected render(): React.ReactNode {
|
||||
return (
|
||||
<SettingsComponent
|
||||
@@ -43,6 +46,7 @@ export class SettingsWidget extends ReactWidget {
|
||||
fileDialogService={this.fileDialogService}
|
||||
windowService={this.windowService}
|
||||
localizationProvider={this.localizationProvider}
|
||||
themeService={this.themeService}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -59,6 +63,9 @@ export class SettingsDialog extends AbstractDialog<Promise<Settings>> {
|
||||
@inject(SettingsWidget)
|
||||
protected readonly widget: SettingsWidget;
|
||||
|
||||
@inject(ThemeService)
|
||||
private readonly themeService: ThemeService;
|
||||
|
||||
constructor(
|
||||
@inject(SettingsDialogProps)
|
||||
protected override readonly props: SettingsDialogProps
|
||||
@@ -121,11 +128,11 @@ export class SettingsDialog extends AbstractDialog<Promise<Settings>> {
|
||||
}
|
||||
|
||||
override async open(): Promise<Promise<Settings> | undefined> {
|
||||
const themeIdBeforeOpen = ThemeService.get().getCurrentTheme().id;
|
||||
const themeIdBeforeOpen = this.themeService.getCurrentTheme().id;
|
||||
const result = await super.open();
|
||||
if (!result) {
|
||||
if (ThemeService.get().getCurrentTheme().id !== themeIdBeforeOpen) {
|
||||
ThemeService.get().setCurrentTheme(themeIdBeforeOpen);
|
||||
if (this.themeService.getCurrentTheme().id !== themeIdBeforeOpen) {
|
||||
this.themeService.setCurrentTheme(themeIdBeforeOpen);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
@@ -5,7 +5,7 @@ import {
|
||||
} 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';
|
||||
import { Deferred } from '@theia/core/lib/common/promise-util';
|
||||
import { deepClone } from '@theia/core/lib/common/objects';
|
||||
import { FileService } from '@theia/filesystem/lib/browser/file-service';
|
||||
import { ThemeService } from '@theia/core/lib/browser/theming';
|
||||
@@ -25,17 +25,20 @@ import {
|
||||
LanguageInfo,
|
||||
} from '@theia/core/lib/common/i18n/localization';
|
||||
import { ElectronCommands } from '@theia/core/lib/electron-browser/menu/electron-menu-contribution';
|
||||
import { DefaultTheme } from '@theia/application-package/lib/application-props';
|
||||
import { FrontendApplicationConfigProvider } from '@theia/core/lib/browser/frontend-application-config-provider';
|
||||
|
||||
export const WINDOW_SETTING = 'window';
|
||||
export const EDITOR_SETTING = 'editor';
|
||||
export const FONT_SIZE_SETTING = `${EDITOR_SETTING}.fontSize`;
|
||||
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`;
|
||||
export const ARDUINO_WINDOW_SETTING = `${ARDUINO_SETTING}.window`;
|
||||
export const COMPILE_SETTING = `${ARDUINO_SETTING}.compile`;
|
||||
export const UPLOAD_SETTING = `${ARDUINO_SETTING}.upload`;
|
||||
export const SKETCHBOOK_SETTING = `${ARDUINO_SETTING}.sketchbook`;
|
||||
export const AUTO_SCALE_SETTING = `${WINDOW_SETTING}.autoScale`;
|
||||
export const AUTO_SCALE_SETTING = `${ARDUINO_WINDOW_SETTING}.autoScale`;
|
||||
export const ZOOM_LEVEL_SETTING = `${WINDOW_SETTING}.zoomLevel`;
|
||||
export const COMPILE_VERBOSE_SETTING = `${COMPILE_SETTING}.verbose`;
|
||||
export const COMPILE_WARNINGS_SETTING = `${COMPILE_SETTING}.warnings`;
|
||||
@@ -53,7 +56,7 @@ export interface Settings {
|
||||
currentLanguage: string;
|
||||
|
||||
autoScaleInterface: boolean; // `arduino.window.autoScale`
|
||||
interfaceScale: number; // `arduino.window.zoomLevel` https://github.com/eclipse-theia/theia/issues/8751
|
||||
interfaceScale: number; // `window.zoomLevel`
|
||||
verboseOnCompile: boolean; // `arduino.compile.verbose`
|
||||
compilerWarnings: CompilerWarnings; // `arduino.compile.warnings`
|
||||
verboseOnUpload: boolean; // `arduino.upload.verbose`
|
||||
@@ -101,6 +104,9 @@ export class SettingsService {
|
||||
@inject(CommandService)
|
||||
protected commandService: CommandService;
|
||||
|
||||
@inject(ThemeService)
|
||||
private readonly themeService: ThemeService;
|
||||
|
||||
protected readonly onDidChangeEmitter = new Emitter<Readonly<Settings>>();
|
||||
readonly onDidChange = this.onDidChangeEmitter.event;
|
||||
protected readonly onDidResetEmitter = new Emitter<Readonly<Settings>>();
|
||||
@@ -141,10 +147,9 @@ export class SettingsService {
|
||||
this.preferenceService.get<number>(FONT_SIZE_SETTING, 12),
|
||||
this.preferenceService.get<string>(
|
||||
'workbench.colorTheme',
|
||||
window.matchMedia &&
|
||||
window.matchMedia('(prefers-color-scheme: dark)').matches
|
||||
? 'arduino-theme-dark'
|
||||
: 'arduino-theme'
|
||||
DefaultTheme.defaultForOSTheme(
|
||||
FrontendApplicationConfigProvider.get().defaultTheme
|
||||
)
|
||||
),
|
||||
this.preferenceService.get<Settings.AutoSave>(
|
||||
AUTO_SAVE_SETTING,
|
||||
@@ -231,11 +236,7 @@ export class SettingsService {
|
||||
'Invalid editor font size. It must be a positive integer.'
|
||||
);
|
||||
}
|
||||
if (
|
||||
!ThemeService.get()
|
||||
.getThemes()
|
||||
.find(({ id }) => id === themeId)
|
||||
) {
|
||||
if (!this.themeService.getThemes().find(({ id }) => id === themeId)) {
|
||||
return nls.localize(
|
||||
'arduino/preferences/invalid.theme',
|
||||
'Invalid theme.'
|
||||
@@ -252,7 +253,6 @@ export class SettingsService {
|
||||
|
||||
private async savePreference(name: string, value: unknown): Promise<void> {
|
||||
await this.preferenceService.set(name, value, PreferenceScope.User);
|
||||
await timeout(5);
|
||||
}
|
||||
|
||||
async save(): Promise<string | true> {
|
||||
@@ -283,19 +283,20 @@ export class SettingsService {
|
||||
(config as any).network = network;
|
||||
(config as any).locale = currentLanguage;
|
||||
|
||||
await this.savePreference('editor.fontSize', editorFontSize);
|
||||
await this.savePreference('workbench.colorTheme', themeId);
|
||||
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);
|
||||
await this.savePreference(ZOOM_LEVEL_SETTING, interfaceScale);
|
||||
await this.savePreference(COMPILE_VERBOSE_SETTING, verboseOnCompile);
|
||||
await this.savePreference(COMPILE_WARNINGS_SETTING, compilerWarnings);
|
||||
await this.savePreference(UPLOAD_VERBOSE_SETTING, verboseOnUpload);
|
||||
await this.savePreference(UPLOAD_VERIFY_SETTING, verifyAfterUpload);
|
||||
await this.savePreference(SHOW_ALL_FILES_SETTING, sketchbookShowAllFiles);
|
||||
await this.configService.setConfiguration(config);
|
||||
await Promise.all([
|
||||
this.savePreference('editor.fontSize', editorFontSize),
|
||||
this.savePreference('workbench.colorTheme', themeId),
|
||||
this.savePreference(AUTO_SAVE_SETTING, autoSave),
|
||||
this.savePreference('editor.quickSuggestions', quickSuggestions),
|
||||
this.savePreference(AUTO_SCALE_SETTING, autoScaleInterface),
|
||||
this.savePreference(ZOOM_LEVEL_SETTING, interfaceScale),
|
||||
this.savePreference(COMPILE_VERBOSE_SETTING, verboseOnCompile),
|
||||
this.savePreference(COMPILE_WARNINGS_SETTING, compilerWarnings),
|
||||
this.savePreference(UPLOAD_VERBOSE_SETTING, verboseOnUpload),
|
||||
this.savePreference(UPLOAD_VERIFY_SETTING, verifyAfterUpload),
|
||||
this.savePreference(SHOW_ALL_FILES_SETTING, sketchbookShowAllFiles),
|
||||
this.configService.setConfiguration(config),
|
||||
]);
|
||||
this.onDidChangeEmitter.fire(this._settings);
|
||||
|
||||
// after saving all the settings, if we need to change the language we need to perform a reload
|
||||
|
@@ -1,63 +1,18 @@
|
||||
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 '@theia/core/shared/@phosphor/widgets';
|
||||
import { DialogProps } from '@theia/core/lib/browser/dialogs';
|
||||
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';
|
||||
|
||||
@injectable()
|
||||
export class UserFieldsDialogWidget extends ReactWidget {
|
||||
private _currentUserFields: BoardUserField[] = [];
|
||||
|
||||
constructor(private cancel: () => void, private accept: () => Promise<void>) {
|
||||
super();
|
||||
}
|
||||
|
||||
set currentUserFields(userFields: BoardUserField[]) {
|
||||
this.setUserFields(userFields);
|
||||
}
|
||||
|
||||
get currentUserFields(): BoardUserField[] {
|
||||
return this._currentUserFields;
|
||||
}
|
||||
|
||||
resetUserFieldsValue(): void {
|
||||
this._currentUserFields = this._currentUserFields.map((field) => {
|
||||
field.value = '';
|
||||
return field;
|
||||
});
|
||||
}
|
||||
|
||||
private setUserFields(userFields: BoardUserField[]): void {
|
||||
this._currentUserFields = userFields;
|
||||
}
|
||||
|
||||
protected render(): React.ReactNode {
|
||||
return (
|
||||
<form>
|
||||
<UserFieldsComponent
|
||||
initialBoardUserFields={this._currentUserFields}
|
||||
updateUserFields={this.setUserFields.bind(this)}
|
||||
cancel={this.cancel}
|
||||
accept={this.accept}
|
||||
/>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
}
|
||||
import { ReactDialog } from '../../theia/dialogs/dialogs';
|
||||
|
||||
@injectable()
|
||||
export class UserFieldsDialogProps extends DialogProps {}
|
||||
|
||||
@injectable()
|
||||
export class UserFieldsDialog extends AbstractDialog<BoardUserField[]> {
|
||||
protected readonly widget: UserFieldsDialogWidget;
|
||||
export class UserFieldsDialog extends ReactDialog<BoardUserField[]> {
|
||||
private _currentUserFields: BoardUserField[] = [];
|
||||
|
||||
constructor(
|
||||
@inject(UserFieldsDialogProps)
|
||||
@@ -69,39 +24,36 @@ export class UserFieldsDialog extends AbstractDialog<BoardUserField[]> {
|
||||
this.titleNode.classList.add('user-fields-dialog-title');
|
||||
this.contentNode.classList.add('user-fields-dialog-content');
|
||||
this.acceptButton = undefined;
|
||||
this.widget = new UserFieldsDialogWidget(
|
||||
this.close.bind(this),
|
||||
this.accept.bind(this)
|
||||
);
|
||||
}
|
||||
|
||||
set value(userFields: BoardUserField[]) {
|
||||
this.widget.currentUserFields = userFields;
|
||||
}
|
||||
|
||||
get value(): BoardUserField[] {
|
||||
return this.widget.currentUserFields;
|
||||
return this._currentUserFields;
|
||||
}
|
||||
|
||||
set value(userFields: BoardUserField[]) {
|
||||
this._currentUserFields = userFields;
|
||||
}
|
||||
|
||||
protected override render(): React.ReactNode {
|
||||
return (
|
||||
<div>
|
||||
<form>
|
||||
<UserFieldsComponent
|
||||
initialBoardUserFields={this.value}
|
||||
updateUserFields={this.doUpdateUserFields}
|
||||
cancel={this.doCancel}
|
||||
accept={this.doAccept}
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
protected override onAfterAttach(msg: Message): void {
|
||||
if (this.widget.isAttached) {
|
||||
Widget.detach(this.widget);
|
||||
}
|
||||
Widget.attach(this.widget, this.contentNode);
|
||||
super.onAfterAttach(msg);
|
||||
this.update();
|
||||
}
|
||||
|
||||
protected override onUpdateRequest(msg: Message): void {
|
||||
super.onUpdateRequest(msg);
|
||||
this.widget.update();
|
||||
}
|
||||
|
||||
protected override onActivateRequest(msg: Message): void {
|
||||
super.onActivateRequest(msg);
|
||||
this.widget.activate();
|
||||
}
|
||||
|
||||
protected override async accept(): Promise<void> {
|
||||
// If the user presses enter and at least
|
||||
// a field is empty don't accept the input
|
||||
@@ -114,8 +66,21 @@ export class UserFieldsDialog extends AbstractDialog<BoardUserField[]> {
|
||||
}
|
||||
|
||||
override close(): void {
|
||||
this.widget.resetUserFieldsValue();
|
||||
this.widget.close();
|
||||
this.resetUserFieldsValue();
|
||||
super.close();
|
||||
}
|
||||
|
||||
private resetUserFieldsValue(): void {
|
||||
this.value = this.value.map((field) => {
|
||||
field.value = '';
|
||||
return field;
|
||||
});
|
||||
}
|
||||
|
||||
private readonly doCancel: () => void = () => this.close();
|
||||
private readonly doAccept: () => Promise<void> = () => this.accept();
|
||||
private readonly doUpdateUserFields: (userFields: BoardUserField[]) => void =
|
||||
(userFields: BoardUserField[]) => {
|
||||
this.value = userFields;
|
||||
};
|
||||
}
|
||||
|
6
arduino-ide-extension/src/browser/icons/loading-dark.svg
Normal file
6
arduino-ide-extension/src/browser/icons/loading-dark.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<!--Copyright (c) Microsoft Corporation. All rights reserved.-->
|
||||
<!--Copyright (C) 2020 TypeFox and others.-->
|
||||
<!--Copied from https://raw.githubusercontent.com/microsoft/vscode-icons/9c90ce81b1f3c309000b80da0763aa09975a85f0/icons/dark/loading.svg -->
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8 0.75C8.17188 0.75 8.33333 0.783854 8.48438 0.851562C8.63542 0.914062 8.76823 1.0026 8.88281 1.11719C8.9974 1.23177 9.08594 1.36458 9.14844 1.51562C9.21615 1.66667 9.25 1.82812 9.25 2C9.25 2.17188 9.21615 2.33333 9.14844 2.48438C9.08594 2.63542 8.9974 2.76823 8.88281 2.88281C8.76823 2.9974 8.63542 3.08854 8.48438 3.15625C8.33333 3.21875 8.17188 3.25 8 3.25C7.82812 3.25 7.66667 3.21875 7.51562 3.15625C7.36458 3.08854 7.23177 2.9974 7.11719 2.88281C7.0026 2.76823 6.91146 2.63542 6.84375 2.48438C6.78125 2.33333 6.75 2.17188 6.75 2C6.75 1.82812 6.78125 1.66667 6.84375 1.51562C6.91146 1.36458 7.0026 1.23177 7.11719 1.11719C7.23177 1.0026 7.36458 0.914062 7.51562 0.851562C7.66667 0.783854 7.82812 0.75 8 0.75ZM2.63281 3.75781C2.63281 3.60156 2.66146 3.45573 2.71875 3.32031C2.77604 3.1849 2.85417 3.06771 2.95312 2.96875C3.05729 2.86458 3.17708 2.78385 3.3125 2.72656C3.45312 2.66406 3.60156 2.63281 3.75781 2.63281C3.91406 2.63281 4.0599 2.66406 4.19531 2.72656C4.33073 2.78385 4.44792 2.86458 4.54688 2.96875C4.65104 3.06771 4.73177 3.1849 4.78906 3.32031C4.85156 3.45573 4.88281 3.60156 4.88281 3.75781C4.88281 3.91406 4.85156 4.0625 4.78906 4.20312C4.73177 4.33854 4.65104 4.45833 4.54688 4.5625C4.44792 4.66146 4.33073 4.73958 4.19531 4.79688C4.0599 4.85417 3.91406 4.88281 3.75781 4.88281C3.60156 4.88281 3.45312 4.85417 3.3125 4.79688C3.17708 4.73958 3.05729 4.66146 2.95312 4.5625C2.85417 4.45833 2.77604 4.33854 2.71875 4.20312C2.66146 4.0625 2.63281 3.91406 2.63281 3.75781ZM2 7C2.14062 7 2.27083 7.02604 2.39062 7.07812C2.51042 7.13021 2.61458 7.20312 2.70312 7.29688C2.79688 7.38542 2.86979 7.48958 2.92188 7.60938C2.97396 7.72917 3 7.85938 3 8C3 8.14062 2.97396 8.27083 2.92188 8.39062C2.86979 8.51042 2.79688 8.61719 2.70312 8.71094C2.61458 8.79948 2.51042 8.86979 2.39062 8.92188C2.27083 8.97396 2.14062 9 2 9C1.85938 9 1.72917 8.97396 1.60938 8.92188C1.48958 8.86979 1.38281 8.79948 1.28906 8.71094C1.20052 8.61719 1.13021 8.51042 1.07812 8.39062C1.02604 8.27083 1 8.14062 1 8C1 7.85938 1.02604 7.72917 1.07812 7.60938C1.13021 7.48958 1.20052 7.38542 1.28906 7.29688C1.38281 7.20312 1.48958 7.13021 1.60938 7.07812C1.72917 7.02604 1.85938 7 2 7ZM2.88281 12.2422C2.88281 12.1224 2.90625 12.0104 2.95312 11.9062C3 11.7969 3.0625 11.7031 3.14062 11.625C3.21875 11.5469 3.3099 11.4844 3.41406 11.4375C3.52344 11.3906 3.63802 11.3672 3.75781 11.3672C3.8776 11.3672 3.98958 11.3906 4.09375 11.4375C4.20312 11.4844 4.29688 11.5469 4.375 11.625C4.45312 11.7031 4.51562 11.7969 4.5625 11.9062C4.60938 12.0104 4.63281 12.1224 4.63281 12.2422C4.63281 12.362 4.60938 12.4766 4.5625 12.5859C4.51562 12.6901 4.45312 12.7812 4.375 12.8594C4.29688 12.9375 4.20312 13 4.09375 13.0469C3.98958 13.0938 3.8776 13.1172 3.75781 13.1172C3.63802 13.1172 3.52344 13.0938 3.41406 13.0469C3.3099 13 3.21875 12.9375 3.14062 12.8594C3.0625 12.7812 3 12.6901 2.95312 12.5859C2.90625 12.4766 2.88281 12.362 2.88281 12.2422ZM8 13.25C8.20833 13.25 8.38542 13.3229 8.53125 13.4688C8.67708 13.6146 8.75 13.7917 8.75 14C8.75 14.2083 8.67708 14.3854 8.53125 14.5312C8.38542 14.6771 8.20833 14.75 8 14.75C7.79167 14.75 7.61458 14.6771 7.46875 14.5312C7.32292 14.3854 7.25 14.2083 7.25 14C7.25 13.7917 7.32292 13.6146 7.46875 13.4688C7.61458 13.3229 7.79167 13.25 8 13.25ZM11.6172 12.2422C11.6172 12.0651 11.6771 11.9167 11.7969 11.7969C11.9167 11.6771 12.0651 11.6172 12.2422 11.6172C12.4193 11.6172 12.5677 11.6771 12.6875 11.7969C12.8073 11.9167 12.8672 12.0651 12.8672 12.2422C12.8672 12.4193 12.8073 12.5677 12.6875 12.6875C12.5677 12.8073 12.4193 12.8672 12.2422 12.8672C12.0651 12.8672 11.9167 12.8073 11.7969 12.6875C11.6771 12.5677 11.6172 12.4193 11.6172 12.2422ZM14 7.5C14.1354 7.5 14.2526 7.54948 14.3516 7.64844C14.4505 7.7474 14.5 7.86458 14.5 8C14.5 8.13542 14.4505 8.2526 14.3516 8.35156C14.2526 8.45052 14.1354 8.5 14 8.5C13.8646 8.5 13.7474 8.45052 13.6484 8.35156C13.5495 8.2526 13.5 8.13542 13.5 8C13.5 7.86458 13.5495 7.7474 13.6484 7.64844C13.7474 7.54948 13.8646 7.5 14 7.5ZM12.2422 2.38281C12.4297 2.38281 12.6068 2.41927 12.7734 2.49219C12.9401 2.5651 13.0859 2.66406 13.2109 2.78906C13.3359 2.91406 13.4349 3.0599 13.5078 3.22656C13.5807 3.39323 13.6172 3.57031 13.6172 3.75781C13.6172 3.94531 13.5807 4.1224 13.5078 4.28906C13.4349 4.45573 13.3359 4.60156 13.2109 4.72656C13.0859 4.85156 12.9401 4.95052 12.7734 5.02344C12.6068 5.09635 12.4297 5.13281 12.2422 5.13281C12.0547 5.13281 11.8776 5.09635 11.7109 5.02344C11.5443 4.95052 11.3984 4.85156 11.2734 4.72656C11.1484 4.60156 11.0495 4.45573 10.9766 4.28906C10.9036 4.1224 10.8672 3.94531 10.8672 3.75781C10.8672 3.57031 10.9036 3.39323 10.9766 3.22656C11.0495 3.0599 11.1484 2.91406 11.2734 2.78906C11.3984 2.66406 11.5443 2.5651 11.7109 2.49219C11.8776 2.41927 12.0547 2.38281 12.2422 2.38281Z" fill="#C5C5C5" />
|
||||
</svg>
|
After Width: | Height: | Size: 5.0 KiB |
@@ -0,0 +1,6 @@
|
||||
<!--Copyright (c) Microsoft Corporation. All rights reserved.-->
|
||||
<!--Copyright (C) 2020 TypeFox and others.-->
|
||||
<!--Copied from https://raw.githubusercontent.com/microsoft/vscode-icons/9c90ce81b1f3c309000b80da0763aa09975a85f0/icons/light/loading.svg -->
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8 0.75C8.17188 0.75 8.33333 0.783854 8.48438 0.851562C8.63542 0.914062 8.76823 1.0026 8.88281 1.11719C8.9974 1.23177 9.08594 1.36458 9.14844 1.51562C9.21615 1.66667 9.25 1.82812 9.25 2C9.25 2.17188 9.21615 2.33333 9.14844 2.48438C9.08594 2.63542 8.9974 2.76823 8.88281 2.88281C8.76823 2.9974 8.63542 3.08854 8.48438 3.15625C8.33333 3.21875 8.17188 3.25 8 3.25C7.82812 3.25 7.66667 3.21875 7.51562 3.15625C7.36458 3.08854 7.23177 2.9974 7.11719 2.88281C7.0026 2.76823 6.91146 2.63542 6.84375 2.48438C6.78125 2.33333 6.75 2.17188 6.75 2C6.75 1.82812 6.78125 1.66667 6.84375 1.51562C6.91146 1.36458 7.0026 1.23177 7.11719 1.11719C7.23177 1.0026 7.36458 0.914062 7.51562 0.851562C7.66667 0.783854 7.82812 0.75 8 0.75ZM2.63281 3.75781C2.63281 3.60156 2.66146 3.45573 2.71875 3.32031C2.77604 3.1849 2.85417 3.06771 2.95312 2.96875C3.05729 2.86458 3.17708 2.78385 3.3125 2.72656C3.45312 2.66406 3.60156 2.63281 3.75781 2.63281C3.91406 2.63281 4.0599 2.66406 4.19531 2.72656C4.33073 2.78385 4.44792 2.86458 4.54688 2.96875C4.65104 3.06771 4.73177 3.1849 4.78906 3.32031C4.85156 3.45573 4.88281 3.60156 4.88281 3.75781C4.88281 3.91406 4.85156 4.0625 4.78906 4.20312C4.73177 4.33854 4.65104 4.45833 4.54688 4.5625C4.44792 4.66146 4.33073 4.73958 4.19531 4.79688C4.0599 4.85417 3.91406 4.88281 3.75781 4.88281C3.60156 4.88281 3.45312 4.85417 3.3125 4.79688C3.17708 4.73958 3.05729 4.66146 2.95312 4.5625C2.85417 4.45833 2.77604 4.33854 2.71875 4.20312C2.66146 4.0625 2.63281 3.91406 2.63281 3.75781ZM2 7C2.14062 7 2.27083 7.02604 2.39062 7.07812C2.51042 7.13021 2.61458 7.20312 2.70312 7.29688C2.79688 7.38542 2.86979 7.48958 2.92188 7.60938C2.97396 7.72917 3 7.85938 3 8C3 8.14062 2.97396 8.27083 2.92188 8.39062C2.86979 8.51042 2.79688 8.61719 2.70312 8.71094C2.61458 8.79948 2.51042 8.86979 2.39062 8.92188C2.27083 8.97396 2.14062 9 2 9C1.85938 9 1.72917 8.97396 1.60938 8.92188C1.48958 8.86979 1.38281 8.79948 1.28906 8.71094C1.20052 8.61719 1.13021 8.51042 1.07812 8.39062C1.02604 8.27083 1 8.14062 1 8C1 7.85938 1.02604 7.72917 1.07812 7.60938C1.13021 7.48958 1.20052 7.38542 1.28906 7.29688C1.38281 7.20312 1.48958 7.13021 1.60938 7.07812C1.72917 7.02604 1.85938 7 2 7ZM2.88281 12.2422C2.88281 12.1224 2.90625 12.0104 2.95312 11.9062C3 11.7969 3.0625 11.7031 3.14062 11.625C3.21875 11.5469 3.3099 11.4844 3.41406 11.4375C3.52344 11.3906 3.63802 11.3672 3.75781 11.3672C3.8776 11.3672 3.98958 11.3906 4.09375 11.4375C4.20312 11.4844 4.29688 11.5469 4.375 11.625C4.45312 11.7031 4.51562 11.7969 4.5625 11.9062C4.60938 12.0104 4.63281 12.1224 4.63281 12.2422C4.63281 12.362 4.60938 12.4766 4.5625 12.5859C4.51562 12.6901 4.45312 12.7812 4.375 12.8594C4.29688 12.9375 4.20312 13 4.09375 13.0469C3.98958 13.0938 3.8776 13.1172 3.75781 13.1172C3.63802 13.1172 3.52344 13.0938 3.41406 13.0469C3.3099 13 3.21875 12.9375 3.14062 12.8594C3.0625 12.7812 3 12.6901 2.95312 12.5859C2.90625 12.4766 2.88281 12.362 2.88281 12.2422ZM8 13.25C8.20833 13.25 8.38542 13.3229 8.53125 13.4688C8.67708 13.6146 8.75 13.7917 8.75 14C8.75 14.2083 8.67708 14.3854 8.53125 14.5312C8.38542 14.6771 8.20833 14.75 8 14.75C7.79167 14.75 7.61458 14.6771 7.46875 14.5312C7.32292 14.3854 7.25 14.2083 7.25 14C7.25 13.7917 7.32292 13.6146 7.46875 13.4688C7.61458 13.3229 7.79167 13.25 8 13.25ZM11.6172 12.2422C11.6172 12.0651 11.6771 11.9167 11.7969 11.7969C11.9167 11.6771 12.0651 11.6172 12.2422 11.6172C12.4193 11.6172 12.5677 11.6771 12.6875 11.7969C12.8073 11.9167 12.8672 12.0651 12.8672 12.2422C12.8672 12.4193 12.8073 12.5677 12.6875 12.6875C12.5677 12.8073 12.4193 12.8672 12.2422 12.8672C12.0651 12.8672 11.9167 12.8073 11.7969 12.6875C11.6771 12.5677 11.6172 12.4193 11.6172 12.2422ZM14 7.5C14.1354 7.5 14.2526 7.54948 14.3516 7.64844C14.4505 7.7474 14.5 7.86458 14.5 8C14.5 8.13542 14.4505 8.2526 14.3516 8.35156C14.2526 8.45052 14.1354 8.5 14 8.5C13.8646 8.5 13.7474 8.45052 13.6484 8.35156C13.5495 8.2526 13.5 8.13542 13.5 8C13.5 7.86458 13.5495 7.7474 13.6484 7.64844C13.7474 7.54948 13.8646 7.5 14 7.5ZM12.2422 2.38281C12.4297 2.38281 12.6068 2.41927 12.7734 2.49219C12.9401 2.5651 13.0859 2.66406 13.2109 2.78906C13.3359 2.91406 13.4349 3.0599 13.5078 3.22656C13.5807 3.39323 13.6172 3.57031 13.6172 3.75781C13.6172 3.94531 13.5807 4.1224 13.5078 4.28906C13.4349 4.45573 13.3359 4.60156 13.2109 4.72656C13.0859 4.85156 12.9401 4.95052 12.7734 5.02344C12.6068 5.09635 12.4297 5.13281 12.2422 5.13281C12.0547 5.13281 11.8776 5.09635 11.7109 5.02344C11.5443 4.95052 11.3984 4.85156 11.2734 4.72656C11.1484 4.60156 11.0495 4.45573 10.9766 4.28906C10.9036 4.1224 10.8672 3.94531 10.8672 3.75781C10.8672 3.57031 10.9036 3.39323 10.9766 3.22656C11.0495 3.0599 11.1484 2.91406 11.2734 2.78906C11.3984 2.66406 11.5443 2.5651 11.7109 2.49219C11.8776 2.41927 12.0547 2.38281 12.2422 2.38281Z" fill="#424242" />
|
||||
</svg>
|
After Width: | Height: | Size: 5.0 KiB |
@@ -1,5 +1,5 @@
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import { URI as Uri } from 'vscode-uri';
|
||||
import { URI as Uri } from '@theia/core/shared/vscode-uri';
|
||||
import URI from '@theia/core/lib/common/uri';
|
||||
import { Deferred } from '@theia/core/lib/common/promise-util';
|
||||
import {
|
||||
@@ -34,7 +34,6 @@ export class LocalCacheFsProvider
|
||||
@inject(AuthenticationClientService)
|
||||
protected readonly authenticationService: AuthenticationClientService;
|
||||
|
||||
// TODO: do we need this? Cannot we `await` on the `init` call from `registerFileSystemProviders`?
|
||||
readonly ready = new Deferred<void>();
|
||||
|
||||
private _localCacheRoot: URI;
|
||||
@@ -153,7 +152,7 @@ export class LocalCacheFsProvider
|
||||
return uri;
|
||||
}
|
||||
|
||||
private toUri(session: AuthenticationSession): URI {
|
||||
toUri(session: AuthenticationSession): URI {
|
||||
// Hack: instead of getting the UUID only, we get `auth0|UUID` after the authentication. `|` cannot be part of filesystem path or filename.
|
||||
return this._localCacheRoot.resolve(session.id.split('|')[1]);
|
||||
}
|
||||
|
@@ -1,6 +1,5 @@
|
||||
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';
|
||||
import {
|
||||
@@ -128,9 +127,7 @@ export class MonitorWidget extends ReactWidget {
|
||||
);
|
||||
};
|
||||
|
||||
protected get lineEndings(): OptionsType<
|
||||
SerialMonitorOutput.SelectOption<MonitorModel.EOL>
|
||||
> {
|
||||
protected get lineEndings(): SerialMonitorOutput.SelectOption<MonitorModel.EOL>[] {
|
||||
return [
|
||||
{
|
||||
label: nls.localize('arduino/serial/noLineEndings', 'No Line Ending'),
|
||||
|
@@ -55,6 +55,7 @@
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.p-Widget.dialogOverlay .dialogControl .spinner,
|
||||
.p-Widget.dialogOverlay .dialogBlock .dialogContent .dialogSection .dialogRow .spinner {
|
||||
background: var(--theia-icon-loading) center center no-repeat;
|
||||
animation: theia-spin 1.25s linear infinite;
|
||||
@@ -63,11 +64,11 @@
|
||||
}
|
||||
|
||||
.p-Widget.dialogOverlay .dialogBlock .dialogContent .dialogSection .dialogRow:first-child {
|
||||
margin-top: 0px;
|
||||
margin-top: 0px;
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
.fl1{
|
||||
.fl1 {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
@@ -80,10 +81,13 @@
|
||||
opacity: .4;
|
||||
}
|
||||
|
||||
|
||||
@media only screen and (max-height: 560px) {
|
||||
.p-Widget.dialogOverlay .dialogBlock {
|
||||
max-height: 400px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.p-Widget.dialogOverlay .error.progress {
|
||||
color: var(--theia-button-background);
|
||||
align-self: center;
|
||||
}
|
||||
|
@@ -20,6 +20,16 @@
|
||||
@import './progress-bar.css';
|
||||
@import './settings-step-input.css';
|
||||
|
||||
/* Revive of the `--theia-icon-loading`. The variable has been removed from Theia while IDE2 still uses is. */
|
||||
/* The SVG icons are still part of Theia (1.31.1) */
|
||||
/* https://github.com/arduino/arduino-ide/pull/1662#issuecomment-1324997134 */
|
||||
body {
|
||||
--theia-icon-loading: url(../icons/loading-light.svg);
|
||||
}
|
||||
body.theia-dark {
|
||||
--theia-icon-loading: url(../icons/loading-dark.svg);
|
||||
}
|
||||
|
||||
.theia-input.warning:focus {
|
||||
outline-width: 1px;
|
||||
outline-style: solid;
|
||||
@@ -166,3 +176,13 @@ button.theia-button.message-box-dialog-button {
|
||||
outline: 1px dashed var(--theia-focusBorder);
|
||||
outline-offset: -2px;
|
||||
}
|
||||
|
||||
.debug-toolbar .debug-action>div {
|
||||
font-family: var(--theia-ui-font-family);
|
||||
font-size: var(--theia-ui-font-size0);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
align-self: center;
|
||||
justify-content: center;
|
||||
min-height: inherit;
|
||||
}
|
||||
|
@@ -33,6 +33,22 @@
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.sketchbook-trees-container .create-new {
|
||||
min-height: 58px;
|
||||
height: 58px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
/*
|
||||
By default, theia-button has a left-margin. IDE2 does not need the left margin
|
||||
for the _New Remote? Sketch_. Otherwise, the button does not fit the default
|
||||
widget width.
|
||||
*/
|
||||
.sketchbook-trees-container .create-new .theia-button {
|
||||
margin-left: unset;
|
||||
}
|
||||
|
||||
.sketchbook-tree__opts {
|
||||
background-color: var(--theia-foreground);
|
||||
-webkit-mask: url(./sketchbook-opts-icon.svg);
|
||||
|
@@ -6,6 +6,8 @@ import {
|
||||
} from '@theia/core/lib/browser/common-frontend-contribution';
|
||||
import { CommandRegistry } from '@theia/core/lib/common/command';
|
||||
import type { OnWillStopAction } from '@theia/core/lib/browser/frontend-application';
|
||||
import { KeybindingRegistry } from '@theia/core/lib/browser';
|
||||
import { isOSX } from '@theia/core';
|
||||
|
||||
@injectable()
|
||||
export class CommonFrontendContribution extends TheiaCommonFrontendContribution {
|
||||
@@ -22,7 +24,7 @@ export class CommonFrontendContribution extends TheiaCommonFrontendContribution
|
||||
CommonCommands.TOGGLE_MAXIMIZED,
|
||||
CommonCommands.PIN_TAB,
|
||||
CommonCommands.UNPIN_TAB,
|
||||
CommonCommands.NEW_FILE,
|
||||
CommonCommands.NEW_UNTITLED_FILE,
|
||||
]) {
|
||||
commandRegistry.unregisterCommand(command);
|
||||
}
|
||||
@@ -50,6 +52,36 @@ export class CommonFrontendContribution extends TheiaCommonFrontendContribution
|
||||
}
|
||||
}
|
||||
|
||||
override registerKeybindings(registry: KeybindingRegistry): void {
|
||||
super.registerKeybindings(registry);
|
||||
// Workaround for https://github.com/eclipse-theia/theia/issues/11875
|
||||
if (isOSX) {
|
||||
registry.unregisterKeybinding('ctrlcmd+tab');
|
||||
registry.unregisterKeybinding('ctrlcmd+alt+d');
|
||||
registry.unregisterKeybinding('ctrlcmd+shift+tab');
|
||||
registry.unregisterKeybinding('ctrlcmd+alt+a');
|
||||
|
||||
registry.registerKeybindings(
|
||||
{
|
||||
command: CommonCommands.NEXT_TAB.id,
|
||||
keybinding: 'ctrl+tab',
|
||||
},
|
||||
{
|
||||
command: CommonCommands.NEXT_TAB.id,
|
||||
keybinding: 'ctrl+alt+d',
|
||||
},
|
||||
{
|
||||
command: CommonCommands.PREVIOUS_TAB.id,
|
||||
keybinding: 'ctrl+shift+tab',
|
||||
},
|
||||
{
|
||||
command: CommonCommands.PREVIOUS_TAB.id,
|
||||
keybinding: 'ctrl+alt+a',
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
override onWillStop(): OnWillStopAction | undefined {
|
||||
// This is NOOP here. All window close and app quit requests are handled in the `Close` contribution.
|
||||
return undefined;
|
||||
|
@@ -1,5 +1,4 @@
|
||||
import { injectable, inject } from '@theia/core/shared/inversify';
|
||||
import { FileService } from '@theia/filesystem/lib/browser/file-service';
|
||||
import { CommandService } from '@theia/core/lib/common/command';
|
||||
import { WorkspaceService } from '@theia/workspace/lib/browser/workspace-service';
|
||||
import { FrontendApplication as TheiaFrontendApplication } from '@theia/core/lib/browser/frontend-application';
|
||||
@@ -8,17 +7,16 @@ import { OpenSketchFiles } from '../../contributions/open-sketch-files';
|
||||
|
||||
@injectable()
|
||||
export class FrontendApplication extends TheiaFrontendApplication {
|
||||
@inject(FileService)
|
||||
protected readonly fileService: FileService;
|
||||
|
||||
@inject(WorkspaceService)
|
||||
protected readonly workspaceService: WorkspaceService;
|
||||
private readonly workspaceService: WorkspaceService;
|
||||
|
||||
@inject(CommandService)
|
||||
protected readonly commandService: CommandService;
|
||||
private readonly commandService: CommandService;
|
||||
|
||||
@inject(SketchesService)
|
||||
protected readonly sketchesService: SketchesService;
|
||||
private readonly sketchesService: SketchesService;
|
||||
|
||||
private layoutWasRestored = false;
|
||||
|
||||
protected override async initializeLayout(): Promise<void> {
|
||||
await super.initializeLayout();
|
||||
@@ -26,10 +24,16 @@ export class FrontendApplication extends TheiaFrontendApplication {
|
||||
for (const root of roots) {
|
||||
await this.commandService.executeCommand(
|
||||
OpenSketchFiles.Commands.OPEN_SKETCH_FILES.id,
|
||||
root.resource
|
||||
root.resource,
|
||||
!this.layoutWasRestored
|
||||
);
|
||||
this.sketchesService.markAsRecentlyOpened(root.resource.toString()); // no await, will get the notification later and rebuild the menu
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected override async restoreLayout(): Promise<boolean> {
|
||||
this.layoutWasRestored = await super.restoreLayout();
|
||||
return this.layoutWasRestored;
|
||||
}
|
||||
}
|
||||
|
@@ -1,13 +0,0 @@
|
||||
import { injectable } from '@theia/core/shared/inversify';
|
||||
import { StatusBarImpl as TheiaStatusBarImpl } from '@theia/core/lib/browser';
|
||||
|
||||
@injectable()
|
||||
export class StatusBarImpl extends TheiaStatusBarImpl {
|
||||
override async removeElement(id: string): Promise<void> {
|
||||
await this.ready;
|
||||
if (this.entries.delete(id)) {
|
||||
// Unlike Theia, IDE2 updates the status bar only if the element to remove was among the entries. Otherwise, it's a NOOP.
|
||||
this.update();
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,61 +0,0 @@
|
||||
import * as React from '@theia/core/shared/react';
|
||||
import { injectable } from '@theia/core/shared/inversify';
|
||||
import { LabelIcon } from '@theia/core/lib/browser/label-parser';
|
||||
import {
|
||||
TabBarToolbar as TheiaTabBarToolbar,
|
||||
TabBarToolbarItem,
|
||||
} from '@theia/core/lib/browser/shell/tab-bar-toolbar';
|
||||
|
||||
@injectable()
|
||||
export class TabBarToolbar extends TheiaTabBarToolbar {
|
||||
/**
|
||||
* Copied over from Theia. Added an ID to the parent of the toolbar item (`--container`).
|
||||
* CSS3 does not support parent selectors but we want to style the parent of the toolbar item.
|
||||
*/
|
||||
protected override renderItem(item: TabBarToolbarItem): React.ReactNode {
|
||||
let innerText = '';
|
||||
const classNames = [];
|
||||
if (item.text) {
|
||||
for (const labelPart of this.labelParser.parse(item.text)) {
|
||||
if (typeof labelPart !== 'string' && LabelIcon.is(labelPart)) {
|
||||
const className = `fa fa-${labelPart.name}${
|
||||
labelPart.animation ? ' fa-' + labelPart.animation : ''
|
||||
}`;
|
||||
classNames.push(...className.split(' '));
|
||||
} else {
|
||||
innerText = labelPart;
|
||||
}
|
||||
}
|
||||
}
|
||||
const command = this.commands.getCommand(item.command);
|
||||
const iconClass =
|
||||
(typeof item.icon === 'function' && item.icon()) ||
|
||||
item.icon ||
|
||||
(command && command.iconClass);
|
||||
if (iconClass) {
|
||||
classNames.push(iconClass);
|
||||
}
|
||||
const tooltip = item.tooltip || (command && command.label);
|
||||
return (
|
||||
<div
|
||||
id={`${item.id}--container`}
|
||||
key={item.id}
|
||||
className={`${TabBarToolbar.Styles.TAB_BAR_TOOLBAR_ITEM}${
|
||||
command && this.commandIsEnabled(command.id) ? ' enabled' : ''
|
||||
}`}
|
||||
onMouseDown={this.onMouseDownEvent}
|
||||
onMouseUp={this.onMouseUpEvent}
|
||||
onMouseOut={this.onMouseUpEvent}
|
||||
>
|
||||
<div
|
||||
id={item.id}
|
||||
className={classNames.join(' ')}
|
||||
onClick={this.executeCommand}
|
||||
title={tooltip}
|
||||
>
|
||||
{innerText}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
26
arduino-ide-extension/src/browser/theia/core/theming.ts
Normal file
26
arduino-ide-extension/src/browser/theia/core/theming.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { ThemeService as TheiaThemeService } from '@theia/core/lib/browser/theming';
|
||||
import type { Theme } from '@theia/core/lib/common/theme';
|
||||
import { injectable } from '@theia/core/shared/inversify';
|
||||
|
||||
export namespace ArduinoThemes {
|
||||
export const Light: Theme = {
|
||||
id: 'arduino-theme',
|
||||
type: 'light',
|
||||
label: 'Light (Arduino)',
|
||||
editorTheme: 'arduino-theme',
|
||||
};
|
||||
export const Dark: Theme = {
|
||||
id: 'arduino-theme-dark',
|
||||
type: 'dark',
|
||||
label: 'Dark (Arduino)',
|
||||
editorTheme: 'arduino-theme-dark',
|
||||
};
|
||||
}
|
||||
|
||||
@injectable()
|
||||
export class ThemeService extends TheiaThemeService {
|
||||
protected override init(): void {
|
||||
this.register(ArduinoThemes.Light, ArduinoThemes.Dark);
|
||||
super.init();
|
||||
}
|
||||
}
|
@@ -1,4 +1,3 @@
|
||||
import type { MaybePromise } from '@theia/core';
|
||||
import type { Widget } from '@theia/core/lib/browser';
|
||||
import { WidgetManager as TheiaWidgetManager } from '@theia/core/lib/browser/widget-manager';
|
||||
import {
|
||||
@@ -8,7 +7,6 @@ import {
|
||||
} from '@theia/core/shared/inversify';
|
||||
import { EditorWidget } from '@theia/editor/lib/browser';
|
||||
import { OutputWidget } from '@theia/output/lib/browser/output-widget';
|
||||
import deepEqual = require('deep-equal');
|
||||
import {
|
||||
CurrentSketch,
|
||||
SketchesServiceClientImpl,
|
||||
@@ -72,44 +70,4 @@ export class WidgetManager extends TheiaWidgetManager {
|
||||
title.className += title.className + ` ${uncloseableClass}`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Customized to find any existing widget based on `options` deepEquals instead of string equals.
|
||||
* See https://github.com/eclipse-theia/theia/issues/11309.
|
||||
*/
|
||||
protected override doGetWidget<T extends Widget>(
|
||||
key: string
|
||||
): MaybePromise<T> | undefined {
|
||||
const pendingWidget = this.findExistingWidget<T>(key);
|
||||
if (pendingWidget) {
|
||||
return pendingWidget as MaybePromise<T>;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private findExistingWidget<T extends Widget>(
|
||||
key: string
|
||||
): MaybePromise<T> | undefined {
|
||||
const parsed = this.parseJson(key);
|
||||
for (const [candidateKey, widget] of [
|
||||
...this.widgetPromises.entries(),
|
||||
...this.pendingWidgetPromises.entries(),
|
||||
]) {
|
||||
const candidate = this.parseJson(candidateKey);
|
||||
if (deepEqual(candidate, parsed)) {
|
||||
return widget as MaybePromise<T>;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
private parseJson(json: string): any {
|
||||
try {
|
||||
return JSON.parse(json);
|
||||
} catch (err) {
|
||||
console.log(`Failed to parse JSON: <${json}>.`, err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,87 @@
|
||||
import * as remote from '@theia/core/electron-shared/@electron/remote';
|
||||
import { FrontendApplicationConfigProvider } from '@theia/core/lib/browser/frontend-application-config-provider';
|
||||
import { NavigatableWidget } from '@theia/core/lib/browser/navigatable-types';
|
||||
import { ApplicationShell } from '@theia/core/lib/browser/shell/application-shell';
|
||||
import { Widget } from '@theia/core/lib/browser/widgets/widget';
|
||||
import { WindowTitleUpdater as TheiaWindowTitleUpdater } from '@theia/core/lib/browser/window/window-title-updater';
|
||||
import { ApplicationServer } from '@theia/core/lib/common/application-protocol';
|
||||
import { isOSX } from '@theia/core/lib/common/os';
|
||||
import {
|
||||
inject,
|
||||
injectable,
|
||||
postConstruct,
|
||||
} from '@theia/core/shared/inversify';
|
||||
import { EditorWidget } from '@theia/editor/lib/browser/editor-widget';
|
||||
import { WorkspaceService } from '@theia/workspace/lib/browser/workspace-service';
|
||||
|
||||
@injectable()
|
||||
export class WindowTitleUpdater extends TheiaWindowTitleUpdater {
|
||||
@inject(ApplicationServer)
|
||||
private readonly applicationServer: ApplicationServer;
|
||||
@inject(ApplicationShell)
|
||||
private readonly applicationShell: ApplicationShell;
|
||||
@inject(WorkspaceService)
|
||||
private readonly workspaceService: WorkspaceService;
|
||||
|
||||
private _previousRepresentedFilename: string | undefined;
|
||||
|
||||
private readonly applicationName =
|
||||
FrontendApplicationConfigProvider.get().applicationName;
|
||||
private applicationVersion: string | undefined;
|
||||
|
||||
@postConstruct()
|
||||
protected init(): void {
|
||||
setTimeout(
|
||||
() =>
|
||||
this.applicationServer.getApplicationInfo().then((info) => {
|
||||
this.applicationVersion = info?.version;
|
||||
if (this.applicationVersion) {
|
||||
this.handleWidgetChange(this.applicationShell.currentWidget);
|
||||
}
|
||||
}),
|
||||
0
|
||||
);
|
||||
}
|
||||
|
||||
protected override handleWidgetChange(widget?: Widget | undefined): void {
|
||||
if (isOSX) {
|
||||
this.maybeUpdateRepresentedFilename(widget);
|
||||
}
|
||||
// Unlike Theia, IDE2 does not want to show in the window title if the current widget is dirty or not.
|
||||
// Hence, IDE2 does not track widgets but updates the window title on current widget change.
|
||||
this.updateTitleWidget(widget);
|
||||
}
|
||||
|
||||
protected override updateTitleWidget(widget?: Widget | undefined): void {
|
||||
let activeEditorShort = '';
|
||||
const rootName = this.workspaceService.workspace?.name ?? '';
|
||||
let appName = `${this.applicationName}${
|
||||
this.applicationVersion ? ` ${this.applicationVersion}` : ''
|
||||
}`;
|
||||
if (rootName) {
|
||||
appName = ` | ${appName}`;
|
||||
}
|
||||
const uri = NavigatableWidget.getUri(widget);
|
||||
if (uri) {
|
||||
const base = uri.path.base;
|
||||
// Do not show the basename of the main sketch file. Only other sketch file names are visible in the title.
|
||||
if (`${rootName}.ino` !== base) {
|
||||
activeEditorShort = ` - ${base} `;
|
||||
}
|
||||
}
|
||||
this.windowTitleService.update({ rootName, appName, activeEditorShort });
|
||||
}
|
||||
|
||||
private maybeUpdateRepresentedFilename(widget?: Widget | undefined): void {
|
||||
if (widget instanceof EditorWidget) {
|
||||
const { uri } = widget.editor;
|
||||
const filename = uri.path.toString();
|
||||
// Do not necessarily require the current window if not needed. It's a synchronous, blocking call.
|
||||
if (this._previousRepresentedFilename !== filename) {
|
||||
const currentWindow = remote.getCurrentWindow();
|
||||
currentWindow.setRepresentedFilename(uri.path.toString());
|
||||
this._previousRepresentedFilename = filename;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,29 @@
|
||||
import * as React from '@theia/core/shared/react';
|
||||
import { DebugAction as TheiaDebugAction } from '@theia/debug/lib/browser/view/debug-action';
|
||||
import {
|
||||
codiconArray,
|
||||
DISABLED_CLASS,
|
||||
} from '@theia/core/lib/browser/widgets/widget';
|
||||
|
||||
// customized debug action to show the contributed command's label when there is no icon
|
||||
export class DebugAction extends TheiaDebugAction {
|
||||
override render(): React.ReactNode {
|
||||
const { enabled, label, iconClass } = this.props;
|
||||
const classNames = ['debug-action', ...codiconArray(iconClass, true)];
|
||||
if (enabled === false) {
|
||||
classNames.push(DISABLED_CLASS);
|
||||
}
|
||||
return (
|
||||
<span
|
||||
tabIndex={0}
|
||||
className={classNames.join(' ')}
|
||||
title={label}
|
||||
onClick={this.props.run}
|
||||
ref={this.setRef}
|
||||
>
|
||||
{!iconClass ||
|
||||
(iconClass.match(/plugin-icon-\d+/) && <div>{label}</div>)}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
}
|
@@ -1,5 +1,9 @@
|
||||
import debounce = require('p-debounce');
|
||||
import { inject, injectable, postConstruct } from '@theia/core/shared/inversify';
|
||||
import {
|
||||
inject,
|
||||
injectable,
|
||||
postConstruct,
|
||||
} from '@theia/core/shared/inversify';
|
||||
import URI from '@theia/core/lib/common/uri';
|
||||
import { Event, Emitter } from '@theia/core/lib/common/event';
|
||||
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
|
||||
@@ -126,7 +130,7 @@ export class DebugConfigurationManager extends TheiaDebugConfigurationManager {
|
||||
const uri = tempFolderUri.resolve('launch.json');
|
||||
const { value } = await this.fileService.read(uri);
|
||||
const configurations = DebugConfigurationModel.parse(JSON.parse(value));
|
||||
return { uri, configurations };
|
||||
return { uri, configurations, compounds: [] };
|
||||
} catch (err) {
|
||||
if (
|
||||
err instanceof FileOperationError &&
|
||||
|
@@ -29,6 +29,7 @@ export class DebugConfigurationModel extends TheiaDebugConfigurationModel {
|
||||
return {
|
||||
uri: this.configUri,
|
||||
configurations: this.config,
|
||||
compounds: [],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,49 @@
|
||||
import { injectable } from '@theia/core/shared/inversify';
|
||||
import { DebugSessionConnection } from '@theia/debug/lib/browser/debug-session-connection';
|
||||
import { DefaultDebugSessionFactory as TheiaDefaultDebugSessionFactory } from '@theia/debug/lib/browser/debug-session-contribution';
|
||||
import { DebugConfigurationSessionOptions } from '@theia/debug/lib/browser/debug-session-options';
|
||||
import {
|
||||
DebugAdapterPath,
|
||||
DebugChannel,
|
||||
ForwardingDebugChannel,
|
||||
} from '@theia/debug/lib/common/debug-service';
|
||||
import { DebugSession } from './debug-session';
|
||||
|
||||
@injectable()
|
||||
export class DefaultDebugSessionFactory extends TheiaDefaultDebugSessionFactory {
|
||||
override get(
|
||||
sessionId: string,
|
||||
options: DebugConfigurationSessionOptions,
|
||||
parentSession?: DebugSession
|
||||
): DebugSession {
|
||||
const connection = new DebugSessionConnection(
|
||||
sessionId,
|
||||
() =>
|
||||
new Promise<DebugChannel>((resolve) =>
|
||||
this.connectionProvider.openChannel(
|
||||
`${DebugAdapterPath}/${sessionId}`,
|
||||
(wsChannel) => {
|
||||
resolve(new ForwardingDebugChannel(wsChannel));
|
||||
},
|
||||
{ reconnecting: false }
|
||||
)
|
||||
),
|
||||
this.getTraceOutputChannel()
|
||||
);
|
||||
// patched debug session
|
||||
return new DebugSession(
|
||||
sessionId,
|
||||
options,
|
||||
parentSession,
|
||||
connection,
|
||||
this.terminalService,
|
||||
this.editorManager,
|
||||
this.breakpoints,
|
||||
this.labelProvider,
|
||||
this.messages,
|
||||
this.fileService,
|
||||
this.debugContributionProvider,
|
||||
this.workspaceService
|
||||
);
|
||||
}
|
||||
}
|
@@ -1,90 +1,120 @@
|
||||
import { injectable } from '@theia/core/shared/inversify';
|
||||
import { DebugError } from '@theia/debug/lib/common/debug-service';
|
||||
import { DebugSession } from '@theia/debug/lib/browser/debug-session';
|
||||
import { DebugSessionOptions } from '@theia/debug/lib/browser/debug-session-options';
|
||||
import type { ContextKey } from '@theia/core/lib/browser/context-key-service';
|
||||
import { injectable, postConstruct } from '@theia/core/shared/inversify';
|
||||
import {
|
||||
DebugSession,
|
||||
DebugState,
|
||||
} from '@theia/debug/lib/browser/debug-session';
|
||||
import { DebugSessionManager as TheiaDebugSessionManager } from '@theia/debug/lib/browser/debug-session-manager';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
import type { DebugConfigurationSessionOptions } from '@theia/debug/lib/browser/debug-session-options';
|
||||
|
||||
function debugStateLabel(state: DebugState): string {
|
||||
switch (state) {
|
||||
case DebugState.Initializing:
|
||||
return 'initializing';
|
||||
case DebugState.Stopped:
|
||||
return 'stopped';
|
||||
case DebugState.Running:
|
||||
return 'running';
|
||||
default:
|
||||
return 'inactive';
|
||||
}
|
||||
}
|
||||
|
||||
@injectable()
|
||||
export class DebugSessionManager extends TheiaDebugSessionManager {
|
||||
override async start(options: DebugSessionOptions): Promise<DebugSession | undefined> {
|
||||
return this.progressService.withProgress(
|
||||
nls.localize('theia/debug/start', 'Start...'),
|
||||
'debug',
|
||||
async () => {
|
||||
try {
|
||||
// Only save when dirty. To avoid saving temporary sketches.
|
||||
// This is a quick fix for not saving the editor when there are no dirty editors.
|
||||
// // https://github.com/bcmi-labs/arduino-editor/pull/172#issuecomment-741831888
|
||||
if (this.shell.canSaveAll()) {
|
||||
await this.shell.saveAll();
|
||||
}
|
||||
await this.fireWillStartDebugSession();
|
||||
const resolved = await this.resolveConfiguration(options);
|
||||
protected debugStateKey: ContextKey<string>;
|
||||
|
||||
//#region "cherry-picked" from here: https://github.com/eclipse-theia/theia/commit/e6b57ba4edabf797f3b4e67bc2968cdb8cc25b1e#diff-08e04edb57cd2af199382337aaf1dbdb31171b37ae4ab38a38d36cd77bc656c7R196-R207
|
||||
if (!resolved) {
|
||||
// As per vscode API: https://code.visualstudio.com/api/references/vscode-api#DebugConfigurationProvider
|
||||
// "Returning the value 'undefined' prevents the debug session from starting.
|
||||
// Returning the value 'null' prevents the debug session from starting and opens the
|
||||
// underlying debug configuration instead."
|
||||
@postConstruct()
|
||||
protected override init(): void {
|
||||
this.debugStateKey = this.contextKeyService.createKey<string>(
|
||||
'debugState',
|
||||
debugStateLabel(this.state)
|
||||
);
|
||||
super.init();
|
||||
}
|
||||
|
||||
if (resolved === null) {
|
||||
this.debugConfigurationManager.openConfiguration();
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
//#endregion end of cherry-pick
|
||||
protected override fireDidChange(current: DebugSession | undefined): void {
|
||||
this.debugTypeKey.set(current?.configuration.type);
|
||||
this.inDebugModeKey.set(this.inDebugMode);
|
||||
this.debugStateKey.set(debugStateLabel(this.state));
|
||||
this.onDidChangeEmitter.fire(current);
|
||||
}
|
||||
|
||||
// preLaunchTask isn't run in case of auto restart as well as postDebugTask
|
||||
if (!options.configuration.__restart) {
|
||||
const taskRun = await this.runTask(
|
||||
options.workspaceFolderUri,
|
||||
resolved.configuration.preLaunchTask,
|
||||
true
|
||||
);
|
||||
if (!taskRun) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
protected override async doStart(
|
||||
sessionId: string,
|
||||
options: DebugConfigurationSessionOptions
|
||||
): Promise<DebugSession> {
|
||||
const parentSession =
|
||||
options.configuration.parentSession &&
|
||||
this._sessions.get(options.configuration.parentSession.id);
|
||||
const contrib = this.sessionContributionRegistry.get(
|
||||
options.configuration.type
|
||||
);
|
||||
const sessionFactory = contrib
|
||||
? contrib.debugSessionFactory()
|
||||
: this.debugSessionFactory;
|
||||
const session = sessionFactory.get(sessionId, options, parentSession);
|
||||
this._sessions.set(sessionId, session);
|
||||
|
||||
const sessionId = await this.debug.createDebugSession(
|
||||
resolved.configuration
|
||||
);
|
||||
return this.doStart(sessionId, resolved);
|
||||
} catch (e) {
|
||||
if (DebugError.NotFound.is(e)) {
|
||||
this.messageService.error(
|
||||
nls.localize(
|
||||
'theia/debug/typeNotSupported',
|
||||
'The debug session type "{0}" is not supported.',
|
||||
e.data.type
|
||||
)
|
||||
);
|
||||
return undefined;
|
||||
}
|
||||
this.debugTypeKey.set(session.configuration.type);
|
||||
// this.onDidCreateDebugSessionEmitter.fire(session); // defer the didCreate event after start https://github.com/eclipse-theia/theia/issues/11916
|
||||
|
||||
this.messageService.error(
|
||||
nls.localize(
|
||||
'theia/debug/startError',
|
||||
'There was an error starting the debug session, check the logs for more details.'
|
||||
)
|
||||
);
|
||||
console.error('Error starting the debug session', e);
|
||||
throw e;
|
||||
let state = DebugState.Inactive;
|
||||
session.onDidChange(() => {
|
||||
if (state !== session.state) {
|
||||
state = session.state;
|
||||
if (state === DebugState.Stopped) {
|
||||
this.onDidStopDebugSessionEmitter.fire(session);
|
||||
}
|
||||
}
|
||||
this.updateCurrentSession(session);
|
||||
});
|
||||
session.onDidChangeBreakpoints((uri) =>
|
||||
this.fireDidChangeBreakpoints({ session, uri })
|
||||
);
|
||||
}
|
||||
override async terminateSession(session?: DebugSession): Promise<void> {
|
||||
if (!session) {
|
||||
this.updateCurrentSession(this._currentSession);
|
||||
session = this._currentSession;
|
||||
}
|
||||
// The cortex-debug extension does not respond to close requests
|
||||
// So we simply terminate the debug session immediately
|
||||
// Alternatively the `super.terminateSession` call will terminate it after 5 seconds without a response
|
||||
await this.debug.terminateDebugSession(session!.id);
|
||||
await super.terminateSession(session);
|
||||
session.on('terminated', async (event) => {
|
||||
const restart = event.body && event.body.restart;
|
||||
if (restart) {
|
||||
// postDebugTask isn't run in case of auto restart as well as preLaunchTask
|
||||
this.doRestart(session, !!restart);
|
||||
} else {
|
||||
await session.disconnect(false, () =>
|
||||
this.debug.terminateDebugSession(session.id)
|
||||
);
|
||||
await this.runTask(
|
||||
session.options.workspaceFolderUri,
|
||||
session.configuration.postDebugTask
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// eslint-disable-next-line unused-imports/no-unused-vars, @typescript-eslint/no-unused-vars
|
||||
session.on('exited', async (event) => {
|
||||
await session.disconnect(false, () =>
|
||||
this.debug.terminateDebugSession(session.id)
|
||||
);
|
||||
});
|
||||
|
||||
session.onDispose(() => this.cleanup(session));
|
||||
session
|
||||
.start()
|
||||
.then(() => {
|
||||
this.onDidCreateDebugSessionEmitter.fire(session); // now fire the didCreate event
|
||||
this.onDidStartDebugSessionEmitter.fire(session);
|
||||
})
|
||||
// eslint-disable-next-line unused-imports/no-unused-vars, @typescript-eslint/no-unused-vars
|
||||
.catch((e) => {
|
||||
session.stop(false, () => {
|
||||
this.debug.terminateDebugSession(session.id);
|
||||
});
|
||||
});
|
||||
session.onDidCustomEvent(({ event, body }) =>
|
||||
this.onDidReceiveDebugSessionCustomEventEmitter.fire({
|
||||
event,
|
||||
body,
|
||||
session,
|
||||
})
|
||||
);
|
||||
return session;
|
||||
}
|
||||
}
|
||||
|
231
arduino-ide-extension/src/browser/theia/debug/debug-session.ts
Normal file
231
arduino-ide-extension/src/browser/theia/debug/debug-session.ts
Normal file
@@ -0,0 +1,231 @@
|
||||
import { FrontendApplicationConfigProvider } from '@theia/core/lib/browser/frontend-application-config-provider';
|
||||
import { Deferred } from '@theia/core/lib/common/promise-util';
|
||||
import { Mutable } from '@theia/core/lib/common/types';
|
||||
import { URI } from '@theia/core/lib/common/uri';
|
||||
import { DebugSession as TheiaDebugSession } from '@theia/debug/lib/browser/debug-session';
|
||||
import { DebugFunctionBreakpoint } from '@theia/debug/lib/browser/model/debug-function-breakpoint';
|
||||
import { DebugSourceBreakpoint } from '@theia/debug/lib/browser/model/debug-source-breakpoint';
|
||||
import {
|
||||
DebugThreadData,
|
||||
StoppedDetails,
|
||||
} from '@theia/debug/lib/browser/model/debug-thread';
|
||||
import { DebugProtocol } from '@vscode/debugprotocol';
|
||||
import { DebugThread } from './debug-thread';
|
||||
|
||||
export class DebugSession extends TheiaDebugSession {
|
||||
/**
|
||||
* The `send('initialize')` request resolves later than `on('initialized')` emits the event.
|
||||
* Hence, the `configure` would use the empty object `capabilities`.
|
||||
* Using the empty `capabilities` could result in missing exception breakpoint filters, as
|
||||
* always `capabilities.exceptionBreakpointFilters` is falsy. This deferred promise works
|
||||
* around this timing issue.
|
||||
* See: https://github.com/eclipse-theia/theia/issues/11886.
|
||||
*/
|
||||
protected didReceiveCapabilities = new Deferred();
|
||||
|
||||
protected override async initialize(): Promise<void> {
|
||||
const clientName = FrontendApplicationConfigProvider.get().applicationName;
|
||||
try {
|
||||
const response = await this.connection.sendRequest('initialize', {
|
||||
clientID: clientName.toLocaleLowerCase().replace(/ /g, '_'),
|
||||
clientName,
|
||||
adapterID: this.configuration.type,
|
||||
locale: 'en-US',
|
||||
linesStartAt1: true,
|
||||
columnsStartAt1: true,
|
||||
pathFormat: 'path',
|
||||
supportsVariableType: false,
|
||||
supportsVariablePaging: false,
|
||||
supportsRunInTerminalRequest: true,
|
||||
});
|
||||
this.updateCapabilities(response?.body || {});
|
||||
this.didReceiveCapabilities.resolve();
|
||||
} catch (err) {
|
||||
this.didReceiveCapabilities.reject(err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
protected override async configure(): Promise<void> {
|
||||
await this.didReceiveCapabilities.promise;
|
||||
return super.configure();
|
||||
}
|
||||
|
||||
override async stop(isRestart: boolean, callback: () => void): Promise<void> {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const _this = this as any;
|
||||
if (!_this.isStopping) {
|
||||
_this.isStopping = true;
|
||||
if (this.configuration.lifecycleManagedByParent && this.parentSession) {
|
||||
await this.parentSession.stop(isRestart, callback);
|
||||
} else {
|
||||
if (this.canTerminate()) {
|
||||
const terminated = this.waitFor('terminated', 5000);
|
||||
try {
|
||||
await this.connection.sendRequest(
|
||||
'terminate',
|
||||
{ restart: isRestart },
|
||||
5000
|
||||
);
|
||||
await terminated;
|
||||
} catch (e) {
|
||||
console.error('Did not receive terminated event in time', e);
|
||||
}
|
||||
} else {
|
||||
const terminateDebuggee =
|
||||
this.initialized && this.capabilities.supportTerminateDebuggee;
|
||||
// Related https://github.com/microsoft/vscode/issues/165138
|
||||
try {
|
||||
await this.sendRequest(
|
||||
'disconnect',
|
||||
{ restart: isRestart, terminateDebuggee },
|
||||
2000
|
||||
);
|
||||
} catch (err) {
|
||||
if (
|
||||
'message' in err &&
|
||||
typeof err.message === 'string' &&
|
||||
err.message.test(err.message)
|
||||
) {
|
||||
// VS Code ignores errors when sending the `disconnect` request.
|
||||
// Debug adapter might not send the `disconnected` event as a response.
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
callback();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override async sendFunctionBreakpoints(
|
||||
affectedUri: URI
|
||||
): Promise<void> {
|
||||
const all = this.breakpoints
|
||||
.getFunctionBreakpoints()
|
||||
.map(
|
||||
(origin) =>
|
||||
new DebugFunctionBreakpoint(origin, this.asDebugBreakpointOptions())
|
||||
);
|
||||
const enabled = all.filter((b) => b.enabled);
|
||||
if (this.capabilities.supportsFunctionBreakpoints) {
|
||||
try {
|
||||
const response = await this.sendRequest('setFunctionBreakpoints', {
|
||||
breakpoints: enabled.map((b) => b.origin.raw),
|
||||
});
|
||||
// Apparently, `body` and `breakpoints` can be missing.
|
||||
// https://github.com/eclipse-theia/theia/issues/11885
|
||||
// https://github.com/microsoft/vscode/blob/80004351ccf0884b58359f7c8c801c91bb827d83/src/vs/workbench/contrib/debug/browser/debugSession.ts#L448-L449
|
||||
if (response && response.body) {
|
||||
response.body.breakpoints.forEach((raw, index) => {
|
||||
// node debug adapter returns more breakpoints sometimes
|
||||
if (enabled[index]) {
|
||||
enabled[index].update({ raw });
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
// could be error or promise rejection of DebugProtocol.SetFunctionBreakpoints
|
||||
if (error instanceof Error) {
|
||||
console.error(`Error setting breakpoints: ${error.message}`);
|
||||
} else {
|
||||
// handle adapters that send failed DebugProtocol.SetFunctionBreakpoints for invalid breakpoints
|
||||
const genericMessage =
|
||||
'Function breakpoint not valid for current debug session';
|
||||
const message = error.message ? `${error.message}` : genericMessage;
|
||||
console.warn(
|
||||
`Could not handle function breakpoints: ${message}, disabling...`
|
||||
);
|
||||
enabled.forEach((b) =>
|
||||
b.update({
|
||||
raw: {
|
||||
verified: false,
|
||||
message,
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.setBreakpoints(affectedUri, all);
|
||||
}
|
||||
|
||||
protected override async sendSourceBreakpoints(
|
||||
affectedUri: URI,
|
||||
sourceModified?: boolean
|
||||
): Promise<void> {
|
||||
const source = await this.toSource(affectedUri);
|
||||
const all = this.breakpoints
|
||||
.findMarkers({ uri: affectedUri })
|
||||
.map(
|
||||
({ data }) =>
|
||||
new DebugSourceBreakpoint(data, this.asDebugBreakpointOptions())
|
||||
);
|
||||
const enabled = all.filter((b) => b.enabled);
|
||||
try {
|
||||
const breakpoints = enabled.map(({ origin }) => origin.raw);
|
||||
const response = await this.sendRequest('setBreakpoints', {
|
||||
source: source.raw,
|
||||
sourceModified,
|
||||
breakpoints,
|
||||
lines: breakpoints.map(({ line }) => line),
|
||||
});
|
||||
response.body.breakpoints.forEach((raw, index) => {
|
||||
// node debug adapter returns more breakpoints sometimes
|
||||
if (enabled[index]) {
|
||||
enabled[index].update({ raw });
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
// could be error or promise rejection of DebugProtocol.SetBreakpointsResponse
|
||||
if (error instanceof Error) {
|
||||
console.error(`Error setting breakpoints: ${error.message}`);
|
||||
} else {
|
||||
// handle adapters that send failed DebugProtocol.SetBreakpointsResponse for invalid breakpoints
|
||||
const genericMessage = 'Breakpoint not valid for current debug session';
|
||||
const message = error.message ? `${error.message}` : genericMessage;
|
||||
console.warn(
|
||||
`Could not handle breakpoints for ${affectedUri}: ${message}, disabling...`
|
||||
);
|
||||
enabled.forEach((b) =>
|
||||
b.update({
|
||||
raw: {
|
||||
verified: false,
|
||||
message,
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
this.setSourceBreakpoints(affectedUri, all);
|
||||
}
|
||||
|
||||
protected override doUpdateThreads(
|
||||
threads: DebugProtocol.Thread[],
|
||||
stoppedDetails?: StoppedDetails
|
||||
): void {
|
||||
const existing = this._threads;
|
||||
this._threads = new Map();
|
||||
for (const raw of threads) {
|
||||
const id = raw.id;
|
||||
const thread = existing.get(id) || new DebugThread(this); // patched debug thread
|
||||
this._threads.set(id, thread);
|
||||
const data: Partial<Mutable<DebugThreadData>> = { raw };
|
||||
if (stoppedDetails) {
|
||||
if (stoppedDetails.threadId === id) {
|
||||
data.stoppedDetails = stoppedDetails;
|
||||
} else if (stoppedDetails.allThreadsStopped) {
|
||||
data.stoppedDetails = {
|
||||
// When a debug adapter notifies us that all threads are stopped,
|
||||
// we do not know why the others are stopped, so we should default
|
||||
// to something generic.
|
||||
reason: '',
|
||||
};
|
||||
}
|
||||
}
|
||||
thread.update(data);
|
||||
}
|
||||
this.updateCurrentThread(stoppedDetails);
|
||||
}
|
||||
}
|
@@ -0,0 +1,32 @@
|
||||
import { WidgetOpenerOptions } from '@theia/core/lib/browser/widget-open-handler';
|
||||
import { Range } from '@theia/core/shared/vscode-languageserver-types';
|
||||
import { DebugStackFrame as TheiaDebugStackFrame } from '@theia/debug/lib/browser/model/debug-stack-frame';
|
||||
import { EditorWidget } from '@theia/editor/lib/browser/editor-widget';
|
||||
|
||||
export class DebugStackFrame extends TheiaDebugStackFrame {
|
||||
override async open(
|
||||
options: WidgetOpenerOptions = {
|
||||
mode: 'reveal',
|
||||
}
|
||||
): Promise<EditorWidget | undefined> {
|
||||
if (!this.source) {
|
||||
return undefined;
|
||||
}
|
||||
const { line, column, endLine, endColumn, source } = this.raw;
|
||||
if (!source) {
|
||||
return undefined;
|
||||
}
|
||||
// create selection based on VS Code
|
||||
// https://github.com/eclipse-theia/theia/issues/11880
|
||||
const selection = Range.create(
|
||||
line,
|
||||
column,
|
||||
endLine || line,
|
||||
endColumn || column
|
||||
);
|
||||
this.source.open({
|
||||
...options,
|
||||
selection,
|
||||
});
|
||||
}
|
||||
}
|
@@ -0,0 +1,22 @@
|
||||
import { DebugStackFrame as TheiaDebugStackFrame } from '@theia/debug/lib/browser/model/debug-stack-frame';
|
||||
import { DebugThread as TheiaDebugThread } from '@theia/debug/lib/browser/model/debug-thread';
|
||||
import { DebugProtocol } from '@vscode/debugprotocol';
|
||||
import { DebugStackFrame } from './debug-stack-frame';
|
||||
|
||||
export class DebugThread extends TheiaDebugThread {
|
||||
protected override doUpdateFrames(
|
||||
frames: DebugProtocol.StackFrame[]
|
||||
): TheiaDebugStackFrame[] {
|
||||
const result = new Set<TheiaDebugStackFrame>();
|
||||
for (const raw of frames) {
|
||||
const id = raw.id;
|
||||
const frame =
|
||||
this._frames.get(id) || new DebugStackFrame(this, this.session); // patched debug stack frame
|
||||
this._frames.set(id, frame);
|
||||
frame.update({ raw });
|
||||
result.add(frame);
|
||||
}
|
||||
this.updateCurrentFrame();
|
||||
return [...result.values()];
|
||||
}
|
||||
}
|
@@ -0,0 +1,85 @@
|
||||
import { ContextKeyService } from '@theia/core/lib/browser/context-key-service';
|
||||
import { CommandRegistry } from '@theia/core/lib/common/command';
|
||||
import {
|
||||
ActionMenuNode,
|
||||
CompositeMenuNode,
|
||||
MenuModelRegistry,
|
||||
} from '@theia/core/lib/common/menu';
|
||||
import { nls } from '@theia/core/lib/common/nls';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import * as React from '@theia/core/shared/react';
|
||||
import { DebugState } from '@theia/debug/lib/browser/debug-session';
|
||||
import { DebugAction } from './debug-action';
|
||||
import { DebugToolBar as TheiaDebugToolbar } from '@theia/debug/lib/browser/view/debug-toolbar-widget';
|
||||
|
||||
@injectable()
|
||||
export class DebugToolbar extends TheiaDebugToolbar {
|
||||
@inject(CommandRegistry) private readonly commandRegistry: CommandRegistry;
|
||||
@inject(MenuModelRegistry)
|
||||
private readonly menuModelRegistry: MenuModelRegistry;
|
||||
@inject(ContextKeyService)
|
||||
private readonly contextKeyService: ContextKeyService;
|
||||
|
||||
protected override render(): React.ReactNode {
|
||||
const { state } = this.model;
|
||||
return (
|
||||
<React.Fragment>
|
||||
{this.renderContributedCommands()}
|
||||
{this.renderContinue()}
|
||||
<DebugAction
|
||||
enabled={state === DebugState.Stopped}
|
||||
run={this.stepOver}
|
||||
label={nls.localizeByDefault('Step Over')}
|
||||
iconClass="debug-step-over"
|
||||
ref={this.setStepRef}
|
||||
/>
|
||||
<DebugAction
|
||||
enabled={state === DebugState.Stopped}
|
||||
run={this.stepIn}
|
||||
label={nls.localizeByDefault('Step Into')}
|
||||
iconClass="debug-step-into"
|
||||
/>
|
||||
<DebugAction
|
||||
enabled={state === DebugState.Stopped}
|
||||
run={this.stepOut}
|
||||
label={nls.localizeByDefault('Step Out')}
|
||||
iconClass="debug-step-out"
|
||||
/>
|
||||
<DebugAction
|
||||
enabled={state !== DebugState.Inactive}
|
||||
run={this.restart}
|
||||
label={nls.localizeByDefault('Restart')}
|
||||
iconClass="debug-restart"
|
||||
/>
|
||||
{this.renderStart()}
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
private renderContributedCommands(): React.ReactNode {
|
||||
return this.menuModelRegistry
|
||||
.getMenu(TheiaDebugToolbar.MENU)
|
||||
.children.filter((node) => node instanceof CompositeMenuNode)
|
||||
.map((node) => (node as CompositeMenuNode).children)
|
||||
.reduce((acc, curr) => acc.concat(curr), [])
|
||||
.filter((node) => node instanceof ActionMenuNode)
|
||||
.map((node) => this.debugAction(node as ActionMenuNode));
|
||||
}
|
||||
|
||||
private debugAction(node: ActionMenuNode): React.ReactNode {
|
||||
const { label, command, when, icon: iconClass = '' } = node;
|
||||
const run = () => this.commandRegistry.executeCommand(command);
|
||||
const enabled = when ? this.contextKeyService.match(when) : true;
|
||||
return (
|
||||
enabled && (
|
||||
<DebugAction
|
||||
key={command}
|
||||
enabled={enabled}
|
||||
label={label}
|
||||
iconClass={iconClass}
|
||||
run={run}
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
@@ -1,17 +0,0 @@
|
||||
import { injectable, inject } from '@theia/core/shared/inversify';
|
||||
|
||||
import {
|
||||
AbstractDialog as TheiaAbstractDialog,
|
||||
codiconArray,
|
||||
DialogProps,
|
||||
} from '@theia/core/lib/browser';
|
||||
|
||||
@injectable()
|
||||
export abstract class AbstractDialog<T> extends TheiaAbstractDialog<T> {
|
||||
constructor(@inject(DialogProps) protected override readonly props: DialogProps) {
|
||||
super(props);
|
||||
|
||||
this.closeCrossNode.classList.remove(...codiconArray('close'));
|
||||
this.closeCrossNode.classList.add('fa', 'fa-close');
|
||||
}
|
||||
}
|
63
arduino-ide-extension/src/browser/theia/dialogs/dialogs.tsx
Normal file
63
arduino-ide-extension/src/browser/theia/dialogs/dialogs.tsx
Normal file
@@ -0,0 +1,63 @@
|
||||
import {
|
||||
AbstractDialog as TheiaAbstractDialog,
|
||||
DialogProps,
|
||||
} from '@theia/core/lib/browser/dialogs';
|
||||
import { ReactDialog as TheiaReactDialog } from '@theia/core/lib/browser/dialogs/react-dialog';
|
||||
import { codiconArray, Message } from '@theia/core/lib/browser/widgets/widget';
|
||||
import {
|
||||
Disposable,
|
||||
DisposableCollection,
|
||||
} from '@theia/core/lib/common/disposable';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import * as React from '@theia/core/shared/react';
|
||||
import { createRoot } from '@theia/core/shared/react-dom/client';
|
||||
|
||||
@injectable()
|
||||
export abstract class AbstractDialog<T> extends TheiaAbstractDialog<T> {
|
||||
constructor(
|
||||
@inject(DialogProps) protected override readonly props: DialogProps
|
||||
) {
|
||||
super(props);
|
||||
|
||||
this.closeCrossNode.classList.remove(...codiconArray('close'));
|
||||
this.closeCrossNode.classList.add('fa', 'fa-close');
|
||||
}
|
||||
}
|
||||
|
||||
@injectable()
|
||||
export abstract class ReactDialog<T> extends TheiaReactDialog<T> {
|
||||
protected override onUpdateRequest(msg: Message): void {
|
||||
// This is tricky to bypass the default Theia code.
|
||||
// Otherwise, there is a warning when opening the dialog for the second time.
|
||||
// You are calling ReactDOMClient.createRoot() on a container that has already been passed to createRoot() before. Instead, call root.render() on the existing root instead if you want to update it.
|
||||
const disposables = new DisposableCollection();
|
||||
if (!this.isMounted) {
|
||||
// toggle the `isMounted` logic for the time being of the super call so that the `createRoot` does not run
|
||||
this.isMounted = true;
|
||||
disposables.push(Disposable.create(() => (this.isMounted = false)));
|
||||
}
|
||||
|
||||
// Always unset the `contentNodeRoot` so there is no double update when calling super.
|
||||
const restoreContentNodeRoot = this.contentNodeRoot;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(this.contentNodeRoot as any) = undefined;
|
||||
disposables.push(
|
||||
Disposable.create(() => (this.contentNodeRoot = restoreContentNodeRoot))
|
||||
);
|
||||
|
||||
try {
|
||||
super.onUpdateRequest(msg);
|
||||
} finally {
|
||||
disposables.dispose();
|
||||
}
|
||||
|
||||
// Use the patched rendering.
|
||||
if (!this.isMounted) {
|
||||
this.contentNodeRoot = createRoot(this.contentNode);
|
||||
// Resetting the prop is missing from the Theia code.
|
||||
// https://github.com/eclipse-theia/theia/blob/v1.31.1/packages/core/src/browser/dialogs/react-dialog.tsx#L41-L47
|
||||
this.isMounted = true;
|
||||
}
|
||||
this.contentNodeRoot?.render(<>{this.render()}</>);
|
||||
}
|
||||
}
|
@@ -20,7 +20,7 @@ export class EditorWidgetFactory extends TheiaEditorWidgetFactory {
|
||||
|
||||
protected override async createEditor(
|
||||
uri: URI,
|
||||
options: NavigatableWidgetOptions
|
||||
options?: NavigatableWidgetOptions
|
||||
): Promise<EditorWidget> {
|
||||
const widget = await super.createEditor(uri, options);
|
||||
return this.maybeUpdateCaption(widget);
|
||||
|
@@ -3,7 +3,7 @@ import {
|
||||
injectable,
|
||||
postConstruct,
|
||||
} from '@theia/core/shared/inversify';
|
||||
import { Diagnostic } from 'vscode-languageserver-types';
|
||||
import { Diagnostic } from '@theia/core/shared/vscode-languageserver-types';
|
||||
import URI from '@theia/core/lib/common/uri';
|
||||
import { ILogger } from '@theia/core';
|
||||
import { Marker } from '@theia/markers/lib/common/marker';
|
||||
|
@@ -1,5 +1,4 @@
|
||||
import * as React from '@theia/core/shared/react';
|
||||
import * as ReactDOM from '@theia/core/shared/react-dom';
|
||||
import {
|
||||
inject,
|
||||
injectable,
|
||||
@@ -25,15 +24,14 @@ export class NotificationsRenderer extends TheiaNotificationsRenderer {
|
||||
}
|
||||
|
||||
protected override render(): void {
|
||||
ReactDOM.render(
|
||||
this.containerRoot.render(
|
||||
<div>
|
||||
<NotificationToastsComponent
|
||||
manager={this.manager}
|
||||
corePreferences={this.corePreferences}
|
||||
/>
|
||||
<NotificationCenterComponent manager={this.manager} />
|
||||
</div>,
|
||||
this.container
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,23 @@
|
||||
import { injectable } from '@theia/core/shared/inversify';
|
||||
import { MonacoThemingService as TheiaMonacoThemingService } from '@theia/monaco/lib/browser/monaco-theming-service';
|
||||
import { ArduinoThemes } from '../core/theming';
|
||||
|
||||
@injectable()
|
||||
export class MonacoThemingService extends TheiaMonacoThemingService {
|
||||
override initialize(): void {
|
||||
super.initialize();
|
||||
const { Light, Dark } = ArduinoThemes;
|
||||
this.registerParsedTheme({
|
||||
id: Light.id,
|
||||
label: Light.label,
|
||||
uiTheme: 'vs',
|
||||
json: require('../../../../src/browser/data/default.color-theme.json'),
|
||||
});
|
||||
this.registerParsedTheme({
|
||||
id: Dark.id,
|
||||
label: Dark.label,
|
||||
uiTheme: 'vs-dark',
|
||||
json: require('../../../../src/browser/data/dark.color-theme.json'),
|
||||
});
|
||||
}
|
||||
}
|
@@ -0,0 +1,66 @@
|
||||
import {
|
||||
Disposable,
|
||||
DisposableCollection,
|
||||
} from '@theia/core/lib/common/disposable';
|
||||
import { DebuggerDescription } from '@theia/debug/lib/common/debug-service';
|
||||
import { DebugMainImpl as TheiaDebugMainImpl } from '@theia/plugin-ext/lib/main/browser/debug/debug-main';
|
||||
import { PluginDebugAdapterContribution } from '@theia/plugin-ext/lib/main/browser/debug/plugin-debug-adapter-contribution';
|
||||
import { PluginDebugSessionFactory } from './plugin-debug-session-factory';
|
||||
|
||||
export class DebugMainImpl extends TheiaDebugMainImpl {
|
||||
override async $registerDebuggerContribution(
|
||||
description: DebuggerDescription
|
||||
): Promise<void> {
|
||||
const debugType = description.type;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const _this = <any>this;
|
||||
const terminalOptionsExt = await _this.debugExt.$getTerminalCreationOptions(
|
||||
debugType
|
||||
);
|
||||
|
||||
if (_this.toDispose.disposed) {
|
||||
return;
|
||||
}
|
||||
|
||||
const debugSessionFactory = new PluginDebugSessionFactory(
|
||||
_this.terminalService,
|
||||
_this.editorManager,
|
||||
_this.breakpointsManager,
|
||||
_this.labelProvider,
|
||||
_this.messages,
|
||||
_this.outputChannelManager,
|
||||
_this.debugPreferences,
|
||||
async (sessionId: string) => {
|
||||
const connection = await _this.connectionMain.ensureConnection(
|
||||
sessionId
|
||||
);
|
||||
return connection;
|
||||
},
|
||||
_this.fileService,
|
||||
terminalOptionsExt,
|
||||
_this.debugContributionProvider,
|
||||
_this.workspaceService
|
||||
);
|
||||
|
||||
const toDispose = new DisposableCollection(
|
||||
Disposable.create(() => _this.debuggerContributions.delete(debugType))
|
||||
);
|
||||
_this.debuggerContributions.set(debugType, toDispose);
|
||||
toDispose.pushAll([
|
||||
_this.pluginDebugService.registerDebugAdapterContribution(
|
||||
new PluginDebugAdapterContribution(
|
||||
description,
|
||||
_this.debugExt,
|
||||
_this.pluginService
|
||||
)
|
||||
),
|
||||
_this.sessionContributionRegistrator.registerDebugSessionContribution({
|
||||
debugType: description.type,
|
||||
debugSessionFactory: () => debugSessionFactory,
|
||||
}),
|
||||
]);
|
||||
_this.toDispose.push(
|
||||
Disposable.create(() => this.$unregisterDebuggerConfiguration(debugType))
|
||||
);
|
||||
}
|
||||
}
|
@@ -1,7 +1,17 @@
|
||||
import { Emitter, Event, JsonRpcProxy } from '@theia/core';
|
||||
import { injectable, interfaces } from '@theia/core/shared/inversify';
|
||||
import { HostedPluginServer } from '@theia/plugin-ext/lib/common/plugin-protocol';
|
||||
import { HostedPluginSupport as TheiaHostedPluginSupport } from '@theia/plugin-ext/lib/hosted/browser/hosted-plugin';
|
||||
import { RPCProtocol } from '@theia/plugin-ext/lib/common/rpc-protocol';
|
||||
import {
|
||||
HostedPluginSupport as TheiaHostedPluginSupport,
|
||||
PluginHost,
|
||||
} from '@theia/plugin-ext/lib/hosted/browser/hosted-plugin';
|
||||
import { PluginWorker } from '@theia/plugin-ext/lib/hosted/browser/plugin-worker';
|
||||
import { setUpPluginApi } from '@theia/plugin-ext/lib/main/browser/main-context';
|
||||
import { PLUGIN_RPC_CONTEXT } from '@theia/plugin-ext/lib/common/plugin-api-rpc';
|
||||
import { DebugMainImpl } from './debug-main';
|
||||
import { ConnectionImpl } from '@theia/plugin-ext/lib/common/connection';
|
||||
|
||||
@injectable()
|
||||
export class HostedPluginSupport extends TheiaHostedPluginSupport {
|
||||
private readonly onDidLoadEmitter = new Emitter<void>();
|
||||
@@ -31,4 +41,26 @@ export class HostedPluginSupport extends TheiaHostedPluginSupport {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
return (this as any).server;
|
||||
}
|
||||
|
||||
// to patch the VS Code extension based debugger
|
||||
// eslint-disable-next-line unused-imports/no-unused-vars, @typescript-eslint/no-unused-vars
|
||||
protected override initRpc(host: PluginHost, pluginId: string): RPCProtocol {
|
||||
const rpc =
|
||||
host === 'frontend' ? new PluginWorker().rpc : this.createServerRpc(host);
|
||||
setUpPluginApi(rpc, this.container);
|
||||
this.patchDebugMain(rpc);
|
||||
this.mainPluginApiProviders
|
||||
.getContributions()
|
||||
.forEach((p) => p.initialize(rpc, this.container));
|
||||
return rpc;
|
||||
}
|
||||
|
||||
private patchDebugMain(rpc: RPCProtocol): void {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const connectionMain = (rpc as any).locals.get(
|
||||
PLUGIN_RPC_CONTEXT.CONNECTION_MAIN.id
|
||||
) as ConnectionImpl;
|
||||
const debugMain = new DebugMainImpl(rpc, connectionMain, this.container);
|
||||
rpc.set(PLUGIN_RPC_CONTEXT.DEBUG_MAIN, debugMain);
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,37 @@
|
||||
import { DebugSession } from '@theia/debug/lib/browser/debug-session';
|
||||
import { DebugSessionConnection } from '@theia/debug/lib/browser/debug-session-connection';
|
||||
import { DebugConfigurationSessionOptions } from '@theia/debug/lib/browser/debug-session-options';
|
||||
import { PluginDebugSessionFactory as TheiaPluginDebugSessionFactory } from '@theia/plugin-ext/lib/main/browser/debug/plugin-debug-session-factory';
|
||||
import { PluginDebugSession } from './plugin-debug-session';
|
||||
|
||||
export class PluginDebugSessionFactory extends TheiaPluginDebugSessionFactory {
|
||||
override get(
|
||||
sessionId: string,
|
||||
options: DebugConfigurationSessionOptions,
|
||||
parentSession?: DebugSession
|
||||
): DebugSession {
|
||||
const connection = new DebugSessionConnection(
|
||||
sessionId,
|
||||
this.connectionFactory,
|
||||
this.getTraceOutputChannel()
|
||||
);
|
||||
|
||||
return new PluginDebugSession(
|
||||
sessionId,
|
||||
options,
|
||||
parentSession,
|
||||
connection,
|
||||
this.terminalService,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
this.editorManager as any,
|
||||
this.breakpoints,
|
||||
this.labelProvider,
|
||||
this.messages,
|
||||
this.fileService,
|
||||
this.terminalOptionsExt,
|
||||
this.debugContributionProvider,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
this.workspaceService as any
|
||||
);
|
||||
}
|
||||
}
|
@@ -0,0 +1,62 @@
|
||||
import { ContributionProvider, MessageClient } from '@theia/core';
|
||||
import { LabelProvider } from '@theia/core/lib/browser';
|
||||
import { BreakpointManager } from '@theia/debug/lib/browser/breakpoint/breakpoint-manager';
|
||||
import { DebugContribution } from '@theia/debug/lib/browser/debug-contribution';
|
||||
import { DebugSession as TheiaDebugSession } from '@theia/debug/lib/browser/debug-session';
|
||||
import { DebugSessionConnection } from '@theia/debug/lib/browser/debug-session-connection';
|
||||
import { DebugConfigurationSessionOptions } from '@theia/debug/lib/browser/debug-session-options';
|
||||
import { FileService } from '@theia/filesystem/lib/browser/file-service';
|
||||
import { TerminalOptionsExt } from '@theia/plugin-ext';
|
||||
import { TerminalService } from '@theia/terminal/lib/browser/base/terminal-service';
|
||||
import {
|
||||
TerminalWidget,
|
||||
TerminalWidgetOptions,
|
||||
} from '@theia/terminal/lib/browser/base/terminal-widget';
|
||||
import { DebugSession } from '../debug/debug-session';
|
||||
import { EditorManager } from '../editor/editor-manager';
|
||||
import { WorkspaceService } from '../workspace/workspace-service';
|
||||
|
||||
// This class extends the patched debug session, and not the default debug session from Theia
|
||||
export class PluginDebugSession extends DebugSession {
|
||||
constructor(
|
||||
override readonly id: string,
|
||||
override readonly options: DebugConfigurationSessionOptions,
|
||||
override readonly parentSession: TheiaDebugSession | undefined,
|
||||
protected override readonly connection: DebugSessionConnection,
|
||||
protected override readonly terminalServer: TerminalService,
|
||||
protected override readonly editorManager: EditorManager,
|
||||
protected override readonly breakpoints: BreakpointManager,
|
||||
protected override readonly labelProvider: LabelProvider,
|
||||
protected override readonly messages: MessageClient,
|
||||
protected override readonly fileService: FileService,
|
||||
protected readonly terminalOptionsExt: TerminalOptionsExt | undefined,
|
||||
protected override readonly debugContributionProvider: ContributionProvider<DebugContribution>,
|
||||
protected override readonly workspaceService: WorkspaceService
|
||||
) {
|
||||
super(
|
||||
id,
|
||||
options,
|
||||
parentSession,
|
||||
connection,
|
||||
terminalServer,
|
||||
editorManager,
|
||||
breakpoints,
|
||||
labelProvider,
|
||||
messages,
|
||||
fileService,
|
||||
debugContributionProvider,
|
||||
workspaceService
|
||||
);
|
||||
}
|
||||
|
||||
protected override async doCreateTerminal(
|
||||
terminalWidgetOptions: TerminalWidgetOptions
|
||||
): Promise<TerminalWidget> {
|
||||
terminalWidgetOptions = Object.assign(
|
||||
{},
|
||||
terminalWidgetOptions,
|
||||
this.terminalOptionsExt
|
||||
);
|
||||
return super.doCreateTerminal(terminalWidgetOptions);
|
||||
}
|
||||
}
|
@@ -0,0 +1,73 @@
|
||||
import { MenuPath } from '@theia/core';
|
||||
import { TAB_BAR_TOOLBAR_CONTEXT_MENU } from '@theia/core/lib/browser/shell/tab-bar-toolbar';
|
||||
import { injectable, postConstruct } from '@theia/core/shared/inversify';
|
||||
import { DebugToolBar } from '@theia/debug/lib/browser/view/debug-toolbar-widget';
|
||||
import { DebugVariablesWidget } from '@theia/debug/lib/browser/view/debug-variables-widget';
|
||||
import {
|
||||
ArgumentAdapter,
|
||||
PluginMenuCommandAdapter as TheiaPluginMenuCommandAdapter,
|
||||
} from '@theia/plugin-ext/lib/main/browser/menus/plugin-menu-command-adapter';
|
||||
import {
|
||||
codeToTheiaMappings,
|
||||
ContributionPoint,
|
||||
} from '@theia/plugin-ext/lib/main/browser/menus/vscode-theia-menu-mappings';
|
||||
|
||||
function patch(
|
||||
toPatch: typeof codeToTheiaMappings,
|
||||
key: string,
|
||||
value: MenuPath[]
|
||||
): void {
|
||||
const loose = toPatch as Map<string, MenuPath[]>;
|
||||
if (!loose.has(key)) {
|
||||
loose.set(key, value);
|
||||
}
|
||||
}
|
||||
// mappings is a const and cannot be customized with DI
|
||||
patch(codeToTheiaMappings, 'debug/variables/context', [
|
||||
DebugVariablesWidget.CONTEXT_MENU,
|
||||
]);
|
||||
patch(codeToTheiaMappings, 'debug/toolBar', [DebugToolBar.MENU]);
|
||||
|
||||
@injectable()
|
||||
export class PluginMenuCommandAdapter extends TheiaPluginMenuCommandAdapter {
|
||||
@postConstruct()
|
||||
protected override init(): void {
|
||||
const toCommentArgs: ArgumentAdapter = (...args) =>
|
||||
this.toCommentArgs(...args);
|
||||
const firstArgOnly: ArgumentAdapter = (...args) => [args[0]];
|
||||
const noArgs: ArgumentAdapter = () => [];
|
||||
const toScmArgs: ArgumentAdapter = (...args) => this.toScmArgs(...args);
|
||||
const selectedResource = () => this.getSelectedResources();
|
||||
const widgetURI: ArgumentAdapter = (widget) =>
|
||||
this.codeEditorUtil.is(widget)
|
||||
? [this.codeEditorUtil.getResourceUri(widget)]
|
||||
: [];
|
||||
(<Array<[ContributionPoint, ArgumentAdapter | undefined]>>[
|
||||
['comments/comment/context', toCommentArgs],
|
||||
['comments/comment/title', toCommentArgs],
|
||||
['comments/commentThread/context', toCommentArgs],
|
||||
['debug/callstack/context', firstArgOnly],
|
||||
['debug/variables/context', firstArgOnly],
|
||||
['debug/toolBar', noArgs],
|
||||
['editor/context', selectedResource],
|
||||
['editor/title', widgetURI],
|
||||
['editor/title/context', selectedResource],
|
||||
['explorer/context', selectedResource],
|
||||
['scm/resourceFolder/context', toScmArgs],
|
||||
['scm/resourceGroup/context', toScmArgs],
|
||||
['scm/resourceState/context', toScmArgs],
|
||||
['scm/title', () => this.toScmArg(this.scmService.selectedRepository)],
|
||||
['timeline/item/context', (...args) => this.toTimelineArgs(...args)],
|
||||
['view/item/context', (...args) => this.toTreeArgs(...args)],
|
||||
['view/title', noArgs],
|
||||
]).forEach(([contributionPoint, adapter]) => {
|
||||
if (adapter) {
|
||||
const paths = codeToTheiaMappings.get(contributionPoint);
|
||||
if (paths) {
|
||||
paths.forEach((path) => this.addArgumentAdapter(path, adapter));
|
||||
}
|
||||
}
|
||||
});
|
||||
this.addArgumentAdapter(TAB_BAR_TOOLBAR_CONTEXT_MENU, widgetURI);
|
||||
}
|
||||
}
|
@@ -0,0 +1,32 @@
|
||||
import { KeybindingRegistry } from '@theia/core/lib/browser/keybinding';
|
||||
import { CommandRegistry } from '@theia/core/lib/common/command';
|
||||
import { MenuModelRegistry } from '@theia/core/lib/common/menu';
|
||||
import { injectable } from '@theia/core/shared/inversify';
|
||||
import {
|
||||
TypeHierarchyCommands,
|
||||
TypeHierarchyContribution as TheiaTypeHierarchyContribution,
|
||||
} from '@theia/typehierarchy/lib/browser/typehierarchy-contribution';
|
||||
|
||||
@injectable()
|
||||
export class TypeHierarchyContribution extends TheiaTypeHierarchyContribution {
|
||||
protected override init(): void {
|
||||
// NOOP
|
||||
}
|
||||
|
||||
override registerCommands(registry: CommandRegistry): void {
|
||||
super.registerCommands(registry);
|
||||
registry.unregisterCommand(TypeHierarchyCommands.OPEN_SUBTYPE.id);
|
||||
registry.unregisterCommand(TypeHierarchyCommands.OPEN_SUPERTYPE.id);
|
||||
}
|
||||
|
||||
override registerMenus(registry: MenuModelRegistry): void {
|
||||
super.registerMenus(registry);
|
||||
registry.unregisterMenuAction(TypeHierarchyCommands.OPEN_SUBTYPE.id);
|
||||
registry.unregisterMenuAction(TypeHierarchyCommands.OPEN_SUPERTYPE.id);
|
||||
}
|
||||
|
||||
override registerKeybindings(registry: KeybindingRegistry): void {
|
||||
super.registerKeybindings(registry);
|
||||
registry.unregisterKeybinding(TypeHierarchyCommands.OPEN_SUBTYPE.id);
|
||||
}
|
||||
}
|
@@ -0,0 +1,9 @@
|
||||
import { injectable } from '@theia/core/shared/inversify';
|
||||
import { TypeHierarchyServiceProvider as TheiaTypeHierarchyServiceProvider } from '@theia/typehierarchy/lib/browser/typehierarchy-service';
|
||||
|
||||
@injectable()
|
||||
export class TypeHierarchyServiceProvider extends TheiaTypeHierarchyServiceProvider {
|
||||
override init(): void {
|
||||
// NOOP
|
||||
}
|
||||
}
|
@@ -1,58 +1,37 @@
|
||||
import * as remote from '@theia/core/electron-shared/@electron/remote';
|
||||
import { injectable, inject, named } from '@theia/core/shared/inversify';
|
||||
import { ContributionProvider } from '@theia/core/lib/common/contribution-provider';
|
||||
import URI from '@theia/core/lib/common/uri';
|
||||
import { EditorWidget } from '@theia/editor/lib/browser';
|
||||
import { ApplicationServer } from '@theia/core/lib/common/application-protocol';
|
||||
import { FrontendApplication } from '@theia/core/lib/browser/frontend-application';
|
||||
import { FocusTracker, Widget } from '@theia/core/lib/browser';
|
||||
import {
|
||||
DEFAULT_WINDOW_HASH,
|
||||
NewWindowOptions,
|
||||
} from '@theia/core/lib/common/window';
|
||||
import { inject, injectable, named } from '@theia/core/shared/inversify';
|
||||
import { FileStat } from '@theia/filesystem/lib/common/files';
|
||||
import {
|
||||
WorkspaceInput,
|
||||
WorkspaceService as TheiaWorkspaceService,
|
||||
} from '@theia/workspace/lib/browser/workspace-service';
|
||||
import {
|
||||
SketchesService,
|
||||
Sketch,
|
||||
SketchesError,
|
||||
SketchesService,
|
||||
} from '../../../common/protocol/sketches-service';
|
||||
import { FileStat } from '@theia/filesystem/lib/common/files';
|
||||
import {
|
||||
StartupTask,
|
||||
StartupTaskProvider,
|
||||
} from '../../../electron-common/startup-task';
|
||||
import { WindowServiceExt } from '../core/window-service-ext';
|
||||
import { ContributionProvider } from '@theia/core/lib/common/contribution-provider';
|
||||
|
||||
@injectable()
|
||||
export class WorkspaceService extends TheiaWorkspaceService {
|
||||
@inject(SketchesService)
|
||||
private readonly sketchService: SketchesService;
|
||||
@inject(ApplicationServer)
|
||||
private readonly applicationServer: ApplicationServer;
|
||||
@inject(WindowServiceExt)
|
||||
private readonly windowServiceExt: WindowServiceExt;
|
||||
@inject(ContributionProvider)
|
||||
@named(StartupTaskProvider)
|
||||
private readonly providers: ContributionProvider<StartupTaskProvider>;
|
||||
|
||||
private version?: string;
|
||||
private _workspaceError: Error | undefined;
|
||||
|
||||
async onStart(application: FrontendApplication): Promise<void> {
|
||||
const info = await this.applicationServer.getApplicationInfo();
|
||||
this.version = info?.version;
|
||||
application.shell.onDidChangeCurrentWidget(
|
||||
this.onCurrentWidgetChange.bind(this)
|
||||
);
|
||||
const newValue = application.shell.currentWidget
|
||||
? application.shell.currentWidget
|
||||
: null;
|
||||
this.onCurrentWidgetChange({ newValue, oldValue: null });
|
||||
}
|
||||
|
||||
get workspaceError(): Error | undefined {
|
||||
return this._workspaceError;
|
||||
}
|
||||
@@ -121,58 +100,6 @@ export class WorkspaceService extends TheiaWorkspaceService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copied from Theia as-is to be able to pass the original `options` down.
|
||||
*/
|
||||
protected override async doOpen(
|
||||
uri: URI,
|
||||
options?: WorkspaceInput
|
||||
): Promise<URI | undefined> {
|
||||
const stat = await this.toFileStat(uri);
|
||||
if (stat) {
|
||||
if (!stat.isDirectory && !this.isWorkspaceFile(stat)) {
|
||||
const message = `Not a valid workspace: ${uri.path.toString()}`;
|
||||
this.messageService.error(message);
|
||||
throw new Error(message);
|
||||
}
|
||||
// The same window has to be preserved too (instead of opening a new one), if the workspace root is not yet available and we are setting it for the first time.
|
||||
// Option passed as parameter has the highest priority (for api developers), then the preference, then the default.
|
||||
await this.roots;
|
||||
const { preserveWindow } = {
|
||||
preserveWindow:
|
||||
this.preferences['workspace.preserveWindow'] || !this.opened,
|
||||
...options,
|
||||
};
|
||||
await this.server.setMostRecentlyUsedWorkspace(uri.toString());
|
||||
if (preserveWindow) {
|
||||
this._workspace = stat;
|
||||
}
|
||||
this.openWindow(stat, Object.assign(options ?? {}, { preserveWindow })); // Unlike Theia, IDE2 passes the whole `input` downstream and not only { preserveWindow }
|
||||
return;
|
||||
}
|
||||
throw new Error(
|
||||
'Invalid workspace root URI. Expected an existing directory or workspace file.'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copied from Theia. Can pass the `options` further down the chain.
|
||||
*/
|
||||
protected override openWindow(uri: FileStat, options?: WorkspaceInput): void {
|
||||
const workspacePath = uri.resource.path.toString();
|
||||
if (this.shouldPreserveWindow(options)) {
|
||||
this.reloadWindow(options); // Unlike Theia, IDE2 passes the `input` downstream.
|
||||
} else {
|
||||
try {
|
||||
this.openNewWindow(workspacePath, options); // Unlike Theia, IDE2 passes the `input` downstream.
|
||||
} catch (error) {
|
||||
// Fall back to reloading the current window in case the browser has blocked the new window
|
||||
this._workspace = uri;
|
||||
this.logger.error(error.toString()).then(() => this.reloadWindow());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override reloadWindow(options?: WorkspaceInput): void {
|
||||
const tasks = this.tasks(options);
|
||||
this.setURLFragment(this._workspace?.resource.path.toString() || '');
|
||||
@@ -192,6 +119,10 @@ export class WorkspaceService extends TheiaWorkspaceService {
|
||||
);
|
||||
}
|
||||
|
||||
protected override updateTitle(): void {
|
||||
// NOOP. IDE2 handles the `window.title` updates solely via the customized `WindowTitleUpdater`.
|
||||
}
|
||||
|
||||
private tasks(options?: WorkspaceInput): StartupTask[] {
|
||||
const tasks = this.providers
|
||||
.getContributions()
|
||||
@@ -202,37 +133,4 @@ export class WorkspaceService extends TheiaWorkspaceService {
|
||||
}
|
||||
return tasks;
|
||||
}
|
||||
|
||||
protected onCurrentWidgetChange({
|
||||
newValue,
|
||||
}: FocusTracker.IChangedArgs<Widget>): void {
|
||||
if (newValue instanceof EditorWidget) {
|
||||
const { uri } = newValue.editor;
|
||||
const currentWindow = remote.getCurrentWindow();
|
||||
currentWindow.setRepresentedFilename(uri.path.toString());
|
||||
if (Sketch.isSketchFile(uri.toString())) {
|
||||
this.updateTitle();
|
||||
} else {
|
||||
const title = this.workspaceTitle;
|
||||
const fileName = this.labelProvider.getName(uri);
|
||||
document.title = this.formatTitle(
|
||||
title ? `${title} - ${fileName}` : fileName
|
||||
);
|
||||
}
|
||||
} else {
|
||||
this.updateTitle();
|
||||
}
|
||||
}
|
||||
|
||||
protected override formatTitle(title?: string): string {
|
||||
const version = this.version ? ` ${this.version}` : '';
|
||||
const name = `${this.applicationName} ${version}`;
|
||||
return title ? `${title} | ${name}` : name;
|
||||
}
|
||||
|
||||
protected get workspaceTitle(): string | undefined {
|
||||
if (this.workspace) {
|
||||
return this.labelProvider.getName(this.workspace.resource);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,17 +1,22 @@
|
||||
import * as React from '@theia/core/shared/react';
|
||||
import Select from 'react-select';
|
||||
import { Styles } from 'react-select/src/styles';
|
||||
import { Props } from 'react-select/src/components';
|
||||
import { ThemeConfig } from 'react-select/src/theme';
|
||||
import type { StylesConfig } from 'react-select/dist/declarations/src/styles';
|
||||
import type { ThemeConfig } from 'react-select/dist/declarations/src/theme';
|
||||
import type { GroupBase } from 'react-select/dist/declarations/src/types';
|
||||
import type { StateManagerProps } from 'react-select/dist/declarations/src/useStateManager';
|
||||
|
||||
export class ArduinoSelect<T> extends Select<T> {
|
||||
constructor(props: Readonly<Props<T, false>>) {
|
||||
export class ArduinoSelect<
|
||||
Option,
|
||||
IsMulti extends boolean = false,
|
||||
Group extends GroupBase<Option> = GroupBase<Option>
|
||||
> extends React.Component<StateManagerProps<Option, IsMulti, Group>> {
|
||||
constructor(props: StateManagerProps<Option, IsMulti, Group>) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
override render(): React.ReactNode {
|
||||
const controlHeight = 27; // from `monitor.css` -> `.serial-monitor-container .head` (`height: 27px;`)
|
||||
const styles: Styles<T, false> = {
|
||||
const styles: StylesConfig = {
|
||||
control: (styles) => ({
|
||||
...styles,
|
||||
minWidth: 120,
|
||||
|
@@ -1,78 +1,77 @@
|
||||
import * as React from '@theia/core/shared/react';
|
||||
import * as ReactDOM from '@theia/core/shared/react-dom';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import { Widget } from '@theia/core/shared/@phosphor/widgets';
|
||||
import { Message, MessageLoop } from '@theia/core/shared/@phosphor/messaging';
|
||||
import { Disposable } from '@theia/core/lib/common/disposable';
|
||||
import { BaseWidget } from '@theia/core/lib/browser/widgets/widget';
|
||||
import type { Root } from '@theia/core/shared/react-dom/client';
|
||||
import {
|
||||
inject,
|
||||
injectable,
|
||||
postConstruct,
|
||||
} from '@theia/core/shared/inversify';
|
||||
import { UserStatus } from './cloud-user-status';
|
||||
import { nls } from '@theia/core/lib/common/nls';
|
||||
import { CloudSketchbookTreeWidget } from './cloud-sketchbook-tree-widget';
|
||||
import { AuthenticationClientService } from '../../auth/authentication-client-service';
|
||||
import { CloudSketchbookTreeModel } from './cloud-sketchbook-tree-model';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
import { BaseSketchbookCompositeWidget } from '../sketchbook/sketchbook-composite-widget';
|
||||
import { CreateNew } from '../sketchbook/create-new';
|
||||
import { AuthenticationSession } from '../../../node/auth/types';
|
||||
|
||||
@injectable()
|
||||
export class CloudSketchbookCompositeWidget extends BaseWidget {
|
||||
export class CloudSketchbookCompositeWidget extends BaseSketchbookCompositeWidget<CloudSketchbookTreeWidget> {
|
||||
@inject(AuthenticationClientService)
|
||||
protected readonly authenticationService: AuthenticationClientService;
|
||||
|
||||
private readonly authenticationService: AuthenticationClientService;
|
||||
@inject(CloudSketchbookTreeWidget)
|
||||
protected readonly cloudSketchbookTreeWidget: CloudSketchbookTreeWidget;
|
||||
|
||||
private compositeNode: HTMLElement;
|
||||
private cloudUserStatusNode: HTMLElement;
|
||||
private readonly cloudSketchbookTreeWidget: CloudSketchbookTreeWidget;
|
||||
private _session: AuthenticationSession | undefined;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.compositeNode = document.createElement('div');
|
||||
this.compositeNode.classList.add('composite-node');
|
||||
this.cloudUserStatusNode = document.createElement('div');
|
||||
this.cloudUserStatusNode.classList.add('cloud-status-node');
|
||||
this.compositeNode.appendChild(this.cloudUserStatusNode);
|
||||
this.node.appendChild(this.compositeNode);
|
||||
this.id = 'cloud-sketchbook-composite-widget';
|
||||
this.title.caption = nls.localize(
|
||||
'arduino/cloud/remoteSketchbook',
|
||||
'Remote Sketchbook'
|
||||
);
|
||||
this.title.iconClass = 'cloud-sketchbook-tree-icon';
|
||||
this.title.closable = false;
|
||||
this.id = 'cloud-sketchbook-composite-widget';
|
||||
}
|
||||
|
||||
public getTreeWidget(): CloudSketchbookTreeWidget {
|
||||
@postConstruct()
|
||||
protected init(): void {
|
||||
this.toDispose.push(
|
||||
this.authenticationService.onSessionDidChange((session) => {
|
||||
const oldSession = this._session;
|
||||
this._session = session;
|
||||
if (!!oldSession !== !!this._session) {
|
||||
this.updateFooter();
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
get treeWidget(): CloudSketchbookTreeWidget {
|
||||
return this.cloudSketchbookTreeWidget;
|
||||
}
|
||||
|
||||
protected override onAfterAttach(message: Message): void {
|
||||
super.onAfterAttach(message);
|
||||
Widget.attach(this.cloudSketchbookTreeWidget, this.compositeNode);
|
||||
ReactDOM.render(
|
||||
<UserStatus
|
||||
model={this.cloudSketchbookTreeWidget.model as CloudSketchbookTreeModel}
|
||||
authenticationService={this.authenticationService}
|
||||
/>,
|
||||
this.cloudUserStatusNode
|
||||
);
|
||||
this.toDisposeOnDetach.push(
|
||||
Disposable.create(() => Widget.detach(this.cloudSketchbookTreeWidget))
|
||||
protected renderFooter(footerRoot: Root): void {
|
||||
footerRoot.render(
|
||||
<>
|
||||
{this._session && (
|
||||
<CreateNew
|
||||
label={nls.localize(
|
||||
'arduino/sketchbook/newRemoteSketch',
|
||||
'New Remote Sketch'
|
||||
)}
|
||||
onClick={this.onDidClickCreateNew}
|
||||
/>
|
||||
)}
|
||||
<UserStatus
|
||||
model={
|
||||
this.cloudSketchbookTreeWidget.model as CloudSketchbookTreeModel
|
||||
}
|
||||
authenticationService={this.authenticationService}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
protected override onActivateRequest(msg: Message): void {
|
||||
super.onActivateRequest(msg);
|
||||
|
||||
/*
|
||||
Sending a resize message is needed because otherwise the cloudSketchbookTreeWidget
|
||||
would render empty
|
||||
*/
|
||||
this.onResize(Widget.ResizeMessage.UnknownSize);
|
||||
}
|
||||
|
||||
protected override onResize(message: Widget.ResizeMessage): void {
|
||||
super.onResize(message);
|
||||
MessageLoop.sendMessage(
|
||||
this.cloudSketchbookTreeWidget,
|
||||
Widget.ResizeMessage.UnknownSize
|
||||
);
|
||||
}
|
||||
private onDidClickCreateNew: () => void = () => {
|
||||
this.commandService.executeCommand('arduino-new-cloud-sketch');
|
||||
};
|
||||
}
|
||||
|
@@ -1,20 +1,26 @@
|
||||
import { inject, injectable, postConstruct } from '@theia/core/shared/inversify';
|
||||
import { TreeNode } from '@theia/core/lib/browser/tree';
|
||||
import {
|
||||
inject,
|
||||
injectable,
|
||||
postConstruct,
|
||||
} from '@theia/core/shared/inversify';
|
||||
import { CompositeTreeNode, TreeNode } from '@theia/core/lib/browser/tree';
|
||||
import { posixSegments, splitSketchPath } from '../../create/create-paths';
|
||||
import { CreateApi } from '../../create/create-api';
|
||||
import { CloudSketchbookTree } from './cloud-sketchbook-tree';
|
||||
import { AuthenticationClientService } from '../../auth/authentication-client-service';
|
||||
import { SketchbookTreeModel } from '../sketchbook/sketchbook-tree-model';
|
||||
import { ArduinoPreferences } from '../../arduino-preferences';
|
||||
import { WorkspaceNode } from '@theia/navigator/lib/browser/navigator-tree';
|
||||
import { CreateUri } from '../../create/create-uri';
|
||||
import { FileStat } from '@theia/filesystem/lib/common/files';
|
||||
import { LocalCacheFsProvider } from '../../local-cache/local-cache-fs-provider';
|
||||
import { FileService } from '@theia/filesystem/lib/browser/file-service';
|
||||
import { FileChangesEvent, FileStat } from '@theia/filesystem/lib/common/files';
|
||||
import {
|
||||
LocalCacheFsProvider,
|
||||
LocalCacheUri,
|
||||
} from '../../local-cache/local-cache-fs-provider';
|
||||
import URI from '@theia/core/lib/common/uri';
|
||||
import { SketchCache } from './cloud-sketch-cache';
|
||||
import { Create } from '../../create/typings';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
import { nls } from '@theia/core/lib/common/nls';
|
||||
import { Deferred } from '@theia/core/lib/common/promise-util';
|
||||
|
||||
export function sketchBaseDir(sketch: Create.Sketch): FileStat {
|
||||
// extract the sketch path
|
||||
@@ -52,26 +58,16 @@ export function sketchesToFileStats(sketches: Create.Sketch[]): FileStat[] {
|
||||
|
||||
@injectable()
|
||||
export class CloudSketchbookTreeModel extends SketchbookTreeModel {
|
||||
@inject(FileService)
|
||||
protected override readonly fileService: FileService;
|
||||
|
||||
@inject(AuthenticationClientService)
|
||||
protected readonly authenticationService: AuthenticationClientService;
|
||||
|
||||
@inject(CreateApi)
|
||||
protected readonly createApi: CreateApi;
|
||||
|
||||
@inject(CloudSketchbookTree)
|
||||
protected readonly cloudSketchbookTree: CloudSketchbookTree;
|
||||
|
||||
@inject(ArduinoPreferences)
|
||||
protected override readonly arduinoPreferences: ArduinoPreferences;
|
||||
|
||||
private readonly createApi: CreateApi;
|
||||
@inject(AuthenticationClientService)
|
||||
private readonly authenticationService: AuthenticationClientService;
|
||||
@inject(LocalCacheFsProvider)
|
||||
protected readonly localCacheFsProvider: LocalCacheFsProvider;
|
||||
|
||||
private readonly localCacheFsProvider: LocalCacheFsProvider;
|
||||
@inject(SketchCache)
|
||||
protected readonly sketchCache: SketchCache;
|
||||
private readonly sketchCache: SketchCache;
|
||||
|
||||
private _localCacheFsProviderReady: Deferred<void> | undefined;
|
||||
|
||||
@postConstruct()
|
||||
protected override init(): void {
|
||||
@@ -81,6 +77,50 @@ export class CloudSketchbookTreeModel extends SketchbookTreeModel {
|
||||
);
|
||||
}
|
||||
|
||||
override *getNodesByUri(uri: URI): IterableIterator<TreeNode> {
|
||||
if (uri.scheme === LocalCacheUri.scheme) {
|
||||
const workspace = this.root;
|
||||
const { session } = this.authenticationService;
|
||||
if (session && WorkspaceNode.is(workspace)) {
|
||||
const currentUri = this.localCacheFsProvider.to(uri);
|
||||
if (currentUri) {
|
||||
const rootPath = this.localCacheFsProvider
|
||||
.toUri(session)
|
||||
.path.toString();
|
||||
const currentPath = currentUri.path.toString();
|
||||
if (rootPath === currentPath) {
|
||||
return workspace;
|
||||
}
|
||||
if (currentPath.startsWith(rootPath)) {
|
||||
const id = currentPath.substring(rootPath.length);
|
||||
const node = this.getNode(id);
|
||||
if (node) {
|
||||
yield node;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override isRootAffected(changes: FileChangesEvent): boolean {
|
||||
return changes.changes
|
||||
.map(({ resource }) => resource)
|
||||
.some(
|
||||
(uri) => uri.parent.toString().startsWith(LocalCacheUri.root.toString()) // all files under the root might affect the tree
|
||||
);
|
||||
}
|
||||
|
||||
override async refresh(
|
||||
parent?: Readonly<CompositeTreeNode>
|
||||
): Promise<CompositeTreeNode | undefined> {
|
||||
if (parent) {
|
||||
return super.refresh(parent);
|
||||
}
|
||||
await this.updateRoot();
|
||||
return super.refresh();
|
||||
}
|
||||
|
||||
override async createRoot(): Promise<TreeNode | undefined> {
|
||||
const { session } = this.authenticationService;
|
||||
if (!session) {
|
||||
@@ -89,7 +129,10 @@ export class CloudSketchbookTreeModel extends SketchbookTreeModel {
|
||||
}
|
||||
this.createApi.init(this.authenticationService, this.arduinoPreferences);
|
||||
this.sketchCache.init();
|
||||
const sketches = await this.createApi.sketches();
|
||||
const [sketches] = await Promise.all([
|
||||
this.createApi.sketches(),
|
||||
this.ensureLocalFsProviderReady(),
|
||||
]);
|
||||
const rootFileStats = sketchesToFileStats(sketches);
|
||||
if (this.workspaceService.opened) {
|
||||
const workspaceNode = WorkspaceNode.createRoot(
|
||||
@@ -108,7 +151,9 @@ export class CloudSketchbookTreeModel extends SketchbookTreeModel {
|
||||
return this.tree as CloudSketchbookTree;
|
||||
}
|
||||
|
||||
protected override recursivelyFindSketchRoot(node: TreeNode): any {
|
||||
protected override recursivelyFindSketchRoot(
|
||||
node: TreeNode
|
||||
): TreeNode | false {
|
||||
if (node && CloudSketchbookTree.CloudSketchDirNode.is(node)) {
|
||||
return node;
|
||||
}
|
||||
@@ -122,13 +167,25 @@ export class CloudSketchbookTreeModel extends SketchbookTreeModel {
|
||||
}
|
||||
|
||||
override async revealFile(uri: URI): Promise<TreeNode | undefined> {
|
||||
await this.localCacheFsProvider.ready.promise;
|
||||
// we use remote uris as keys for the tree
|
||||
// convert local URIs
|
||||
const remoteuri = this.localCacheFsProvider.from(uri);
|
||||
if (remoteuri) {
|
||||
return super.revealFile(remoteuri);
|
||||
const remoteUri = this.localCacheFsProvider.from(uri);
|
||||
if (remoteUri) {
|
||||
return super.revealFile(remoteUri);
|
||||
} else {
|
||||
return super.revealFile(uri);
|
||||
}
|
||||
}
|
||||
|
||||
private async ensureLocalFsProviderReady(): Promise<void> {
|
||||
if (this._localCacheFsProviderReady) {
|
||||
return this._localCacheFsProviderReady.promise;
|
||||
}
|
||||
this._localCacheFsProviderReady = new Deferred();
|
||||
this.fileService
|
||||
.access(LocalCacheUri.root)
|
||||
.then(() => this._localCacheFsProviderReady?.resolve());
|
||||
return this._localCacheFsProviderReady.promise;
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import * as React from '@theia/core/shared/react';
|
||||
import { inject, injectable, postConstruct } from '@theia/core/shared/inversify';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import { TreeModel } from '@theia/core/lib/browser/tree/tree-model';
|
||||
import { CloudSketchbookTreeModel } from './cloud-sketchbook-tree-model';
|
||||
import { AuthenticationClientService } from '../../auth/authentication-client-service';
|
||||
@@ -27,12 +27,6 @@ export class CloudSketchbookTreeWidget extends SketchbookTreeWidget {
|
||||
@inject(CloudSketchbookTree)
|
||||
protected readonly cloudSketchbookTree: CloudSketchbookTree;
|
||||
|
||||
@postConstruct()
|
||||
protected override async init(): Promise<void> {
|
||||
await super.init();
|
||||
this.addClass('tree-container'); // Adds `height: 100%` to the tree. Otherwise you cannot see it.
|
||||
}
|
||||
|
||||
protected override renderTree(model: TreeModel): React.ReactNode {
|
||||
if (this.shouldShowWelcomeView()) return this.renderViewWelcome();
|
||||
if (this.shouldShowEmptyView()) return this.renderEmptyView();
|
||||
|
@@ -136,7 +136,7 @@ export class CloudSketchbookTree extends SketchbookTree {
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.runWithState(node, 'pulling', async (node) => {
|
||||
return this.runWithState(node, 'pulling', async (node) => {
|
||||
const commandsCopy = node.commands;
|
||||
node.commands = [];
|
||||
|
||||
@@ -196,7 +196,7 @@ export class CloudSketchbookTree extends SketchbookTree {
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.runWithState(node, 'pushing', async (node) => {
|
||||
return this.runWithState(node, 'pushing', async (node) => {
|
||||
if (!CloudSketchbookTree.CloudSketchTreeNode.isSynced(node)) {
|
||||
throw new Error(
|
||||
nls.localize(
|
||||
@@ -269,7 +269,7 @@ export class CloudSketchbookTree extends SketchbookTree {
|
||||
return prev;
|
||||
}
|
||||
|
||||
// do not map "do_not_sync" files/directoris and their descendants
|
||||
// do not map "do_not_sync" files/directories and their descendants
|
||||
const segments = path[1].split(posix.sep) || [];
|
||||
if (
|
||||
segments.some((segment) => Create.do_not_sync_files.includes(segment))
|
||||
|
@@ -2,6 +2,7 @@ import { inject, injectable, postConstruct } from '@theia/core/shared/inversify'
|
||||
import { CloudSketchbookCompositeWidget } from './cloud-sketchbook-composite-widget';
|
||||
import { SketchbookWidget } from '../sketchbook/sketchbook-widget';
|
||||
import { ArduinoPreferences } from '../../arduino-preferences';
|
||||
import { BaseSketchbookCompositeWidget } from '../sketchbook/sketchbook-composite-widget';
|
||||
|
||||
@injectable()
|
||||
export class CloudSketchbookWidget extends SketchbookWidget {
|
||||
@@ -19,8 +20,8 @@ export class CloudSketchbookWidget extends SketchbookWidget {
|
||||
override getTreeWidget(): any {
|
||||
const widget: any = this.sketchbookTreesContainer.selectedWidgets().next();
|
||||
|
||||
if (widget && typeof widget.getTreeWidget !== 'undefined') {
|
||||
return (widget as CloudSketchbookCompositeWidget).getTreeWidget();
|
||||
if (widget instanceof BaseSketchbookCompositeWidget) {
|
||||
return widget.treeWidget;
|
||||
}
|
||||
return widget;
|
||||
}
|
||||
@@ -30,7 +31,7 @@ export class CloudSketchbookWidget extends SketchbookWidget {
|
||||
this.sketchbookTreesContainer.activateWidget(this.widget);
|
||||
} else {
|
||||
this.sketchbookTreesContainer.activateWidget(
|
||||
this.localSketchbookTreeWidget
|
||||
this.sketchbookCompositeWidget
|
||||
);
|
||||
}
|
||||
this.setDocumentMode();
|
||||
|
@@ -0,0 +1,20 @@
|
||||
import * as React from '@theia/core/shared/react';
|
||||
|
||||
export class CreateNew extends React.Component<CreateNew.Props> {
|
||||
override render(): React.ReactNode {
|
||||
return (
|
||||
<div className="create-new">
|
||||
<button className="theia-button secondary" onClick={this.props.onClick}>
|
||||
{this.props.label}
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export namespace CreateNew {
|
||||
export interface Props {
|
||||
readonly label: string;
|
||||
readonly onClick: () => void;
|
||||
}
|
||||
}
|
@@ -0,0 +1,93 @@
|
||||
import * as React from '@theia/core/shared/react';
|
||||
import { createRoot, Root } from '@theia/core/shared/react-dom/client';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import { nls } from '@theia/core/lib/common/nls';
|
||||
import { Widget } from '@theia/core/shared/@phosphor/widgets';
|
||||
import { Message, MessageLoop } from '@theia/core/shared/@phosphor/messaging';
|
||||
import { Disposable } from '@theia/core/lib/common/disposable';
|
||||
import { BaseWidget } from '@theia/core/lib/browser/widgets/widget';
|
||||
import { CommandService } from '@theia/core/lib/common/command';
|
||||
import { SketchbookTreeWidget } from './sketchbook-tree-widget';
|
||||
import { CreateNew } from '../sketchbook/create-new';
|
||||
|
||||
@injectable()
|
||||
export abstract class BaseSketchbookCompositeWidget<
|
||||
TW extends SketchbookTreeWidget
|
||||
> extends BaseWidget {
|
||||
@inject(CommandService)
|
||||
protected readonly commandService: CommandService;
|
||||
|
||||
private readonly compositeNode: HTMLElement;
|
||||
private readonly footerRoot: Root;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.compositeNode = document.createElement('div');
|
||||
this.compositeNode.classList.add('composite-node');
|
||||
const footerNode = document.createElement('div');
|
||||
footerNode.classList.add('footer-node');
|
||||
this.compositeNode.appendChild(footerNode);
|
||||
this.footerRoot = createRoot(footerNode);
|
||||
this.node.appendChild(this.compositeNode);
|
||||
this.title.closable = false;
|
||||
}
|
||||
|
||||
abstract get treeWidget(): TW;
|
||||
protected abstract renderFooter(footerRoot: Root): void;
|
||||
protected updateFooter(): void {
|
||||
this.renderFooter(this.footerRoot);
|
||||
}
|
||||
|
||||
protected override onAfterAttach(message: Message): void {
|
||||
super.onAfterAttach(message);
|
||||
Widget.attach(this.treeWidget, this.compositeNode);
|
||||
this.renderFooter(this.footerRoot);
|
||||
this.toDisposeOnDetach.push(
|
||||
Disposable.create(() => Widget.detach(this.treeWidget))
|
||||
);
|
||||
}
|
||||
|
||||
protected override onActivateRequest(message: Message): void {
|
||||
super.onActivateRequest(message);
|
||||
// Sending a resize message is needed because otherwise the tree widget would render empty
|
||||
this.onResize(Widget.ResizeMessage.UnknownSize);
|
||||
}
|
||||
|
||||
protected override onResize(message: Widget.ResizeMessage): void {
|
||||
super.onResize(message);
|
||||
MessageLoop.sendMessage(this.treeWidget, Widget.ResizeMessage.UnknownSize);
|
||||
}
|
||||
}
|
||||
|
||||
@injectable()
|
||||
export class SketchbookCompositeWidget extends BaseSketchbookCompositeWidget<SketchbookTreeWidget> {
|
||||
@inject(SketchbookTreeWidget)
|
||||
private readonly sketchbookTreeWidget: SketchbookTreeWidget;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.id = 'sketchbook-composite-widget';
|
||||
this.title.caption = nls.localize(
|
||||
'arduino/sketch/titleLocalSketchbook',
|
||||
'Local Sketchbook'
|
||||
);
|
||||
this.title.iconClass = 'sketchbook-tree-icon';
|
||||
}
|
||||
|
||||
get treeWidget(): SketchbookTreeWidget {
|
||||
return this.sketchbookTreeWidget;
|
||||
}
|
||||
|
||||
protected renderFooter(footerRoot: Root): void {
|
||||
footerRoot.render(
|
||||
<CreateNew
|
||||
label={nls.localize('arduino/sketchbook/newSketch', 'New Sketch')}
|
||||
onClick={this.onDidClickCreateNew}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
private onDidClickCreateNew: () => void = () => {
|
||||
this.commandService.executeCommand('arduino-new-sketch');
|
||||
};
|
||||
}
|
@@ -59,6 +59,7 @@ export class SketchbookTreeWidget extends FileTreeWidget {
|
||||
'Local Sketchbook'
|
||||
);
|
||||
this.title.closable = false;
|
||||
this.addClass('tree-container'); // Adds `height: 100%` to the tree. Otherwise you cannot see it.
|
||||
}
|
||||
|
||||
@postConstruct()
|
||||
|
@@ -11,15 +11,21 @@ import { Disposable } from '@theia/core/lib/common/disposable';
|
||||
import { BaseWidget } from '@theia/core/lib/browser/widgets/widget';
|
||||
import { SketchbookTreeWidget } from './sketchbook-tree-widget';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
import { CloudSketchbookCompositeWidget } from '../cloud-sketchbook/cloud-sketchbook-composite-widget';
|
||||
import { URI } from '../../contributions/contribution';
|
||||
import {
|
||||
BaseSketchbookCompositeWidget,
|
||||
SketchbookCompositeWidget,
|
||||
} from './sketchbook-composite-widget';
|
||||
|
||||
@injectable()
|
||||
export class SketchbookWidget extends BaseWidget {
|
||||
static LABEL = nls.localize('arduino/sketch/titleSketchbook', 'Sketchbook');
|
||||
static readonly LABEL = nls.localize(
|
||||
'arduino/sketch/titleSketchbook',
|
||||
'Sketchbook'
|
||||
);
|
||||
|
||||
@inject(SketchbookTreeWidget)
|
||||
protected readonly localSketchbookTreeWidget: SketchbookTreeWidget;
|
||||
@inject(SketchbookCompositeWidget)
|
||||
protected readonly sketchbookCompositeWidget: SketchbookCompositeWidget;
|
||||
|
||||
protected readonly sketchbookTreesContainer: DockPanel;
|
||||
|
||||
@@ -36,7 +42,7 @@ export class SketchbookWidget extends BaseWidget {
|
||||
|
||||
@postConstruct()
|
||||
protected init(): void {
|
||||
this.sketchbookTreesContainer.addWidget(this.localSketchbookTreeWidget);
|
||||
this.sketchbookTreesContainer.addWidget(this.sketchbookCompositeWidget);
|
||||
}
|
||||
|
||||
protected override onAfterAttach(message: Message): void {
|
||||
@@ -47,8 +53,26 @@ export class SketchbookWidget extends BaseWidget {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* The currently selected sketchbook tree widget inside the view.
|
||||
*/
|
||||
getTreeWidget(): SketchbookTreeWidget {
|
||||
return this.localSketchbookTreeWidget;
|
||||
return this.sketchbookCompositeWidget.treeWidget;
|
||||
}
|
||||
|
||||
/**
|
||||
* An array of all sketchbook tree widgets managed by the view.
|
||||
*/
|
||||
getTreeWidgets(): SketchbookTreeWidget[] {
|
||||
return toArray(this.sketchbookTreesContainer.widgets()).reduce(
|
||||
(acc, curr) => {
|
||||
if (curr instanceof BaseSketchbookCompositeWidget) {
|
||||
acc.push(curr.treeWidget);
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
[] as SketchbookTreeWidget[]
|
||||
);
|
||||
}
|
||||
|
||||
activeTreeWidgetId(): string | undefined {
|
||||
@@ -80,8 +104,8 @@ export class SketchbookWidget extends BaseWidget {
|
||||
if (widget instanceof SketchbookTreeWidget) {
|
||||
return widget;
|
||||
}
|
||||
if (widget instanceof CloudSketchbookCompositeWidget) {
|
||||
return widget.getTreeWidget();
|
||||
if (widget instanceof BaseSketchbookCompositeWidget) {
|
||||
return widget.treeWidget;
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
@@ -1,21 +1,25 @@
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import * as remote from '@theia/core/electron-shared/@electron/remote';
|
||||
import { isOSX } from '@theia/core/lib/common/os';
|
||||
import { FrontendApplicationConfigProvider } from '@theia/core/lib/browser/frontend-application-config-provider';
|
||||
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
|
||||
import {
|
||||
CompositeMenuNode,
|
||||
CommandMenuNode,
|
||||
CompoundMenuNode,
|
||||
CompoundMenuNodeRole,
|
||||
MAIN_MENU_BAR,
|
||||
MenuNode,
|
||||
MenuPath,
|
||||
} from '@theia/core/lib/common/menu';
|
||||
import { isOSX } from '@theia/core/lib/common/os';
|
||||
import {
|
||||
ElectronMainMenuFactory as TheiaElectronMainMenuFactory,
|
||||
ElectronMenuItemRole,
|
||||
ElectronMenuOptions,
|
||||
} from '@theia/core/lib/electron-browser/menu/electron-main-menu-factory';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import {
|
||||
ArduinoMenus,
|
||||
PlaceholderMenuNode,
|
||||
} from '../../../browser/menu/arduino-menus';
|
||||
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
|
||||
|
||||
@injectable()
|
||||
export class ElectronMainMenuFactory extends TheiaElectronMainMenuFactory {
|
||||
@@ -38,7 +42,9 @@ export class ElectronMainMenuFactory extends TheiaElectronMainMenuFactory {
|
||||
override createElectronMenuBar(): Electron.Menu {
|
||||
this._toggledCommands.clear(); // https://github.com/eclipse-theia/theia/issues/8977
|
||||
const menuModel = this.menuProvider.getMenu(MAIN_MENU_BAR);
|
||||
const template = this.fillMenuTemplate([], menuModel);
|
||||
const template = this.fillMenuTemplate([], menuModel, [], {
|
||||
rootMenuPath: MAIN_MENU_BAR,
|
||||
});
|
||||
if (isOSX) {
|
||||
template.unshift(this.createOSXMenu());
|
||||
}
|
||||
@@ -66,11 +72,15 @@ export class ElectronMainMenuFactory extends TheiaElectronMainMenuFactory {
|
||||
|
||||
override createElectronContextMenu(
|
||||
menuPath: MenuPath,
|
||||
args?: any[]
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
args?: any[],
|
||||
context?: HTMLElement
|
||||
): Electron.Menu {
|
||||
const menuModel = this.menuProvider.getMenu(menuPath);
|
||||
const template = this.fillMenuTemplate([], menuModel, args, {
|
||||
showDisabled: false,
|
||||
context,
|
||||
rootMenuPath: menuPath,
|
||||
});
|
||||
return remote.Menu.buildFromTemplate(this.escapeAmpersand(template));
|
||||
}
|
||||
@@ -94,20 +104,26 @@ export class ElectronMainMenuFactory extends TheiaElectronMainMenuFactory {
|
||||
|
||||
protected override createOSXMenu(): Electron.MenuItemConstructorOptions {
|
||||
const { submenu } = super.createOSXMenu();
|
||||
const label = 'Arduino IDE';
|
||||
const label = FrontendApplicationConfigProvider.get().applicationName;
|
||||
if (!!submenu && Array.isArray(submenu)) {
|
||||
const [, , /* about */ /* preferences */ ...rest] = submenu;
|
||||
const about = this.fillMenuTemplate(
|
||||
[],
|
||||
this.menuProvider.getMenu(ArduinoMenus.HELP__ABOUT_GROUP)
|
||||
this.menuProvider.getMenu(ArduinoMenus.HELP__ABOUT_GROUP),
|
||||
[],
|
||||
{ rootMenuPath: ArduinoMenus.HELP__ABOUT_GROUP }
|
||||
);
|
||||
const preferences = this.fillMenuTemplate(
|
||||
[],
|
||||
this.menuProvider.getMenu(ArduinoMenus.FILE__PREFERENCES_GROUP)
|
||||
this.menuProvider.getMenu(ArduinoMenus.FILE__PREFERENCES_GROUP),
|
||||
[],
|
||||
{ rootMenuPath: ArduinoMenus.FILE__PREFERENCES_GROUP }
|
||||
);
|
||||
const advanced = this.fillMenuTemplate(
|
||||
[],
|
||||
this.menuProvider.getMenu(ArduinoMenus.FILE__ADVANCED_GROUP)
|
||||
this.menuProvider.getMenu(ArduinoMenus.FILE__ADVANCED_GROUP),
|
||||
[],
|
||||
{ rootMenuPath: ArduinoMenus.FILE__ADVANCED_GROUP }
|
||||
);
|
||||
return {
|
||||
label,
|
||||
@@ -124,7 +140,7 @@ export class ElectronMainMenuFactory extends TheiaElectronMainMenuFactory {
|
||||
return { label, submenu };
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars, unused-imports/no-unused-vars
|
||||
protected override roleFor(id: string): ElectronMenuItemRole | undefined {
|
||||
// MenuItem `roles` are completely broken on macOS:
|
||||
// - https://github.com/eclipse-theia/theia/issues/11217,
|
||||
@@ -133,20 +149,133 @@ export class ElectronMainMenuFactory extends TheiaElectronMainMenuFactory {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
protected override handleElectronDefault(
|
||||
menuNode: CompositeMenuNode,
|
||||
args: any[] = [],
|
||||
options?: ElectronMenuOptions
|
||||
protected override fillMenuTemplate(
|
||||
parentItems: Electron.MenuItemConstructorOptions[],
|
||||
menuModel: MenuNode,
|
||||
args: unknown[] | undefined,
|
||||
options: ElectronMenuOptions
|
||||
): Electron.MenuItemConstructorOptions[] {
|
||||
if (menuNode instanceof PlaceholderMenuNode) {
|
||||
return [
|
||||
{
|
||||
label: menuNode.label,
|
||||
enabled: false,
|
||||
visible: true,
|
||||
},
|
||||
];
|
||||
if (menuModel instanceof PlaceholderMenuNode) {
|
||||
parentItems.push({
|
||||
label: menuModel.label,
|
||||
enabled: false,
|
||||
visible: true,
|
||||
});
|
||||
} else {
|
||||
this.superFillMenuTemplate(parentItems, menuModel, args, options);
|
||||
}
|
||||
return [];
|
||||
return parentItems;
|
||||
}
|
||||
|
||||
// Copied from 1.31.1 Theia as is to customize the enablement of the menu items.
|
||||
// Source: https://github.com/eclipse-theia/theia/blob/5e641750af83383f2ce0cb3432ec333df70778a8/packages/core/src/electron-browser/menu/electron-main-menu-factory.ts#L132-L203
|
||||
// See https://github.com/arduino/arduino-ide/issues/1533
|
||||
private superFillMenuTemplate(
|
||||
parentItems: Electron.MenuItemConstructorOptions[],
|
||||
menu: MenuNode,
|
||||
args: unknown[] = [],
|
||||
options: ElectronMenuOptions
|
||||
): Electron.MenuItemConstructorOptions[] {
|
||||
const showDisabled = options?.showDisabled !== false;
|
||||
|
||||
if (
|
||||
CompoundMenuNode.is(menu) &&
|
||||
menu.children.length &&
|
||||
this.undefinedOrMatch(menu.when, options.context)
|
||||
) {
|
||||
const role = CompoundMenuNode.getRole(menu);
|
||||
if (role === CompoundMenuNodeRole.Group && menu.id === 'inline') {
|
||||
return parentItems;
|
||||
}
|
||||
const children = CompoundMenuNode.getFlatChildren(menu.children);
|
||||
const myItems: Electron.MenuItemConstructorOptions[] = [];
|
||||
children.forEach((child) =>
|
||||
this.fillMenuTemplate(myItems, child, args, options)
|
||||
);
|
||||
if (myItems.length === 0) {
|
||||
return parentItems;
|
||||
}
|
||||
if (role === CompoundMenuNodeRole.Submenu) {
|
||||
parentItems.push({ label: menu.label, submenu: myItems });
|
||||
} else if (role === CompoundMenuNodeRole.Group && menu.id !== 'inline') {
|
||||
if (
|
||||
parentItems.length &&
|
||||
parentItems[parentItems.length - 1].type !== 'separator'
|
||||
) {
|
||||
parentItems.push({ type: 'separator' });
|
||||
}
|
||||
parentItems.push(...myItems);
|
||||
parentItems.push({ type: 'separator' });
|
||||
}
|
||||
} else if (menu.command) {
|
||||
const node =
|
||||
menu.altNode && this.context.altPressed
|
||||
? menu.altNode
|
||||
: (menu as MenuNode & CommandMenuNode);
|
||||
const commandId = node.command;
|
||||
|
||||
// That is only a sanity check at application startup.
|
||||
if (!this.commandRegistry.getCommand(commandId)) {
|
||||
console.debug(
|
||||
`Skipping menu item with missing command: "${commandId}".`
|
||||
);
|
||||
return parentItems;
|
||||
}
|
||||
|
||||
if (
|
||||
!this.menuCommandExecutor.isVisible(
|
||||
options.rootMenuPath,
|
||||
commandId,
|
||||
...args
|
||||
) ||
|
||||
!this.undefinedOrMatch(node.when, options.context)
|
||||
) {
|
||||
return parentItems;
|
||||
}
|
||||
|
||||
// We should omit rendering context-menu items which are disabled.
|
||||
if (
|
||||
!showDisabled &&
|
||||
!this.menuCommandExecutor.isEnabled(
|
||||
options.rootMenuPath,
|
||||
commandId,
|
||||
...args
|
||||
)
|
||||
) {
|
||||
return parentItems;
|
||||
}
|
||||
|
||||
const bindings =
|
||||
this.keybindingRegistry.getKeybindingsForCommand(commandId);
|
||||
|
||||
const accelerator = bindings[0] && this.acceleratorFor(bindings[0]);
|
||||
|
||||
const menuItem: Electron.MenuItemConstructorOptions = {
|
||||
id: node.id,
|
||||
label: node.label,
|
||||
type: this.commandRegistry.getToggledHandler(commandId, ...args)
|
||||
? 'checkbox'
|
||||
: 'normal',
|
||||
checked: this.commandRegistry.isToggled(commandId, ...args),
|
||||
enabled: this.commandRegistry.isEnabled(commandId, ...args), // Unlike Theia https://github.com/eclipse-theia/theia/blob/v1.31.1/packages/core/src/electron-browser/menu/electron-main-menu-factory.ts#L183
|
||||
visible: true,
|
||||
accelerator,
|
||||
click: () => this.execute(commandId, args, options.rootMenuPath),
|
||||
};
|
||||
|
||||
if (isOSX) {
|
||||
const role = this.roleFor(node.id);
|
||||
if (role) {
|
||||
menuItem.role = role;
|
||||
delete menuItem.click;
|
||||
}
|
||||
}
|
||||
parentItems.push(menuItem);
|
||||
|
||||
if (this.commandRegistry.getToggledHandler(commandId, ...args)) {
|
||||
this._toggledCommands.add(commandId);
|
||||
}
|
||||
}
|
||||
return parentItems;
|
||||
}
|
||||
}
|
||||
|
@@ -5,6 +5,7 @@ import {
|
||||
ElectronMainApplication as TheiaElectronMainApplication,
|
||||
ElectronMainApplicationContribution,
|
||||
} from '@theia/core/lib/electron-main/electron-main-application';
|
||||
import { ElectronMessagingContribution as TheiaElectronMessagingContribution } from '@theia/core/lib/electron-main/messaging/electron-messaging-contribution';
|
||||
import { TheiaElectronWindow as DefaultTheiaElectronWindow } from '@theia/core/lib/electron-main/theia-electron-window';
|
||||
import { ContainerModule } from '@theia/core/shared/inversify';
|
||||
import {
|
||||
@@ -17,8 +18,8 @@ import { IsTempSketch } from '../node/is-temp-sketch';
|
||||
import { IDEUpdaterImpl } from './ide-updater/ide-updater-impl';
|
||||
import { ElectronMainApplication } from './theia/electron-main-application';
|
||||
import { ElectronMainWindowServiceImpl } from './theia/electron-main-window-service';
|
||||
import { ElectronMessagingContribution } from './theia/electron-messaging-contribution';
|
||||
import { TheiaElectronWindow } from './theia/theia-electron-window';
|
||||
import { ElectronNativeKeymap } from '@theia/core/lib/electron-main/electron-native-keymap';
|
||||
|
||||
export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
bind(ElectronMainApplication).toSelf().inSingletonScope();
|
||||
@@ -60,7 +61,9 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
|
||||
bind(IsTempSketch).toSelf().inSingletonScope();
|
||||
|
||||
// https://github.com/eclipse-theia/theia/issues/11688
|
||||
bind(ElectronNativeKeymap).toSelf().inSingletonScope();
|
||||
bind(ElectronMainApplicationContribution).toService(ElectronNativeKeymap);
|
||||
// Fix for cannot reload window: https://github.com/eclipse-theia/theia/issues/11600
|
||||
bind(ElectronMessagingContribution).toSelf().inSingletonScope();
|
||||
rebind(TheiaElectronMessagingContribution).toService(
|
||||
ElectronMessagingContribution
|
||||
);
|
||||
});
|
||||
|
@@ -9,7 +9,7 @@ import {
|
||||
import { fork } from 'child_process';
|
||||
import { AddressInfo } from 'net';
|
||||
import { join, isAbsolute, resolve } from 'path';
|
||||
import { promises as fs, Stats } from 'fs';
|
||||
import { promises as fs } from 'fs';
|
||||
import { MaybePromise } from '@theia/core/lib/common/types';
|
||||
import { ElectronSecurityToken } from '@theia/core/lib/electron-common/electron-token';
|
||||
import { FrontendApplicationConfig } from '@theia/application-package/lib/application-props';
|
||||
@@ -27,7 +27,8 @@ import {
|
||||
CLOSE_PLOTTER_WINDOW,
|
||||
SHOW_PLOTTER_WINDOW,
|
||||
} from '../../common/ipc-communication';
|
||||
import isValidPath = require('is-valid-path');
|
||||
import { ErrnoException } from '../../node/utils/errors';
|
||||
import { isAccessibleSketchPath } from '../../node/sketches-service-impl';
|
||||
|
||||
app.commandLine.appendSwitch('disable-http-cache');
|
||||
|
||||
@@ -145,7 +146,10 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
|
||||
event.preventDefault();
|
||||
const resolvedPath = await this.resolvePath(path, cwd);
|
||||
if (resolvedPath) {
|
||||
const sketchFolderPath = await this.isValidSketchPath(resolvedPath);
|
||||
const sketchFolderPath = await isAccessibleSketchPath(
|
||||
resolvedPath,
|
||||
true
|
||||
);
|
||||
if (sketchFolderPath) {
|
||||
this.openFilePromise.reject(new InterruptWorkspaceRestoreError());
|
||||
await this.openSketch(sketchFolderPath);
|
||||
@@ -158,56 +162,10 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The `path` argument is valid, if accessible and either pointing to a `.ino` file,
|
||||
* or it's a directory, and one of the files in the directory is an `.ino` file.
|
||||
*
|
||||
* If `undefined`, `path` was pointing to neither an accessible sketch file nor a sketch folder.
|
||||
*
|
||||
* The sketch folder name and sketch file name can be different. This method is not sketch folder name compliant.
|
||||
* The `path` must be an absolute, resolved path.
|
||||
*/
|
||||
private async isValidSketchPath(path: string): Promise<string | undefined> {
|
||||
let stats: Stats | undefined = undefined;
|
||||
try {
|
||||
stats = await fs.stat(path);
|
||||
} catch (err) {
|
||||
if ('code' in err && err.code === 'ENOENT') {
|
||||
return undefined;
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
if (!stats) {
|
||||
return undefined;
|
||||
}
|
||||
if (stats.isFile() && path.endsWith('.ino')) {
|
||||
return path;
|
||||
}
|
||||
try {
|
||||
const entries = await fs.readdir(path, { withFileTypes: true });
|
||||
const sketchFilename = entries
|
||||
.filter((entry) => entry.isFile() && entry.name.endsWith('.ino'))
|
||||
.map(({ name }) => name)
|
||||
.sort((left, right) => left.localeCompare(right))[0];
|
||||
if (sketchFilename) {
|
||||
return join(path, sketchFilename);
|
||||
}
|
||||
// If no sketches found in the folder, but the folder exists,
|
||||
// return with the path of the empty folder and let IDE2's frontend
|
||||
// figure out the workspace root.
|
||||
return path;
|
||||
} catch (err) {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
private async resolvePath(
|
||||
maybePath: string,
|
||||
cwd: string
|
||||
): Promise<string | undefined> {
|
||||
if (!isValidPath(maybePath)) {
|
||||
return undefined;
|
||||
}
|
||||
if (isAbsolute(maybePath)) {
|
||||
return maybePath;
|
||||
}
|
||||
@@ -215,7 +173,7 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
|
||||
const resolved = await fs.realpath(resolve(cwd, maybePath));
|
||||
return resolved;
|
||||
} catch (err) {
|
||||
if ('code' in err && err.code === 'ENOENT') {
|
||||
if (ErrnoException.isENOENT(err)) {
|
||||
return undefined;
|
||||
}
|
||||
throw err;
|
||||
@@ -256,7 +214,10 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
|
||||
if (!resolvedPath) {
|
||||
continue;
|
||||
}
|
||||
const sketchFolderPath = await this.isValidSketchPath(resolvedPath);
|
||||
const sketchFolderPath = await isAccessibleSketchPath(
|
||||
resolvedPath,
|
||||
true
|
||||
);
|
||||
if (sketchFolderPath) {
|
||||
workspace.file = sketchFolderPath;
|
||||
if (this.isTempSketch.is(workspace.file)) {
|
||||
@@ -287,7 +248,7 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
|
||||
if (!resolvedPath) {
|
||||
continue;
|
||||
}
|
||||
const sketchFolderPath = await this.isValidSketchPath(resolvedPath);
|
||||
const sketchFolderPath = await isAccessibleSketchPath(resolvedPath, true);
|
||||
if (sketchFolderPath) {
|
||||
path = sketchFolderPath;
|
||||
break;
|
||||
@@ -355,10 +316,7 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
|
||||
argv: string[],
|
||||
cwd: string
|
||||
): Promise<void> {
|
||||
if (
|
||||
!os.isOSX &&
|
||||
(await this.launchFromArgs({ cwd, argv, secondInstance: true }))
|
||||
) {
|
||||
if (await this.launchFromArgs({ cwd, argv, secondInstance: true })) {
|
||||
// Application has received a file in its arguments
|
||||
return;
|
||||
}
|
||||
|
@@ -0,0 +1,44 @@
|
||||
import { ChannelMultiplexer } from '@theia/core/lib/common/message-rpc/channel';
|
||||
import {
|
||||
ElectronMessagingContribution as TheiaElectronMessagingContribution,
|
||||
ElectronWebContentChannel,
|
||||
} from '@theia/core/lib/electron-main/messaging/electron-messaging-contribution';
|
||||
import { injectable } from '@theia/core/shared/inversify';
|
||||
|
||||
// Electron window cannot reload: https://github.com/eclipse-theia/theia/issues/11600
|
||||
// This patch fixes it by removing the channel multiplexer from the cache.
|
||||
// A related PR in Theia: https://github.com/eclipse-theia/theia/pull/11810
|
||||
@injectable()
|
||||
export class ElectronMessagingContribution extends TheiaElectronMessagingContribution {
|
||||
// Based on: https://github.com/kittaakos/theia/commit/12dd318df589f1c48de2b58545912d8385919b22
|
||||
protected override createWindowChannelData(sender: Electron.WebContents): {
|
||||
channel: ElectronWebContentChannel;
|
||||
multiplexer: ChannelMultiplexer;
|
||||
} {
|
||||
const mainChannel = this.createWindowMainChannel(sender);
|
||||
const multiplexer = new ChannelMultiplexer(mainChannel);
|
||||
multiplexer.onDidOpenChannel((openEvent) => {
|
||||
const { channel, id } = openEvent;
|
||||
if (this.channelHandlers.route(id, channel)) {
|
||||
console.debug(`Opening channel for service path '${id}'.`);
|
||||
channel.onClose(() =>
|
||||
console.debug(`Closing channel on service path '${id}'.`)
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// When refreshing the browser window.
|
||||
sender.once('did-navigate', () => {
|
||||
multiplexer.onUnderlyingChannelClose({ reason: 'Window was refreshed' });
|
||||
this.windowChannelMultiplexer.delete(sender.id);
|
||||
});
|
||||
// When closing the browser window.
|
||||
sender.once('destroyed', () => {
|
||||
multiplexer.onUnderlyingChannelClose({ reason: 'Window was closed' });
|
||||
this.windowChannelMultiplexer.delete(sender.id);
|
||||
});
|
||||
const data = { channel: mainChannel, multiplexer };
|
||||
this.windowChannelMultiplexer.set(sender.id, data);
|
||||
return data;
|
||||
}
|
||||
}
|
@@ -1,46 +1,16 @@
|
||||
import { injectable } from '@theia/core/shared/inversify';
|
||||
import { ipcMain, IpcMainEvent } from '@theia/electron/shared/electron';
|
||||
import {
|
||||
RELOAD_REQUESTED_SIGNAL,
|
||||
StopReason,
|
||||
} from '@theia/core/lib/electron-common/messaging/electron-messages';
|
||||
import { TheiaElectronWindow as DefaultTheiaElectronWindow } from '@theia/core/lib/electron-main/theia-electron-window';
|
||||
import { FileUri } from '@theia/core/lib/node';
|
||||
import URI from '@theia/core/lib/common/uri';
|
||||
import { createDisposableListener } from '@theia/core/lib/electron-main/event-utils';
|
||||
import { TheiaElectronWindow as DefaultTheiaElectronWindow } from '@theia/core/lib/electron-main/theia-electron-window';
|
||||
import { injectable } from '@theia/core/shared/inversify';
|
||||
import { ipcMain, IpcMainEvent } from '@theia/electron/shared/electron';
|
||||
import { StartupTask } from '../../electron-common/startup-task';
|
||||
import { load } from './window';
|
||||
|
||||
@injectable()
|
||||
export class TheiaElectronWindow extends DefaultTheiaElectronWindow {
|
||||
protected override async handleStopRequest(
|
||||
onSafeCallback: () => unknown,
|
||||
reason: StopReason
|
||||
): Promise<boolean> {
|
||||
// Only confirm close to windows that have loaded our frontend.
|
||||
// Both the windows's URL and the FS path of the `index.html` should be converted to the "same" format to be able to compare them. (#11226)
|
||||
// Notes:
|
||||
// - Windows: file:///C:/path/to/somewhere vs file:///c%3A/path/to/somewhere
|
||||
// - macOS: file:///Applications/App%20Name.app/Contents vs /Applications/App Name.app/Contents
|
||||
// This URL string comes from electron, we can expect that this is properly encoded URL. For example, a space is `%20`
|
||||
const currentUrl = new URI(this.window.webContents.getURL()).toString();
|
||||
// THEIA_FRONTEND_HTML_PATH is an FS path, we have to covert to an encoded URI string.
|
||||
const frontendUri = FileUri.create(
|
||||
this.globals.THEIA_FRONTEND_HTML_PATH
|
||||
).toString();
|
||||
const safeToClose =
|
||||
!currentUrl.includes(frontendUri) || (await this.checkSafeToStop(reason));
|
||||
if (safeToClose) {
|
||||
try {
|
||||
await onSafeCallback();
|
||||
return true;
|
||||
} catch (e) {
|
||||
console.warn(`Request ${StopReason[reason]} failed.`, e);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected override reload(tasks?: StartupTask[]): void {
|
||||
this.handleStopRequest(() => {
|
||||
this.applicationState = 'init';
|
||||
|
@@ -16,6 +16,7 @@ import { BackendApplicationContribution } from '@theia/core/lib/node/backend-app
|
||||
import { ArduinoDaemon, NotificationServiceServer } from '../common/protocol';
|
||||
import { CLI_CONFIG } from './cli-config';
|
||||
import { getExecPath, spawnCommand } from './exec-util';
|
||||
import { ErrnoException } from './utils/errors';
|
||||
|
||||
@injectable()
|
||||
export class ArduinoDaemonImpl
|
||||
@@ -184,7 +185,7 @@ export class ArduinoDaemonImpl
|
||||
}
|
||||
return false;
|
||||
} catch (error) {
|
||||
if ('code' in error && error.code === 'ENOENT') {
|
||||
if (ErrnoException.isENOENT(error)) {
|
||||
return false;
|
||||
}
|
||||
throw error;
|
||||
|
@@ -100,8 +100,8 @@ import WebSocketProviderImpl from './web-socket/web-socket-provider-impl';
|
||||
import { WebSocketProvider } from './web-socket/web-socket-provider';
|
||||
import { ClangFormatter } from './clang-formatter';
|
||||
import { FormatterPath } from '../common/protocol/formatter';
|
||||
import { LocalizationBackendContribution } from './i18n/localization-backend-contribution';
|
||||
import { LocalizationBackendContribution as TheiaLocalizationBackendContribution } from '@theia/core/lib/node/i18n/localization-backend-contribution';
|
||||
import { HostedPluginLocalizationService } from './theia/plugin-ext/hosted-plugin-localization-service';
|
||||
import { HostedPluginLocalizationService as TheiaHostedPluginLocalizationService } from '@theia/plugin-ext/lib/hosted/node/hosted-plugin-localization-service';
|
||||
import { SurveyNotificationServiceImpl } from './survey-service-impl';
|
||||
import {
|
||||
SurveyNotificationService,
|
||||
@@ -109,6 +109,10 @@ import {
|
||||
} from '../common/protocol/survey-service';
|
||||
import { IsTempSketch } from './is-temp-sketch';
|
||||
import { rebindNsfwFileSystemWatcher } from './theia/filesystem/nsfw-watcher/nsfw-bindings';
|
||||
import { MessagingContribution } from './theia/core/messaging-contribution';
|
||||
import { MessagingService } from '@theia/core/lib/node/messaging/messaging-service';
|
||||
import { HostedPluginReader } from './theia/plugin-ext/plugin-reader';
|
||||
import { HostedPluginReader as TheiaHostedPluginReader } from '@theia/plugin-ext/lib/hosted/node/plugin-reader';
|
||||
|
||||
export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
bind(BackendApplication).toSelf().inSingletonScope();
|
||||
@@ -360,9 +364,9 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
bind(BackendApplicationContribution).toService(PlotterBackendContribution);
|
||||
bind(ArduinoLocalizationContribution).toSelf().inSingletonScope();
|
||||
bind(LocalizationContribution).toService(ArduinoLocalizationContribution);
|
||||
bind(LocalizationBackendContribution).toSelf().inSingletonScope();
|
||||
rebind(TheiaLocalizationBackendContribution).toService(
|
||||
LocalizationBackendContribution
|
||||
bind(HostedPluginLocalizationService).toSelf().inSingletonScope();
|
||||
rebind(TheiaHostedPluginLocalizationService).toService(
|
||||
HostedPluginLocalizationService
|
||||
);
|
||||
|
||||
// Survey notification bindings
|
||||
@@ -379,6 +383,15 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
.inSingletonScope();
|
||||
|
||||
bind(IsTempSketch).toSelf().inSingletonScope();
|
||||
rebind(MessagingService.Identifier)
|
||||
.to(MessagingContribution)
|
||||
.inSingletonScope();
|
||||
|
||||
// Removed undesired contributions from VS Code extensions
|
||||
// Such as the RTOS view from the `cortex-debug` extension
|
||||
// https://github.com/arduino/arduino-ide/pull/1706#pullrequestreview-1195595080
|
||||
bind(HostedPluginReader).toSelf().inSingletonScope();
|
||||
rebind(TheiaHostedPluginReader).toService(HostedPluginReader);
|
||||
});
|
||||
|
||||
function bindChildLogger(bind: interfaces.Bind, name: string): void {
|
||||
|
@@ -22,9 +22,6 @@ interface IArduinoCoreServiceService extends grpc.ServiceDefinition<grpc.Untyped
|
||||
destroy: IArduinoCoreServiceService_IDestroy;
|
||||
updateIndex: IArduinoCoreServiceService_IUpdateIndex;
|
||||
updateLibrariesIndex: IArduinoCoreServiceService_IUpdateLibrariesIndex;
|
||||
updateCoreLibrariesIndex: IArduinoCoreServiceService_IUpdateCoreLibrariesIndex;
|
||||
outdated: IArduinoCoreServiceService_IOutdated;
|
||||
upgrade: IArduinoCoreServiceService_IUpgrade;
|
||||
version: IArduinoCoreServiceService_IVersion;
|
||||
newSketch: IArduinoCoreServiceService_INewSketch;
|
||||
loadSketch: IArduinoCoreServiceService_ILoadSketch;
|
||||
@@ -106,33 +103,6 @@ interface IArduinoCoreServiceService_IUpdateLibrariesIndex extends grpc.MethodDe
|
||||
responseSerialize: grpc.serialize<cc_arduino_cli_commands_v1_commands_pb.UpdateLibrariesIndexResponse>;
|
||||
responseDeserialize: grpc.deserialize<cc_arduino_cli_commands_v1_commands_pb.UpdateLibrariesIndexResponse>;
|
||||
}
|
||||
interface IArduinoCoreServiceService_IUpdateCoreLibrariesIndex extends grpc.MethodDefinition<cc_arduino_cli_commands_v1_commands_pb.UpdateCoreLibrariesIndexRequest, cc_arduino_cli_commands_v1_commands_pb.UpdateCoreLibrariesIndexResponse> {
|
||||
path: "/cc.arduino.cli.commands.v1.ArduinoCoreService/UpdateCoreLibrariesIndex";
|
||||
requestStream: false;
|
||||
responseStream: true;
|
||||
requestSerialize: grpc.serialize<cc_arduino_cli_commands_v1_commands_pb.UpdateCoreLibrariesIndexRequest>;
|
||||
requestDeserialize: grpc.deserialize<cc_arduino_cli_commands_v1_commands_pb.UpdateCoreLibrariesIndexRequest>;
|
||||
responseSerialize: grpc.serialize<cc_arduino_cli_commands_v1_commands_pb.UpdateCoreLibrariesIndexResponse>;
|
||||
responseDeserialize: grpc.deserialize<cc_arduino_cli_commands_v1_commands_pb.UpdateCoreLibrariesIndexResponse>;
|
||||
}
|
||||
interface IArduinoCoreServiceService_IOutdated extends grpc.MethodDefinition<cc_arduino_cli_commands_v1_commands_pb.OutdatedRequest, cc_arduino_cli_commands_v1_commands_pb.OutdatedResponse> {
|
||||
path: "/cc.arduino.cli.commands.v1.ArduinoCoreService/Outdated";
|
||||
requestStream: false;
|
||||
responseStream: false;
|
||||
requestSerialize: grpc.serialize<cc_arduino_cli_commands_v1_commands_pb.OutdatedRequest>;
|
||||
requestDeserialize: grpc.deserialize<cc_arduino_cli_commands_v1_commands_pb.OutdatedRequest>;
|
||||
responseSerialize: grpc.serialize<cc_arduino_cli_commands_v1_commands_pb.OutdatedResponse>;
|
||||
responseDeserialize: grpc.deserialize<cc_arduino_cli_commands_v1_commands_pb.OutdatedResponse>;
|
||||
}
|
||||
interface IArduinoCoreServiceService_IUpgrade extends grpc.MethodDefinition<cc_arduino_cli_commands_v1_commands_pb.UpgradeRequest, cc_arduino_cli_commands_v1_commands_pb.UpgradeResponse> {
|
||||
path: "/cc.arduino.cli.commands.v1.ArduinoCoreService/Upgrade";
|
||||
requestStream: false;
|
||||
responseStream: true;
|
||||
requestSerialize: grpc.serialize<cc_arduino_cli_commands_v1_commands_pb.UpgradeRequest>;
|
||||
requestDeserialize: grpc.deserialize<cc_arduino_cli_commands_v1_commands_pb.UpgradeRequest>;
|
||||
responseSerialize: grpc.serialize<cc_arduino_cli_commands_v1_commands_pb.UpgradeResponse>;
|
||||
responseDeserialize: grpc.deserialize<cc_arduino_cli_commands_v1_commands_pb.UpgradeResponse>;
|
||||
}
|
||||
interface IArduinoCoreServiceService_IVersion extends grpc.MethodDefinition<cc_arduino_cli_commands_v1_commands_pb.VersionRequest, cc_arduino_cli_commands_v1_commands_pb.VersionResponse> {
|
||||
path: "/cc.arduino.cli.commands.v1.ArduinoCoreService/Version";
|
||||
requestStream: false;
|
||||
@@ -448,9 +418,6 @@ export interface IArduinoCoreServiceServer {
|
||||
destroy: grpc.handleUnaryCall<cc_arduino_cli_commands_v1_commands_pb.DestroyRequest, cc_arduino_cli_commands_v1_commands_pb.DestroyResponse>;
|
||||
updateIndex: grpc.handleServerStreamingCall<cc_arduino_cli_commands_v1_commands_pb.UpdateIndexRequest, cc_arduino_cli_commands_v1_commands_pb.UpdateIndexResponse>;
|
||||
updateLibrariesIndex: grpc.handleServerStreamingCall<cc_arduino_cli_commands_v1_commands_pb.UpdateLibrariesIndexRequest, cc_arduino_cli_commands_v1_commands_pb.UpdateLibrariesIndexResponse>;
|
||||
updateCoreLibrariesIndex: grpc.handleServerStreamingCall<cc_arduino_cli_commands_v1_commands_pb.UpdateCoreLibrariesIndexRequest, cc_arduino_cli_commands_v1_commands_pb.UpdateCoreLibrariesIndexResponse>;
|
||||
outdated: grpc.handleUnaryCall<cc_arduino_cli_commands_v1_commands_pb.OutdatedRequest, cc_arduino_cli_commands_v1_commands_pb.OutdatedResponse>;
|
||||
upgrade: grpc.handleServerStreamingCall<cc_arduino_cli_commands_v1_commands_pb.UpgradeRequest, cc_arduino_cli_commands_v1_commands_pb.UpgradeResponse>;
|
||||
version: grpc.handleUnaryCall<cc_arduino_cli_commands_v1_commands_pb.VersionRequest, cc_arduino_cli_commands_v1_commands_pb.VersionResponse>;
|
||||
newSketch: grpc.handleUnaryCall<cc_arduino_cli_commands_v1_commands_pb.NewSketchRequest, cc_arduino_cli_commands_v1_commands_pb.NewSketchResponse>;
|
||||
loadSketch: grpc.handleUnaryCall<cc_arduino_cli_commands_v1_commands_pb.LoadSketchRequest, cc_arduino_cli_commands_v1_commands_pb.LoadSketchResponse>;
|
||||
@@ -500,13 +467,6 @@ export interface IArduinoCoreServiceClient {
|
||||
updateIndex(request: cc_arduino_cli_commands_v1_commands_pb.UpdateIndexRequest, metadata?: grpc.Metadata, options?: Partial<grpc.CallOptions>): grpc.ClientReadableStream<cc_arduino_cli_commands_v1_commands_pb.UpdateIndexResponse>;
|
||||
updateLibrariesIndex(request: cc_arduino_cli_commands_v1_commands_pb.UpdateLibrariesIndexRequest, options?: Partial<grpc.CallOptions>): grpc.ClientReadableStream<cc_arduino_cli_commands_v1_commands_pb.UpdateLibrariesIndexResponse>;
|
||||
updateLibrariesIndex(request: cc_arduino_cli_commands_v1_commands_pb.UpdateLibrariesIndexRequest, metadata?: grpc.Metadata, options?: Partial<grpc.CallOptions>): grpc.ClientReadableStream<cc_arduino_cli_commands_v1_commands_pb.UpdateLibrariesIndexResponse>;
|
||||
updateCoreLibrariesIndex(request: cc_arduino_cli_commands_v1_commands_pb.UpdateCoreLibrariesIndexRequest, options?: Partial<grpc.CallOptions>): grpc.ClientReadableStream<cc_arduino_cli_commands_v1_commands_pb.UpdateCoreLibrariesIndexResponse>;
|
||||
updateCoreLibrariesIndex(request: cc_arduino_cli_commands_v1_commands_pb.UpdateCoreLibrariesIndexRequest, metadata?: grpc.Metadata, options?: Partial<grpc.CallOptions>): grpc.ClientReadableStream<cc_arduino_cli_commands_v1_commands_pb.UpdateCoreLibrariesIndexResponse>;
|
||||
outdated(request: cc_arduino_cli_commands_v1_commands_pb.OutdatedRequest, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_commands_pb.OutdatedResponse) => void): grpc.ClientUnaryCall;
|
||||
outdated(request: cc_arduino_cli_commands_v1_commands_pb.OutdatedRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_commands_pb.OutdatedResponse) => void): grpc.ClientUnaryCall;
|
||||
outdated(request: cc_arduino_cli_commands_v1_commands_pb.OutdatedRequest, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_commands_pb.OutdatedResponse) => void): grpc.ClientUnaryCall;
|
||||
upgrade(request: cc_arduino_cli_commands_v1_commands_pb.UpgradeRequest, options?: Partial<grpc.CallOptions>): grpc.ClientReadableStream<cc_arduino_cli_commands_v1_commands_pb.UpgradeResponse>;
|
||||
upgrade(request: cc_arduino_cli_commands_v1_commands_pb.UpgradeRequest, metadata?: grpc.Metadata, options?: Partial<grpc.CallOptions>): grpc.ClientReadableStream<cc_arduino_cli_commands_v1_commands_pb.UpgradeResponse>;
|
||||
version(request: cc_arduino_cli_commands_v1_commands_pb.VersionRequest, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_commands_pb.VersionResponse) => void): grpc.ClientUnaryCall;
|
||||
version(request: cc_arduino_cli_commands_v1_commands_pb.VersionRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_commands_pb.VersionResponse) => void): grpc.ClientUnaryCall;
|
||||
version(request: cc_arduino_cli_commands_v1_commands_pb.VersionRequest, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_commands_pb.VersionResponse) => void): grpc.ClientUnaryCall;
|
||||
@@ -609,13 +569,6 @@ export class ArduinoCoreServiceClient extends grpc.Client implements IArduinoCor
|
||||
public updateIndex(request: cc_arduino_cli_commands_v1_commands_pb.UpdateIndexRequest, metadata?: grpc.Metadata, options?: Partial<grpc.CallOptions>): grpc.ClientReadableStream<cc_arduino_cli_commands_v1_commands_pb.UpdateIndexResponse>;
|
||||
public updateLibrariesIndex(request: cc_arduino_cli_commands_v1_commands_pb.UpdateLibrariesIndexRequest, options?: Partial<grpc.CallOptions>): grpc.ClientReadableStream<cc_arduino_cli_commands_v1_commands_pb.UpdateLibrariesIndexResponse>;
|
||||
public updateLibrariesIndex(request: cc_arduino_cli_commands_v1_commands_pb.UpdateLibrariesIndexRequest, metadata?: grpc.Metadata, options?: Partial<grpc.CallOptions>): grpc.ClientReadableStream<cc_arduino_cli_commands_v1_commands_pb.UpdateLibrariesIndexResponse>;
|
||||
public updateCoreLibrariesIndex(request: cc_arduino_cli_commands_v1_commands_pb.UpdateCoreLibrariesIndexRequest, options?: Partial<grpc.CallOptions>): grpc.ClientReadableStream<cc_arduino_cli_commands_v1_commands_pb.UpdateCoreLibrariesIndexResponse>;
|
||||
public updateCoreLibrariesIndex(request: cc_arduino_cli_commands_v1_commands_pb.UpdateCoreLibrariesIndexRequest, metadata?: grpc.Metadata, options?: Partial<grpc.CallOptions>): grpc.ClientReadableStream<cc_arduino_cli_commands_v1_commands_pb.UpdateCoreLibrariesIndexResponse>;
|
||||
public outdated(request: cc_arduino_cli_commands_v1_commands_pb.OutdatedRequest, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_commands_pb.OutdatedResponse) => void): grpc.ClientUnaryCall;
|
||||
public outdated(request: cc_arduino_cli_commands_v1_commands_pb.OutdatedRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_commands_pb.OutdatedResponse) => void): grpc.ClientUnaryCall;
|
||||
public outdated(request: cc_arduino_cli_commands_v1_commands_pb.OutdatedRequest, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_commands_pb.OutdatedResponse) => void): grpc.ClientUnaryCall;
|
||||
public upgrade(request: cc_arduino_cli_commands_v1_commands_pb.UpgradeRequest, options?: Partial<grpc.CallOptions>): grpc.ClientReadableStream<cc_arduino_cli_commands_v1_commands_pb.UpgradeResponse>;
|
||||
public upgrade(request: cc_arduino_cli_commands_v1_commands_pb.UpgradeRequest, metadata?: grpc.Metadata, options?: Partial<grpc.CallOptions>): grpc.ClientReadableStream<cc_arduino_cli_commands_v1_commands_pb.UpgradeResponse>;
|
||||
public version(request: cc_arduino_cli_commands_v1_commands_pb.VersionRequest, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_commands_pb.VersionResponse) => void): grpc.ClientUnaryCall;
|
||||
public version(request: cc_arduino_cli_commands_v1_commands_pb.VersionRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_commands_pb.VersionResponse) => void): grpc.ClientUnaryCall;
|
||||
public version(request: cc_arduino_cli_commands_v1_commands_pb.VersionRequest, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_commands_pb.VersionResponse) => void): grpc.ClientUnaryCall;
|
||||
|
@@ -599,28 +599,6 @@ function deserialize_cc_arduino_cli_commands_v1_NewSketchResponse(buffer_arg) {
|
||||
return cc_arduino_cli_commands_v1_commands_pb.NewSketchResponse.deserializeBinary(new Uint8Array(buffer_arg));
|
||||
}
|
||||
|
||||
function serialize_cc_arduino_cli_commands_v1_OutdatedRequest(arg) {
|
||||
if (!(arg instanceof cc_arduino_cli_commands_v1_commands_pb.OutdatedRequest)) {
|
||||
throw new Error('Expected argument of type cc.arduino.cli.commands.v1.OutdatedRequest');
|
||||
}
|
||||
return Buffer.from(arg.serializeBinary());
|
||||
}
|
||||
|
||||
function deserialize_cc_arduino_cli_commands_v1_OutdatedRequest(buffer_arg) {
|
||||
return cc_arduino_cli_commands_v1_commands_pb.OutdatedRequest.deserializeBinary(new Uint8Array(buffer_arg));
|
||||
}
|
||||
|
||||
function serialize_cc_arduino_cli_commands_v1_OutdatedResponse(arg) {
|
||||
if (!(arg instanceof cc_arduino_cli_commands_v1_commands_pb.OutdatedResponse)) {
|
||||
throw new Error('Expected argument of type cc.arduino.cli.commands.v1.OutdatedResponse');
|
||||
}
|
||||
return Buffer.from(arg.serializeBinary());
|
||||
}
|
||||
|
||||
function deserialize_cc_arduino_cli_commands_v1_OutdatedResponse(buffer_arg) {
|
||||
return cc_arduino_cli_commands_v1_commands_pb.OutdatedResponse.deserializeBinary(new Uint8Array(buffer_arg));
|
||||
}
|
||||
|
||||
function serialize_cc_arduino_cli_commands_v1_PlatformDownloadRequest(arg) {
|
||||
if (!(arg instanceof cc_arduino_cli_commands_v1_core_pb.PlatformDownloadRequest)) {
|
||||
throw new Error('Expected argument of type cc.arduino.cli.commands.v1.PlatformDownloadRequest');
|
||||
@@ -775,28 +753,6 @@ function deserialize_cc_arduino_cli_commands_v1_SupportedUserFieldsResponse(buff
|
||||
return cc_arduino_cli_commands_v1_upload_pb.SupportedUserFieldsResponse.deserializeBinary(new Uint8Array(buffer_arg));
|
||||
}
|
||||
|
||||
function serialize_cc_arduino_cli_commands_v1_UpdateCoreLibrariesIndexRequest(arg) {
|
||||
if (!(arg instanceof cc_arduino_cli_commands_v1_commands_pb.UpdateCoreLibrariesIndexRequest)) {
|
||||
throw new Error('Expected argument of type cc.arduino.cli.commands.v1.UpdateCoreLibrariesIndexRequest');
|
||||
}
|
||||
return Buffer.from(arg.serializeBinary());
|
||||
}
|
||||
|
||||
function deserialize_cc_arduino_cli_commands_v1_UpdateCoreLibrariesIndexRequest(buffer_arg) {
|
||||
return cc_arduino_cli_commands_v1_commands_pb.UpdateCoreLibrariesIndexRequest.deserializeBinary(new Uint8Array(buffer_arg));
|
||||
}
|
||||
|
||||
function serialize_cc_arduino_cli_commands_v1_UpdateCoreLibrariesIndexResponse(arg) {
|
||||
if (!(arg instanceof cc_arduino_cli_commands_v1_commands_pb.UpdateCoreLibrariesIndexResponse)) {
|
||||
throw new Error('Expected argument of type cc.arduino.cli.commands.v1.UpdateCoreLibrariesIndexResponse');
|
||||
}
|
||||
return Buffer.from(arg.serializeBinary());
|
||||
}
|
||||
|
||||
function deserialize_cc_arduino_cli_commands_v1_UpdateCoreLibrariesIndexResponse(buffer_arg) {
|
||||
return cc_arduino_cli_commands_v1_commands_pb.UpdateCoreLibrariesIndexResponse.deserializeBinary(new Uint8Array(buffer_arg));
|
||||
}
|
||||
|
||||
function serialize_cc_arduino_cli_commands_v1_UpdateIndexRequest(arg) {
|
||||
if (!(arg instanceof cc_arduino_cli_commands_v1_commands_pb.UpdateIndexRequest)) {
|
||||
throw new Error('Expected argument of type cc.arduino.cli.commands.v1.UpdateIndexRequest');
|
||||
@@ -841,28 +797,6 @@ function deserialize_cc_arduino_cli_commands_v1_UpdateLibrariesIndexResponse(buf
|
||||
return cc_arduino_cli_commands_v1_commands_pb.UpdateLibrariesIndexResponse.deserializeBinary(new Uint8Array(buffer_arg));
|
||||
}
|
||||
|
||||
function serialize_cc_arduino_cli_commands_v1_UpgradeRequest(arg) {
|
||||
if (!(arg instanceof cc_arduino_cli_commands_v1_commands_pb.UpgradeRequest)) {
|
||||
throw new Error('Expected argument of type cc.arduino.cli.commands.v1.UpgradeRequest');
|
||||
}
|
||||
return Buffer.from(arg.serializeBinary());
|
||||
}
|
||||
|
||||
function deserialize_cc_arduino_cli_commands_v1_UpgradeRequest(buffer_arg) {
|
||||
return cc_arduino_cli_commands_v1_commands_pb.UpgradeRequest.deserializeBinary(new Uint8Array(buffer_arg));
|
||||
}
|
||||
|
||||
function serialize_cc_arduino_cli_commands_v1_UpgradeResponse(arg) {
|
||||
if (!(arg instanceof cc_arduino_cli_commands_v1_commands_pb.UpgradeResponse)) {
|
||||
throw new Error('Expected argument of type cc.arduino.cli.commands.v1.UpgradeResponse');
|
||||
}
|
||||
return Buffer.from(arg.serializeBinary());
|
||||
}
|
||||
|
||||
function deserialize_cc_arduino_cli_commands_v1_UpgradeResponse(buffer_arg) {
|
||||
return cc_arduino_cli_commands_v1_commands_pb.UpgradeResponse.deserializeBinary(new Uint8Array(buffer_arg));
|
||||
}
|
||||
|
||||
function serialize_cc_arduino_cli_commands_v1_UploadRequest(arg) {
|
||||
if (!(arg instanceof cc_arduino_cli_commands_v1_upload_pb.UploadRequest)) {
|
||||
throw new Error('Expected argument of type cc.arduino.cli.commands.v1.UploadRequest');
|
||||
@@ -1015,42 +949,6 @@ updateLibrariesIndex: {
|
||||
responseSerialize: serialize_cc_arduino_cli_commands_v1_UpdateLibrariesIndexResponse,
|
||||
responseDeserialize: deserialize_cc_arduino_cli_commands_v1_UpdateLibrariesIndexResponse,
|
||||
},
|
||||
// Update packages indexes for both Cores and Libraries
|
||||
updateCoreLibrariesIndex: {
|
||||
path: '/cc.arduino.cli.commands.v1.ArduinoCoreService/UpdateCoreLibrariesIndex',
|
||||
requestStream: false,
|
||||
responseStream: true,
|
||||
requestType: cc_arduino_cli_commands_v1_commands_pb.UpdateCoreLibrariesIndexRequest,
|
||||
responseType: cc_arduino_cli_commands_v1_commands_pb.UpdateCoreLibrariesIndexResponse,
|
||||
requestSerialize: serialize_cc_arduino_cli_commands_v1_UpdateCoreLibrariesIndexRequest,
|
||||
requestDeserialize: deserialize_cc_arduino_cli_commands_v1_UpdateCoreLibrariesIndexRequest,
|
||||
responseSerialize: serialize_cc_arduino_cli_commands_v1_UpdateCoreLibrariesIndexResponse,
|
||||
responseDeserialize: deserialize_cc_arduino_cli_commands_v1_UpdateCoreLibrariesIndexResponse,
|
||||
},
|
||||
// Outdated returns a message with a list of outdated Cores and Libraries
|
||||
outdated: {
|
||||
path: '/cc.arduino.cli.commands.v1.ArduinoCoreService/Outdated',
|
||||
requestStream: false,
|
||||
responseStream: false,
|
||||
requestType: cc_arduino_cli_commands_v1_commands_pb.OutdatedRequest,
|
||||
responseType: cc_arduino_cli_commands_v1_commands_pb.OutdatedResponse,
|
||||
requestSerialize: serialize_cc_arduino_cli_commands_v1_OutdatedRequest,
|
||||
requestDeserialize: deserialize_cc_arduino_cli_commands_v1_OutdatedRequest,
|
||||
responseSerialize: serialize_cc_arduino_cli_commands_v1_OutdatedResponse,
|
||||
responseDeserialize: deserialize_cc_arduino_cli_commands_v1_OutdatedResponse,
|
||||
},
|
||||
// Upgrade both Cores and Libraries
|
||||
upgrade: {
|
||||
path: '/cc.arduino.cli.commands.v1.ArduinoCoreService/Upgrade',
|
||||
requestStream: false,
|
||||
responseStream: true,
|
||||
requestType: cc_arduino_cli_commands_v1_commands_pb.UpgradeRequest,
|
||||
responseType: cc_arduino_cli_commands_v1_commands_pb.UpgradeResponse,
|
||||
requestSerialize: serialize_cc_arduino_cli_commands_v1_UpgradeRequest,
|
||||
requestDeserialize: deserialize_cc_arduino_cli_commands_v1_UpgradeRequest,
|
||||
responseSerialize: serialize_cc_arduino_cli_commands_v1_UpgradeResponse,
|
||||
responseDeserialize: deserialize_cc_arduino_cli_commands_v1_UpgradeResponse,
|
||||
},
|
||||
// Get the version of Arduino CLI in use.
|
||||
version: {
|
||||
path: '/cc.arduino.cli.commands.v1.ArduinoCoreService/Version',
|
||||
|
@@ -313,166 +313,6 @@ export namespace UpdateLibrariesIndexResponse {
|
||||
}
|
||||
}
|
||||
|
||||
export class UpdateCoreLibrariesIndexRequest extends jspb.Message {
|
||||
|
||||
hasInstance(): boolean;
|
||||
clearInstance(): void;
|
||||
getInstance(): cc_arduino_cli_commands_v1_common_pb.Instance | undefined;
|
||||
setInstance(value?: cc_arduino_cli_commands_v1_common_pb.Instance): UpdateCoreLibrariesIndexRequest;
|
||||
|
||||
|
||||
serializeBinary(): Uint8Array;
|
||||
toObject(includeInstance?: boolean): UpdateCoreLibrariesIndexRequest.AsObject;
|
||||
static toObject(includeInstance: boolean, msg: UpdateCoreLibrariesIndexRequest): UpdateCoreLibrariesIndexRequest.AsObject;
|
||||
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
|
||||
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
|
||||
static serializeBinaryToWriter(message: UpdateCoreLibrariesIndexRequest, writer: jspb.BinaryWriter): void;
|
||||
static deserializeBinary(bytes: Uint8Array): UpdateCoreLibrariesIndexRequest;
|
||||
static deserializeBinaryFromReader(message: UpdateCoreLibrariesIndexRequest, reader: jspb.BinaryReader): UpdateCoreLibrariesIndexRequest;
|
||||
}
|
||||
|
||||
export namespace UpdateCoreLibrariesIndexRequest {
|
||||
export type AsObject = {
|
||||
instance?: cc_arduino_cli_commands_v1_common_pb.Instance.AsObject,
|
||||
}
|
||||
}
|
||||
|
||||
export class UpdateCoreLibrariesIndexResponse extends jspb.Message {
|
||||
|
||||
hasDownloadProgress(): boolean;
|
||||
clearDownloadProgress(): void;
|
||||
getDownloadProgress(): cc_arduino_cli_commands_v1_common_pb.DownloadProgress | undefined;
|
||||
setDownloadProgress(value?: cc_arduino_cli_commands_v1_common_pb.DownloadProgress): UpdateCoreLibrariesIndexResponse;
|
||||
|
||||
|
||||
serializeBinary(): Uint8Array;
|
||||
toObject(includeInstance?: boolean): UpdateCoreLibrariesIndexResponse.AsObject;
|
||||
static toObject(includeInstance: boolean, msg: UpdateCoreLibrariesIndexResponse): UpdateCoreLibrariesIndexResponse.AsObject;
|
||||
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
|
||||
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
|
||||
static serializeBinaryToWriter(message: UpdateCoreLibrariesIndexResponse, writer: jspb.BinaryWriter): void;
|
||||
static deserializeBinary(bytes: Uint8Array): UpdateCoreLibrariesIndexResponse;
|
||||
static deserializeBinaryFromReader(message: UpdateCoreLibrariesIndexResponse, reader: jspb.BinaryReader): UpdateCoreLibrariesIndexResponse;
|
||||
}
|
||||
|
||||
export namespace UpdateCoreLibrariesIndexResponse {
|
||||
export type AsObject = {
|
||||
downloadProgress?: cc_arduino_cli_commands_v1_common_pb.DownloadProgress.AsObject,
|
||||
}
|
||||
}
|
||||
|
||||
export class OutdatedRequest extends jspb.Message {
|
||||
|
||||
hasInstance(): boolean;
|
||||
clearInstance(): void;
|
||||
getInstance(): cc_arduino_cli_commands_v1_common_pb.Instance | undefined;
|
||||
setInstance(value?: cc_arduino_cli_commands_v1_common_pb.Instance): OutdatedRequest;
|
||||
|
||||
|
||||
serializeBinary(): Uint8Array;
|
||||
toObject(includeInstance?: boolean): OutdatedRequest.AsObject;
|
||||
static toObject(includeInstance: boolean, msg: OutdatedRequest): OutdatedRequest.AsObject;
|
||||
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
|
||||
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
|
||||
static serializeBinaryToWriter(message: OutdatedRequest, writer: jspb.BinaryWriter): void;
|
||||
static deserializeBinary(bytes: Uint8Array): OutdatedRequest;
|
||||
static deserializeBinaryFromReader(message: OutdatedRequest, reader: jspb.BinaryReader): OutdatedRequest;
|
||||
}
|
||||
|
||||
export namespace OutdatedRequest {
|
||||
export type AsObject = {
|
||||
instance?: cc_arduino_cli_commands_v1_common_pb.Instance.AsObject,
|
||||
}
|
||||
}
|
||||
|
||||
export class OutdatedResponse extends jspb.Message {
|
||||
clearOutdatedLibrariesList(): void;
|
||||
getOutdatedLibrariesList(): Array<cc_arduino_cli_commands_v1_lib_pb.InstalledLibrary>;
|
||||
setOutdatedLibrariesList(value: Array<cc_arduino_cli_commands_v1_lib_pb.InstalledLibrary>): OutdatedResponse;
|
||||
addOutdatedLibraries(value?: cc_arduino_cli_commands_v1_lib_pb.InstalledLibrary, index?: number): cc_arduino_cli_commands_v1_lib_pb.InstalledLibrary;
|
||||
|
||||
clearOutdatedPlatformsList(): void;
|
||||
getOutdatedPlatformsList(): Array<cc_arduino_cli_commands_v1_common_pb.Platform>;
|
||||
setOutdatedPlatformsList(value: Array<cc_arduino_cli_commands_v1_common_pb.Platform>): OutdatedResponse;
|
||||
addOutdatedPlatforms(value?: cc_arduino_cli_commands_v1_common_pb.Platform, index?: number): cc_arduino_cli_commands_v1_common_pb.Platform;
|
||||
|
||||
|
||||
serializeBinary(): Uint8Array;
|
||||
toObject(includeInstance?: boolean): OutdatedResponse.AsObject;
|
||||
static toObject(includeInstance: boolean, msg: OutdatedResponse): OutdatedResponse.AsObject;
|
||||
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
|
||||
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
|
||||
static serializeBinaryToWriter(message: OutdatedResponse, writer: jspb.BinaryWriter): void;
|
||||
static deserializeBinary(bytes: Uint8Array): OutdatedResponse;
|
||||
static deserializeBinaryFromReader(message: OutdatedResponse, reader: jspb.BinaryReader): OutdatedResponse;
|
||||
}
|
||||
|
||||
export namespace OutdatedResponse {
|
||||
export type AsObject = {
|
||||
outdatedLibrariesList: Array<cc_arduino_cli_commands_v1_lib_pb.InstalledLibrary.AsObject>,
|
||||
outdatedPlatformsList: Array<cc_arduino_cli_commands_v1_common_pb.Platform.AsObject>,
|
||||
}
|
||||
}
|
||||
|
||||
export class UpgradeRequest extends jspb.Message {
|
||||
|
||||
hasInstance(): boolean;
|
||||
clearInstance(): void;
|
||||
getInstance(): cc_arduino_cli_commands_v1_common_pb.Instance | undefined;
|
||||
setInstance(value?: cc_arduino_cli_commands_v1_common_pb.Instance): UpgradeRequest;
|
||||
|
||||
getSkipPostInstall(): boolean;
|
||||
setSkipPostInstall(value: boolean): UpgradeRequest;
|
||||
|
||||
|
||||
serializeBinary(): Uint8Array;
|
||||
toObject(includeInstance?: boolean): UpgradeRequest.AsObject;
|
||||
static toObject(includeInstance: boolean, msg: UpgradeRequest): UpgradeRequest.AsObject;
|
||||
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
|
||||
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
|
||||
static serializeBinaryToWriter(message: UpgradeRequest, writer: jspb.BinaryWriter): void;
|
||||
static deserializeBinary(bytes: Uint8Array): UpgradeRequest;
|
||||
static deserializeBinaryFromReader(message: UpgradeRequest, reader: jspb.BinaryReader): UpgradeRequest;
|
||||
}
|
||||
|
||||
export namespace UpgradeRequest {
|
||||
export type AsObject = {
|
||||
instance?: cc_arduino_cli_commands_v1_common_pb.Instance.AsObject,
|
||||
skipPostInstall: boolean,
|
||||
}
|
||||
}
|
||||
|
||||
export class UpgradeResponse extends jspb.Message {
|
||||
|
||||
hasProgress(): boolean;
|
||||
clearProgress(): void;
|
||||
getProgress(): cc_arduino_cli_commands_v1_common_pb.DownloadProgress | undefined;
|
||||
setProgress(value?: cc_arduino_cli_commands_v1_common_pb.DownloadProgress): UpgradeResponse;
|
||||
|
||||
|
||||
hasTaskProgress(): boolean;
|
||||
clearTaskProgress(): void;
|
||||
getTaskProgress(): cc_arduino_cli_commands_v1_common_pb.TaskProgress | undefined;
|
||||
setTaskProgress(value?: cc_arduino_cli_commands_v1_common_pb.TaskProgress): UpgradeResponse;
|
||||
|
||||
|
||||
serializeBinary(): Uint8Array;
|
||||
toObject(includeInstance?: boolean): UpgradeResponse.AsObject;
|
||||
static toObject(includeInstance: boolean, msg: UpgradeResponse): UpgradeResponse.AsObject;
|
||||
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
|
||||
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
|
||||
static serializeBinaryToWriter(message: UpgradeResponse, writer: jspb.BinaryWriter): void;
|
||||
static deserializeBinary(bytes: Uint8Array): UpgradeResponse;
|
||||
static deserializeBinaryFromReader(message: UpgradeResponse, reader: jspb.BinaryReader): UpgradeResponse;
|
||||
}
|
||||
|
||||
export namespace UpgradeResponse {
|
||||
export type AsObject = {
|
||||
progress?: cc_arduino_cli_commands_v1_common_pb.DownloadProgress.AsObject,
|
||||
taskProgress?: cc_arduino_cli_commands_v1_common_pb.TaskProgress.AsObject,
|
||||
}
|
||||
}
|
||||
|
||||
export class VersionRequest extends jspb.Message {
|
||||
|
||||
serializeBinary(): Uint8Array;
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -807,6 +807,9 @@ export class Library extends jspb.Message {
|
||||
getCompatibleWithMap(): jspb.Map<string, boolean>;
|
||||
clearCompatibleWithMap(): void;
|
||||
|
||||
getInDevelopment(): boolean;
|
||||
setInDevelopment(value: boolean): Library;
|
||||
|
||||
|
||||
serializeBinary(): Uint8Array;
|
||||
toObject(includeInstance?: boolean): Library.AsObject;
|
||||
@@ -847,6 +850,7 @@ export namespace Library {
|
||||
providesIncludesList: Array<string>,
|
||||
|
||||
compatibleWithMap: Array<[string, boolean]>,
|
||||
inDevelopment: boolean,
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -5458,7 +5458,8 @@ proto.cc.arduino.cli.commands.v1.Library.toObject = function(includeInstance, ms
|
||||
layout: jspb.Message.getFieldWithDefault(msg, 25, 0),
|
||||
examplesList: (f = jspb.Message.getRepeatedField(msg, 26)) == null ? undefined : f,
|
||||
providesIncludesList: (f = jspb.Message.getRepeatedField(msg, 27)) == null ? undefined : f,
|
||||
compatibleWithMap: (f = msg.getCompatibleWithMap()) ? f.toObject(includeInstance, undefined) : []
|
||||
compatibleWithMap: (f = msg.getCompatibleWithMap()) ? f.toObject(includeInstance, undefined) : [],
|
||||
inDevelopment: jspb.Message.getBooleanFieldWithDefault(msg, 29, false)
|
||||
};
|
||||
|
||||
if (includeInstance) {
|
||||
@@ -5599,6 +5600,10 @@ proto.cc.arduino.cli.commands.v1.Library.deserializeBinaryFromReader = function(
|
||||
jspb.Map.deserializeBinary(message, reader, jspb.BinaryReader.prototype.readString, jspb.BinaryReader.prototype.readBool, null, "", false);
|
||||
});
|
||||
break;
|
||||
case 29:
|
||||
var value = /** @type {boolean} */ (reader.readBool());
|
||||
msg.setInDevelopment(value);
|
||||
break;
|
||||
default:
|
||||
reader.skipField();
|
||||
break;
|
||||
@@ -5797,6 +5802,13 @@ proto.cc.arduino.cli.commands.v1.Library.serializeBinaryToWriter = function(mess
|
||||
if (f && f.getLength() > 0) {
|
||||
f.serializeBinary(28, writer, jspb.BinaryWriter.prototype.writeString, jspb.BinaryWriter.prototype.writeBool);
|
||||
}
|
||||
f = message.getInDevelopment();
|
||||
if (f) {
|
||||
writer.writeBool(
|
||||
29,
|
||||
f
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -6334,6 +6346,24 @@ proto.cc.arduino.cli.commands.v1.Library.prototype.clearCompatibleWithMap = func
|
||||
return this;};
|
||||
|
||||
|
||||
/**
|
||||
* optional bool in_development = 29;
|
||||
* @return {boolean}
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.Library.prototype.getInDevelopment = function() {
|
||||
return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 29, false));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {boolean} value
|
||||
* @return {!proto.cc.arduino.cli.commands.v1.Library} returns this
|
||||
*/
|
||||
proto.cc.arduino.cli.commands.v1.Library.prototype.setInDevelopment = function(value) {
|
||||
return jspb.Message.setProto3BooleanField(this, 29, value);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@@ -26,6 +26,7 @@ import { DefaultCliConfig, CLI_CONFIG } from './cli-config';
|
||||
import { Deferred } from '@theia/core/lib/common/promise-util';
|
||||
import { EnvVariablesServer } from '@theia/core/lib/common/env-variables';
|
||||
import { deepClone } from '@theia/core';
|
||||
import { ErrnoException } from './utils/errors';
|
||||
|
||||
const deepmerge = require('deepmerge');
|
||||
|
||||
@@ -146,7 +147,7 @@ export class ConfigServiceImpl
|
||||
const fallbackModel = await this.getFallbackCliConfig();
|
||||
return deepmerge(fallbackModel, model) as DefaultCliConfig;
|
||||
} catch (error) {
|
||||
if ('code' in error && error.code === 'ENOENT') {
|
||||
if (ErrnoException.isENOENT(error)) {
|
||||
if (initializeIfAbsent) {
|
||||
await this.initCliConfigTo(dirname(cliConfigPath));
|
||||
return this.loadCliConfig(false);
|
||||
|
@@ -12,7 +12,6 @@ import {
|
||||
CreateRequest,
|
||||
InitRequest,
|
||||
InitResponse,
|
||||
UpdateCoreLibrariesIndexResponse,
|
||||
UpdateIndexRequest,
|
||||
UpdateIndexResponse,
|
||||
UpdateLibrariesIndexRequest,
|
||||
@@ -347,10 +346,7 @@ export class CoreClientProvider {
|
||||
}
|
||||
|
||||
private async doUpdateIndex<
|
||||
R extends
|
||||
| UpdateIndexResponse
|
||||
| UpdateLibrariesIndexResponse
|
||||
| UpdateCoreLibrariesIndexResponse // not used by IDE2
|
||||
R extends UpdateIndexResponse | UpdateLibrariesIndexResponse
|
||||
>(
|
||||
responseProvider: () => grpc.ClientReadableStream<R>,
|
||||
progressHandler?: IndexesUpdateProgressHandler,
|
||||
|
@@ -62,7 +62,7 @@ export function spawnCommand(
|
||||
});
|
||||
cp.on('exit', (code, signal) => {
|
||||
if (code === 0) {
|
||||
const result = Buffer.concat(outBuffers).toString('utf8').trim();
|
||||
const result = Buffer.concat(outBuffers).toString('utf8');
|
||||
resolve(result);
|
||||
return;
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user