Compare commits
34 Commits
mike/fix-t
...
v0.26.3
Author | SHA1 | Date | |
---|---|---|---|
64f0f5b773 | |||
f452f9bf00 | |||
97705234c6 | |||
30dfc167d3 | |||
d8105627c0 | |||
6b7fac3642 | |||
35805916aa | |||
4a4400e979 | |||
efd1f288b9 | |||
0337ab9cff | |||
f0dda692f6 | |||
2ce0c59d08 | |||
393b43d485 | |||
4fbcde8773 | |||
12d444fa69 | |||
683b4488af | |||
e1c1e07046 | |||
984420c155 | |||
7bad60dfa3 | |||
aaca88220c | |||
360384e8c8 | |||
ab2ad1313f | |||
897205acc2 | |||
862ca1124e | |||
d9981d9d7b | |||
8df0581831 | |||
54e6358df1 | |||
daf20a978d | |||
8e64798dda | |||
a1ceb4fa47 | |||
2db8d13051 | |||
aceb8052e2 | |||
62fae1e93b | |||
2abfbb9788 |
@ -4,9 +4,9 @@ set -euo pipefail
|
||||
if [[ ! -f "test-results/.last-run.json" ]]; then
|
||||
# if no last run artifact, than run plawright normally
|
||||
echo "run playwright normally"
|
||||
if [[ "$3" == "ubuntu-latest" ]]; then
|
||||
if [[ "$3" == ubuntu-latest* ]]; then
|
||||
yarn test:playwright:browser:chrome:ubuntu -- --shard=$1/$2 || true
|
||||
elif [[ "$3" == "windows-latest" ]]; then
|
||||
elif [[ "$3" == windows-latest* ]]; then
|
||||
yarn test:playwright:browser:chrome:windows -- --shard=$1/$2 || true
|
||||
else
|
||||
echo "Do not run playwright. Unable to detect os runtime."
|
||||
@ -26,9 +26,9 @@ while [[ $retry -le $max_retrys ]]; do
|
||||
if [[ $failed_tests -gt 0 ]]; then
|
||||
echo "retried=true" >>$GITHUB_OUTPUT
|
||||
echo "run playwright with last failed tests and retry $retry"
|
||||
if [[ "$3" == "ubuntu-latest" ]]; then
|
||||
if [[ "$3" == ubuntu-latest* ]]; then
|
||||
yarn test:playwright:browser:chrome:ubuntu -- --last-failed || true
|
||||
elif [[ "$3" == "windows-latest" ]]; then
|
||||
elif [[ "$3" == windows-latest* ]]; then
|
||||
yarn test:playwright:browser:chrome:windows -- --last-failed || true
|
||||
else
|
||||
echo "Do not run playwright. Unable to detect os runtime."
|
||||
|
12
.github/ci-cd-scripts/playwright-electron.sh
vendored
@ -4,11 +4,11 @@ set -euo pipefail
|
||||
if [[ ! -f "test-results/.last-run.json" ]]; then
|
||||
# if no last run artifact, than run plawright normally
|
||||
echo "run playwright normally"
|
||||
if [[ "$1" == "ubuntu-latest" ]]; then
|
||||
if [[ "$1" == ubuntu-latest* ]]; then
|
||||
xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- yarn test:playwright:electron:ubuntu || true
|
||||
elif [[ "$1" == "windows-latest" ]]; then
|
||||
elif [[ "$1" == windows-latest* ]]; then
|
||||
yarn test:playwright:electron:windows || true
|
||||
elif [[ "$1" == "macos-14" ]]; then
|
||||
elif [[ "$1" == macos-14* ]]; then
|
||||
yarn test:playwright:electron:macos || true
|
||||
else
|
||||
echo "Do not run playwright. Unable to detect os runtime."
|
||||
@ -28,11 +28,11 @@ while [[ $retry -le $max_retrys ]]; do
|
||||
if [[ $failed_tests -gt 0 ]]; then
|
||||
echo "retried=true" >>$GITHUB_OUTPUT
|
||||
echo "run playwright with last failed tests and retry $retry"
|
||||
if [[ "$1" == "ubuntu-latest" ]]; then
|
||||
if [[ "$1" == ubuntu-latest* ]]; then
|
||||
xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- yarn test:playwright:electron:ubuntu -- --last-failed || true
|
||||
elif [[ "$1" == "windows-latest" ]]; then
|
||||
elif [[ "$1" == windows-latest* ]]; then
|
||||
yarn test:playwright:electron:windows -- --last-failed || true
|
||||
elif [[ "$1" == "macos-14" ]]; then
|
||||
elif [[ "$1" == macos-14* ]]; then
|
||||
yarn test:playwright:electron:macos -- --last-failed || true
|
||||
else
|
||||
echo "Do not run playwright. Unable to detect os runtime."
|
||||
|
6
.github/dependabot.yml
vendored
@ -8,21 +8,21 @@ updates:
|
||||
- package-ecosystem: 'npm' # See documentation for possible values
|
||||
directory: '/' # Location of package manifests
|
||||
schedule:
|
||||
interval: 'daily'
|
||||
interval: 'weekly'
|
||||
reviewers:
|
||||
- franknoirot
|
||||
- irev-dev
|
||||
- package-ecosystem: 'github-actions' # See documentation for possible values
|
||||
directory: '/' # Location of package manifests
|
||||
schedule:
|
||||
interval: 'daily'
|
||||
interval: 'weekly'
|
||||
reviewers:
|
||||
- adamchalmers
|
||||
- jessfraz
|
||||
- package-ecosystem: 'cargo' # See documentation for possible values
|
||||
directory: '/src/wasm-lib/' # Location of package manifests
|
||||
schedule:
|
||||
interval: 'daily'
|
||||
interval: 'weekly'
|
||||
reviewers:
|
||||
- adamchalmers
|
||||
- jessfraz
|
||||
|
13
.github/workflows/build-test-publish-apps.yml
vendored
@ -85,7 +85,7 @@ jobs:
|
||||
- name: Prepare electron-builder.yml file for updater test
|
||||
if: ${{ env.CUT_RELEASE_PR == 'true' }}
|
||||
run: |
|
||||
yq -i '.publish[0].url = "https://dl.zoo.dev/releases/modeling-app/updater-test-release-notes"' electron-builder.yml
|
||||
yq -i '.publish[0].url = "https://dl.zoo.dev/releases/modeling-app/updater-test"' electron-builder.yml
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
if: ${{ env.CUT_RELEASE_PR == 'true' }}
|
||||
@ -181,6 +181,7 @@ jobs:
|
||||
- name: Build the app (release)
|
||||
if: ${{ env.BUILD_RELEASE == 'true' }}
|
||||
env:
|
||||
PUBLISH_FOR_PULL_REQUEST: true
|
||||
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||||
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
|
||||
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
|
||||
@ -360,17 +361,17 @@ jobs:
|
||||
run: "ls -R out"
|
||||
|
||||
- name: Authenticate to Google Cloud
|
||||
uses: 'google-github-actions/auth@v2.1.6'
|
||||
uses: 'google-github-actions/auth@v2.1.7'
|
||||
with:
|
||||
credentials_json: '${{ secrets.GOOGLE_CLOUD_DL_SA }}'
|
||||
|
||||
- name: Set up Google Cloud SDK
|
||||
uses: google-github-actions/setup-gcloud@v2.1.0
|
||||
uses: google-github-actions/setup-gcloud@v2.1.2
|
||||
with:
|
||||
project_id: ${{ env.GOOGLE_CLOUD_PROJECT_ID }}
|
||||
|
||||
- name: Upload release files to public bucket
|
||||
uses: google-github-actions/upload-cloud-storage@v2.2.0
|
||||
uses: google-github-actions/upload-cloud-storage@v2.2.1
|
||||
with:
|
||||
path: out
|
||||
glob: 'Zoo*'
|
||||
@ -378,7 +379,7 @@ jobs:
|
||||
destination: ${{ env.BUCKET_DIR }}
|
||||
|
||||
- name: Upload update endpoint to public bucket
|
||||
uses: google-github-actions/upload-cloud-storage@v2.2.0
|
||||
uses: google-github-actions/upload-cloud-storage@v2.2.1
|
||||
with:
|
||||
path: out
|
||||
glob: 'latest*'
|
||||
@ -386,7 +387,7 @@ jobs:
|
||||
destination: ${{ env.BUCKET_DIR }}
|
||||
|
||||
- name: Upload download endpoint to public bucket
|
||||
uses: google-github-actions/upload-cloud-storage@v2.2.0
|
||||
uses: google-github-actions/upload-cloud-storage@v2.2.1
|
||||
with:
|
||||
path: last_download.json
|
||||
destination: ${{ env.BUCKET_DIR }}
|
||||
|
6
.github/workflows/e2e-tests.yml
vendored
@ -39,7 +39,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest]
|
||||
os: [ubuntu-latest-8-cores, windows-latest-8-cores]
|
||||
shardIndex: [1, 2, 3, 4]
|
||||
shardTotal: [4]
|
||||
runs-on: ${{ matrix.os }}
|
||||
@ -227,7 +227,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest, macos-14]
|
||||
os: [ubuntu-latest-8-cores, windows-latest-8-cores, macos-14-large]
|
||||
timeout-minutes: 60
|
||||
runs-on: ${{ matrix.os }}
|
||||
needs: check-rust-changes
|
||||
@ -287,7 +287,7 @@ jobs:
|
||||
brew install gnu-sed
|
||||
echo "/opt/homebrew/opt/gnu-sed/libexec/gnubin" >> $GITHUB_PATH
|
||||
- name: Install vector
|
||||
if: ${{ !startsWith(matrix.os, 'windows') }}
|
||||
if: ${{ startsWith(matrix.os, 'ubuntu') }}
|
||||
shell: bash
|
||||
run: |
|
||||
curl --proto '=https' --tlsv1.2 -sSfL https://sh.vector.dev > /tmp/vector.sh
|
||||
|
25
.github/workflows/static-analysis.yml
vendored
@ -81,6 +81,31 @@ jobs:
|
||||
- name: Run codespell
|
||||
run: codespell --config .codespellrc # Edit this file to tweak the typo list and other configuration.
|
||||
|
||||
yarn-unit-test-kcl-samples:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'yarn'
|
||||
|
||||
- run: yarn install
|
||||
- run: yarn build:wasm
|
||||
|
||||
- run: yarn simpleserver:bg
|
||||
if: ${{ github.event_name != 'release' && github.event_name != 'schedule' }}
|
||||
|
||||
- name: Install Chromium Browser
|
||||
if: ${{ github.event_name != 'release' && github.event_name != 'schedule' }}
|
||||
run: yarn playwright install chromium --with-deps
|
||||
|
||||
- name: run unit tests for kcl samples
|
||||
if: ${{ github.event_name != 'release' && github.event_name != 'schedule' }}
|
||||
run: yarn test:unit:kcl-samples
|
||||
env:
|
||||
VITE_KC_DEV_TOKEN: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
||||
|
||||
yarn-unit-test:
|
||||
runs-on: ubuntu-latest
|
||||
|
@ -84,9 +84,13 @@ layout: manual
|
||||
* [`rem`](kcl/rem)
|
||||
* [`revolve`](kcl/revolve)
|
||||
* [`segAng`](kcl/segAng)
|
||||
* [`segEnd`](kcl/segEnd)
|
||||
* [`segEndX`](kcl/segEndX)
|
||||
* [`segEndY`](kcl/segEndY)
|
||||
* [`segLen`](kcl/segLen)
|
||||
* [`segStart`](kcl/segStart)
|
||||
* [`segStartX`](kcl/segStartX)
|
||||
* [`segStartY`](kcl/segStartY)
|
||||
* [`shell`](kcl/shell)
|
||||
* [`sin`](kcl/sin)
|
||||
* [`sqrt`](kcl/sqrt)
|
||||
|
53
docs/kcl/segEnd.md
Normal file
56
docs/kcl/segStart.md
Normal file
43
docs/kcl/segStartX.md
Normal file
44
docs/kcl/segStartY.md
Normal file
4166
docs/kcl/std.json
@ -632,16 +632,18 @@ test.describe('Editor tests', () => {
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
|
||||
// this test might be brittle as we add and remove functions
|
||||
// but should also be easy to update.
|
||||
// tests clicking on an option, selection the first option
|
||||
// and arrowing down to an option
|
||||
|
||||
await u.codeLocator.click()
|
||||
await page.keyboard.type('sketch001 = start')
|
||||
|
||||
// expect there to be six auto complete options
|
||||
await expect(page.locator('.cm-completionLabel')).toHaveCount(8)
|
||||
// expect there to be some auto complete options
|
||||
// exact number depends on the KCL stdlib, so let's just check it's > 0 for now.
|
||||
await expect(async () => {
|
||||
const children = await page.locator('.cm-completionLabel').count()
|
||||
expect(children).toBeGreaterThan(0)
|
||||
}).toPass()
|
||||
// this makes sure we can accept a completion with click
|
||||
await page.getByText('startSketchOn').click()
|
||||
await page.keyboard.type("'XZ'")
|
||||
@ -985,7 +987,7 @@ test.describe('Editor tests', () => {
|
||||
|> extrude(5, %)`)
|
||||
})
|
||||
|
||||
test(
|
||||
test.fixme(
|
||||
`Can use the import stdlib function on a local OBJ file`,
|
||||
{ tag: '@electron' },
|
||||
async ({ browserName }, testInfo) => {
|
||||
|
@ -26,10 +26,6 @@ test.describe('integrations tests', () => {
|
||||
'Creating a new file or switching file while in sketchMode should exit sketchMode',
|
||||
{ tag: '@electron' },
|
||||
async ({ tronApp, homePage, scene, editor, toolbar }) => {
|
||||
test.skip(
|
||||
process.platform === 'win32',
|
||||
'windows times out will waiting for the execution indicator?'
|
||||
)
|
||||
await tronApp.initialise({
|
||||
fixtures: { homePage, scene, editor, toolbar },
|
||||
folderSetupFn: async (dir) => {
|
||||
@ -55,7 +51,6 @@ test.describe('integrations tests', () => {
|
||||
sortBy: 'last-modified-desc',
|
||||
})
|
||||
await homePage.openProject('test-sample')
|
||||
// windows times out here, hence the skip above
|
||||
await scene.waitForExecutionDone()
|
||||
})
|
||||
await test.step('enter sketch mode', async () => {
|
||||
@ -71,10 +66,13 @@ test.describe('integrations tests', () => {
|
||||
await toolbar.editSketch()
|
||||
await expect(toolbar.exitSketchBtn).toBeVisible()
|
||||
})
|
||||
|
||||
const fileName = 'Untitled.kcl'
|
||||
await test.step('check sketch mode is exited when creating new file', async () => {
|
||||
await toolbar.fileTreeBtn.click()
|
||||
await toolbar.expectFileTreeState(['main.kcl'])
|
||||
await toolbar.createFile({ wait: true })
|
||||
|
||||
await toolbar.createFile({ fileName, waitForToastToDisappear: true })
|
||||
|
||||
// check we're out of sketch mode
|
||||
await expect(toolbar.exitSketchBtn).not.toBeVisible()
|
||||
@ -93,10 +91,10 @@ test.describe('integrations tests', () => {
|
||||
})
|
||||
await toolbar.editSketch()
|
||||
await expect(toolbar.exitSketchBtn).toBeVisible()
|
||||
await toolbar.expectFileTreeState(['main.kcl', 'Untitled.kcl'])
|
||||
await toolbar.expectFileTreeState(['main.kcl', fileName])
|
||||
})
|
||||
await test.step('check sketch mode is exited when opening a different file', async () => {
|
||||
await toolbar.openFile('untitled.kcl', { wait: false })
|
||||
await toolbar.openFile(fileName, { wait: false })
|
||||
|
||||
// check we're out of sketch mode
|
||||
await expect(toolbar.exitSketchBtn).not.toBeVisible()
|
||||
@ -109,7 +107,7 @@ test.describe('when using the file tree to', () => {
|
||||
const fromFile = 'main.kcl'
|
||||
const toFile = 'hello.kcl'
|
||||
|
||||
test(
|
||||
test.fixme(
|
||||
`rename ${fromFile} to ${toFile}, and doesn't crash on reload and settings load`,
|
||||
{ tag: '@electron' },
|
||||
async ({ browser: _, tronApp }, testInfo) => {
|
||||
@ -157,7 +155,7 @@ test.describe('when using the file tree to', () => {
|
||||
}
|
||||
)
|
||||
|
||||
test(
|
||||
test.fixme(
|
||||
`create many new untitled files they increment their names`,
|
||||
{ tag: '@electron' },
|
||||
async ({ browser: _, tronApp }, testInfo) => {
|
||||
@ -298,7 +296,7 @@ test.describe('when using the file tree to', () => {
|
||||
}
|
||||
)
|
||||
|
||||
test(
|
||||
test.fixme(
|
||||
'loading small file, then large, then back to small',
|
||||
{
|
||||
tag: '@electron',
|
||||
|
@ -195,7 +195,7 @@ export class SceneFixture {
|
||||
}
|
||||
|
||||
waitForExecutionDone = async () => {
|
||||
await expect(this.exeIndicator).toBeVisible()
|
||||
await expect(this.exeIndicator).toBeVisible({ timeout: 30000 })
|
||||
}
|
||||
|
||||
expectPixelColor = async (
|
||||
|
@ -16,6 +16,7 @@ export class ToolbarFixture {
|
||||
fileCreateToast!: Locator
|
||||
filePane!: Locator
|
||||
exeIndicator!: Locator
|
||||
treeInputField!: Locator
|
||||
|
||||
constructor(page: Page) {
|
||||
this.page = page
|
||||
@ -31,6 +32,7 @@ export class ToolbarFixture {
|
||||
this.editSketchBtn = page.getByText('Edit Sketch')
|
||||
this.fileTreeBtn = page.locator('[id="files-button-holder"]')
|
||||
this.createFileBtn = page.getByTestId('create-file-button')
|
||||
this.treeInputField = page.getByTestId('tree-input-field')
|
||||
|
||||
this.filePane = page.locator('#files-pane')
|
||||
this.fileCreateToast = page.getByText('Successfully created')
|
||||
@ -59,10 +61,15 @@ export class ToolbarFixture {
|
||||
expectFileTreeState = async (expected: string[]) => {
|
||||
await expect.poll(this._serialiseFileTree).toEqual(expected)
|
||||
}
|
||||
createFile = async ({ wait }: { wait: boolean } = { wait: false }) => {
|
||||
createFile = async (args: {
|
||||
fileName: string
|
||||
waitForToastToDisappear: boolean
|
||||
}) => {
|
||||
await this.createFileBtn.click()
|
||||
await this.treeInputField.fill(args.fileName)
|
||||
await this.treeInputField.press('Enter')
|
||||
await expect(this.fileCreateToast).toBeVisible()
|
||||
if (wait) {
|
||||
if (args.waitForToastToDisappear) {
|
||||
await this.fileCreateToast.waitFor({ state: 'detached' })
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ export const isErrorWhitelisted = (exception: Error) => {
|
||||
{
|
||||
name: '"{"kind"',
|
||||
message:
|
||||
'"engine","sourceRanges":[[0,0]],"msg":"Failed to get string from response from engine: `JsValue(undefined)`"}"',
|
||||
'"engine","sourceRanges":[[0,0,0]],"msg":"Failed to get string from response from engine: `JsValue(undefined)`"}"',
|
||||
stack: '',
|
||||
foundInSpec: 'e2e/playwright/testing-settings.spec.ts',
|
||||
project: 'Google Chrome',
|
||||
@ -156,8 +156,8 @@ export const isErrorWhitelisted = (exception: Error) => {
|
||||
{
|
||||
name: 'Unhandled Promise Rejection',
|
||||
message:
|
||||
'{"kind":"engine","sourceRanges":[[0,0]],"msg":"Failed to get string from response from engine: `JsValue(undefined)`"}',
|
||||
stack: `Unhandled Promise Rejection: {"kind":"engine","sourceRanges":[[0,0]],"msg":"Failed to get string from response from engine: \`JsValue(undefined)\`"}
|
||||
'{"kind":"engine","sourceRanges":[[0,0,0]],"msg":"Failed to get string from response from engine: `JsValue(undefined)`"}',
|
||||
stack: `Unhandled Promise Rejection: {"kind":"engine","sourceRanges":[[0,0,0]],"msg":"Failed to get string from response from engine: \`JsValue(undefined)\`"}
|
||||
at unknown (http://localhost:3000/src/lang/std/engineConnection.ts:1245:26)`,
|
||||
foundInSpec:
|
||||
'e2e/playwright/onboarding-tests.spec.ts Click through each onboarding step',
|
||||
@ -253,7 +253,7 @@ export const isErrorWhitelisted = (exception: Error) => {
|
||||
{
|
||||
name: '{"kind"',
|
||||
stack: ``,
|
||||
message: `engine","sourceRanges":[[0,0]],"msg":"Failed to wait for promise from engine: JsValue(\\"Force interrupt, executionIsStale, new AST requested\\")"}`,
|
||||
message: `engine","sourceRanges":[[0,0,0]],"msg":"Failed to wait for promise from engine: JsValue(\\"Force interrupt, executionIsStale, new AST requested\\")"}`,
|
||||
project: 'Google Chrome',
|
||||
foundInSpec: 'e2e/playwright/testing-settings.spec.ts',
|
||||
},
|
||||
|
@ -854,7 +854,7 @@ test(
|
||||
}
|
||||
)
|
||||
|
||||
test(
|
||||
test.fixme(
|
||||
'Deleting projects, can delete individual project, can still create projects after deleting all',
|
||||
{ tag: '@electron' },
|
||||
async ({ browserName }, testInfo) => {
|
||||
@ -1669,7 +1669,8 @@ test(
|
||||
}
|
||||
)
|
||||
|
||||
test(
|
||||
// Flaky
|
||||
test.fixme(
|
||||
'Original project name persist after onboarding',
|
||||
{ tag: '@electron' },
|
||||
async ({ browserName }, testInfo) => {
|
||||
|
@ -1031,7 +1031,7 @@ test.describe('Grid visibility', { tag: '@snapshot' }, () => {
|
||||
})
|
||||
})
|
||||
|
||||
test('theme persists', async ({ page, context }) => {
|
||||
test.fixme('theme persists', async ({ page, context }) => {
|
||||
const u = await getUtils(page)
|
||||
await context.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
|
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 52 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 46 KiB |
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 58 KiB |
Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 55 KiB |
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 50 KiB |
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 47 KiB |
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 46 KiB |
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 44 KiB |
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 43 KiB |
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 40 KiB |
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 40 KiB |
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 48 KiB |
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 47 KiB |
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 33 KiB |
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 52 KiB |
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 53 KiB |
Before Width: | Height: | Size: 74 KiB After Width: | Height: | Size: 73 KiB |
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 64 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 47 KiB |
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 64 KiB |
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 62 KiB |
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 36 KiB |
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 39 KiB |
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 40 KiB |
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 39 KiB |
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 39 KiB |
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 36 KiB |
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 36 KiB |
@ -415,7 +415,7 @@ test.describe('Testing settings', () => {
|
||||
)
|
||||
|
||||
// It was much easier to test the logo color than the background stream color.
|
||||
test(
|
||||
test.fixme(
|
||||
'user settings reload on external change, on project and modeling view',
|
||||
{ tag: '@electron' },
|
||||
async ({ browserName }, testInfo) => {
|
||||
|
@ -256,181 +256,186 @@ test('First escape in tool pops you out of tool, second exits sketch mode', asyn
|
||||
).not.toBeVisible()
|
||||
})
|
||||
|
||||
test('Basic default modeling and sketch hotkeys work', async ({ page }) => {
|
||||
const u = await getUtils(page)
|
||||
test.fixme(
|
||||
'Basic default modeling and sketch hotkeys work',
|
||||
async ({ page }) => {
|
||||
const u = await getUtils(page)
|
||||
|
||||
// This test can run long if it takes a little too long to load
|
||||
// the engine.
|
||||
test.setTimeout(90000)
|
||||
// This test has a weird bug on ubuntu
|
||||
test.skip(
|
||||
process.platform === 'linux',
|
||||
'weird playwright bug on ubuntu https://github.com/KittyCAD/modeling-app/issues/2444'
|
||||
)
|
||||
// Load the app with the code pane open
|
||||
// This test can run long if it takes a little too long to load
|
||||
// the engine.
|
||||
test.setTimeout(90000)
|
||||
// This test has a weird bug on ubuntu
|
||||
// Funny, it's flaking on Windows too :). I think there is just something
|
||||
// actually wrong.
|
||||
test.skip(
|
||||
process.platform === 'linux',
|
||||
'weird playwright bug on ubuntu https://github.com/KittyCAD/modeling-app/issues/2444'
|
||||
)
|
||||
// Load the app with the code pane open
|
||||
|
||||
await test.step(`Set up test`, async () => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'store',
|
||||
JSON.stringify({
|
||||
state: {
|
||||
openPanes: ['code'],
|
||||
},
|
||||
version: 0,
|
||||
await test.step(`Set up test`, async () => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'store',
|
||||
JSON.stringify({
|
||||
state: {
|
||||
openPanes: ['code'],
|
||||
},
|
||||
version: 0,
|
||||
})
|
||||
)
|
||||
})
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await u.openDebugPanel()
|
||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||
await u.closeDebugPanel()
|
||||
})
|
||||
|
||||
const codePane = page.locator('.cm-content')
|
||||
const lineButton = page.getByRole('button', {
|
||||
name: 'line Line',
|
||||
exact: true,
|
||||
})
|
||||
const arcButton = page.getByRole('button', {
|
||||
name: 'arc Tangential Arc',
|
||||
exact: true,
|
||||
})
|
||||
const extrudeButton = page.getByRole('button', { name: 'Extrude' })
|
||||
const commandBarComboBox = page.getByPlaceholder('Search commands')
|
||||
const exitSketchButton = page.getByRole('button', { name: 'Exit Sketch' })
|
||||
|
||||
await test.step(`Type code with modeling hotkeys, shouldn't fire`, async () => {
|
||||
await codePane.click()
|
||||
await page.keyboard.type('//')
|
||||
await page.keyboard.press('s')
|
||||
await expect(commandBarComboBox).not.toBeVisible()
|
||||
await page.keyboard.press('e')
|
||||
await expect(commandBarComboBox).not.toBeVisible()
|
||||
await expect(codePane).toHaveText('//se')
|
||||
})
|
||||
|
||||
// Blur focus from the code editor, use the s command to sketch
|
||||
await test.step(`Blur editor focus, enter sketch`, async () => {
|
||||
/**
|
||||
* TODO: There is a bug somewhere that causes this test to fail
|
||||
* if you toggle the codePane closed before your trigger the
|
||||
* start of the sketch.
|
||||
* and a separate Safari-only bug that causes the test to fail
|
||||
* if the pane is open the entire test. The maintainer of CodeMirror
|
||||
* has pinpointed this to the unusual browser behavior:
|
||||
* https://discuss.codemirror.net/t/how-to-force-unfocus-of-the-codemirror-element-in-safari/8095/3
|
||||
*/
|
||||
await blurCodeEditor()
|
||||
await page.waitForTimeout(1000)
|
||||
await page.keyboard.press('s')
|
||||
await page.waitForTimeout(1000)
|
||||
await page.mouse.move(800, 300, { steps: 5 })
|
||||
await page.mouse.click(800, 300)
|
||||
await page.waitForTimeout(1000)
|
||||
await expect(lineButton).toHaveAttribute('aria-pressed', 'true', {
|
||||
timeout: 15_000,
|
||||
})
|
||||
})
|
||||
|
||||
// Use some sketch hotkeys to create a sketch (l and a for now)
|
||||
await test.step(`Incomplete sketch with hotkeys`, async () => {
|
||||
await test.step(`Draw a line`, async () => {
|
||||
await page.mouse.move(700, 200, { steps: 5 })
|
||||
await page.mouse.click(700, 200)
|
||||
await page.mouse.move(800, 250, { steps: 5 })
|
||||
await page.mouse.click(800, 250)
|
||||
})
|
||||
|
||||
await test.step(`Unequip line tool`, async () => {
|
||||
await page.keyboard.press('l')
|
||||
await expect(lineButton).not.toHaveAttribute('aria-pressed', 'true')
|
||||
})
|
||||
|
||||
await test.step(`Draw a tangential arc`, async () => {
|
||||
await page.keyboard.press('a')
|
||||
await expect(arcButton).toHaveAttribute('aria-pressed', 'true', {
|
||||
timeout: 10_000,
|
||||
})
|
||||
)
|
||||
await page.mouse.move(1000, 100, { steps: 5 })
|
||||
await page.mouse.click(1000, 100)
|
||||
})
|
||||
|
||||
await test.step(`Unequip with escape, equip line tool`, async () => {
|
||||
await page.keyboard.press('Escape')
|
||||
await page.keyboard.press('l')
|
||||
await page.waitForTimeout(50)
|
||||
await expect(lineButton).toHaveAttribute('aria-pressed', 'true')
|
||||
})
|
||||
})
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await u.openDebugPanel()
|
||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||
await u.closeDebugPanel()
|
||||
})
|
||||
|
||||
const codePane = page.locator('.cm-content')
|
||||
const lineButton = page.getByRole('button', {
|
||||
name: 'line Line',
|
||||
exact: true,
|
||||
})
|
||||
const arcButton = page.getByRole('button', {
|
||||
name: 'arc Tangential Arc',
|
||||
exact: true,
|
||||
})
|
||||
const extrudeButton = page.getByRole('button', { name: 'Extrude' })
|
||||
const commandBarComboBox = page.getByPlaceholder('Search commands')
|
||||
const exitSketchButton = page.getByRole('button', { name: 'Exit Sketch' })
|
||||
await test.step(`Type code with sketch hotkeys, shouldn't fire`, async () => {
|
||||
// Since there's code now, we have to get to the end of the line
|
||||
await page.locator('.cm-line').last().click()
|
||||
await page.keyboard.down('ControlOrMeta')
|
||||
await page.keyboard.press('ArrowRight')
|
||||
await page.keyboard.up('ControlOrMeta')
|
||||
|
||||
await test.step(`Type code with modeling hotkeys, shouldn't fire`, async () => {
|
||||
await codePane.click()
|
||||
await page.keyboard.type('//')
|
||||
await page.keyboard.press('s')
|
||||
await expect(commandBarComboBox).not.toBeVisible()
|
||||
await page.keyboard.press('e')
|
||||
await expect(commandBarComboBox).not.toBeVisible()
|
||||
await expect(codePane).toHaveText('//se')
|
||||
})
|
||||
|
||||
// Blur focus from the code editor, use the s command to sketch
|
||||
await test.step(`Blur editor focus, enter sketch`, async () => {
|
||||
/**
|
||||
* TODO: There is a bug somewhere that causes this test to fail
|
||||
* if you toggle the codePane closed before your trigger the
|
||||
* start of the sketch.
|
||||
* and a separate Safari-only bug that causes the test to fail
|
||||
* if the pane is open the entire test. The maintainer of CodeMirror
|
||||
* has pinpointed this to the unusual browser behavior:
|
||||
* https://discuss.codemirror.net/t/how-to-force-unfocus-of-the-codemirror-element-in-safari/8095/3
|
||||
*/
|
||||
await blurCodeEditor()
|
||||
await page.waitForTimeout(1000)
|
||||
await page.keyboard.press('s')
|
||||
await page.waitForTimeout(1000)
|
||||
await page.mouse.move(800, 300, { steps: 5 })
|
||||
await page.mouse.click(800, 300)
|
||||
await page.waitForTimeout(1000)
|
||||
await expect(lineButton).toHaveAttribute('aria-pressed', 'true', {
|
||||
timeout: 15_000,
|
||||
await page.keyboard.press('Enter')
|
||||
await page.keyboard.type('//')
|
||||
await page.keyboard.press('l')
|
||||
await expect(lineButton).toHaveAttribute('aria-pressed', 'true')
|
||||
await page.keyboard.press('a')
|
||||
await expect(lineButton).toHaveAttribute('aria-pressed', 'true')
|
||||
await expect(codePane).toContainText('//la')
|
||||
await page.keyboard.press('Backspace')
|
||||
await page.keyboard.press('Backspace')
|
||||
await page.keyboard.press('Backspace')
|
||||
await page.keyboard.press('Backspace')
|
||||
})
|
||||
})
|
||||
|
||||
// Use some sketch hotkeys to create a sketch (l and a for now)
|
||||
await test.step(`Incomplete sketch with hotkeys`, async () => {
|
||||
await test.step(`Draw a line`, async () => {
|
||||
await test.step(`Close profile and exit sketch`, async () => {
|
||||
await blurCodeEditor()
|
||||
await page.mouse.move(700, 200, { steps: 5 })
|
||||
await page.mouse.click(700, 200)
|
||||
await page.mouse.move(800, 250, { steps: 5 })
|
||||
await page.mouse.click(800, 250)
|
||||
})
|
||||
|
||||
await test.step(`Unequip line tool`, async () => {
|
||||
await page.keyboard.press('l')
|
||||
await expect(lineButton).not.toHaveAttribute('aria-pressed', 'true')
|
||||
})
|
||||
|
||||
await test.step(`Draw a tangential arc`, async () => {
|
||||
await page.keyboard.press('a')
|
||||
await expect(arcButton).toHaveAttribute('aria-pressed', 'true', {
|
||||
timeout: 10_000,
|
||||
})
|
||||
await page.mouse.move(1000, 100, { steps: 5 })
|
||||
await page.mouse.click(1000, 100)
|
||||
})
|
||||
|
||||
await test.step(`Unequip with escape, equip line tool`, async () => {
|
||||
// On close it will unequip the line tool.
|
||||
await expect(lineButton).toHaveAttribute('aria-pressed', 'false')
|
||||
await expect(exitSketchButton).toBeEnabled()
|
||||
await page.keyboard.press('Escape')
|
||||
await page.keyboard.press('l')
|
||||
await page.waitForTimeout(50)
|
||||
await expect(lineButton).toHaveAttribute('aria-pressed', 'true')
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'Exit Sketch' })
|
||||
).not.toBeVisible()
|
||||
})
|
||||
})
|
||||
|
||||
await test.step(`Type code with sketch hotkeys, shouldn't fire`, async () => {
|
||||
// Since there's code now, we have to get to the end of the line
|
||||
await page.locator('.cm-line').last().click()
|
||||
await page.keyboard.down('ControlOrMeta')
|
||||
await page.keyboard.press('ArrowRight')
|
||||
await page.keyboard.up('ControlOrMeta')
|
||||
|
||||
await page.keyboard.press('Enter')
|
||||
await page.keyboard.type('//')
|
||||
await page.keyboard.press('l')
|
||||
await expect(lineButton).toHaveAttribute('aria-pressed', 'true')
|
||||
await page.keyboard.press('a')
|
||||
await expect(lineButton).toHaveAttribute('aria-pressed', 'true')
|
||||
await expect(codePane).toContainText('//la')
|
||||
await page.keyboard.press('Backspace')
|
||||
await page.keyboard.press('Backspace')
|
||||
await page.keyboard.press('Backspace')
|
||||
await page.keyboard.press('Backspace')
|
||||
})
|
||||
|
||||
await test.step(`Close profile and exit sketch`, async () => {
|
||||
await blurCodeEditor()
|
||||
await page.mouse.move(700, 200, { steps: 5 })
|
||||
await page.mouse.click(700, 200)
|
||||
// On close it will unequip the line tool.
|
||||
await expect(lineButton).toHaveAttribute('aria-pressed', 'false')
|
||||
await expect(exitSketchButton).toBeEnabled()
|
||||
await page.keyboard.press('Escape')
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'Exit Sketch' })
|
||||
).not.toBeVisible()
|
||||
})
|
||||
|
||||
// Extrude with e
|
||||
await test.step(`Extrude the sketch`, async () => {
|
||||
await page.mouse.click(750, 150)
|
||||
await blurCodeEditor()
|
||||
await expect(extrudeButton).toBeEnabled()
|
||||
await page.keyboard.press('e')
|
||||
await page.waitForTimeout(500)
|
||||
await page.mouse.move(800, 200, { steps: 5 })
|
||||
await page.mouse.click(800, 200)
|
||||
await expect(page.getByRole('button', { name: 'Continue' })).toBeVisible({
|
||||
timeout: 20_000,
|
||||
// Extrude with e
|
||||
await test.step(`Extrude the sketch`, async () => {
|
||||
await page.mouse.click(750, 150)
|
||||
await blurCodeEditor()
|
||||
await expect(extrudeButton).toBeEnabled()
|
||||
await page.keyboard.press('e')
|
||||
await page.waitForTimeout(500)
|
||||
await page.mouse.move(800, 200, { steps: 5 })
|
||||
await page.mouse.click(800, 200)
|
||||
await expect(page.getByRole('button', { name: 'Continue' })).toBeVisible({
|
||||
timeout: 20_000,
|
||||
})
|
||||
await page.getByRole('button', { name: 'Continue' }).click()
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'Submit command' })
|
||||
).toBeVisible()
|
||||
await page.getByRole('button', { name: 'Submit command' }).click()
|
||||
await expect(page.locator('.cm-content')).toContainText('extrude(')
|
||||
})
|
||||
await page.getByRole('button', { name: 'Continue' }).click()
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'Submit command' })
|
||||
).toBeVisible()
|
||||
await page.getByRole('button', { name: 'Submit command' }).click()
|
||||
await expect(page.locator('.cm-content')).toContainText('extrude(')
|
||||
})
|
||||
|
||||
// await codePaneButton.click()
|
||||
// await expect(u.codeLocator).not.toBeVisible()
|
||||
// await codePaneButton.click()
|
||||
// await expect(u.codeLocator).not.toBeVisible()
|
||||
|
||||
/**
|
||||
* work-around: to stop `keyboard.press()` from typing in the editor even when it should be blurred
|
||||
*/
|
||||
async function blurCodeEditor() {
|
||||
await page.getByRole('button', { name: 'Commands' }).click()
|
||||
await page.waitForTimeout(100)
|
||||
await page.keyboard.press('Escape')
|
||||
await page.waitForTimeout(100)
|
||||
/**
|
||||
* work-around: to stop `keyboard.press()` from typing in the editor even when it should be blurred
|
||||
*/
|
||||
async function blurCodeEditor() {
|
||||
await page.getByRole('button', { name: 'Commands' }).click()
|
||||
await page.waitForTimeout(100)
|
||||
await page.keyboard.press('Escape')
|
||||
await page.waitForTimeout(100)
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
test('Delete key does not navigate back', async ({ page }) => {
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
|
22
package.json
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "zoo-modeling-app",
|
||||
"version": "0.26.2",
|
||||
"version": "0.26.3",
|
||||
"private": true,
|
||||
"productName": "Zoo Modeling App",
|
||||
"author": {
|
||||
@ -14,7 +14,7 @@
|
||||
"dependencies": {
|
||||
"@codemirror/autocomplete": "^6.17.0",
|
||||
"@codemirror/commands": "^6.6.0",
|
||||
"@codemirror/language": "^6.10.2",
|
||||
"@codemirror/language": "^6.10.3",
|
||||
"@codemirror/lint": "^6.8.1",
|
||||
"@codemirror/search": "^6.5.6",
|
||||
"@codemirror/state": "^6.4.1",
|
||||
@ -40,7 +40,7 @@
|
||||
"codemirror": "^6.0.1",
|
||||
"decamelize": "^6.0.0",
|
||||
"electron-squirrel-startup": "^1.0.1",
|
||||
"electron-updater": "^6.3.0",
|
||||
"electron-updater": "^6.3.9",
|
||||
"fuse.js": "^7.0.0",
|
||||
"html2canvas-pro": "^1.5.8",
|
||||
"isomorphic-fetch": "^3.0.0",
|
||||
@ -60,7 +60,7 @@
|
||||
"sketch-helpers": "^0.0.4",
|
||||
"three": "^0.166.1",
|
||||
"ua-parser-js": "^1.0.37",
|
||||
"uuid": "^9.0.1",
|
||||
"uuid": "^11.0.2",
|
||||
"vscode-jsonrpc": "^8.2.1",
|
||||
"vscode-languageserver-protocol": "^3.17.5",
|
||||
"vscode-uri": "^3.0.8",
|
||||
@ -105,7 +105,8 @@
|
||||
"tronb:package": "electron-builder --config electron-builder.yml",
|
||||
"test-setup": "yarn install && yarn build:wasm",
|
||||
"test": "vitest --mode development",
|
||||
"test:unit": "vitest run --mode development",
|
||||
"test:unit": "vitest run --mode development --exclude **/kclSamples.test.ts",
|
||||
"test:unit:kcl-samples": "vitest run --mode development ./src/lang/kclSamples.test.ts",
|
||||
"test:playwright:browser:chrome": "playwright test --project='Google Chrome' --config=playwright.ci.config.ts --grep-invert='@snapshot|@electron'",
|
||||
"test:playwright:browser:chrome:windows": "playwright test --project=\"Google Chrome\" --config=playwright.ci.config.ts --grep-invert=\"@snapshot|@electron|@skipWin\"",
|
||||
"test:playwright:browser:chrome:ubuntu": "playwright test --project='Google Chrome' --config=playwright.ci.config.ts --grep-invert='@snapshot|@electron|@skipLinux'",
|
||||
@ -117,7 +118,8 @@
|
||||
"test:playwright:electron:windows:local": "yarn tron:package && NODE_ENV=development playwright test --config=playwright.electron.config.ts --grep=@electron --grep-invert=@skipWin",
|
||||
"test:playwright:electron:macos:local": "yarn tron:package && NODE_ENV=development playwright test --config=playwright.electron.config.ts --grep=@electron --grep-invert=@skipMacos",
|
||||
"test:playwright:electron:ubuntu:local": "yarn tron:package && NODE_ENV=development playwright test --config=playwright.electron.config.ts --grep=@electron --grep-invert=@skipLinux",
|
||||
"test:unit:local": "yarn simpleserver:bg && yarn test:unit; kill-port 3000"
|
||||
"test:unit:local": "yarn simpleserver:bg && yarn test:unit; kill-port 3000",
|
||||
"test:unit:kcl-samples:local": "yarn simpleserver:bg && yarn test:unit:kcl-samples; kill-port 3000"
|
||||
},
|
||||
"prettier": {
|
||||
"trailingComma": "es5",
|
||||
@ -144,8 +146,8 @@
|
||||
"@electron-forge/maker-deb": "^7.4.0",
|
||||
"@electron-forge/maker-rpm": "^7.4.0",
|
||||
"@electron-forge/maker-squirrel": "^7.4.0",
|
||||
"@electron-forge/maker-wix": "^7.4.0",
|
||||
"@electron-forge/maker-zip": "^7.4.0",
|
||||
"@electron-forge/maker-wix": "^7.5.0",
|
||||
"@electron-forge/maker-zip": "^7.5.0",
|
||||
"@electron-forge/plugin-auto-unpack-natives": "^7.4.0",
|
||||
"@electron-forge/plugin-fuses": "^7.4.0",
|
||||
"@electron-forge/plugin-vite": "^7.4.0",
|
||||
@ -171,7 +173,7 @@
|
||||
"@types/ua-parser-js": "^0.7.39",
|
||||
"@types/uuid": "^9.0.8",
|
||||
"@types/wicg-file-system-access": "^2023.10.5",
|
||||
"@types/ws": "^8.5.10",
|
||||
"@types/ws": "^8.5.13",
|
||||
"@typescript-eslint/eslint-plugin": "^5.0.0",
|
||||
"@typescript-eslint/parser": "^5.0.0",
|
||||
"@vitejs/plugin-react": "^4.3.0",
|
||||
@ -187,7 +189,7 @@
|
||||
"eslint-plugin-css-modules": "^2.12.0",
|
||||
"eslint-plugin-import": "^2.30.0",
|
||||
"eslint-plugin-suggest-no-throw": "^1.0.0",
|
||||
"happy-dom": "^14.3.10",
|
||||
"happy-dom": "^15.10.2",
|
||||
"http-server": "^14.1.1",
|
||||
"husky": "^9.1.5",
|
||||
"kill-port": "^2.0.1",
|
||||
|
@ -6,10 +6,10 @@ import { Dispatch, useCallback, useRef, useState } from 'react'
|
||||
import { useNavigate, useRouteLoaderData } from 'react-router-dom'
|
||||
import { Disclosure } from '@headlessui/react'
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
import { faChevronRight } from '@fortawesome/free-solid-svg-icons'
|
||||
import { faChevronRight, faPencil } from '@fortawesome/free-solid-svg-icons'
|
||||
import { useFileContext } from 'hooks/useFileContext'
|
||||
import styles from './FileTree.module.css'
|
||||
import { sortProject } from 'lib/desktopFS'
|
||||
import { sortFilesAndDirectories } from 'lib/desktopFS'
|
||||
import { FILE_EXT } from 'lib/constants'
|
||||
import { CustomIcon } from './CustomIcon'
|
||||
import { codeManager, kclManager } from 'lib/singletons'
|
||||
@ -27,6 +27,36 @@ function getIndentationCSS(level: number) {
|
||||
return `calc(1rem * ${level + 1})`
|
||||
}
|
||||
|
||||
function TreeEntryInput(props: {
|
||||
level: number
|
||||
onSubmit: (value: string) => void
|
||||
}) {
|
||||
const [value, setValue] = useState('')
|
||||
const onKeyPress = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
||||
if (e.key !== 'Enter') return
|
||||
props.onSubmit(value)
|
||||
}
|
||||
|
||||
return (
|
||||
<label>
|
||||
<span className="sr-only">Entry input</span>
|
||||
<input
|
||||
data-testid="tree-input-field"
|
||||
type="text"
|
||||
autoFocus
|
||||
autoCapitalize="off"
|
||||
autoCorrect="off"
|
||||
className="w-full py-1 bg-transparent text-chalkboard-100 placeholder:text-chalkboard-70 dark:text-chalkboard-10 dark:placeholder:text-chalkboard-50 focus:outline-none focus:ring-0"
|
||||
onBlur={() => props.onSubmit(value)}
|
||||
onChange={(e) => setValue(e.target.value)}
|
||||
onKeyPress={onKeyPress}
|
||||
style={{ paddingInlineStart: getIndentationCSS(props.level) }}
|
||||
value={value}
|
||||
/>
|
||||
</label>
|
||||
)
|
||||
}
|
||||
|
||||
function RenameForm({
|
||||
fileOrDir,
|
||||
onSubmit,
|
||||
@ -113,23 +143,44 @@ function DeleteFileTreeItemDialog({
|
||||
}
|
||||
|
||||
const FileTreeItem = ({
|
||||
parentDir,
|
||||
project,
|
||||
currentFile,
|
||||
lastDirectoryClicked,
|
||||
fileOrDir,
|
||||
onNavigateToFile,
|
||||
onClickDirectory,
|
||||
onCreateFile,
|
||||
onCreateFolder,
|
||||
newTreeEntry,
|
||||
level = 0,
|
||||
treeSelection,
|
||||
setTreeSelection,
|
||||
}: {
|
||||
parentDir: FileEntry | undefined
|
||||
project?: IndexLoaderData['project']
|
||||
currentFile?: IndexLoaderData['file']
|
||||
lastDirectoryClicked?: FileEntry
|
||||
fileOrDir: FileEntry
|
||||
onNavigateToFile?: () => void
|
||||
onClickDirectory: (
|
||||
open: boolean,
|
||||
path: FileEntry,
|
||||
parentDir: FileEntry | undefined
|
||||
) => void
|
||||
onCreateFile: (name: string) => void
|
||||
onCreateFolder: (name: string) => void
|
||||
newTreeEntry: TreeEntry
|
||||
level?: number
|
||||
treeSelection: FileEntry | undefined
|
||||
setTreeSelection: Dispatch<React.SetStateAction<FileEntry | undefined>>
|
||||
}) => {
|
||||
const { send: fileSend, context: fileContext } = useFileContext()
|
||||
const { onFileOpen, onFileClose } = useLspContext()
|
||||
const navigate = useNavigate()
|
||||
const [isConfirmingDelete, setIsConfirmingDelete] = useState(false)
|
||||
const isCurrentFile = fileOrDir.path === currentFile?.path
|
||||
const isFileOrDirHighlighted = treeSelection?.path === fileOrDir?.path
|
||||
const itemRef = useRef(null)
|
||||
|
||||
// Since every file or directory gets its own FileTreeItem, we can do this.
|
||||
@ -156,6 +207,10 @@ const FileTreeItem = ({
|
||||
[fileOrDir.path]
|
||||
)
|
||||
|
||||
const showNewTreeEntry =
|
||||
newTreeEntry !== undefined &&
|
||||
fileOrDir.path === fileContext.selectedDirectory.path
|
||||
|
||||
const isRenaming = fileContext.itemsBeingRenamed.includes(fileOrDir.path)
|
||||
const removeCurrentItemFromRenaming = useCallback(
|
||||
() =>
|
||||
@ -179,13 +234,6 @@ const FileTreeItem = ({
|
||||
})
|
||||
}, [fileContext.itemsBeingRenamed, fileOrDir.path, fileSend])
|
||||
|
||||
const clickDirectory = () => {
|
||||
fileSend({
|
||||
type: 'Set selected directory',
|
||||
directory: fileOrDir,
|
||||
})
|
||||
}
|
||||
|
||||
function handleKeyUp(e: React.KeyboardEvent<HTMLButtonElement>) {
|
||||
if (e.metaKey && e.key === 'Backspace') {
|
||||
// Open confirmation dialog
|
||||
@ -199,6 +247,8 @@ const FileTreeItem = ({
|
||||
}
|
||||
|
||||
async function handleClick() {
|
||||
setTreeSelection(fileOrDir)
|
||||
|
||||
if (fileOrDir.children !== null) return // Don't open directories
|
||||
|
||||
if (fileOrDir.name?.endsWith(FILE_EXT) === false && project?.path) {
|
||||
@ -220,16 +270,19 @@ const FileTreeItem = ({
|
||||
// Open kcl files
|
||||
navigate(`${PATHS.FILE}/${encodeURIComponent(fileOrDir.path)}`)
|
||||
}
|
||||
|
||||
onNavigateToFile?.()
|
||||
}
|
||||
|
||||
// The below handles both the "root" of all directories and all subs. It's
|
||||
// why some code is duplicated.
|
||||
return (
|
||||
<div className="contents" data-testid="file-tree-item" ref={itemRef}>
|
||||
{fileOrDir.children === null ? (
|
||||
<li
|
||||
className={
|
||||
'group m-0 p-0 border-solid border-0 hover:bg-primary/5 focus-within:bg-primary/5 dark:hover:bg-primary/20 dark:focus-within:bg-primary/20 ' +
|
||||
(isCurrentFile
|
||||
(isFileOrDirHighlighted || isCurrentFile
|
||||
? '!bg-primary/10 !text-primary dark:!bg-primary/20 dark:!text-inherit'
|
||||
: '')
|
||||
}
|
||||
@ -266,14 +319,13 @@ const FileTreeItem = ({
|
||||
<Disclosure.Button
|
||||
className={
|
||||
' group border-none text-sm rounded-none p-0 m-0 flex items-center justify-start w-full py-0.5 hover:text-primary hover:bg-primary/5 dark:hover:text-inherit dark:hover:bg-primary/10' +
|
||||
(fileContext.selectedDirectory.path.includes(fileOrDir.path)
|
||||
? ' ui-open:bg-primary/10'
|
||||
: '')
|
||||
(isFileOrDirHighlighted ? ' ui-open:bg-primary/10' : '')
|
||||
}
|
||||
style={{ paddingInlineStart: getIndentationCSS(level) }}
|
||||
onClick={(e) => e.currentTarget.focus()}
|
||||
onClickCapture={clickDirectory}
|
||||
onFocusCapture={clickDirectory}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
onClickDirectory(open, fileOrDir, parentDir)
|
||||
}}
|
||||
onKeyDown={(e) => e.key === 'Enter' && e.preventDefault()}
|
||||
onKeyUp={handleKeyUp}
|
||||
>
|
||||
@ -315,35 +367,69 @@ const FileTreeItem = ({
|
||||
>
|
||||
<ul
|
||||
className="m-0 p-0"
|
||||
onClickCapture={(e) => {
|
||||
fileSend({
|
||||
type: 'Set selected directory',
|
||||
directory: fileOrDir,
|
||||
})
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
onClickDirectory(open, fileOrDir, parentDir)
|
||||
}}
|
||||
onFocusCapture={(e) =>
|
||||
fileSend({
|
||||
type: 'Set selected directory',
|
||||
directory: fileOrDir,
|
||||
})
|
||||
}
|
||||
>
|
||||
{fileOrDir.children?.map((child) => (
|
||||
<FileTreeItem
|
||||
fileOrDir={child}
|
||||
project={project}
|
||||
currentFile={currentFile}
|
||||
onNavigateToFile={onNavigateToFile}
|
||||
level={level + 1}
|
||||
key={level + '-' + child.path}
|
||||
/>
|
||||
))}
|
||||
{showNewTreeEntry && (
|
||||
<div
|
||||
className="flex items-center"
|
||||
style={{
|
||||
paddingInlineStart: getIndentationCSS(level + 1),
|
||||
}}
|
||||
>
|
||||
<FontAwesomeIcon
|
||||
icon={faPencil}
|
||||
className="inline-block mr-2 m-0 p-0 w-2 h-2"
|
||||
/>
|
||||
<TreeEntryInput
|
||||
level={-1}
|
||||
onSubmit={(value: string) =>
|
||||
newTreeEntry === 'file'
|
||||
? onCreateFile(value)
|
||||
: onCreateFolder(value)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{sortFilesAndDirectories(fileOrDir.children || []).map(
|
||||
(child) => (
|
||||
<FileTreeItem
|
||||
parentDir={fileOrDir}
|
||||
fileOrDir={child}
|
||||
project={project}
|
||||
currentFile={currentFile}
|
||||
onCreateFile={onCreateFile}
|
||||
onCreateFolder={onCreateFolder}
|
||||
newTreeEntry={newTreeEntry}
|
||||
lastDirectoryClicked={lastDirectoryClicked}
|
||||
onClickDirectory={onClickDirectory}
|
||||
onNavigateToFile={onNavigateToFile}
|
||||
level={level + 1}
|
||||
key={level + '-' + child.path}
|
||||
treeSelection={treeSelection}
|
||||
setTreeSelection={setTreeSelection}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
{!showNewTreeEntry && fileOrDir.children?.length === 0 && (
|
||||
<div
|
||||
className="flex items-center text-chalkboard-50"
|
||||
style={{
|
||||
paddingInlineStart: getIndentationCSS(level + 1),
|
||||
}}
|
||||
>
|
||||
<div>No files</div>
|
||||
</div>
|
||||
)}
|
||||
</ul>
|
||||
</Disclosure.Panel>
|
||||
</div>
|
||||
)}
|
||||
</Disclosure>
|
||||
)}
|
||||
|
||||
{isConfirmingDelete && (
|
||||
<DeleteFileTreeItemDialog
|
||||
fileOrDir={fileOrDir}
|
||||
@ -407,27 +493,15 @@ interface FileTreeProps {
|
||||
) => void
|
||||
}
|
||||
|
||||
export const FileTreeMenu = () => {
|
||||
const { send } = useFileContext()
|
||||
const { send: modelingSend } = useModelingContext()
|
||||
|
||||
function createFile() {
|
||||
send({
|
||||
type: 'Create file',
|
||||
data: { name: '', makeDir: false, shouldSetToRename: true },
|
||||
})
|
||||
modelingSend({ type: 'Cancel' })
|
||||
}
|
||||
|
||||
function createFolder() {
|
||||
send({
|
||||
type: 'Create file',
|
||||
data: { name: '', makeDir: true, shouldSetToRename: true },
|
||||
})
|
||||
}
|
||||
|
||||
useHotkeyWrapper(['mod + n'], createFile)
|
||||
useHotkeyWrapper(['mod + shift + n'], createFolder)
|
||||
export const FileTreeMenu = ({
|
||||
onCreateFile,
|
||||
onCreateFolder,
|
||||
}: {
|
||||
onCreateFile: () => void
|
||||
onCreateFolder: () => void
|
||||
}) => {
|
||||
useHotkeyWrapper(['mod + n'], onCreateFile)
|
||||
useHotkeyWrapper(['mod + shift + n'], onCreateFolder)
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -440,7 +514,7 @@ export const FileTreeMenu = () => {
|
||||
bgClassName: 'bg-transparent',
|
||||
}}
|
||||
className="!p-0 !bg-transparent hover:text-primary border-transparent hover:border-primary !outline-none"
|
||||
onClick={createFile}
|
||||
onClick={onCreateFile}
|
||||
>
|
||||
<Tooltip position="bottom-right" delay={750}>
|
||||
Create file
|
||||
@ -456,7 +530,7 @@ export const FileTreeMenu = () => {
|
||||
bgClassName: 'bg-transparent',
|
||||
}}
|
||||
className="!p-0 !bg-transparent hover:text-primary border-transparent hover:border-primary !outline-none"
|
||||
onClick={createFolder}
|
||||
onClick={onCreateFolder}
|
||||
>
|
||||
<Tooltip position="bottom-right" delay={750}>
|
||||
Create folder
|
||||
@ -466,30 +540,110 @@ export const FileTreeMenu = () => {
|
||||
)
|
||||
}
|
||||
|
||||
type TreeEntry = 'file' | 'folder' | undefined
|
||||
|
||||
export const useFileTreeOperations = () => {
|
||||
const { send } = useFileContext()
|
||||
const { send: modelingSend } = useModelingContext()
|
||||
|
||||
// As long as this is undefined, a new "file tree entry prompt" is not shown.
|
||||
const [newTreeEntry, setNewTreeEntry] = useState<TreeEntry>(undefined)
|
||||
|
||||
function createFile(args: { dryRun: boolean; name?: string }) {
|
||||
if (args.dryRun) {
|
||||
setNewTreeEntry('file')
|
||||
return
|
||||
}
|
||||
|
||||
// Clear so that the entry prompt goes away.
|
||||
setNewTreeEntry(undefined)
|
||||
|
||||
if (!args.name) return
|
||||
|
||||
send({
|
||||
type: 'Create file',
|
||||
data: { name: args.name, makeDir: false, shouldSetToRename: false },
|
||||
})
|
||||
modelingSend({ type: 'Cancel' })
|
||||
}
|
||||
|
||||
function createFolder(args: { dryRun: boolean; name?: string }) {
|
||||
if (args.dryRun) {
|
||||
setNewTreeEntry('folder')
|
||||
return
|
||||
}
|
||||
|
||||
setNewTreeEntry(undefined)
|
||||
|
||||
if (!args.name) return
|
||||
|
||||
send({
|
||||
type: 'Create file',
|
||||
data: { name: args.name, makeDir: true, shouldSetToRename: false },
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
createFile,
|
||||
createFolder,
|
||||
newTreeEntry,
|
||||
}
|
||||
}
|
||||
|
||||
export const FileTree = ({
|
||||
className = '',
|
||||
onNavigateToFile: closePanel,
|
||||
}: FileTreeProps) => {
|
||||
const { createFile, createFolder, newTreeEntry } = useFileTreeOperations()
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
<div className="flex items-center gap-1 px-4 py-1 bg-chalkboard-20/40 dark:bg-chalkboard-80/50 border-b border-b-chalkboard-30 dark:border-b-chalkboard-80">
|
||||
<h2 className="flex-1 m-0 p-0 text-sm mono">Files</h2>
|
||||
<FileTreeMenu />
|
||||
<FileTreeMenu
|
||||
onCreateFile={() => createFile({ dryRun: true })}
|
||||
onCreateFolder={() => createFolder({ dryRun: true })}
|
||||
/>
|
||||
</div>
|
||||
<FileTreeInner onNavigateToFile={closePanel} />
|
||||
<FileTreeInner
|
||||
onNavigateToFile={closePanel}
|
||||
newTreeEntry={newTreeEntry}
|
||||
onCreateFile={(name: string) => createFile({ dryRun: false, name })}
|
||||
onCreateFolder={(name: string) => createFolder({ dryRun: false, name })}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export const FileTreeInner = ({
|
||||
onNavigateToFile,
|
||||
onCreateFile,
|
||||
onCreateFolder,
|
||||
newTreeEntry,
|
||||
}: {
|
||||
onCreateFile: (name: string) => void
|
||||
onCreateFolder: (name: string) => void
|
||||
newTreeEntry: TreeEntry
|
||||
onNavigateToFile?: () => void
|
||||
}) => {
|
||||
const loaderData = useRouteLoaderData(PATHS.FILE) as IndexLoaderData
|
||||
const { send: fileSend, context: fileContext } = useFileContext()
|
||||
const { send: modelingSend } = useModelingContext()
|
||||
|
||||
const [lastDirectoryClicked, setLastDirectoryClicked] = useState<
|
||||
FileEntry | undefined
|
||||
>(undefined)
|
||||
|
||||
const [treeSelection, setTreeSelection] = useState<FileEntry | undefined>(
|
||||
loaderData.file
|
||||
)
|
||||
|
||||
const onNavigateToFile_ = () => {
|
||||
// Reset modeling state when navigating to a new file
|
||||
onNavigateToFile?.()
|
||||
modelingSend({ type: 'Cancel' })
|
||||
}
|
||||
|
||||
// Refresh the file tree when there are changes.
|
||||
useFileSystemWatcher(
|
||||
async (eventType, path) => {
|
||||
@ -513,33 +667,81 @@ export const FileTreeInner = ({
|
||||
)
|
||||
)
|
||||
|
||||
const clickDirectory = () => {
|
||||
const onTreeEntryInputSubmit = (value: string) => {
|
||||
if (newTreeEntry === 'file') {
|
||||
onCreateFile(value)
|
||||
onNavigateToFile_()
|
||||
} else {
|
||||
onCreateFolder(value)
|
||||
}
|
||||
}
|
||||
|
||||
const onClickDirectory = (
|
||||
open_: boolean,
|
||||
fileOrDir: FileEntry,
|
||||
parentDir: FileEntry | undefined
|
||||
) => {
|
||||
// open true is closed... it's broken. Save me. I've inverted it here for
|
||||
// sanity.
|
||||
const open = !open_
|
||||
|
||||
const target = open ? fileOrDir : parentDir
|
||||
|
||||
// We're at the root, can't select anything further
|
||||
if (!target) return
|
||||
|
||||
setTreeSelection(target)
|
||||
setLastDirectoryClicked(target)
|
||||
fileSend({
|
||||
type: 'Set selected directory',
|
||||
directory: fileContext.project,
|
||||
directory: target,
|
||||
})
|
||||
}
|
||||
|
||||
const showNewTreeEntry =
|
||||
newTreeEntry !== undefined &&
|
||||
fileContext.selectedDirectory.path === loaderData.project?.path
|
||||
|
||||
return (
|
||||
<div
|
||||
className="overflow-auto pb-12 absolute inset-0"
|
||||
data-testid="file-pane-scroll-container"
|
||||
>
|
||||
<ul className="m-0 p-0 text-sm" onClickCapture={clickDirectory}>
|
||||
{sortProject(fileContext.project?.children || []).map((fileOrDir) => (
|
||||
<FileTreeItem
|
||||
project={fileContext.project}
|
||||
currentFile={loaderData?.file}
|
||||
fileOrDir={fileOrDir}
|
||||
onNavigateToFile={() => {
|
||||
// Reset modeling state when navigating to a new file
|
||||
modelingSend({ type: 'Cancel' })
|
||||
onNavigateToFile?.()
|
||||
}}
|
||||
key={fileOrDir.path}
|
||||
/>
|
||||
))}
|
||||
</ul>
|
||||
<div className="relative">
|
||||
<div
|
||||
className="overflow-auto pb-12 absolute inset-0"
|
||||
data-testid="file-pane-scroll-container"
|
||||
>
|
||||
<ul className="m-0 p-0 text-sm">
|
||||
{showNewTreeEntry && (
|
||||
<div
|
||||
className="flex items-center"
|
||||
style={{ paddingInlineStart: getIndentationCSS(0) }}
|
||||
>
|
||||
<FontAwesomeIcon
|
||||
icon={faPencil}
|
||||
className="inline-block mr-2 m-0 p-0 w-2 h-2"
|
||||
/>
|
||||
<TreeEntryInput level={-1} onSubmit={onTreeEntryInputSubmit} />
|
||||
</div>
|
||||
)}
|
||||
{sortFilesAndDirectories(fileContext.project?.children || []).map(
|
||||
(fileOrDir) => (
|
||||
<FileTreeItem
|
||||
parentDir={fileContext.project}
|
||||
project={fileContext.project}
|
||||
currentFile={loaderData?.file}
|
||||
lastDirectoryClicked={lastDirectoryClicked}
|
||||
fileOrDir={fileOrDir}
|
||||
onCreateFile={onCreateFile}
|
||||
onCreateFolder={onCreateFolder}
|
||||
newTreeEntry={newTreeEntry}
|
||||
onClickDirectory={onClickDirectory}
|
||||
onNavigateToFile={onNavigateToFile_}
|
||||
key={fileOrDir.path}
|
||||
treeSelection={treeSelection}
|
||||
setTreeSelection={setTreeSelection}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -158,36 +158,39 @@ export const ModelingMachineProvider = ({
|
||||
'enable copilot': () => {
|
||||
editorManager.setCopilotEnabled(true)
|
||||
},
|
||||
'sketch exit execute': ({ context: { store } }) => {
|
||||
;(async () => {
|
||||
// When cancelling the sketch mode we should disable sketch mode within the engine.
|
||||
await engineCommandManager.sendSceneCommand({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd_id: uuidv4(),
|
||||
cmd: { type: 'sketch_mode_disable' },
|
||||
})
|
||||
// tsc reports this typing as perfectly fine, but eslint is complaining.
|
||||
// It's actually nonsensical, so I'm quieting.
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||
'sketch exit execute': async ({
|
||||
context: { store },
|
||||
}): Promise<void> => {
|
||||
// When cancelling the sketch mode we should disable sketch mode within the engine.
|
||||
await engineCommandManager.sendSceneCommand({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd_id: uuidv4(),
|
||||
cmd: { type: 'sketch_mode_disable' },
|
||||
})
|
||||
|
||||
sceneInfra.camControls.syncDirection = 'clientToEngine'
|
||||
sceneInfra.camControls.syncDirection = 'clientToEngine'
|
||||
|
||||
if (cameraProjection.current === 'perspective') {
|
||||
await sceneInfra.camControls.snapToPerspectiveBeforeHandingBackControlToEngine()
|
||||
}
|
||||
if (cameraProjection.current === 'perspective') {
|
||||
await sceneInfra.camControls.snapToPerspectiveBeforeHandingBackControlToEngine()
|
||||
}
|
||||
|
||||
sceneInfra.camControls.syncDirection = 'engineToClient'
|
||||
sceneInfra.camControls.syncDirection = 'engineToClient'
|
||||
|
||||
store.videoElement?.pause()
|
||||
store.videoElement?.pause()
|
||||
|
||||
kclManager
|
||||
.executeCode()
|
||||
.then(() => {
|
||||
if (engineCommandManager.engineConnection?.idleMode) return
|
||||
return kclManager
|
||||
.executeCode()
|
||||
.then(() => {
|
||||
if (engineCommandManager.engineConnection?.idleMode) return
|
||||
|
||||
store.videoElement?.play().catch((e) => {
|
||||
console.warn('Video playing was prevented', e)
|
||||
})
|
||||
store.videoElement?.play().catch((e) => {
|
||||
console.warn('Video playing was prevented', e)
|
||||
})
|
||||
.catch(reportRejection)
|
||||
})().catch(reportRejection)
|
||||
})
|
||||
.catch(reportRejection)
|
||||
},
|
||||
'Set mouse state': assign(({ context, event }) => {
|
||||
if (event.type !== 'Set mouse state') return {}
|
||||
|
@ -48,7 +48,7 @@ export const ModelingPaneHeader = ({
|
||||
bgClassName: 'bg-transparent dark:bg-transparent',
|
||||
}}
|
||||
className="!p-0 !bg-transparent hover:text-primary border-transparent dark:!border-transparent hover:!border-primary dark:hover:!border-chalkboard-70 !outline-none"
|
||||
onClick={onClose}
|
||||
onClick={() => onClose()}
|
||||
>
|
||||
<Tooltip position="bottom-right" delay={750}>
|
||||
Close
|
||||
@ -59,14 +59,12 @@ export const ModelingPaneHeader = ({
|
||||
}
|
||||
|
||||
export const ModelingPane = ({
|
||||
title,
|
||||
icon,
|
||||
id,
|
||||
children,
|
||||
className,
|
||||
Menu,
|
||||
detailsTestId,
|
||||
onClose,
|
||||
title,
|
||||
...props
|
||||
}: ModelingPaneProps) => {
|
||||
const { settings } = useSettingsAuthContext()
|
||||
@ -78,6 +76,7 @@ export const ModelingPane = ({
|
||||
return (
|
||||
<section
|
||||
{...props}
|
||||
title={title && typeof title === 'string' ? title : ''}
|
||||
data-testid={detailsTestId}
|
||||
id={id}
|
||||
className={
|
||||
@ -88,14 +87,7 @@ export const ModelingPane = ({
|
||||
(className || '')
|
||||
}
|
||||
>
|
||||
<ModelingPaneHeader
|
||||
id={id}
|
||||
icon={icon}
|
||||
title={title}
|
||||
Menu={Menu}
|
||||
onClose={onClose}
|
||||
/>
|
||||
<div className="relative w-full">{children}</div>
|
||||
{children}
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
@ -5,16 +5,18 @@ import { CamDebugSettings } from 'clientSideScene/ClientSideSceneComp'
|
||||
|
||||
export const DebugPane = () => {
|
||||
return (
|
||||
<section
|
||||
data-testid="debug-panel"
|
||||
className="absolute inset-0 p-2 box-border overflow-auto"
|
||||
>
|
||||
<div className="flex flex-col">
|
||||
<EngineCommands />
|
||||
<CamDebugSettings />
|
||||
<AstExplorer />
|
||||
<DebugFeatureTree />
|
||||
</div>
|
||||
</section>
|
||||
<div className="relative">
|
||||
<section
|
||||
data-testid="debug-panel"
|
||||
className="absolute inset-0 p-2 box-border overflow-auto"
|
||||
>
|
||||
<div className="flex flex-col">
|
||||
<EngineCommands />
|
||||
<CamDebugSettings />
|
||||
<AstExplorer />
|
||||
<DebugFeatureTree />
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -174,27 +174,31 @@ export const KclEditorPane = () => {
|
||||
const initialCode = useRef(codeManager.code)
|
||||
|
||||
return (
|
||||
<div
|
||||
id="code-mirror-override"
|
||||
className={'absolute inset-0 ' + (cursorBlinking.current ? 'blink' : '')}
|
||||
>
|
||||
<CodeEditor
|
||||
initialDocValue={initialCode.current}
|
||||
extensions={editorExtensions}
|
||||
theme={theme}
|
||||
onCreateEditor={(_editorView) => {
|
||||
if (_editorView === null) return
|
||||
<div className="relative">
|
||||
<div
|
||||
id="code-mirror-override"
|
||||
className={
|
||||
'absolute inset-0 ' + (cursorBlinking.current ? 'blink' : '')
|
||||
}
|
||||
>
|
||||
<CodeEditor
|
||||
initialDocValue={initialCode.current}
|
||||
extensions={editorExtensions}
|
||||
theme={theme}
|
||||
onCreateEditor={(_editorView) => {
|
||||
if (_editorView === null) return
|
||||
|
||||
editorManager.setEditorView(_editorView)
|
||||
editorManager.setEditorView(_editorView)
|
||||
|
||||
// On first load of this component, ensure we show the current errors
|
||||
// in the editor.
|
||||
// Make sure we don't add them twice.
|
||||
if (diagnosticCount(_editorView.state) === 0) {
|
||||
kclManager.setDiagnosticsForCurrentErrors()
|
||||
}
|
||||
}}
|
||||
/>
|
||||
// On first load of this component, ensure we show the current errors
|
||||
// in the editor.
|
||||
// Make sure we don't add them twice.
|
||||
if (diagnosticCount(_editorView.state) === 0) {
|
||||
kclManager.setDiagnosticsForCurrentErrors()
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -43,14 +43,14 @@ describe('processMemory', () => {
|
||||
tag: null,
|
||||
id: expect.any(String),
|
||||
faceId: expect.any(String),
|
||||
sourceRange: [170, 194],
|
||||
sourceRange: [170, 194, 0],
|
||||
},
|
||||
{
|
||||
type: 'extrudePlane',
|
||||
tag: null,
|
||||
id: expect.any(String),
|
||||
faceId: expect.any(String),
|
||||
sourceRange: [202, 230],
|
||||
sourceRange: [202, 230, 0],
|
||||
},
|
||||
],
|
||||
theSketch: [
|
||||
|
@ -2,11 +2,17 @@ import { IconDefinition, faBugSlash } from '@fortawesome/free-solid-svg-icons'
|
||||
import { KclEditorMenu } from 'components/ModelingSidebar/ModelingPanes/KclEditorMenu'
|
||||
import { CustomIconName } from 'components/CustomIcon'
|
||||
import { KclEditorPane } from 'components/ModelingSidebar/ModelingPanes/KclEditorPane'
|
||||
import { ModelingPaneHeader } from 'components/ModelingSidebar/ModelingPane'
|
||||
import { MouseEventHandler, ReactNode } from 'react'
|
||||
import { MemoryPane, MemoryPaneMenu } from './MemoryPane'
|
||||
import { LogsPane } from './LoggingPanes'
|
||||
import { DebugPane } from './DebugPane'
|
||||
import { FileTreeInner, FileTreeMenu, FileTreeRoot } from 'components/FileTree'
|
||||
import {
|
||||
FileTreeInner,
|
||||
FileTreeMenu,
|
||||
FileTreeRoot,
|
||||
useFileTreeOperations,
|
||||
} from 'components/FileTree'
|
||||
import { useKclContext } from 'lang/KclProvider'
|
||||
import { editorManager } from 'lib/singletons'
|
||||
import { ContextFrom } from 'xstate'
|
||||
@ -38,20 +44,19 @@ interface PaneCallbackProps {
|
||||
|
||||
export type SidebarPane = {
|
||||
id: SidebarType
|
||||
title: ReactNode
|
||||
sidebarName?: string
|
||||
sidebarName: string
|
||||
icon: CustomIconName | IconDefinition
|
||||
keybinding: string
|
||||
Content: ReactNode | React.FC
|
||||
Menu?: ReactNode | React.FC
|
||||
Content: React.FC<{ id: SidebarType; onClose: () => void }>
|
||||
hide?: boolean | ((props: PaneCallbackProps) => boolean)
|
||||
showBadge?: BadgeInfo
|
||||
}
|
||||
|
||||
export type SidebarAction = {
|
||||
id: string
|
||||
title: ReactNode
|
||||
sidebarName: string
|
||||
icon: CustomIconName
|
||||
title: ReactNode
|
||||
iconClassName?: string // Just until we get rid of FontAwesome icons
|
||||
keybinding: string
|
||||
action: () => void
|
||||
@ -59,14 +64,30 @@ export type SidebarAction = {
|
||||
disable?: () => string | undefined
|
||||
}
|
||||
|
||||
// For now a lot of icons are the same but the reality is they could totally
|
||||
// be different, like an icon based on some data for the pane, or the icon
|
||||
// changes to be a spinning loader on loading.
|
||||
|
||||
export const sidebarPanes: SidebarPane[] = [
|
||||
{
|
||||
id: 'code',
|
||||
title: 'KCL Code',
|
||||
icon: 'code',
|
||||
Content: KclEditorPane,
|
||||
sidebarName: 'KCL Code',
|
||||
Content: (props: { id: SidebarType; onClose: () => void }) => {
|
||||
return (
|
||||
<>
|
||||
<ModelingPaneHeader
|
||||
id={props.id}
|
||||
icon="code"
|
||||
title="KCL Code"
|
||||
Menu={<KclEditorMenu />}
|
||||
onClose={props.onClose}
|
||||
/>
|
||||
<KclEditorPane />
|
||||
</>
|
||||
)
|
||||
},
|
||||
keybinding: 'Shift + C',
|
||||
Menu: KclEditorMenu,
|
||||
showBadge: {
|
||||
value: ({ kclContext }) => {
|
||||
return kclContext.errors.length
|
||||
@ -79,34 +100,96 @@ export const sidebarPanes: SidebarPane[] = [
|
||||
},
|
||||
{
|
||||
id: 'files',
|
||||
title: <FileTreeRoot />,
|
||||
sidebarName: 'Project Files',
|
||||
icon: 'folder',
|
||||
Content: FileTreeInner,
|
||||
sidebarName: 'Project Files',
|
||||
Content: (props: { id: SidebarType; onClose: () => void }) => {
|
||||
const { createFile, createFolder, newTreeEntry } = useFileTreeOperations()
|
||||
|
||||
return (
|
||||
<>
|
||||
<ModelingPaneHeader
|
||||
id={props.id}
|
||||
icon="folder"
|
||||
title={<FileTreeRoot />}
|
||||
Menu={
|
||||
<FileTreeMenu
|
||||
onCreateFile={() => createFile({ dryRun: true })}
|
||||
onCreateFolder={() => createFolder({ dryRun: true })}
|
||||
/>
|
||||
}
|
||||
onClose={props.onClose}
|
||||
/>
|
||||
<FileTreeInner
|
||||
onCreateFile={(name: string) => createFile({ dryRun: false, name })}
|
||||
onCreateFolder={(name: string) =>
|
||||
createFolder({ dryRun: false, name })
|
||||
}
|
||||
newTreeEntry={newTreeEntry}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
},
|
||||
keybinding: 'Shift + F',
|
||||
Menu: FileTreeMenu,
|
||||
hide: ({ platform }) => platform === 'web',
|
||||
},
|
||||
{
|
||||
id: 'variables',
|
||||
title: 'Variables',
|
||||
icon: 'make-variable',
|
||||
Content: MemoryPane,
|
||||
Menu: MemoryPaneMenu,
|
||||
sidebarName: 'Variables',
|
||||
Content: (props: { id: SidebarType; onClose: () => void }) => {
|
||||
return (
|
||||
<>
|
||||
<ModelingPaneHeader
|
||||
id={props.id}
|
||||
icon="make-variable"
|
||||
title="Variables"
|
||||
Menu={<MemoryPaneMenu />}
|
||||
onClose={props.onClose}
|
||||
/>
|
||||
<MemoryPane />
|
||||
</>
|
||||
)
|
||||
},
|
||||
keybinding: 'Shift + V',
|
||||
},
|
||||
{
|
||||
id: 'logs',
|
||||
title: 'Logs',
|
||||
icon: 'logs',
|
||||
Content: LogsPane,
|
||||
sidebarName: 'Logs',
|
||||
Content: (props: { id: SidebarType; onClose: () => void }) => {
|
||||
return (
|
||||
<>
|
||||
<ModelingPaneHeader
|
||||
id={props.id}
|
||||
icon="logs"
|
||||
title="Logs"
|
||||
Menu={null}
|
||||
onClose={props.onClose}
|
||||
/>
|
||||
<LogsPane />
|
||||
</>
|
||||
)
|
||||
},
|
||||
keybinding: 'Shift + L',
|
||||
},
|
||||
{
|
||||
id: 'debug',
|
||||
title: 'Debug',
|
||||
icon: faBugSlash,
|
||||
Content: DebugPane,
|
||||
sidebarName: 'Debug',
|
||||
Content: (props: { id: SidebarType; onClose: () => void }) => {
|
||||
return (
|
||||
<>
|
||||
<ModelingPaneHeader
|
||||
id={props.id}
|
||||
icon={faBugSlash}
|
||||
title="Debug"
|
||||
Menu={null}
|
||||
onClose={props.onClose}
|
||||
/>
|
||||
<DebugPane />
|
||||
</>
|
||||
)
|
||||
},
|
||||
keybinding: 'Shift + D',
|
||||
hide: ({ settings }) => !settings.modeling.showDebugPanel.current,
|
||||
},
|
||||
|
@ -1,11 +0,0 @@
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr;
|
||||
grid-template-rows: 1fr 1fr;
|
||||
row-gap: 0.25rem;
|
||||
align-items: stretch;
|
||||
position: relative;
|
||||
padding-block: 1px;
|
||||
max-width: 100%;
|
||||
flex: 1 1 0;
|
||||
}
|
||||
|
@ -5,14 +5,12 @@ import {
|
||||
useCallback,
|
||||
useEffect,
|
||||
useMemo,
|
||||
ReactNode,
|
||||
useContext,
|
||||
} from 'react'
|
||||
import { useHotkeys } from 'react-hotkeys-hook'
|
||||
import { SidebarAction, SidebarType, sidebarPanes } from './ModelingPanes'
|
||||
import Tooltip from 'components/Tooltip'
|
||||
import { ActionIcon } from 'components/ActionIcon'
|
||||
import styles from './ModelingSidebar.module.css'
|
||||
import { ModelingPane } from './ModelingPane'
|
||||
import { isDesktop } from 'lib/isDesktop'
|
||||
import { useModelingContext } from 'hooks/useModelingContext'
|
||||
@ -62,6 +60,7 @@ export function ModelingSidebar({ paneOpacity }: ModelingSidebarProps) {
|
||||
{
|
||||
id: 'export',
|
||||
title: 'Export part',
|
||||
sidebarName: 'Export part',
|
||||
icon: 'floppyDiskArrow',
|
||||
keybinding: 'Ctrl + Shift + E',
|
||||
action: () =>
|
||||
@ -73,6 +72,7 @@ export function ModelingSidebar({ paneOpacity }: ModelingSidebarProps) {
|
||||
{
|
||||
id: 'make',
|
||||
title: 'Make part',
|
||||
sidebarName: 'Make part',
|
||||
icon: 'printer3d',
|
||||
keybinding: 'Ctrl + Shift + M',
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||
@ -182,7 +182,7 @@ export function ModelingSidebar({ paneOpacity }: ModelingSidebarProps) {
|
||||
bottomRight: 'hidden',
|
||||
}}
|
||||
>
|
||||
<div id="app-sidebar" className={styles.grid + ' flex-1'}>
|
||||
<div id="app-sidebar" className="flex flex-row h-full">
|
||||
<ul
|
||||
className={
|
||||
(context.store?.openPanes.length === 0 ? 'rounded-r ' : '') +
|
||||
@ -220,7 +220,7 @@ export function ModelingSidebar({ paneOpacity }: ModelingSidebarProps) {
|
||||
key={action.id}
|
||||
paneConfig={{
|
||||
id: action.id,
|
||||
title: action.title,
|
||||
sidebarName: action.sidebarName,
|
||||
icon: action.icon,
|
||||
keybinding: action.keybinding,
|
||||
iconClassName: action.iconClassName,
|
||||
@ -237,10 +237,8 @@ export function ModelingSidebar({ paneOpacity }: ModelingSidebarProps) {
|
||||
<ul
|
||||
id="pane-section"
|
||||
className={
|
||||
'ml-[-1px] col-start-2 col-span-1 flex flex-col gap-2 ' +
|
||||
(context.store?.openPanes.length >= 1
|
||||
? `row-start-1 row-end-3`
|
||||
: `hidden`)
|
||||
'ml-[-1px] col-start-2 col-span-1 flex flex-col items-stretch gap-2 ' +
|
||||
(context.store?.openPanes.length >= 1 ? `w-full` : `hidden`)
|
||||
}
|
||||
>
|
||||
{filteredPanes
|
||||
@ -249,13 +247,15 @@ export function ModelingSidebar({ paneOpacity }: ModelingSidebarProps) {
|
||||
<ModelingPane
|
||||
key={pane.id}
|
||||
icon={pane.icon}
|
||||
title={pane.sidebarName}
|
||||
onClose={() => {}}
|
||||
id={`${pane.id}-pane`}
|
||||
title={pane.title}
|
||||
Menu={pane.Menu}
|
||||
onClose={() => togglePane(pane.id)}
|
||||
>
|
||||
{pane.Content instanceof Function ? (
|
||||
<pane.Content />
|
||||
<pane.Content
|
||||
id={pane.id}
|
||||
onClose={() => togglePane(pane.id)}
|
||||
/>
|
||||
) : (
|
||||
pane.Content
|
||||
)}
|
||||
@ -271,8 +271,7 @@ interface ModelingPaneButtonProps
|
||||
extends React.HTMLAttributes<HTMLButtonElement> {
|
||||
paneConfig: {
|
||||
id: string
|
||||
title: ReactNode
|
||||
sidebarName?: string
|
||||
sidebarName: string
|
||||
icon: CustomIconName | IconDefinition
|
||||
keybinding: string
|
||||
iconClassName?: string
|
||||
@ -301,10 +300,7 @@ function ModelingPaneButton({
|
||||
<button
|
||||
className="group pointer-events-auto flex items-center justify-center border-transparent dark:border-transparent disabled:!border-transparent p-0 m-0 rounded-sm !outline-0 focus-visible:border-primary"
|
||||
onClick={onClick}
|
||||
name={
|
||||
paneConfig.sidebarName ??
|
||||
(typeof paneConfig.title === 'string' ? paneConfig.title : '')
|
||||
}
|
||||
name={paneConfig.sidebarName}
|
||||
data-testid={paneConfig.id + '-pane-button'}
|
||||
disabled={disabledText !== undefined}
|
||||
aria-disabled={disabledText !== undefined}
|
||||
@ -320,7 +316,7 @@ function ModelingPaneButton({
|
||||
}
|
||||
/>
|
||||
<span className="sr-only">
|
||||
{paneConfig.sidebarName ?? paneConfig.title}
|
||||
{paneConfig.sidebarName}
|
||||
{paneIsOpen !== undefined ? ` pane` : ''}
|
||||
</span>
|
||||
<Tooltip
|
||||
@ -329,7 +325,7 @@ function ModelingPaneButton({
|
||||
hoverOnly
|
||||
>
|
||||
<span className="flex-1">
|
||||
{paneConfig.sidebarName ?? paneConfig.title}
|
||||
{paneConfig.sidebarName}
|
||||
{disabledText !== undefined ? ` (${disabledText})` : ''}
|
||||
{paneIsOpen !== undefined ? ` pane` : ''}
|
||||
</span>
|
||||
|
@ -38,6 +38,7 @@ export class KclManager {
|
||||
body: [],
|
||||
start: 0,
|
||||
end: 0,
|
||||
moduleId: 0,
|
||||
nonCodeMeta: {
|
||||
nonCodeNodes: {},
|
||||
startNodes: [],
|
||||
@ -204,6 +205,7 @@ export class KclManager {
|
||||
body: [],
|
||||
start: 0,
|
||||
end: 0,
|
||||
moduleId: 0,
|
||||
nonCodeMeta: {
|
||||
nonCodeNodes: {},
|
||||
startNodes: [],
|
||||
|
@ -1903,6 +1903,6 @@ describe('parsing errors', () => {
|
||||
const error = result as KCLError
|
||||
expect(error.kind).toBe('syntax')
|
||||
expect(error.msg).toBe('Unexpected token: (')
|
||||
expect(error.sourceRanges).toEqual([[27, 28]])
|
||||
expect(error.sourceRanges).toEqual([[27, 28, 0]])
|
||||
})
|
||||
})
|
||||
|
@ -19,7 +19,7 @@ const mySketch001 = startSketchOn('XY')
|
||||
const sketch001 = execState.memory.get('mySketch001')
|
||||
expect(sketch001).toEqual({
|
||||
type: 'UserVal',
|
||||
__meta: [{ sourceRange: [46, 71] }],
|
||||
__meta: [{ sourceRange: [46, 71, 0] }],
|
||||
value: {
|
||||
type: 'Sketch',
|
||||
on: expect.any(Object),
|
||||
@ -29,7 +29,7 @@ const mySketch001 = startSketchOn('XY')
|
||||
tag: null,
|
||||
__geoMeta: {
|
||||
id: expect.any(String),
|
||||
sourceRange: [46, 71],
|
||||
sourceRange: [46, 71, 0],
|
||||
},
|
||||
},
|
||||
paths: [
|
||||
@ -39,7 +39,7 @@ const mySketch001 = startSketchOn('XY')
|
||||
to: [-1.59, -1.54],
|
||||
from: [0, 0],
|
||||
__geoMeta: {
|
||||
sourceRange: [77, 102],
|
||||
sourceRange: [77, 102, 0],
|
||||
id: expect.any(String),
|
||||
},
|
||||
},
|
||||
@ -49,13 +49,13 @@ const mySketch001 = startSketchOn('XY')
|
||||
from: [-1.59, -1.54],
|
||||
tag: null,
|
||||
__geoMeta: {
|
||||
sourceRange: [108, 132],
|
||||
sourceRange: [108, 132, 0],
|
||||
id: expect.any(String),
|
||||
},
|
||||
},
|
||||
],
|
||||
id: expect.any(String),
|
||||
__meta: [{ sourceRange: [46, 71] }],
|
||||
__meta: [{ sourceRange: [46, 71, 0] }],
|
||||
},
|
||||
})
|
||||
})
|
||||
@ -80,14 +80,14 @@ const mySketch001 = startSketchOn('XY')
|
||||
faceId: expect.any(String),
|
||||
tag: null,
|
||||
id: expect.any(String),
|
||||
sourceRange: [77, 102],
|
||||
sourceRange: [77, 102, 0],
|
||||
},
|
||||
{
|
||||
type: 'extrudePlane',
|
||||
faceId: expect.any(String),
|
||||
tag: null,
|
||||
id: expect.any(String),
|
||||
sourceRange: [108, 132],
|
||||
sourceRange: [108, 132, 0],
|
||||
},
|
||||
],
|
||||
sketch: {
|
||||
@ -104,7 +104,7 @@ const mySketch001 = startSketchOn('XY')
|
||||
tag: null,
|
||||
__geoMeta: {
|
||||
id: expect.any(String),
|
||||
sourceRange: [77, 102],
|
||||
sourceRange: [77, 102, 0],
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -114,7 +114,7 @@ const mySketch001 = startSketchOn('XY')
|
||||
tag: null,
|
||||
__geoMeta: {
|
||||
id: expect.any(String),
|
||||
sourceRange: [108, 132],
|
||||
sourceRange: [108, 132, 0],
|
||||
},
|
||||
},
|
||||
],
|
||||
@ -122,7 +122,7 @@ const mySketch001 = startSketchOn('XY')
|
||||
height: 2,
|
||||
startCapId: expect.any(String),
|
||||
endCapId: expect.any(String),
|
||||
__meta: [{ sourceRange: [46, 71] }],
|
||||
__meta: [{ sourceRange: [46, 71, 0] }],
|
||||
})
|
||||
})
|
||||
test('sketch extrude and sketch on one of the faces', async () => {
|
||||
@ -162,7 +162,7 @@ const sk2 = startSketchOn('XY')
|
||||
faceId: expect.any(String),
|
||||
tag: null,
|
||||
id: expect.any(String),
|
||||
sourceRange: [69, 89],
|
||||
sourceRange: [69, 89, 0],
|
||||
},
|
||||
{
|
||||
type: 'extrudePlane',
|
||||
@ -174,14 +174,14 @@ const sk2 = startSketchOn('XY')
|
||||
value: 'p',
|
||||
},
|
||||
id: expect.any(String),
|
||||
sourceRange: [95, 117],
|
||||
sourceRange: [95, 117, 0],
|
||||
},
|
||||
{
|
||||
type: 'extrudePlane',
|
||||
faceId: expect.any(String),
|
||||
tag: null,
|
||||
id: expect.any(String),
|
||||
sourceRange: [123, 142],
|
||||
sourceRange: [123, 142, 0],
|
||||
},
|
||||
],
|
||||
sketch: {
|
||||
@ -194,7 +194,7 @@ const sk2 = startSketchOn('XY')
|
||||
p: {
|
||||
__meta: [
|
||||
{
|
||||
sourceRange: [114, 116],
|
||||
sourceRange: [114, 116, 0],
|
||||
},
|
||||
],
|
||||
type: 'TagIdentifier',
|
||||
@ -210,7 +210,7 @@ const sk2 = startSketchOn('XY')
|
||||
tag: null,
|
||||
__geoMeta: {
|
||||
id: expect.any(String),
|
||||
sourceRange: [69, 89],
|
||||
sourceRange: [69, 89, 0],
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -225,7 +225,7 @@ const sk2 = startSketchOn('XY')
|
||||
},
|
||||
__geoMeta: {
|
||||
id: expect.any(String),
|
||||
sourceRange: [95, 117],
|
||||
sourceRange: [95, 117, 0],
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -235,7 +235,7 @@ const sk2 = startSketchOn('XY')
|
||||
tag: null,
|
||||
__geoMeta: {
|
||||
id: expect.any(String),
|
||||
sourceRange: [123, 142],
|
||||
sourceRange: [123, 142, 0],
|
||||
},
|
||||
},
|
||||
],
|
||||
@ -243,7 +243,7 @@ const sk2 = startSketchOn('XY')
|
||||
height: 2,
|
||||
startCapId: expect.any(String),
|
||||
endCapId: expect.any(String),
|
||||
__meta: [{ sourceRange: [38, 63] }],
|
||||
__meta: [{ sourceRange: [38, 63, 0] }],
|
||||
},
|
||||
{
|
||||
type: 'Solid',
|
||||
@ -254,7 +254,7 @@ const sk2 = startSketchOn('XY')
|
||||
faceId: expect.any(String),
|
||||
tag: null,
|
||||
id: expect.any(String),
|
||||
sourceRange: [373, 393],
|
||||
sourceRange: [373, 393, 0],
|
||||
},
|
||||
{
|
||||
type: 'extrudePlane',
|
||||
@ -266,14 +266,14 @@ const sk2 = startSketchOn('XY')
|
||||
value: 'o',
|
||||
},
|
||||
id: expect.any(String),
|
||||
sourceRange: [399, 420],
|
||||
sourceRange: [399, 420, 0],
|
||||
},
|
||||
{
|
||||
type: 'extrudePlane',
|
||||
faceId: expect.any(String),
|
||||
tag: null,
|
||||
id: expect.any(String),
|
||||
sourceRange: [426, 445],
|
||||
sourceRange: [426, 445, 0],
|
||||
},
|
||||
],
|
||||
sketch: {
|
||||
@ -286,7 +286,7 @@ const sk2 = startSketchOn('XY')
|
||||
o: {
|
||||
__meta: [
|
||||
{
|
||||
sourceRange: [417, 419],
|
||||
sourceRange: [417, 419, 0],
|
||||
},
|
||||
],
|
||||
type: 'TagIdentifier',
|
||||
@ -302,7 +302,7 @@ const sk2 = startSketchOn('XY')
|
||||
tag: null,
|
||||
__geoMeta: {
|
||||
id: expect.any(String),
|
||||
sourceRange: [373, 393],
|
||||
sourceRange: [373, 393, 0],
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -317,7 +317,7 @@ const sk2 = startSketchOn('XY')
|
||||
},
|
||||
__geoMeta: {
|
||||
id: expect.any(String),
|
||||
sourceRange: [399, 420],
|
||||
sourceRange: [399, 420, 0],
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -327,7 +327,7 @@ const sk2 = startSketchOn('XY')
|
||||
tag: null,
|
||||
__geoMeta: {
|
||||
id: expect.any(String),
|
||||
sourceRange: [426, 445],
|
||||
sourceRange: [426, 445, 0],
|
||||
},
|
||||
},
|
||||
],
|
||||
@ -335,7 +335,7 @@ const sk2 = startSketchOn('XY')
|
||||
height: 2,
|
||||
startCapId: expect.any(String),
|
||||
endCapId: expect.any(String),
|
||||
__meta: [{ sourceRange: [342, 367] }],
|
||||
__meta: [{ sourceRange: [342, 367, 0] }],
|
||||
},
|
||||
])
|
||||
})
|
||||
|
@ -9,8 +9,8 @@ describe('test kclErrToDiagnostic', () => {
|
||||
kind: 'semantic',
|
||||
msg: 'Semantic error',
|
||||
sourceRanges: [
|
||||
[0, 1],
|
||||
[2, 3],
|
||||
[0, 1, 0],
|
||||
[2, 3, 0],
|
||||
],
|
||||
},
|
||||
{
|
||||
@ -19,8 +19,8 @@ describe('test kclErrToDiagnostic', () => {
|
||||
kind: 'type',
|
||||
msg: 'Type error',
|
||||
sourceRanges: [
|
||||
[4, 5],
|
||||
[6, 7],
|
||||
[4, 5, 0],
|
||||
[6, 7, 0],
|
||||
],
|
||||
},
|
||||
]
|
||||
|
@ -4,15 +4,17 @@ import { posToOffset } from '@kittycad/codemirror-lsp-client'
|
||||
import { Diagnostic as LspDiagnostic } from 'vscode-languageserver-protocol'
|
||||
import { Text } from '@codemirror/state'
|
||||
|
||||
const TOP_LEVEL_MODULE_ID = 0
|
||||
|
||||
type ExtractKind<T> = T extends { kind: infer K } ? K : never
|
||||
export class KCLError extends Error {
|
||||
kind: ExtractKind<RustKclError> | 'name'
|
||||
sourceRanges: [number, number][]
|
||||
sourceRanges: [number, number, number][]
|
||||
msg: string
|
||||
constructor(
|
||||
kind: ExtractKind<RustKclError> | 'name',
|
||||
msg: string,
|
||||
sourceRanges: [number, number][]
|
||||
sourceRanges: [number, number, number][]
|
||||
) {
|
||||
super()
|
||||
this.kind = kind
|
||||
@ -23,63 +25,63 @@ export class KCLError extends Error {
|
||||
}
|
||||
|
||||
export class KCLLexicalError extends KCLError {
|
||||
constructor(msg: string, sourceRanges: [number, number][]) {
|
||||
constructor(msg: string, sourceRanges: [number, number, number][]) {
|
||||
super('lexical', msg, sourceRanges)
|
||||
Object.setPrototypeOf(this, KCLSyntaxError.prototype)
|
||||
}
|
||||
}
|
||||
|
||||
export class KCLInternalError extends KCLError {
|
||||
constructor(msg: string, sourceRanges: [number, number][]) {
|
||||
constructor(msg: string, sourceRanges: [number, number, number][]) {
|
||||
super('internal', msg, sourceRanges)
|
||||
Object.setPrototypeOf(this, KCLSyntaxError.prototype)
|
||||
}
|
||||
}
|
||||
|
||||
export class KCLSyntaxError extends KCLError {
|
||||
constructor(msg: string, sourceRanges: [number, number][]) {
|
||||
constructor(msg: string, sourceRanges: [number, number, number][]) {
|
||||
super('syntax', msg, sourceRanges)
|
||||
Object.setPrototypeOf(this, KCLSyntaxError.prototype)
|
||||
}
|
||||
}
|
||||
|
||||
export class KCLSemanticError extends KCLError {
|
||||
constructor(msg: string, sourceRanges: [number, number][]) {
|
||||
constructor(msg: string, sourceRanges: [number, number, number][]) {
|
||||
super('semantic', msg, sourceRanges)
|
||||
Object.setPrototypeOf(this, KCLSemanticError.prototype)
|
||||
}
|
||||
}
|
||||
|
||||
export class KCLTypeError extends KCLError {
|
||||
constructor(msg: string, sourceRanges: [number, number][]) {
|
||||
constructor(msg: string, sourceRanges: [number, number, number][]) {
|
||||
super('type', msg, sourceRanges)
|
||||
Object.setPrototypeOf(this, KCLTypeError.prototype)
|
||||
}
|
||||
}
|
||||
|
||||
export class KCLUnimplementedError extends KCLError {
|
||||
constructor(msg: string, sourceRanges: [number, number][]) {
|
||||
constructor(msg: string, sourceRanges: [number, number, number][]) {
|
||||
super('unimplemented', msg, sourceRanges)
|
||||
Object.setPrototypeOf(this, KCLUnimplementedError.prototype)
|
||||
}
|
||||
}
|
||||
|
||||
export class KCLUnexpectedError extends KCLError {
|
||||
constructor(msg: string, sourceRanges: [number, number][]) {
|
||||
constructor(msg: string, sourceRanges: [number, number, number][]) {
|
||||
super('unexpected', msg, sourceRanges)
|
||||
Object.setPrototypeOf(this, KCLUnexpectedError.prototype)
|
||||
}
|
||||
}
|
||||
|
||||
export class KCLValueAlreadyDefined extends KCLError {
|
||||
constructor(key: string, sourceRanges: [number, number][]) {
|
||||
constructor(key: string, sourceRanges: [number, number, number][]) {
|
||||
super('name', `Key ${key} was already defined elsewhere`, sourceRanges)
|
||||
Object.setPrototypeOf(this, KCLValueAlreadyDefined.prototype)
|
||||
}
|
||||
}
|
||||
|
||||
export class KCLUndefinedValueError extends KCLError {
|
||||
constructor(key: string, sourceRanges: [number, number][]) {
|
||||
constructor(key: string, sourceRanges: [number, number, number][]) {
|
||||
super('name', `Key ${key} has not been defined`, sourceRanges)
|
||||
Object.setPrototypeOf(this, KCLUndefinedValueError.prototype)
|
||||
}
|
||||
@ -97,13 +99,22 @@ export function lspDiagnosticsToKclErrors(
|
||||
.flatMap(
|
||||
({ range, message }) =>
|
||||
new KCLError('unexpected', message, [
|
||||
[posToOffset(doc, range.start)!, posToOffset(doc, range.end)!],
|
||||
[
|
||||
posToOffset(doc, range.start)!,
|
||||
posToOffset(doc, range.end)!,
|
||||
TOP_LEVEL_MODULE_ID,
|
||||
],
|
||||
])
|
||||
)
|
||||
.filter(({ sourceRanges }) => {
|
||||
const [from, to] = sourceRanges[0]
|
||||
const [from, to, moduleId] = sourceRanges[0]
|
||||
return (
|
||||
from !== null && to !== null && from !== undefined && to !== undefined
|
||||
from !== null &&
|
||||
to !== null &&
|
||||
from !== undefined &&
|
||||
to !== undefined &&
|
||||
// Filter out errors that are not from the top-level module.
|
||||
moduleId === TOP_LEVEL_MODULE_ID
|
||||
)
|
||||
})
|
||||
.sort((a, b) => {
|
||||
@ -127,8 +138,16 @@ export function kclErrorsToDiagnostics(
|
||||
errors: KCLError[]
|
||||
): CodeMirrorDiagnostic[] {
|
||||
return errors?.flatMap((err) => {
|
||||
return err.sourceRanges.map(([from, to]) => {
|
||||
return { from, to, message: err.msg, severity: 'error' }
|
||||
})
|
||||
const sourceRanges: CodeMirrorDiagnostic[] = err.sourceRanges
|
||||
// Filter out errors that are not from the top-level module.
|
||||
.filter(([_start, _end, moduleId]) => moduleId === TOP_LEVEL_MODULE_ID)
|
||||
.map(([from, to]) => {
|
||||
return { from, to, message: err.msg, severity: 'error' }
|
||||
})
|
||||
// Make sure we didn't filter out all the source ranges.
|
||||
if (sourceRanges.length === 0) {
|
||||
sourceRanges.push({ from: 0, to: 0, message: err.msg, severity: 'error' })
|
||||
}
|
||||
return sourceRanges
|
||||
})
|
||||
}
|
||||
|
@ -65,7 +65,7 @@ const newVar = myVar + 1`
|
||||
to: [0, 2],
|
||||
from: [0, 0],
|
||||
__geoMeta: {
|
||||
sourceRange: [72, 97],
|
||||
sourceRange: [72, 97, 0],
|
||||
id: expect.any(String),
|
||||
},
|
||||
tag: {
|
||||
@ -81,7 +81,7 @@ const newVar = myVar + 1`
|
||||
from: [0, 2],
|
||||
tag: null,
|
||||
__geoMeta: {
|
||||
sourceRange: [103, 119],
|
||||
sourceRange: [103, 119, 0],
|
||||
id: expect.any(String),
|
||||
},
|
||||
},
|
||||
@ -90,7 +90,7 @@ const newVar = myVar + 1`
|
||||
to: [5, -1],
|
||||
from: [2, 3],
|
||||
__geoMeta: {
|
||||
sourceRange: [125, 154],
|
||||
sourceRange: [125, 154, 0],
|
||||
id: expect.any(String),
|
||||
},
|
||||
tag: {
|
||||
@ -160,14 +160,14 @@ const newVar = myVar + 1`
|
||||
tag: null,
|
||||
__geoMeta: {
|
||||
id: expect.any(String),
|
||||
sourceRange: [39, 63],
|
||||
sourceRange: [39, 63, 0],
|
||||
},
|
||||
},
|
||||
tags: {
|
||||
myPath: {
|
||||
__meta: [
|
||||
{
|
||||
sourceRange: [109, 116],
|
||||
sourceRange: [109, 116, 0],
|
||||
},
|
||||
],
|
||||
type: 'TagIdentifier',
|
||||
@ -182,7 +182,7 @@ const newVar = myVar + 1`
|
||||
from: [0, 0],
|
||||
tag: null,
|
||||
__geoMeta: {
|
||||
sourceRange: [69, 85],
|
||||
sourceRange: [69, 85, 0],
|
||||
id: expect.any(String),
|
||||
},
|
||||
},
|
||||
@ -191,7 +191,7 @@ const newVar = myVar + 1`
|
||||
to: [0, 1],
|
||||
from: [1, 1],
|
||||
__geoMeta: {
|
||||
sourceRange: [91, 117],
|
||||
sourceRange: [91, 117, 0],
|
||||
id: expect.any(String),
|
||||
},
|
||||
tag: {
|
||||
@ -207,15 +207,15 @@ const newVar = myVar + 1`
|
||||
from: [0, 1],
|
||||
tag: null,
|
||||
__geoMeta: {
|
||||
sourceRange: [123, 139],
|
||||
sourceRange: [123, 139, 0],
|
||||
id: expect.any(String),
|
||||
},
|
||||
},
|
||||
],
|
||||
id: expect.any(String),
|
||||
__meta: [{ sourceRange: [39, 63] }],
|
||||
__meta: [{ sourceRange: [39, 63, 0] }],
|
||||
},
|
||||
__meta: [{ sourceRange: [39, 63] }],
|
||||
__meta: [{ sourceRange: [39, 63, 0] }],
|
||||
})
|
||||
})
|
||||
it('execute array expression', async () => {
|
||||
@ -229,7 +229,7 @@ const newVar = myVar + 1`
|
||||
value: 3,
|
||||
__meta: [
|
||||
{
|
||||
sourceRange: [14, 15],
|
||||
sourceRange: [14, 15, 0],
|
||||
},
|
||||
],
|
||||
})
|
||||
@ -238,7 +238,7 @@ const newVar = myVar + 1`
|
||||
value: [1, '2', 3, 9],
|
||||
__meta: [
|
||||
{
|
||||
sourceRange: [27, 49],
|
||||
sourceRange: [27, 49, 0],
|
||||
},
|
||||
],
|
||||
})
|
||||
@ -257,7 +257,7 @@ const newVar = myVar + 1`
|
||||
value: { aStr: 'str', anum: 2, identifier: 3, binExp: 9 },
|
||||
__meta: [
|
||||
{
|
||||
sourceRange: [27, 83],
|
||||
sourceRange: [27, 83, 0],
|
||||
},
|
||||
],
|
||||
})
|
||||
@ -272,7 +272,7 @@ const newVar = myVar + 1`
|
||||
value: '123',
|
||||
__meta: [
|
||||
{
|
||||
sourceRange: [41, 50],
|
||||
sourceRange: [41, 50, 0],
|
||||
},
|
||||
],
|
||||
})
|
||||
@ -426,7 +426,7 @@ const theExtrude = startSketchOn('XY')
|
||||
new KCLError(
|
||||
'undefined_value',
|
||||
'memory item key `myVarZ` is not defined',
|
||||
[[129, 135]]
|
||||
[[129, 135, 0]]
|
||||
)
|
||||
)
|
||||
})
|
||||
|
87
src/lang/kclSamples.test.ts
Normal file
@ -0,0 +1,87 @@
|
||||
import { parse, initPromise, programMemoryInit } from './wasm'
|
||||
import { enginelessExecutor } from '../lib/testHelpers'
|
||||
import { assert } from 'vitest'
|
||||
// These unit tests makes web requests to a public github repository.
|
||||
|
||||
interface KclSampleFile {
|
||||
file: string
|
||||
title: string
|
||||
filename: string
|
||||
description: string
|
||||
}
|
||||
|
||||
beforeAll(async () => {
|
||||
await initPromise
|
||||
})
|
||||
|
||||
// Only used to actually fetch an older version of KCL code that will break in the parser.
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
async function getBrokenSampleCodeForLocalTesting() {
|
||||
const result = await fetch(
|
||||
'https://raw.githubusercontent.com/KittyCAD/kcl-samples/5ccd04a1773ebdbfd02684057917ce5dbe0eaab3/80-20-rail.kcl'
|
||||
)
|
||||
const text = await result.text()
|
||||
return text
|
||||
}
|
||||
|
||||
async function getKclSampleCodeFromGithub(file: string): Promise<string> {
|
||||
const result = await fetch(
|
||||
`https://raw.githubusercontent.com/KittyCAD/kcl-samples/refs/heads/main/${file}/${file}.kcl`
|
||||
)
|
||||
const text = await result.text()
|
||||
return text
|
||||
}
|
||||
|
||||
async function getFileNamesFromManifestJSON(): Promise<KclSampleFile[]> {
|
||||
const result = await fetch(
|
||||
'https://raw.githubusercontent.com/KittyCAD/kcl-samples/refs/heads/main/manifest.json'
|
||||
)
|
||||
const json = await result.json()
|
||||
json.forEach((file: KclSampleFile) => {
|
||||
const filenameWithoutExtension = file.file.split('.')[0]
|
||||
file.filename = filenameWithoutExtension
|
||||
})
|
||||
return json
|
||||
}
|
||||
|
||||
// Value to use across all tests!
|
||||
let files: KclSampleFile[] = []
|
||||
|
||||
describe('Test KCL Samples from public Github repository', () => {
|
||||
describe('When parsing source code', () => {
|
||||
// THIS RUNS ACROSS OTHER TESTS!
|
||||
it('should fetch files', async () => {
|
||||
files = await getFileNamesFromManifestJSON()
|
||||
})
|
||||
// Run through all of the files in the manifest json. This will allow us to be automatically updated
|
||||
// with the latest changes in github. We won't be hard coding the filenames
|
||||
it(
|
||||
'should run through all the files',
|
||||
async () => {
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
const file: KclSampleFile = files[i]
|
||||
const code = await getKclSampleCodeFromGithub(file.filename)
|
||||
const parsed = parse(code)
|
||||
assert(!(parsed instanceof Error))
|
||||
}
|
||||
},
|
||||
files.length * 1000
|
||||
)
|
||||
})
|
||||
|
||||
describe('when performing enginelessExecutor', () => {
|
||||
it(
|
||||
'should run through all the files',
|
||||
async () => {
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
const file: KclSampleFile = files[i]
|
||||
const code = await getKclSampleCodeFromGithub(file.filename)
|
||||
const parsed = parse(code)
|
||||
assert(!(parsed instanceof Error))
|
||||
await enginelessExecutor(parsed, programMemoryInit())
|
||||
}
|
||||
},
|
||||
files.length * 1000
|
||||
)
|
||||
})
|
||||
})
|
@ -101,15 +101,15 @@ describe('Testing findUniqueName', () => {
|
||||
it('should find a unique name', () => {
|
||||
const result = findUniqueName(
|
||||
JSON.stringify([
|
||||
{ type: 'Identifier', name: 'yo01', start: 0, end: 0 },
|
||||
{ type: 'Identifier', name: 'yo02', start: 0, end: 0 },
|
||||
{ type: 'Identifier', name: 'yo03', start: 0, end: 0 },
|
||||
{ type: 'Identifier', name: 'yo04', start: 0, end: 0 },
|
||||
{ type: 'Identifier', name: 'yo05', start: 0, end: 0 },
|
||||
{ type: 'Identifier', name: 'yo06', start: 0, end: 0 },
|
||||
{ type: 'Identifier', name: 'yo07', start: 0, end: 0 },
|
||||
{ type: 'Identifier', name: 'yo08', start: 0, end: 0 },
|
||||
{ type: 'Identifier', name: 'yo09', start: 0, end: 0 },
|
||||
{ type: 'Identifier', name: 'yo01', start: 0, end: 0, moduleId: 0 },
|
||||
{ type: 'Identifier', name: 'yo02', start: 0, end: 0, moduleId: 0 },
|
||||
{ type: 'Identifier', name: 'yo03', start: 0, end: 0, moduleId: 0 },
|
||||
{ type: 'Identifier', name: 'yo04', start: 0, end: 0, moduleId: 0 },
|
||||
{ type: 'Identifier', name: 'yo05', start: 0, end: 0, moduleId: 0 },
|
||||
{ type: 'Identifier', name: 'yo06', start: 0, end: 0, moduleId: 0 },
|
||||
{ type: 'Identifier', name: 'yo07', start: 0, end: 0, moduleId: 0 },
|
||||
{ type: 'Identifier', name: 'yo08', start: 0, end: 0, moduleId: 0 },
|
||||
{ type: 'Identifier', name: 'yo09', start: 0, end: 0, moduleId: 0 },
|
||||
] satisfies Node<Identifier>[]),
|
||||
'yo',
|
||||
2
|
||||
@ -124,6 +124,7 @@ describe('Testing addSketchTo', () => {
|
||||
body: [],
|
||||
start: 0,
|
||||
end: 0,
|
||||
moduleId: 0,
|
||||
nonCodeMeta: { nonCodeNodes: {}, startNodes: [] },
|
||||
},
|
||||
'yz'
|
||||
|
@ -242,6 +242,7 @@ export function mutateObjExpProp(
|
||||
value: updateWith,
|
||||
start: 0,
|
||||
end: 0,
|
||||
moduleId: 0,
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -577,6 +578,7 @@ export function createLiteral(value: string | number): Node<Literal> {
|
||||
type: 'Literal',
|
||||
start: 0,
|
||||
end: 0,
|
||||
moduleId: 0,
|
||||
value,
|
||||
raw: `${value}`,
|
||||
}
|
||||
@ -587,6 +589,7 @@ export function createTagDeclarator(value: string): Node<TagDeclarator> {
|
||||
type: 'TagDeclarator',
|
||||
start: 0,
|
||||
end: 0,
|
||||
moduleId: 0,
|
||||
|
||||
value,
|
||||
}
|
||||
@ -597,6 +600,7 @@ export function createIdentifier(name: string): Node<Identifier> {
|
||||
type: 'Identifier',
|
||||
start: 0,
|
||||
end: 0,
|
||||
moduleId: 0,
|
||||
|
||||
name,
|
||||
}
|
||||
@ -607,6 +611,7 @@ export function createPipeSubstitution(): Node<PipeSubstitution> {
|
||||
type: 'PipeSubstitution',
|
||||
start: 0,
|
||||
end: 0,
|
||||
moduleId: 0,
|
||||
}
|
||||
}
|
||||
|
||||
@ -618,10 +623,12 @@ export function createCallExpressionStdLib(
|
||||
type: 'CallExpression',
|
||||
start: 0,
|
||||
end: 0,
|
||||
moduleId: 0,
|
||||
callee: {
|
||||
type: 'Identifier',
|
||||
start: 0,
|
||||
end: 0,
|
||||
moduleId: 0,
|
||||
|
||||
name,
|
||||
},
|
||||
@ -638,10 +645,12 @@ export function createCallExpression(
|
||||
type: 'CallExpression',
|
||||
start: 0,
|
||||
end: 0,
|
||||
moduleId: 0,
|
||||
callee: {
|
||||
type: 'Identifier',
|
||||
start: 0,
|
||||
end: 0,
|
||||
moduleId: 0,
|
||||
|
||||
name,
|
||||
},
|
||||
@ -657,6 +666,7 @@ export function createArrayExpression(
|
||||
type: 'ArrayExpression',
|
||||
start: 0,
|
||||
end: 0,
|
||||
moduleId: 0,
|
||||
|
||||
nonCodeMeta: nonCodeMetaEmpty(),
|
||||
elements,
|
||||
@ -670,6 +680,7 @@ export function createPipeExpression(
|
||||
type: 'PipeExpression',
|
||||
start: 0,
|
||||
end: 0,
|
||||
moduleId: 0,
|
||||
|
||||
body,
|
||||
nonCodeMeta: nonCodeMetaEmpty(),
|
||||
@ -686,12 +697,14 @@ export function createVariableDeclaration(
|
||||
type: 'VariableDeclaration',
|
||||
start: 0,
|
||||
end: 0,
|
||||
moduleId: 0,
|
||||
|
||||
declarations: [
|
||||
{
|
||||
type: 'VariableDeclarator',
|
||||
start: 0,
|
||||
end: 0,
|
||||
moduleId: 0,
|
||||
|
||||
id: createIdentifier(varName),
|
||||
init,
|
||||
@ -709,12 +722,14 @@ export function createObjectExpression(properties: {
|
||||
type: 'ObjectExpression',
|
||||
start: 0,
|
||||
end: 0,
|
||||
moduleId: 0,
|
||||
|
||||
nonCodeMeta: nonCodeMetaEmpty(),
|
||||
properties: Object.entries(properties).map(([key, value]) => ({
|
||||
type: 'ObjectProperty',
|
||||
start: 0,
|
||||
end: 0,
|
||||
moduleId: 0,
|
||||
key: createIdentifier(key),
|
||||
|
||||
value,
|
||||
@ -730,6 +745,7 @@ export function createUnaryExpression(
|
||||
type: 'UnaryExpression',
|
||||
start: 0,
|
||||
end: 0,
|
||||
moduleId: 0,
|
||||
|
||||
operator,
|
||||
argument,
|
||||
@ -745,6 +761,7 @@ export function createBinaryExpression([left, operator, right]: [
|
||||
type: 'BinaryExpression',
|
||||
start: 0,
|
||||
end: 0,
|
||||
moduleId: 0,
|
||||
|
||||
operator,
|
||||
left,
|
||||
|
@ -13,6 +13,7 @@ Map {
|
||||
"range": [
|
||||
37,
|
||||
64,
|
||||
0,
|
||||
],
|
||||
},
|
||||
"pathIds": [
|
||||
@ -31,6 +32,7 @@ Map {
|
||||
"range": [
|
||||
37,
|
||||
64,
|
||||
0,
|
||||
],
|
||||
},
|
||||
"planeId": "UUID",
|
||||
@ -56,6 +58,7 @@ Map {
|
||||
"range": [
|
||||
70,
|
||||
86,
|
||||
0,
|
||||
],
|
||||
},
|
||||
"edgeIds": [
|
||||
@ -77,6 +80,7 @@ Map {
|
||||
"range": [
|
||||
92,
|
||||
119,
|
||||
0,
|
||||
],
|
||||
},
|
||||
"edgeCutId": "UUID",
|
||||
@ -99,6 +103,7 @@ Map {
|
||||
"range": [
|
||||
125,
|
||||
150,
|
||||
0,
|
||||
],
|
||||
},
|
||||
"edgeIds": [
|
||||
@ -120,6 +125,7 @@ Map {
|
||||
"range": [
|
||||
156,
|
||||
203,
|
||||
0,
|
||||
],
|
||||
},
|
||||
"edgeIds": [
|
||||
@ -141,6 +147,7 @@ Map {
|
||||
"range": [
|
||||
209,
|
||||
217,
|
||||
0,
|
||||
],
|
||||
},
|
||||
"edgeIds": [],
|
||||
@ -162,6 +169,7 @@ Map {
|
||||
"range": [
|
||||
231,
|
||||
254,
|
||||
0,
|
||||
],
|
||||
},
|
||||
"edgeIds": [
|
||||
@ -289,6 +297,7 @@ Map {
|
||||
"range": [
|
||||
260,
|
||||
299,
|
||||
0,
|
||||
],
|
||||
},
|
||||
"consumedEdgeId": "UUID",
|
||||
@ -307,6 +316,7 @@ Map {
|
||||
"range": [
|
||||
350,
|
||||
377,
|
||||
0,
|
||||
],
|
||||
},
|
||||
"planeId": "UUID",
|
||||
@ -331,6 +341,7 @@ Map {
|
||||
"range": [
|
||||
383,
|
||||
398,
|
||||
0,
|
||||
],
|
||||
},
|
||||
"edgeIds": [
|
||||
@ -352,6 +363,7 @@ Map {
|
||||
"range": [
|
||||
404,
|
||||
420,
|
||||
0,
|
||||
],
|
||||
},
|
||||
"edgeIds": [
|
||||
@ -373,6 +385,7 @@ Map {
|
||||
"range": [
|
||||
426,
|
||||
473,
|
||||
0,
|
||||
],
|
||||
},
|
||||
"edgeIds": [
|
||||
@ -394,6 +407,7 @@ Map {
|
||||
"range": [
|
||||
479,
|
||||
487,
|
||||
0,
|
||||
],
|
||||
},
|
||||
"edgeIds": [],
|
||||
@ -415,6 +429,7 @@ Map {
|
||||
"range": [
|
||||
501,
|
||||
522,
|
||||
0,
|
||||
],
|
||||
},
|
||||
"edgeIds": [
|
||||
|
@ -49,6 +49,26 @@ sketch002 = startSketchOn(extrude001, seg02)
|
||||
extrude002 = extrude(5, sketch002)
|
||||
`
|
||||
|
||||
const exampleCodeNo3D = `sketch003 = startSketchOn('YZ')
|
||||
|> startProfileAt([5.82, 0], %)
|
||||
|> angledLine([180, 11.54], %, $rectangleSegmentA001)
|
||||
|> angledLine([
|
||||
segAng(rectangleSegmentA001) - 90,
|
||||
8.21
|
||||
], %, $rectangleSegmentB001)
|
||||
|> angledLine([
|
||||
segAng(rectangleSegmentA001),
|
||||
-segLen(rectangleSegmentA001)
|
||||
], %, $rectangleSegmentC001)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)
|
||||
sketch004 = startSketchOn('-XZ')
|
||||
|> startProfileAt([0, 14.36], %)
|
||||
|> line([15.49, 0.05], %)
|
||||
|> tangentialArcTo([0, 0], %)
|
||||
|> tangentialArcTo([-6.8, 8.17], %)
|
||||
`
|
||||
|
||||
const sketchOnFaceOnFaceEtc = `sketch001 = startSketchOn('XZ')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> line([4, 8], %)
|
||||
@ -83,6 +103,7 @@ extrude004 = extrude(3, sketch004)
|
||||
const codeToWriteCacheFor = {
|
||||
exampleCode1,
|
||||
sketchOnFaceOnFaceEtc,
|
||||
exampleCodeNo3D,
|
||||
} as const
|
||||
|
||||
type CodeKey = keyof typeof codeToWriteCacheFor
|
||||
@ -236,6 +257,69 @@ describe('testing createArtifactGraph', () => {
|
||||
await GraphTheGraph(theMap, 2000, 2000, 'exampleCode1.png')
|
||||
}, 20000)
|
||||
})
|
||||
|
||||
describe(`code with sketches but no extrusions or other 3D elements`, () => {
|
||||
let ast: Program
|
||||
let theMap: ReturnType<typeof createArtifactGraph>
|
||||
it(`setup`, () => {
|
||||
// putting this logic in here because describe blocks runs before beforeAll has finished
|
||||
const {
|
||||
orderedCommands,
|
||||
responseMap,
|
||||
ast: _ast,
|
||||
} = getCommands('exampleCodeNo3D')
|
||||
ast = _ast
|
||||
theMap = createArtifactGraph({ orderedCommands, responseMap, ast })
|
||||
})
|
||||
|
||||
it('there should be two planes, one for each sketch path', () => {
|
||||
const planes = [...filterArtifacts({ types: ['plane'] }, theMap)].map(
|
||||
(plane) => expandPlane(plane[1], theMap)
|
||||
)
|
||||
expect(planes).toHaveLength(2)
|
||||
planes.forEach((path) => {
|
||||
expect(path.type).toBe('plane')
|
||||
})
|
||||
})
|
||||
it('there should be two paths, one on each plane', () => {
|
||||
const paths = [...filterArtifacts({ types: ['path'] }, theMap)].map(
|
||||
(path) => expandPath(path[1], theMap)
|
||||
)
|
||||
expect(paths).toHaveLength(2)
|
||||
paths.forEach((path) => {
|
||||
if (err(path)) throw path
|
||||
expect(path.type).toBe('path')
|
||||
})
|
||||
})
|
||||
|
||||
it(`there should be 1 solid2D, just for the first closed path`, () => {
|
||||
const solid2Ds = [...filterArtifacts({ types: ['solid2D'] }, theMap)]
|
||||
expect(solid2Ds).toHaveLength(1)
|
||||
})
|
||||
|
||||
it('there should be no extrusions', () => {
|
||||
const extrusions = [...filterArtifacts({ types: ['sweep'] }, theMap)].map(
|
||||
(extrusion) => expandSweep(extrusion[1], theMap)
|
||||
)
|
||||
expect(extrusions).toHaveLength(0)
|
||||
})
|
||||
|
||||
it('there should be 8 segments, 4 + 1 (close) from the first sketch and 3 from the second', () => {
|
||||
const segments = [...filterArtifacts({ types: ['segment'] }, theMap)].map(
|
||||
(segment) => expandSegment(segment[1], theMap)
|
||||
)
|
||||
expect(segments).toHaveLength(8)
|
||||
})
|
||||
|
||||
it('screenshot graph', async () => {
|
||||
// Ostensibly this takes a screen shot of the graph of the artifactGraph
|
||||
// but it's it also tests that all of the id links are correct because if one
|
||||
// of the edges refers to a non-existent node, the graph will throw.
|
||||
// further more we can check that each edge is bi-directional, if it's not
|
||||
// by checking the arrow heads going both ways, on the graph.
|
||||
await GraphTheGraph(theMap, 2000, 2000, 'exampleCodeNo3D.png')
|
||||
}, 20000)
|
||||
})
|
||||
})
|
||||
|
||||
describe('capture graph of sketchOnFaceOnFace...', () => {
|
||||
@ -457,7 +541,10 @@ async function GraphTheGraph(
|
||||
`./src/lang/std/artifactMapGraphs/${imageName}`
|
||||
)
|
||||
// chop the top 30 pixels off the image
|
||||
const originalImg = PNG.sync.read(fs.readFileSync(originalImgPath))
|
||||
const originalImgExists = fs.existsSync(originalImgPath)
|
||||
const originalImg = originalImgExists
|
||||
? PNG.sync.read(fs.readFileSync(originalImgPath))
|
||||
: null
|
||||
// const img1Data = new Uint8Array(img1.data)
|
||||
// const img1DataChopped = img1Data.slice(30 * img1.width * 4)
|
||||
// img1.data = Buffer.from(img1DataChopped)
|
||||
@ -468,10 +555,10 @@ async function GraphTheGraph(
|
||||
const newImageDataChopped = newImageData.slice(30 * newImage.width * 4)
|
||||
newImage.data = Buffer.from(newImageDataChopped)
|
||||
|
||||
const { width, height } = originalImg
|
||||
const { width, height } = originalImg ?? newImage
|
||||
const diff = new PNG({ width, height })
|
||||
|
||||
const imageSizeDifferent = originalImg.data.length !== newImage.data.length
|
||||
const imageSizeDifferent = originalImg?.data.length !== newImage.data.length
|
||||
let numDiffPixels = 0
|
||||
if (!imageSizeDifferent) {
|
||||
numDiffPixels = pixelmatch(
|
||||
@ -523,7 +610,7 @@ describe('testing getArtifactsToUpdate', () => {
|
||||
sweepId: '',
|
||||
codeRef: {
|
||||
pathToNode: [['body', '']],
|
||||
range: [37, 64],
|
||||
range: [37, 64, 0],
|
||||
},
|
||||
},
|
||||
])
|
||||
@ -535,7 +622,7 @@ describe('testing getArtifactsToUpdate', () => {
|
||||
surfaceIds: [],
|
||||
edgeIds: [],
|
||||
codeRef: {
|
||||
range: [231, 254],
|
||||
range: [231, 254, 0],
|
||||
pathToNode: [['body', '']],
|
||||
},
|
||||
},
|
||||
@ -545,7 +632,7 @@ describe('testing getArtifactsToUpdate', () => {
|
||||
planeId: expect.any(String),
|
||||
sweepId: expect.any(String),
|
||||
codeRef: {
|
||||
range: [37, 64],
|
||||
range: [37, 64, 0],
|
||||
pathToNode: [['body', '']],
|
||||
},
|
||||
solid2dId: expect.any(String),
|
||||
@ -558,7 +645,7 @@ describe('testing getArtifactsToUpdate', () => {
|
||||
surfaceId: '',
|
||||
edgeIds: [],
|
||||
codeRef: {
|
||||
range: [70, 86],
|
||||
range: [70, 86, 0],
|
||||
pathToNode: [['body', '']],
|
||||
},
|
||||
},
|
||||
@ -568,7 +655,7 @@ describe('testing getArtifactsToUpdate', () => {
|
||||
planeId: expect.any(String),
|
||||
sweepId: expect.any(String),
|
||||
codeRef: {
|
||||
range: [37, 64],
|
||||
range: [37, 64, 0],
|
||||
pathToNode: [['body', '']],
|
||||
},
|
||||
solid2dId: expect.any(String),
|
||||
@ -582,7 +669,7 @@ describe('testing getArtifactsToUpdate', () => {
|
||||
edgeIds: [],
|
||||
surfaceId: '',
|
||||
codeRef: {
|
||||
range: [260, 299],
|
||||
range: [260, 299, 0],
|
||||
pathToNode: [['body', '']],
|
||||
},
|
||||
},
|
||||
@ -592,7 +679,7 @@ describe('testing getArtifactsToUpdate', () => {
|
||||
surfaceId: expect.any(String),
|
||||
edgeIds: expect.any(Array),
|
||||
codeRef: {
|
||||
range: [92, 119],
|
||||
range: [92, 119, 0],
|
||||
pathToNode: [['body', '']],
|
||||
},
|
||||
edgeCutId: expect.any(String),
|
||||
@ -612,7 +699,7 @@ describe('testing getArtifactsToUpdate', () => {
|
||||
surfaceId: expect.any(String),
|
||||
edgeIds: expect.any(Array),
|
||||
codeRef: {
|
||||
range: [156, 203],
|
||||
range: [156, 203, 0],
|
||||
pathToNode: [['body', '']],
|
||||
},
|
||||
},
|
||||
@ -623,7 +710,7 @@ describe('testing getArtifactsToUpdate', () => {
|
||||
surfaceIds: expect.any(Array),
|
||||
edgeIds: expect.any(Array),
|
||||
codeRef: {
|
||||
range: [231, 254],
|
||||
range: [231, 254, 0],
|
||||
pathToNode: [['body', '']],
|
||||
},
|
||||
},
|
||||
@ -640,7 +727,7 @@ describe('testing getArtifactsToUpdate', () => {
|
||||
surfaceId: expect.any(String),
|
||||
edgeIds: expect.any(Array),
|
||||
codeRef: {
|
||||
range: [125, 150],
|
||||
range: [125, 150, 0],
|
||||
pathToNode: [['body', '']],
|
||||
},
|
||||
},
|
||||
@ -651,7 +738,7 @@ describe('testing getArtifactsToUpdate', () => {
|
||||
surfaceIds: expect.any(Array),
|
||||
edgeIds: expect.any(Array),
|
||||
codeRef: {
|
||||
range: [231, 254],
|
||||
range: [231, 254, 0],
|
||||
pathToNode: [['body', '']],
|
||||
},
|
||||
},
|
||||
@ -668,7 +755,7 @@ describe('testing getArtifactsToUpdate', () => {
|
||||
surfaceId: expect.any(String),
|
||||
edgeIds: expect.any(Array),
|
||||
codeRef: {
|
||||
range: [92, 119],
|
||||
range: [92, 119, 0],
|
||||
pathToNode: [['body', '']],
|
||||
},
|
||||
edgeCutId: expect.any(String),
|
||||
@ -680,7 +767,7 @@ describe('testing getArtifactsToUpdate', () => {
|
||||
surfaceIds: expect.any(Array),
|
||||
edgeIds: expect.any(Array),
|
||||
codeRef: {
|
||||
range: [231, 254],
|
||||
range: [231, 254, 0],
|
||||
pathToNode: [['body', '']],
|
||||
},
|
||||
},
|
||||
@ -697,7 +784,7 @@ describe('testing getArtifactsToUpdate', () => {
|
||||
surfaceId: expect.any(String),
|
||||
edgeIds: expect.any(Array),
|
||||
codeRef: {
|
||||
range: [70, 86],
|
||||
range: [70, 86, 0],
|
||||
pathToNode: [['body', '']],
|
||||
},
|
||||
},
|
||||
@ -708,7 +795,7 @@ describe('testing getArtifactsToUpdate', () => {
|
||||
surfaceIds: expect.any(Array),
|
||||
edgeIds: expect.any(Array),
|
||||
codeRef: {
|
||||
range: [231, 254],
|
||||
range: [231, 254, 0],
|
||||
pathToNode: [['body', '']],
|
||||
},
|
||||
},
|
||||
@ -726,7 +813,7 @@ describe('testing getArtifactsToUpdate', () => {
|
||||
surfaceIds: expect.any(Array),
|
||||
edgeIds: expect.any(Array),
|
||||
codeRef: {
|
||||
range: [231, 254],
|
||||
range: [231, 254, 0],
|
||||
pathToNode: [['body', '']],
|
||||
},
|
||||
},
|
||||
@ -744,7 +831,7 @@ describe('testing getArtifactsToUpdate', () => {
|
||||
surfaceIds: expect.any(Array),
|
||||
edgeIds: expect.any(Array),
|
||||
codeRef: {
|
||||
range: [231, 254],
|
||||
range: [231, 254, 0],
|
||||
pathToNode: [['body', '']],
|
||||
},
|
||||
},
|
||||
|
@ -36,9 +36,12 @@ interface solid2D {
|
||||
}
|
||||
export interface PathArtifactRich {
|
||||
type: 'path'
|
||||
/** A path must always lie on a plane */
|
||||
plane: PlaneArtifact | WallArtifact
|
||||
/** A path must always contain 0 or more segments */
|
||||
segments: Array<SegmentArtifact>
|
||||
sweep: SweepArtifact
|
||||
/** A path may not result in a sweep artifact */
|
||||
sweep?: SweepArtifact
|
||||
codeRef: CommonCommandProperties
|
||||
}
|
||||
|
||||
@ -587,13 +590,15 @@ export function expandPath(
|
||||
{ keys: path.segIds, types: ['segment'] },
|
||||
artifactGraph
|
||||
)
|
||||
const sweep = getArtifactOfTypes(
|
||||
{
|
||||
key: path.sweepId,
|
||||
types: ['sweep'],
|
||||
},
|
||||
artifactGraph
|
||||
)
|
||||
const sweep = path.sweepId
|
||||
? getArtifactOfTypes(
|
||||
{
|
||||
key: path.sweepId,
|
||||
types: ['sweep'],
|
||||
},
|
||||
artifactGraph
|
||||
)
|
||||
: undefined
|
||||
const plane = getArtifactOfTypes(
|
||||
{ key: path.planeId, types: ['plane', 'wall'] },
|
||||
artifactGraph
|
||||
|
BIN
src/lang/std/artifactMapGraphs/exampleCodeNo3D.png
Normal file
After Width: | Height: | Size: 92 KiB |
@ -1823,11 +1823,13 @@ export const updateStartProfileAtArgs: SketchLineHelper['updateArgs'] = ({
|
||||
modifiedAst: {
|
||||
start: 0,
|
||||
end: 0,
|
||||
moduleId: 0,
|
||||
body: [],
|
||||
|
||||
nonCodeMeta: {
|
||||
start: 0,
|
||||
end: 0,
|
||||
moduleId: 0,
|
||||
startNodes: [],
|
||||
nonCodeNodes: [],
|
||||
},
|
||||
|
@ -120,8 +120,8 @@ const initialise = async () => {
|
||||
|
||||
export const initPromise = initialise()
|
||||
|
||||
export const rangeTypeFix = (ranges: number[][]): [number, number][] =>
|
||||
ranges.map(([start, end]) => [start, end])
|
||||
export const rangeTypeFix = (ranges: number[][]): [number, number, number][] =>
|
||||
ranges.map(([start, end, moduleId]) => [start, end, moduleId])
|
||||
|
||||
export const parse = (code: string | Error): Node<Program> | Error => {
|
||||
if (err(code)) return code
|
||||
|
@ -5,7 +5,6 @@ import {
|
||||
INDEX_IDENTIFIER,
|
||||
MAX_PADDING,
|
||||
ONBOARDING_PROJECT_NAME,
|
||||
PROJECT_ENTRYPOINT,
|
||||
} from 'lib/constants'
|
||||
import { bracket } from './exampleKcl'
|
||||
import { PATHS } from './paths'
|
||||
@ -22,36 +21,20 @@ export const isHidden = (fileOrDir: FileEntry) =>
|
||||
export const isDir = (fileOrDir: FileEntry) =>
|
||||
'children' in fileOrDir && fileOrDir.children !== undefined
|
||||
|
||||
// Deeply sort the files and directories in a project like VS Code does:
|
||||
// The main.kcl file is always first, then files, then directories
|
||||
// Shallow sort the files and directories
|
||||
// Files and directories are sorted alphabetically
|
||||
export function sortProject(project: FileEntry[]): FileEntry[] {
|
||||
const sortedProject = project.sort((a, b) => {
|
||||
if (a.name === PROJECT_ENTRYPOINT) {
|
||||
return -1
|
||||
} else if (b.name === PROJECT_ENTRYPOINT) {
|
||||
export function sortFilesAndDirectories(files: FileEntry[]): FileEntry[] {
|
||||
return files.sort((a, b) => {
|
||||
if (a.children === null && b.children !== null) {
|
||||
return 1
|
||||
} else if (a.children === null && b.children !== null) {
|
||||
return -1
|
||||
} else if (a.children !== null && b.children === null) {
|
||||
return 1
|
||||
return -1
|
||||
} else if (a.name && b.name) {
|
||||
return a.name.localeCompare(b.name)
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
})
|
||||
|
||||
return sortedProject.map((fileOrDir: FileEntry) => {
|
||||
if ('children' in fileOrDir && fileOrDir.children !== null) {
|
||||
return {
|
||||
...fileOrDir,
|
||||
children: sortProject(fileOrDir.children || []),
|
||||
}
|
||||
} else {
|
||||
return fileOrDir
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// create a regex to match the project name
|
||||
|
485
src/wasm-lib/Cargo.lock
generated
@ -121,9 +121,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.91"
|
||||
version = "1.0.93"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c042108f3ed77fd83760a5fd79b53be043192bb3b9dba91d8c574c0ada7850c8"
|
||||
checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
]
|
||||
@ -165,7 +165,7 @@ dependencies = [
|
||||
"futures-sink",
|
||||
"log",
|
||||
"pin-project-lite",
|
||||
"thiserror",
|
||||
"thiserror 1.0.68",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -176,7 +176,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -187,7 +187,7 @@ checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -204,7 +204,7 @@ checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -465,7 +465,7 @@ dependencies = [
|
||||
"heck 0.5.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -656,7 +656,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -667,7 +667,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -722,7 +722,7 @@ checksum = "4078275de501a61ceb9e759d37bdd3d7210e654dbc167ac1a3678ef4435ed57b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.87",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
@ -751,7 +751,7 @@ dependencies = [
|
||||
"rustfmt-wrapper",
|
||||
"serde",
|
||||
"serde_tokenstream",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -762,7 +762,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -789,7 +789,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -827,7 +827,7 @@ checksum = "a1ab991c1362ac86c61ab6f556cff143daa22e5a15e4e189df818b2fd19fe65b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -988,7 +988,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1084,7 +1084,7 @@ dependencies = [
|
||||
"inflections",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1140,7 +1140,7 @@ dependencies = [
|
||||
"pest_derive",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"thiserror 1.0.68",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1377,6 +1377,124 @@ dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_collections"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_locid"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"litemap",
|
||||
"tinystr",
|
||||
"writeable",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_locid_transform"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_locid",
|
||||
"icu_locid_transform_data",
|
||||
"icu_provider",
|
||||
"tinystr",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_locid_transform_data"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e"
|
||||
|
||||
[[package]]
|
||||
name = "icu_normalizer"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_collections",
|
||||
"icu_normalizer_data",
|
||||
"icu_properties",
|
||||
"icu_provider",
|
||||
"smallvec",
|
||||
"utf16_iter",
|
||||
"utf8_iter",
|
||||
"write16",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_normalizer_data"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516"
|
||||
|
||||
[[package]]
|
||||
name = "icu_properties"
|
||||
version = "1.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_collections",
|
||||
"icu_locid_transform",
|
||||
"icu_properties_data",
|
||||
"icu_provider",
|
||||
"tinystr",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_properties_data"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569"
|
||||
|
||||
[[package]]
|
||||
name = "icu_provider"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_locid",
|
||||
"icu_provider_macros",
|
||||
"stable_deref_trait",
|
||||
"tinystr",
|
||||
"writeable",
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_provider_macros"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ident_case"
|
||||
version = "1.0.1"
|
||||
@ -1385,12 +1503,23 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "0.5.0"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6"
|
||||
checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e"
|
||||
dependencies = [
|
||||
"unicode-bidi",
|
||||
"unicode-normalization",
|
||||
"idna_adapter",
|
||||
"smallvec",
|
||||
"utf8_iter",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "idna_adapter"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71"
|
||||
dependencies = [
|
||||
"icu_normalizer",
|
||||
"icu_properties",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1414,7 +1543,7 @@ dependencies = [
|
||||
"image",
|
||||
"itertools 0.12.1",
|
||||
"rayon",
|
||||
"thiserror",
|
||||
"thiserror 1.0.68",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1453,9 +1582,9 @@ checksum = "a257582fdcde896fd96463bf2d40eefea0580021c0712a0e2b028b60b47a837a"
|
||||
|
||||
[[package]]
|
||||
name = "insta"
|
||||
version = "1.41.0"
|
||||
version = "1.41.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1f72d3e19488cf7d8ea52d2fc0f8754fc933398b337cd3cbdb28aaeb35159ef"
|
||||
checksum = "7e9ffc4d4892617c50a928c52b2961cb5174b6fc6ebf252b2fac9d21955c48b8"
|
||||
dependencies = [
|
||||
"console",
|
||||
"lazy_static",
|
||||
@ -1587,7 +1716,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"sha2",
|
||||
"tabled",
|
||||
"thiserror",
|
||||
"thiserror 2.0.0",
|
||||
"tokio",
|
||||
"tokio-tungstenite",
|
||||
"toml",
|
||||
@ -1614,7 +1743,7 @@ dependencies = [
|
||||
"pretty_assertions",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1677,7 +1806,7 @@ dependencies = [
|
||||
"serde_bytes",
|
||||
"serde_json",
|
||||
"serde_urlencoded",
|
||||
"thiserror",
|
||||
"thiserror 1.0.68",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"url",
|
||||
@ -1686,9 +1815,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kittycad-modeling-cmds"
|
||||
version = "0.2.71"
|
||||
version = "0.2.72"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c6d2160dcb0e5373b1242a760dbf17fb9c12de523c410c11b145381c852b377b"
|
||||
checksum = "e41880dbe19df3150992988f438c9ba9022ea822d6e4e5ed53d28965de198ec7"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
@ -1718,7 +1847,7 @@ dependencies = [
|
||||
"kittycad-modeling-cmds-macros-impl",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1729,7 +1858,7 @@ checksum = "6607507a8a0e4273b943179f0a3ef8e90712308d1d3095246040c29cfdbf985b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1774,6 +1903,12 @@ version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
|
||||
|
||||
[[package]]
|
||||
name = "litemap"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.12"
|
||||
@ -2016,7 +2151,7 @@ dependencies = [
|
||||
"js-sys",
|
||||
"once_cell",
|
||||
"pin-project-lite",
|
||||
"thiserror",
|
||||
"thiserror 1.0.68",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2034,7 +2169,7 @@ dependencies = [
|
||||
"opentelemetry",
|
||||
"percent-encoding",
|
||||
"rand 0.8.5",
|
||||
"thiserror",
|
||||
"thiserror 1.0.68",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2129,7 +2264,7 @@ dependencies = [
|
||||
"regex",
|
||||
"regex-syntax 0.8.5",
|
||||
"structmeta",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2143,7 +2278,7 @@ dependencies = [
|
||||
"regex",
|
||||
"regex-syntax 0.8.5",
|
||||
"structmeta",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2159,7 +2294,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fdbef9d1d47087a895abd220ed25eb4ad973a5e26f6a4367b038c25e28dfc2d9"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"thiserror",
|
||||
"thiserror 1.0.68",
|
||||
"ucd-trie",
|
||||
]
|
||||
|
||||
@ -2183,7 +2318,7 @@ dependencies = [
|
||||
"pest_meta",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2215,7 +2350,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"strum",
|
||||
"thiserror",
|
||||
"thiserror 1.0.68",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2241,7 +2376,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2352,6 +2487,28 @@ dependencies = [
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error-attr2"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error2"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802"
|
||||
dependencies = [
|
||||
"proc-macro-error-attr2",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.89"
|
||||
@ -2363,9 +2520,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pyo3"
|
||||
version = "0.22.5"
|
||||
version = "0.22.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d922163ba1f79c04bc49073ba7b32fd5a8d3b76a87c955921234b8e77333c51"
|
||||
checksum = "f402062616ab18202ae8319da13fa4279883a2b8a9d9f83f20dbade813ce1884"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"indoc",
|
||||
@ -2381,9 +2538,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pyo3-build-config"
|
||||
version = "0.22.5"
|
||||
version = "0.22.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc38c5feeb496c8321091edf3d63e9a6829eab4b863b4a6a65f26f3e9cc6b179"
|
||||
checksum = "b14b5775b5ff446dd1056212d778012cbe8a0fbffd368029fd9e25b514479c38"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"target-lexicon",
|
||||
@ -2391,9 +2548,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pyo3-ffi"
|
||||
version = "0.22.5"
|
||||
version = "0.22.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94845622d88ae274d2729fcefc850e63d7a3ddff5e3ce11bd88486db9f1d357d"
|
||||
checksum = "9ab5bcf04a2cdcbb50c7d6105de943f543f9ed92af55818fd17b660390fc8636"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"pyo3-build-config",
|
||||
@ -2401,27 +2558,27 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pyo3-macros"
|
||||
version = "0.22.5"
|
||||
version = "0.22.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e655aad15e09b94ffdb3ce3d217acf652e26bbc37697ef012f5e5e348c716e5e"
|
||||
checksum = "0fd24d897903a9e6d80b968368a34e1525aeb719d568dba8b3d4bfa5dc67d453"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"pyo3-macros-backend",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyo3-macros-backend"
|
||||
version = "0.22.5"
|
||||
version = "0.22.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae1e3f09eecd94618f60a455a23def79f79eba4dc561a97324bf9ac8c6df30ce"
|
||||
checksum = "36c011a03ba1e50152b4b394b479826cad97e7a21eb52df179cd91ac411cbfbe"
|
||||
dependencies = [
|
||||
"heck 0.5.0",
|
||||
"proc-macro2",
|
||||
"pyo3-build-config",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2446,7 +2603,7 @@ dependencies = [
|
||||
"rustc-hash",
|
||||
"rustls",
|
||||
"socket2",
|
||||
"thiserror",
|
||||
"thiserror 1.0.68",
|
||||
"tokio",
|
||||
"tracing",
|
||||
]
|
||||
@ -2463,7 +2620,7 @@ dependencies = [
|
||||
"rustc-hash",
|
||||
"rustls",
|
||||
"slab",
|
||||
"thiserror",
|
||||
"thiserror 1.0.68",
|
||||
"tinyvec",
|
||||
"tracing",
|
||||
]
|
||||
@ -2650,9 +2807,9 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
||||
|
||||
[[package]]
|
||||
name = "reqwest"
|
||||
version = "0.12.8"
|
||||
version = "0.12.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f713147fbe92361e52392c73b8c9e48c04c6625bce969ef54dc901e58e042a7b"
|
||||
checksum = "a77c62af46e79de0a562e1a9849205ffcb7fc1238876e9bd743357570e04046f"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"bytes",
|
||||
@ -2717,7 +2874,7 @@ dependencies = [
|
||||
"http 1.1.0",
|
||||
"reqwest",
|
||||
"serde",
|
||||
"thiserror",
|
||||
"thiserror 1.0.68",
|
||||
"tower-service",
|
||||
]
|
||||
|
||||
@ -2814,7 +2971,7 @@ checksum = "f1adc9dfed5cc999077978cc7163b9282c5751c8d39827c4ea8c8c220ca5a440"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"tempfile",
|
||||
"thiserror",
|
||||
"thiserror 1.0.68",
|
||||
"toml",
|
||||
"toolchain_find",
|
||||
]
|
||||
@ -2926,6 +3083,7 @@ dependencies = [
|
||||
"chrono",
|
||||
"dyn-clone",
|
||||
"indexmap 1.9.3",
|
||||
"indexmap 2.6.0",
|
||||
"schemars_derive",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@ -2942,7 +3100,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde_derive_internals",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3006,7 +3164,7 @@ checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3017,7 +3175,7 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3041,7 +3199,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3062,7 +3220,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3178,6 +3336,12 @@ version = "0.9.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
|
||||
|
||||
[[package]]
|
||||
name = "stable_deref_trait"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
|
||||
|
||||
[[package]]
|
||||
name = "str_indices"
|
||||
version = "0.4.3"
|
||||
@ -3199,7 +3363,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"structmeta-derive",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3210,7 +3374,7 @@ checksum = "152a0b65a590ff6c3da95cabe2353ee04e6167c896b28e3b14478c2636c922fc"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3232,7 +3396,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustversion",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3254,9 +3418,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.85"
|
||||
version = "2.0.87"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5023162dfcd14ef8f32034d8bcd4cc5ddc61ef7a247c024a33e24e1f24d21b56"
|
||||
checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -3280,7 +3444,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3343,22 +3507,42 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.65"
|
||||
version = "1.0.68"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d11abd9594d9b38965ef50805c5e469ca9cc6f197f883f717e0269a3057b3d5"
|
||||
checksum = "02dd99dc800bbb97186339685293e1cc5d9df1f8fae2d0aecd9ff1c77efea892"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
"thiserror-impl 1.0.68",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "15291287e9bff1bc6f9ff3409ed9af665bec7a5fc8ac079ea96be07bca0e2668"
|
||||
dependencies = [
|
||||
"thiserror-impl 2.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.65"
|
||||
version = "1.0.68"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae71770322cbd277e69d762a16c444af02aa0575ac0d174f0b9562d3b37f8602"
|
||||
checksum = "a7c61ec9a6f64d2793d8a45faba21efbe3ced62a886d44c36a009b2b519b4c7e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22efd00f33f93fa62848a7cab956c3d38c8d43095efda1decfc2b3a5dc0b8972"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3402,6 +3586,16 @@ dependencies = [
|
||||
"time-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinystr"
|
||||
version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinytemplate"
|
||||
version = "1.2.1"
|
||||
@ -3454,7 +3648,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3596,7 +3790,7 @@ checksum = "84fd902d4e0b9a4b27f2f440108dc034e1758628a9b702f8ec61ad66355422fa"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3624,7 +3818,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3690,9 +3884,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3a2f31991cee3dce1ca4f929a8a04fdd11fd8801aac0f2030b0fa8a0a3fef6b9"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"indexmap 2.6.0",
|
||||
"lazy_static",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"thiserror 1.0.68",
|
||||
"ts-rs-macros",
|
||||
"url",
|
||||
"uuid",
|
||||
@ -3706,7 +3901,7 @@ checksum = "0ea0b99e8ec44abd6f94a18f28f7934437809dd062820797c52401298116f70e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.87",
|
||||
"termcolor",
|
||||
]
|
||||
|
||||
@ -3726,7 +3921,7 @@ dependencies = [
|
||||
"rustls",
|
||||
"rustls-pki-types",
|
||||
"sha1",
|
||||
"thiserror",
|
||||
"thiserror 1.0.68",
|
||||
"utf-8",
|
||||
]
|
||||
|
||||
@ -3763,27 +3958,12 @@ dependencies = [
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-bidi"
|
||||
version = "0.3.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-normalization"
|
||||
version = "0.1.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956"
|
||||
dependencies = [
|
||||
"tinyvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
version = "1.12.0"
|
||||
@ -3816,9 +3996,9 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.5.2"
|
||||
version = "2.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c"
|
||||
checksum = "8d157f1b96d14500ffdc1f10ba712e780825526c03d9a49b4d0324b0d9113ada"
|
||||
dependencies = [
|
||||
"form_urlencoded",
|
||||
"idna",
|
||||
@ -3838,6 +4018,18 @@ version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
|
||||
|
||||
[[package]]
|
||||
name = "utf16_iter"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246"
|
||||
|
||||
[[package]]
|
||||
name = "utf8_iter"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.2"
|
||||
@ -3857,9 +4049,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "validator"
|
||||
version = "0.18.1"
|
||||
version = "0.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db79c75af171630a3148bd3e6d7c4f42b6a9a014c2945bc5ed0020cbb8d9478e"
|
||||
checksum = "d0b4a29d8709210980a09379f27ee31549b73292c87ab9899beee1c0d3be6303"
|
||||
dependencies = [
|
||||
"idna",
|
||||
"once_cell",
|
||||
@ -3873,16 +4065,16 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "validator_derive"
|
||||
version = "0.18.2"
|
||||
version = "0.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df0bcf92720c40105ac4b2dda2a4ea3aa717d4d6a862cc217da653a4bd5c6b10"
|
||||
checksum = "bac855a2ce6f843beb229757e6e570a42e837bcb15e5f449dd48d5747d41bf77"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"once_cell",
|
||||
"proc-macro-error",
|
||||
"proc-macro-error2",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3944,7 +4136,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.87",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
@ -3979,7 +4171,7 @@ checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.87",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
@ -4303,6 +4495,18 @@ dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "write16"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936"
|
||||
|
||||
[[package]]
|
||||
name = "writeable"
|
||||
version = "0.5.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51"
|
||||
|
||||
[[package]]
|
||||
name = "wyz"
|
||||
version = "0.5.1"
|
||||
@ -4327,6 +4531,30 @@ version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049"
|
||||
|
||||
[[package]]
|
||||
name = "yoke"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c5b1314b079b0930c31e3af543d8ee1757b1951ae1e1565ec704403a7240ca5"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"stable_deref_trait",
|
||||
"yoke-derive",
|
||||
"zerofrom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yoke-derive"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.7.35"
|
||||
@ -4345,7 +4573,28 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.85",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerofrom"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55"
|
||||
dependencies = [
|
||||
"zerofrom-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerofrom-derive"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -4354,6 +4603,28 @@ version = "1.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
|
||||
|
||||
[[package]]
|
||||
name = "zerovec"
|
||||
version = "0.10.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079"
|
||||
dependencies = [
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
"zerovec-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerovec-derive"
|
||||
version = "0.10.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zip"
|
||||
version = "2.2.0"
|
||||
@ -4366,5 +4637,5 @@ dependencies = [
|
||||
"displaydoc",
|
||||
"indexmap 2.6.0",
|
||||
"memchr",
|
||||
"thiserror",
|
||||
"thiserror 1.0.68",
|
||||
]
|
||||
|
@ -76,7 +76,7 @@ members = [
|
||||
[workspace.dependencies]
|
||||
http = "1"
|
||||
kittycad = { version = "0.3.25", default-features = false, features = ["js", "requests"] }
|
||||
kittycad-modeling-cmds = { version = "0.2.71", features = ["websocket"] }
|
||||
kittycad-modeling-cmds = { version = "0.2.72", features = ["websocket"] }
|
||||
|
||||
[[test]]
|
||||
name = "executor"
|
||||
|
@ -20,10 +20,10 @@ quote = "1"
|
||||
regex = "1.11"
|
||||
serde = { version = "1.0.214", features = ["derive"] }
|
||||
serde_tokenstream = "0.2"
|
||||
syn = { version = "2.0.85", features = ["full"] }
|
||||
syn = { version = "2.0.87", features = ["full"] }
|
||||
|
||||
[dev-dependencies]
|
||||
anyhow = "1.0.91"
|
||||
anyhow = "1.0.93"
|
||||
expectorate = "1.1.0"
|
||||
pretty_assertions = "1.4.1"
|
||||
rustfmt-wrapper = "0.2.1"
|
||||
|
@ -173,9 +173,7 @@ fn do_stdlib_inner(
|
||||
quote! {
|
||||
let code_blocks = vec![#(#cb),*];
|
||||
code_blocks.iter().map(|cb| {
|
||||
let tokens = crate::token::lexer(cb).unwrap();
|
||||
let parser = crate::parser::Parser::new(tokens);
|
||||
let program = parser.ast().unwrap();
|
||||
let program = crate::parser::top_level_parse(cb).unwrap();
|
||||
|
||||
let mut options: crate::ast::types::FormatOptions = Default::default();
|
||||
options.insert_final_newline = false;
|
||||
@ -750,9 +748,7 @@ fn generate_code_block_test(fn_name: &str, code_block: &str, index: usize) -> pr
|
||||
quote! {
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn #test_name_mock() {
|
||||
let tokens = crate::token::lexer(#code_block).unwrap();
|
||||
let parser = crate::parser::Parser::new(tokens);
|
||||
let program = parser.ast().unwrap();
|
||||
let program = crate::parser::top_level_parse(#code_block).unwrap();
|
||||
let id_generator = crate::executor::IdGenerator::default();
|
||||
let ctx = crate::executor::ExecutorContext {
|
||||
engine: std::sync::Arc::new(Box::new(crate::engine::conn_mock::EngineConnection::new().await.unwrap())),
|
||||
|
@ -2,9 +2,7 @@
|
||||
mod test_examples_someFn {
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_mock_example_someFn0() {
|
||||
let tokens = crate::token::lexer("someFn()").unwrap();
|
||||
let parser = crate::parser::Parser::new(tokens);
|
||||
let program = parser.ast().unwrap();
|
||||
let program = crate::parser::top_level_parse("someFn()").unwrap();
|
||||
let id_generator = crate::executor::IdGenerator::default();
|
||||
let ctx = crate::executor::ExecutorContext {
|
||||
engine: std::sync::Arc::new(Box::new(
|
||||
@ -113,9 +111,7 @@ impl crate::docs::StdLibFn for SomeFn {
|
||||
code_blocks
|
||||
.iter()
|
||||
.map(|cb| {
|
||||
let tokens = crate::token::lexer(cb).unwrap();
|
||||
let parser = crate::parser::Parser::new(tokens);
|
||||
let program = parser.ast().unwrap();
|
||||
let program = crate::parser::top_level_parse(cb).unwrap();
|
||||
let mut options: crate::ast::types::FormatOptions = Default::default();
|
||||
options.insert_final_newline = false;
|
||||
program.recast(&options, 0)
|
||||
|
@ -2,9 +2,7 @@
|
||||
mod test_examples_someFn {
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_mock_example_someFn0() {
|
||||
let tokens = crate::token::lexer("someFn()").unwrap();
|
||||
let parser = crate::parser::Parser::new(tokens);
|
||||
let program = parser.ast().unwrap();
|
||||
let program = crate::parser::top_level_parse("someFn()").unwrap();
|
||||
let id_generator = crate::executor::IdGenerator::default();
|
||||
let ctx = crate::executor::ExecutorContext {
|
||||
engine: std::sync::Arc::new(Box::new(
|
||||
@ -113,9 +111,7 @@ impl crate::docs::StdLibFn for SomeFn {
|
||||
code_blocks
|
||||
.iter()
|
||||
.map(|cb| {
|
||||
let tokens = crate::token::lexer(cb).unwrap();
|
||||
let parser = crate::parser::Parser::new(tokens);
|
||||
let program = parser.ast().unwrap();
|
||||
let program = crate::parser::top_level_parse(cb).unwrap();
|
||||
let mut options: crate::ast::types::FormatOptions = Default::default();
|
||||
options.insert_final_newline = false;
|
||||
program.recast(&options, 0)
|
||||
|
@ -2,9 +2,9 @@
|
||||
mod test_examples_show {
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_mock_example_show0() {
|
||||
let tokens = crate::token::lexer("This is another code block.\nyes sirrr.\nshow").unwrap();
|
||||
let parser = crate::parser::Parser::new(tokens);
|
||||
let program = parser.ast().unwrap();
|
||||
let program =
|
||||
crate::parser::top_level_parse("This is another code block.\nyes sirrr.\nshow")
|
||||
.unwrap();
|
||||
let id_generator = crate::executor::IdGenerator::default();
|
||||
let ctx = crate::executor::ExecutorContext {
|
||||
engine: std::sync::Arc::new(Box::new(
|
||||
@ -36,9 +36,8 @@ mod test_examples_show {
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_mock_example_show1() {
|
||||
let tokens = crate::token::lexer("This is code.\nIt does other shit.\nshow").unwrap();
|
||||
let parser = crate::parser::Parser::new(tokens);
|
||||
let program = parser.ast().unwrap();
|
||||
let program =
|
||||
crate::parser::top_level_parse("This is code.\nIt does other shit.\nshow").unwrap();
|
||||
let id_generator = crate::executor::IdGenerator::default();
|
||||
let ctx = crate::executor::ExecutorContext {
|
||||
engine: std::sync::Arc::new(Box::new(
|
||||
@ -150,9 +149,7 @@ impl crate::docs::StdLibFn for Show {
|
||||
code_blocks
|
||||
.iter()
|
||||
.map(|cb| {
|
||||
let tokens = crate::token::lexer(cb).unwrap();
|
||||
let parser = crate::parser::Parser::new(tokens);
|
||||
let program = parser.ast().unwrap();
|
||||
let program = crate::parser::top_level_parse(cb).unwrap();
|
||||
let mut options: crate::ast::types::FormatOptions = Default::default();
|
||||
options.insert_final_newline = false;
|
||||
program.recast(&options, 0)
|
||||
|
@ -2,9 +2,8 @@
|
||||
mod test_examples_show {
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_mock_example_show0() {
|
||||
let tokens = crate::token::lexer("This is code.\nIt does other shit.\nshow").unwrap();
|
||||
let parser = crate::parser::Parser::new(tokens);
|
||||
let program = parser.ast().unwrap();
|
||||
let program =
|
||||
crate::parser::top_level_parse("This is code.\nIt does other shit.\nshow").unwrap();
|
||||
let id_generator = crate::executor::IdGenerator::default();
|
||||
let ctx = crate::executor::ExecutorContext {
|
||||
engine: std::sync::Arc::new(Box::new(
|
||||
@ -113,9 +112,7 @@ impl crate::docs::StdLibFn for Show {
|
||||
code_blocks
|
||||
.iter()
|
||||
.map(|cb| {
|
||||
let tokens = crate::token::lexer(cb).unwrap();
|
||||
let parser = crate::parser::Parser::new(tokens);
|
||||
let program = parser.ast().unwrap();
|
||||
let program = crate::parser::top_level_parse(cb).unwrap();
|
||||
let mut options: crate::ast::types::FormatOptions = Default::default();
|
||||
options.insert_final_newline = false;
|
||||
program.recast(&options, 0)
|
||||
|
@ -2,10 +2,9 @@
|
||||
mod test_examples_my_func {
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_mock_example_my_func0() {
|
||||
let tokens =
|
||||
crate::token::lexer("This is another code block.\nyes sirrr.\nmyFunc").unwrap();
|
||||
let parser = crate::parser::Parser::new(tokens);
|
||||
let program = parser.ast().unwrap();
|
||||
let program =
|
||||
crate::parser::top_level_parse("This is another code block.\nyes sirrr.\nmyFunc")
|
||||
.unwrap();
|
||||
let id_generator = crate::executor::IdGenerator::default();
|
||||
let ctx = crate::executor::ExecutorContext {
|
||||
engine: std::sync::Arc::new(Box::new(
|
||||
@ -37,9 +36,8 @@ mod test_examples_my_func {
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_mock_example_my_func1() {
|
||||
let tokens = crate::token::lexer("This is code.\nIt does other shit.\nmyFunc").unwrap();
|
||||
let parser = crate::parser::Parser::new(tokens);
|
||||
let program = parser.ast().unwrap();
|
||||
let program =
|
||||
crate::parser::top_level_parse("This is code.\nIt does other shit.\nmyFunc").unwrap();
|
||||
let id_generator = crate::executor::IdGenerator::default();
|
||||
let ctx = crate::executor::ExecutorContext {
|
||||
engine: std::sync::Arc::new(Box::new(
|
||||
@ -151,9 +149,7 @@ impl crate::docs::StdLibFn for MyFunc {
|
||||
code_blocks
|
||||
.iter()
|
||||
.map(|cb| {
|
||||
let tokens = crate::token::lexer(cb).unwrap();
|
||||
let parser = crate::parser::Parser::new(tokens);
|
||||
let program = parser.ast().unwrap();
|
||||
let program = crate::parser::top_level_parse(cb).unwrap();
|
||||
let mut options: crate::ast::types::FormatOptions = Default::default();
|
||||
options.insert_final_newline = false;
|
||||
program.recast(&options, 0)
|
||||
|
@ -2,10 +2,9 @@
|
||||
mod test_examples_line_to {
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_mock_example_line_to0() {
|
||||
let tokens =
|
||||
crate::token::lexer("This is another code block.\nyes sirrr.\nlineTo").unwrap();
|
||||
let parser = crate::parser::Parser::new(tokens);
|
||||
let program = parser.ast().unwrap();
|
||||
let program =
|
||||
crate::parser::top_level_parse("This is another code block.\nyes sirrr.\nlineTo")
|
||||
.unwrap();
|
||||
let id_generator = crate::executor::IdGenerator::default();
|
||||
let ctx = crate::executor::ExecutorContext {
|
||||
engine: std::sync::Arc::new(Box::new(
|
||||
@ -37,9 +36,8 @@ mod test_examples_line_to {
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_mock_example_line_to1() {
|
||||
let tokens = crate::token::lexer("This is code.\nIt does other shit.\nlineTo").unwrap();
|
||||
let parser = crate::parser::Parser::new(tokens);
|
||||
let program = parser.ast().unwrap();
|
||||
let program =
|
||||
crate::parser::top_level_parse("This is code.\nIt does other shit.\nlineTo").unwrap();
|
||||
let id_generator = crate::executor::IdGenerator::default();
|
||||
let ctx = crate::executor::ExecutorContext {
|
||||
engine: std::sync::Arc::new(Box::new(
|
||||
@ -159,9 +157,7 @@ impl crate::docs::StdLibFn for LineTo {
|
||||
code_blocks
|
||||
.iter()
|
||||
.map(|cb| {
|
||||
let tokens = crate::token::lexer(cb).unwrap();
|
||||
let parser = crate::parser::Parser::new(tokens);
|
||||
let program = parser.ast().unwrap();
|
||||
let program = crate::parser::top_level_parse(cb).unwrap();
|
||||
let mut options: crate::ast::types::FormatOptions = Default::default();
|
||||
options.insert_final_newline = false;
|
||||
program.recast(&options, 0)
|
||||
|
@ -2,9 +2,8 @@
|
||||
mod test_examples_min {
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_mock_example_min0() {
|
||||
let tokens = crate::token::lexer("This is another code block.\nyes sirrr.\nmin").unwrap();
|
||||
let parser = crate::parser::Parser::new(tokens);
|
||||
let program = parser.ast().unwrap();
|
||||
let program =
|
||||
crate::parser::top_level_parse("This is another code block.\nyes sirrr.\nmin").unwrap();
|
||||
let id_generator = crate::executor::IdGenerator::default();
|
||||
let ctx = crate::executor::ExecutorContext {
|
||||
engine: std::sync::Arc::new(Box::new(
|
||||
@ -36,9 +35,8 @@ mod test_examples_min {
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_mock_example_min1() {
|
||||
let tokens = crate::token::lexer("This is code.\nIt does other shit.\nmin").unwrap();
|
||||
let parser = crate::parser::Parser::new(tokens);
|
||||
let program = parser.ast().unwrap();
|
||||
let program =
|
||||
crate::parser::top_level_parse("This is code.\nIt does other shit.\nmin").unwrap();
|
||||
let id_generator = crate::executor::IdGenerator::default();
|
||||
let ctx = crate::executor::ExecutorContext {
|
||||
engine: std::sync::Arc::new(Box::new(
|
||||
@ -150,9 +148,7 @@ impl crate::docs::StdLibFn for Min {
|
||||
code_blocks
|
||||
.iter()
|
||||
.map(|cb| {
|
||||
let tokens = crate::token::lexer(cb).unwrap();
|
||||
let parser = crate::parser::Parser::new(tokens);
|
||||
let program = parser.ast().unwrap();
|
||||
let program = crate::parser::top_level_parse(cb).unwrap();
|
||||
let mut options: crate::ast::types::FormatOptions = Default::default();
|
||||
options.insert_final_newline = false;
|
||||
program.recast(&options, 0)
|
||||
|
@ -2,9 +2,8 @@
|
||||
mod test_examples_show {
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_mock_example_show0() {
|
||||
let tokens = crate::token::lexer("This is code.\nIt does other shit.\nshow").unwrap();
|
||||
let parser = crate::parser::Parser::new(tokens);
|
||||
let program = parser.ast().unwrap();
|
||||
let program =
|
||||
crate::parser::top_level_parse("This is code.\nIt does other shit.\nshow").unwrap();
|
||||
let id_generator = crate::executor::IdGenerator::default();
|
||||
let ctx = crate::executor::ExecutorContext {
|
||||
engine: std::sync::Arc::new(Box::new(
|
||||
@ -113,9 +112,7 @@ impl crate::docs::StdLibFn for Show {
|
||||
code_blocks
|
||||
.iter()
|
||||
.map(|cb| {
|
||||
let tokens = crate::token::lexer(cb).unwrap();
|
||||
let parser = crate::parser::Parser::new(tokens);
|
||||
let program = parser.ast().unwrap();
|
||||
let program = crate::parser::top_level_parse(cb).unwrap();
|
||||
let mut options: crate::ast::types::FormatOptions = Default::default();
|
||||
options.insert_final_newline = false;
|
||||
program.recast(&options, 0)
|
||||
|
@ -2,9 +2,8 @@
|
||||
mod test_examples_import {
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_mock_example_import0() {
|
||||
let tokens = crate::token::lexer("This is code.\nIt does other shit.\nimport").unwrap();
|
||||
let parser = crate::parser::Parser::new(tokens);
|
||||
let program = parser.ast().unwrap();
|
||||
let program =
|
||||
crate::parser::top_level_parse("This is code.\nIt does other shit.\nimport").unwrap();
|
||||
let id_generator = crate::executor::IdGenerator::default();
|
||||
let ctx = crate::executor::ExecutorContext {
|
||||
engine: std::sync::Arc::new(Box::new(
|
||||
@ -113,9 +112,7 @@ impl crate::docs::StdLibFn for Import {
|
||||
code_blocks
|
||||
.iter()
|
||||
.map(|cb| {
|
||||
let tokens = crate::token::lexer(cb).unwrap();
|
||||
let parser = crate::parser::Parser::new(tokens);
|
||||
let program = parser.ast().unwrap();
|
||||
let program = crate::parser::top_level_parse(cb).unwrap();
|
||||
let mut options: crate::ast::types::FormatOptions = Default::default();
|
||||
options.insert_final_newline = false;
|
||||
program.recast(&options, 0)
|
||||
|
@ -2,9 +2,8 @@
|
||||
mod test_examples_import {
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_mock_example_import0() {
|
||||
let tokens = crate::token::lexer("This is code.\nIt does other shit.\nimport").unwrap();
|
||||
let parser = crate::parser::Parser::new(tokens);
|
||||
let program = parser.ast().unwrap();
|
||||
let program =
|
||||
crate::parser::top_level_parse("This is code.\nIt does other shit.\nimport").unwrap();
|
||||
let id_generator = crate::executor::IdGenerator::default();
|
||||
let ctx = crate::executor::ExecutorContext {
|
||||
engine: std::sync::Arc::new(Box::new(
|
||||
@ -113,9 +112,7 @@ impl crate::docs::StdLibFn for Import {
|
||||
code_blocks
|
||||
.iter()
|
||||
.map(|cb| {
|
||||
let tokens = crate::token::lexer(cb).unwrap();
|
||||
let parser = crate::parser::Parser::new(tokens);
|
||||
let program = parser.ast().unwrap();
|
||||
let program = crate::parser::top_level_parse(cb).unwrap();
|
||||
let mut options: crate::ast::types::FormatOptions = Default::default();
|
||||
options.insert_final_newline = false;
|
||||
program.recast(&options, 0)
|
||||
|
@ -2,9 +2,8 @@
|
||||
mod test_examples_import {
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_mock_example_import0() {
|
||||
let tokens = crate::token::lexer("This is code.\nIt does other shit.\nimport").unwrap();
|
||||
let parser = crate::parser::Parser::new(tokens);
|
||||
let program = parser.ast().unwrap();
|
||||
let program =
|
||||
crate::parser::top_level_parse("This is code.\nIt does other shit.\nimport").unwrap();
|
||||
let id_generator = crate::executor::IdGenerator::default();
|
||||
let ctx = crate::executor::ExecutorContext {
|
||||
engine: std::sync::Arc::new(Box::new(
|
||||
@ -113,9 +112,7 @@ impl crate::docs::StdLibFn for Import {
|
||||
code_blocks
|
||||
.iter()
|
||||
.map(|cb| {
|
||||
let tokens = crate::token::lexer(cb).unwrap();
|
||||
let parser = crate::parser::Parser::new(tokens);
|
||||
let program = parser.ast().unwrap();
|
||||
let program = crate::parser::top_level_parse(cb).unwrap();
|
||||
let mut options: crate::ast::types::FormatOptions = Default::default();
|
||||
options.insert_final_newline = false;
|
||||
program.recast(&options, 0)
|
||||
|