Compare commits

...

18 Commits

Author SHA1 Message Date
e4edffa569 bump kcl friends (#5993)
Signed-off-by: Jess Frazelle <github@jessfraz.com>
2025-03-25 11:05:17 -07:00
c28bad267a Add utilities to check platform to determine if a test should run (#5983)
* Add tests for utility to bypass unreliable tests

* Limit skip to just Windows

* Ignore unit tests from Playwright

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2025-03-25 13:24:41 -04:00
81f92bc7f9 Codify dependencies to run web end-to-end tests (#5926) 2025-03-25 10:35:49 -04:00
58dc3382eb Fix to update the code in the editor before executing (#5976)
* Fix spelling

* Fix to update the code in the editor before executing
2025-03-25 05:06:27 -04:00
f7f7d9fa2c Remove all tooltip delays throughout the app (#5969) 2025-03-24 22:58:54 -04:00
8c3408fda3 Fix: Ignore KCL changes when loading settings and fixed useEffect to use .current (#5126)
* fix: fixed use effect logic to use .current, fixed useEffect file watcher to not watch .kcl files when loading settings

* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)

* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)

* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)

* bump

* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)

* bump

* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)

* bump

* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)

* fix: merge wrote this back...

* fix: deleted unused variable init

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* bump

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Pierre Jacquier <pierre@zoo.dev>
2025-03-24 23:23:12 +00:00
7c9f1248d4 Extract extrude to variables and format onboarding code (#5949)
* Extract extrude to variables and format onboarding code

* A snapshot a day keeps the bugs away! 📷🐛

* add spaces between sketches and extrudes

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: jgomez720 <114548659+jgomez720@users.noreply.github.com>
2025-03-24 17:10:41 -04:00
89dd4fb039 Add edge and segment selection in point-and-click Helix flow (#5866)
* WIP: Add edge and segment selection in point-and-click Helix flow
Fixes #5393

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* Working edge based helix edit

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* Add utility function for shared code between revolve and helix

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* Use updateModelingState in codemod

* A snapshot a day keeps the bugs away! 📷🐛

* Add skip: true for edge helix to be consistent with axis as options

* A snapshot a day keeps the bugs away! 📷🐛

* Add support for sweepEdge and tests

* Lint

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* Clean up snapshots

* Fix up tests after ccw change

* Use displayName: 'CounterClockWise' cause ccw not cutting it

* Fix tsc

* Update 2020 snapshots after helix change

* Update 2020 snapshots after helix change

* A snapshot a day keeps the bugs away! 📷🐛

* Another one :djkhaled:

* Clean up

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2025-03-24 16:08:19 -04:00
fdeb2b3f49 Feature: Implement read write access checking on Project Directory and report any issues in home page (#5676)
* chore: skeleton to detect read write directories and if we have access to notify user

* chore: adding buttont to easily change project directory

* chore: cleaning up home page error bar layout and button

* fix: adding clearer comments

* fix: ugly console debugging but I need to save off progress

* fix: removing project dir check on empty string

* fix: debug progress to save off listProjects once. Still bugged...

* fix: more hard coded debugging to get project loading optimizted

* fix: yarp, we got another one bois

* fix: cleaning up code

* fix: massive bug comment to warn devs about chokidar bugs

* fix: returning error instead of throwing

* fix: cleaning up PR

* fix: fixed loading the projects when the project directory changes

* fix: remove testing code

* fix: only skip directories if you can access the project directory since we don't need to view them

* fix: unit tests, turning off noisey localhost vitest garbage

* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)

* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)

* fix: deleted testing state

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Pierre Jacquier <pierrejacquier39@gmail.com>
Co-authored-by: Pierre Jacquier <pierre@zoo.dev>
2025-03-24 19:57:01 +00:00
65c455ae7c Use app token to sync 'all-e2e' branch (#5968) 2025-03-24 19:17:04 +00:00
c3e12e5ff7 Update 2020 snapshots after helix change (#5970)
* Update 2020 snapshots after helix change

* Another one :djkhaled:

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2025-03-24 14:28:01 -04:00
4dce1612c1 Bump the major group in /rust/kcl-language-server with 4 updates (#5966)
* Bump the major group in /rust/kcl-language-server with 4 updates

Bumps the major group in /rust/kcl-language-server with 4 updates: [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin), [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser), [@vscode/vsce](https://github.com/Microsoft/vsce) and [glob](https://github.com/isaacs/node-glob).


Updates `@typescript-eslint/eslint-plugin` from 6.21.0 to 8.27.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.27.0/packages/eslint-plugin)

Updates `@typescript-eslint/parser` from 6.21.0 to 8.27.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.27.0/packages/parser)

Updates `@vscode/vsce` from 2.30.0 to 3.3.0
- [Release notes](https://github.com/Microsoft/vsce/releases)
- [Commits](https://github.com/Microsoft/vsce/compare/v2.30.0...v3.3.0)

Updates `glob` from 10.4.5 to 11.0.1
- [Changelog](https://github.com/isaacs/node-glob/blob/main/changelog.md)
- [Commits](https://github.com/isaacs/node-glob/compare/v10.4.5...v11.0.1)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-type: direct:development
  update-type: version-update:semver-major
  dependency-group: major
- dependency-name: "@typescript-eslint/parser"
  dependency-type: direct:development
  update-type: version-update:semver-major
  dependency-group: major
- dependency-name: "@vscode/vsce"
  dependency-type: direct:development
  update-type: version-update:semver-major
  dependency-group: major
- dependency-name: glob
  dependency-type: direct:development
  update-type: version-update:semver-major
  dependency-group: major
...

Signed-off-by: dependabot[bot] <support@github.com>

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2025-03-24 10:09:04 -07:00
1b98897120 Bump @types/node from 22.13.10 to 22.13.13 in /packages/codemirror-lsp-client in the patch group (#5962)
Bump @types/node in /packages/codemirror-lsp-client in the patch group

Bumps the patch group in /packages/codemirror-lsp-client with 1 update: [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node).


Updates `@types/node` from 22.13.10 to 22.13.13
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-24 09:03:46 -04:00
3b2abe5814 Declare std kwarg functions in KCL and migrate circle (#5955)
* Support calling KCL std KW fns, and move circle to KCL std

Signed-off-by: Nick Cameron <nrc@ncameron.org>

* Doc comments on parameters

Signed-off-by: Nick Cameron <nrc@ncameron.org>

* Update grammar

Signed-off-by: Nick Cameron <nrc@ncameron.org>

* Change use of counterClockWise to ccw

Signed-off-by: Nick Cameron <nrc@ncameron.org>

---------

Signed-off-by: Nick Cameron <nrc@ncameron.org>
2025-03-24 21:55:24 +13:00
dddcd5ff46 Support paths to names rather than just raw idents (#5778)
* Support paths to names rather than just raw idents

Signed-off-by: Nick Cameron <nrc@ncameron.org>

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

---------

Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2025-03-24 20:58:55 +13:00
cfbb03765e Add fixme to a file-tree test (#5941) 2025-03-22 07:25:39 -04:00
0d0dd1019b Enable tests to run on pushes to 'all-e2e' branch (#5945)
* Enable tests to run on pushes to 'all-e2e' branch

* Document branch behavior
2025-03-22 01:55:54 +00:00
de2b1b3bea Fix so that only comments doesn't format to empty (#5944) 2025-03-22 00:56:04 +00:00
372 changed files with 89096 additions and 27348 deletions

2
.envrc
View File

@ -1 +1,3 @@
source_up_if_exists
use flake . use flake .

View File

@ -1,7 +1,9 @@
name: E2E Tests name: E2E Tests
on: on:
push: push:
branches: [ main ] branches:
- main
- all-e2e # this bypasses `fixme()` using `orRunWhenFullSuiteEnabled()`
pull_request: pull_request:
schedule: schedule:
- cron: 0 * * * * # hourly - cron: 0 * * * * # hourly
@ -15,7 +17,6 @@ permissions:
pull-requests: write pull-requests: write
actions: read actions: read
jobs: jobs:
conditions: conditions:
@ -67,14 +68,16 @@ jobs:
- name: Display conditions - name: Display conditions
shell: bash shell: bash
run: | run: |
# For debugging purposes. # For debugging purposes
set -euo pipefail set -euo pipefail
echo "GITHUB_REF: $GITHUB_REF"
echo "GITHUB_HEAD_REF: $GITHUB_HEAD_REF"
echo "GITHUB_BASE_REF: $GITHUB_BASE_REF"
echo "significant: ${{ steps.path-changes.outputs.significant }}" echo "significant: ${{ steps.path-changes.outputs.significant }}"
echo "should-run: ${{ steps.should-run.outputs.should-run }}" echo "should-run: ${{ steps.should-run.outputs.should-run }}"
prepare-wasm: prepare-wasm:
# seperate job on Ubuntu to build or fetch the wasm blob once on the fastest runner # separate job on Ubuntu to build or fetch the wasm blob once on the fastest runner
runs-on: runs-on=${{ github.run_id }}/family=i7ie.2xlarge/image=ubuntu22-full-x64 runs-on: runs-on=${{ github.run_id }}/family=i7ie.2xlarge/image=ubuntu22-full-x64
needs: conditions needs: conditions
steps: steps:
@ -160,7 +163,6 @@ jobs:
path: | path: |
rust/kcl-wasm-lib/pkg/kcl_wasm_lib* rust/kcl-wasm-lib/pkg/kcl_wasm_lib*
snapshots: snapshots:
name: playwright:snapshots:ubuntu name: playwright:snapshots:ubuntu
runs-on: runs-on=${{ github.run_id }}/family=i7ie.2xlarge/image=ubuntu22-full-x64 runs-on: runs-on=${{ github.run_id }}/family=i7ie.2xlarge/image=ubuntu22-full-x64
@ -243,7 +245,7 @@ jobs:
retention-days: 30 retention-days: 30
overwrite: true overwrite: true
- name: check for changes - name: Check for changes
if: ${{ needs.conditions.outputs.should-run == 'true' && github.ref != 'refs/heads/main' }} if: ${{ needs.conditions.outputs.should-run == 'true' && github.ref != 'refs/heads/main' }}
shell: bash shell: bash
id: git-check id: git-check

View File

@ -141,13 +141,13 @@ jobs:
if: ${{ github.event_name != 'release' && github.event_name != 'schedule' }} if: ${{ github.event_name != 'release' && github.event_name != 'schedule' }}
run: yarn playwright install chromium --with-deps run: yarn playwright install chromium --with-deps
- name: run unit tests - name: Run unit tests
if: ${{ github.event_name != 'release' && github.event_name != 'schedule' }} if: ${{ github.event_name != 'release' && github.event_name != 'schedule' }}
run: xvfb-run -a yarn test:unit run: xvfb-run -a yarn test:unit
env: env:
VITE_KC_DEV_TOKEN: ${{ secrets.KITTYCAD_API_TOKEN_DEV }} VITE_KC_DEV_TOKEN: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
- name: check for changes - name: Check for changes
if: ${{ github.event_name != 'release' && github.event_name != 'schedule' }} if: ${{ github.event_name != 'release' && github.event_name != 'schedule' }}
id: git-check id: git-check
run: | run: |

View File

@ -14,10 +14,19 @@ permissions:
jobs: jobs:
update-branch: update-branch:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/create-github-app-token@v1
id: app-token
with:
app-id: ${{ secrets.MODELING_APP_GH_APP_ID }}
private-key: ${{ secrets.MODELING_APP_GH_APP_PRIVATE_KEY }}
owner: ${{ github.repository_owner }}
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- shell: bash with:
token: ${{ steps.app-token.outputs.token }}
- name: Sync with main
run: | run: |
# checkout our branch # checkout our branch
git checkout all-e2e || git checkout -b all-e2e git checkout all-e2e || git checkout -b all-e2e
@ -26,4 +35,5 @@ jobs:
# reset to main # reset to main
git reset --hard origin/main git reset --hard origin/main
# force push it # force push it
git remote set-url origin https://x-access-token:${{ steps.app-token.outputs.token }}@github.com/${{ github.repository }}.git
git push --force origin all-e2e git push --force origin all-e2e

View File

@ -20,8 +20,12 @@ $(WASM_PACK):
############################################################################### ###############################################################################
# BUILD # BUILD
RUST_SOURCES := $(wildcard rust/*) $(wildcard rust/**/*) CARGO_SOURCES := rust/.cargo/config.toml $(wildcard rust/Cargo.*) $(wildcard rust/**/Cargo.*)
TYPESCRIPT_SOURCES := $(wildcard src/**/*.tsx) $(wildcard src/**/*.ts) RUST_SOURCES := $(wildcard rust/**/*.rs)
REACT_SOURCES := $(wildcard src/*.tsx) $(wildcard src/**/*.tsx)
TYPESCRIPT_SOURCES := tsconfig.* $(wildcard src/*.ts) $(wildcard src/**/*.ts)
VITE_SOURCES := $(wildcard vite.*) $(wildcard vite/**/*.tsx)
.PHONY: build .PHONY: build
build: build-web build-desktop build: build-web build-desktop
@ -32,13 +36,13 @@ build-web: public/kcl_wasm_lib_bg.wasm build/index.html
.PHONY: build-desktop .PHONY: build-desktop
build-desktop: public/kcl_wasm_lib_bg.wasm .vite/build/main.js build-desktop: public/kcl_wasm_lib_bg.wasm .vite/build/main.js
public/kcl_wasm_lib_bg.wasm: $(RUST_SOURCES) public/kcl_wasm_lib_bg.wasm: $(CARGO_SOURCES)$(RUST_SOURCES)
yarn build:wasm yarn build:wasm
build/index.html: $(TYPESCRIPT_SOURCES) build/index.html: $(REACT_SOURCES) $(TYPESCRIPT_SOURCES) $(VITE_SOURCES)
yarn build:local yarn build:local
.vite/build/main.js: $(TYPESCRIPT_SOURCES) .vite/build/main.js: $(REACT_SOURCES) $(TYPESCRIPT_SOURCES) $(VITE_SOURCES)
yarn tronb:vite:dev yarn tronb:vite:dev
############################################################################### ###############################################################################
@ -73,19 +77,29 @@ run-desktop: install build-desktop ## Start the desktop app
############################################################################### ###############################################################################
# TEST # TEST
GREP ?= "" E2E_WORKERS ?= 1
E2E_FAILURES ?= 1
E2E_GREP ?= ""
.PHONY: test .PHONY: test
test: test-unit test-e2e test: test-unit test-e2e
.PHONY: test-unit .PHONY: test-unit
test-unit: install ## Run the unit tests test-unit: install ## Run the unit tests
@ nc -z localhost 3000 || ( echo "Error: localhost:3000 not available, 'make run-web' first" && exit 1 ) @ curl -fs localhost:3000 >/dev/null || ( echo "Error: localhost:3000 not available, 'make run-web' first" && exit 1 )
yarn test:unit yarn test:unit
.PHONY: test-e2e .PHONY: test-e2e
test-e2e: install build-desktop ## Run the e2e tests test-e2e: test-e2e-desktop
yarn test:playwright:electron --workers=1 --grep=$(GREP)
.PHONY: test-e2e-web
test-e2e-web: install build-web ## Run the web e2e tests
@ curl -fs localhost:3000 >/dev/null || ( echo "Error: localhost:3000 not available, 'make run-web' first" && exit 1 )
yarn chrome:test --headed --workers=$(E2E_WORKERS) --max-failures=$(E2E_FAILURES) --grep=$(E2E_GREP)
.PHONY: test-e2e-desktop
test-e2e-desktop: install build-desktop ## Run the desktop e2e tests
yarn test:playwright:electron --workers=$(E2E_WORKERS) --max-failures=$(E2E_FAILURES) --grep=$(E2E_GREP)
############################################################################### ###############################################################################
# CLEAN # CLEAN

File diff suppressed because one or more lines are too long

View File

@ -10,10 +10,10 @@ Construct a circle derived from 3 points.
```js ```js
circleThreePoint( circleThreePoint(
sketchSurfaceOrGroup: SketchOrSurface,
p1: [number], p1: [number],
p2: [number], p2: [number],
p3: [number], p3: [number],
sketchSurfaceOrGroup: SketchOrSurface,
tag?: TagDeclarator, tag?: TagDeclarator,
): Sketch ): Sketch
``` ```
@ -23,10 +23,10 @@ circleThreePoint(
| Name | Type | Description | Required | | Name | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `sketchSurfaceOrGroup` | [`SketchOrSurface`](/docs/kcl/types/SketchOrSurface) | Plane or surface to sketch on. | Yes |
| `p1` | [`[number]`](/docs/kcl/types/number) | 1st point to derive the circle. | Yes | | `p1` | [`[number]`](/docs/kcl/types/number) | 1st point to derive the circle. | Yes |
| `p2` | [`[number]`](/docs/kcl/types/number) | 2nd point to derive the circle. | Yes | | `p2` | [`[number]`](/docs/kcl/types/number) | 2nd point to derive the circle. | Yes |
| `p3` | [`[number]`](/docs/kcl/types/number) | 3rd point to derive the circle. | Yes | | `p3` | [`[number]`](/docs/kcl/types/number) | 3rd point to derive the circle. | Yes |
| `sketchSurfaceOrGroup` | [`SketchOrSurface`](/docs/kcl/types/SketchOrSurface) | Plane or surface to sketch on. | Yes |
| [`tag`](/docs/kcl/types/tag) | [`TagDeclarator`](/docs/kcl/types#tag-declaration) | Identifier for the circle to reference elsewhere. | No | | [`tag`](/docs/kcl/types/tag) | [`TagDeclarator`](/docs/kcl/types#tag-declaration) | Identifier for the circle to reference elsewhere. | No |
### Returns ### Returns

View File

@ -15,7 +15,7 @@ std::math::E: number = 2.71828182845904523536028747135266250_
### Examples ### Examples
```js ```js
exampleSketch = startSketchOn("XZ") exampleSketch = startSketchOn(XZ)
|> startProfileAt([0, 0], %) |> startProfileAt([0, 0], %)
|> angledLine({ |> angledLine({
angle = 30, angle = 30,

View File

@ -17,7 +17,7 @@ std::math::PI: number = 3.14159265358979323846264338327950288_
```js ```js
circumference = 70 circumference = 70
exampleSketch = startSketchOn("XZ") exampleSketch = startSketchOn(XZ)
|> circle(center = [0, 0], radius = circumference/ (2 * PI)) |> circle(center = [0, 0], radius = circumference/ (2 * PI))
example = extrude(exampleSketch, length = 5) example = extrude(exampleSketch, length = 5)

View File

@ -15,7 +15,7 @@ std::math::TAU: number = 6.28318530717958647692528676655900577_
### Examples ### Examples
```js ```js
exampleSketch = startSketchOn("XZ") exampleSketch = startSketchOn(XZ)
|> startProfileAt([0, 0], %) |> startProfileAt([0, 0], %)
|> angledLine({ |> angledLine({
angle = 50, angle = 50,

File diff suppressed because one or more lines are too long

View File

@ -61,7 +61,6 @@ layout: manual
* [`bezierCurve`](kcl/bezierCurve) * [`bezierCurve`](kcl/bezierCurve)
* [`ceil`](kcl/ceil) * [`ceil`](kcl/ceil)
* [`chamfer`](kcl/chamfer) * [`chamfer`](kcl/chamfer)
* [`circle`](kcl/circle)
* [`circleThreePoint`](kcl/circleThreePoint) * [`circleThreePoint`](kcl/circleThreePoint)
* [`close`](kcl/close) * [`close`](kcl/close)
* [`cm`](kcl/cm) * [`cm`](kcl/cm)
@ -145,3 +144,5 @@ layout: manual
* [`cos`](kcl/std-math-cos) * [`cos`](kcl/std-math-cos)
* [`sin`](kcl/std-math-sin) * [`sin`](kcl/std-math-sin)
* [`tan`](kcl/std-math-tan) * [`tan`](kcl/std-math-tan)
* **std::sketch**
* [`circle`](kcl/std-sketch-circle)

View File

@ -9,7 +9,7 @@ Compute the cosine of a number (in radians).
```js ```js
cos(num: number(rad)): number(_) cos(@num: number(rad)): number(_)
``` ```
@ -27,7 +27,7 @@ cos(num: number(rad)): number(_)
### Examples ### Examples
```js ```js
exampleSketch = startSketchOn("XZ") exampleSketch = startSketchOn(XZ)
|> startProfileAt([0, 0], %) |> startProfileAt([0, 0], %)
|> angledLine({ |> angledLine({
angle = 30, angle = 30,

View File

@ -9,7 +9,7 @@ Compute the sine of a number (in radians).
```js ```js
sin(num: number(rad)): number(_) sin(@num: number(rad)): number(_)
``` ```
@ -27,7 +27,7 @@ sin(num: number(rad)): number(_)
### Examples ### Examples
```js ```js
exampleSketch = startSketchOn("XZ") exampleSketch = startSketchOn(XZ)
|> startProfileAt([0, 0], %) |> startProfileAt([0, 0], %)
|> angledLine({ |> angledLine({
angle = 50, angle = 50,

View File

@ -9,7 +9,7 @@ Compute the tangent of a number (in radians).
```js ```js
tan(num: number(rad)): number(_) tan(@num: number(rad)): number(_)
``` ```
@ -27,7 +27,7 @@ tan(num: number(rad)): number(_)
### Examples ### Examples
```js ```js
exampleSketch = startSketchOn("XZ") exampleSketch = startSketchOn(XZ)
|> startProfileAt([0, 0], %) |> startProfileAt([0, 0], %)
|> angledLine({ |> angledLine({
angle = 50, angle = 50,

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -10,8 +10,8 @@ A point in two dimensional space.
type Point2d = [number; 2] type Point2d = [number; 2]
``` ```
`Point2d` is an alias for a two-element array of [number](/docs/kcl/types/number)s. To write a value [`Point2d`](/docs/kcl/types/Point2d) is an alias for a two-element array of [number](/docs/kcl/types/number)s. To write a value
with type `Point2d`, use an array, e.g., `[0, 0]` or `[5.0, 3.14]`. with type [`Point2d`](/docs/kcl/types/Point2d), use an array, e.g., `[0, 0]` or `[5.0, 3.14]`.

View File

@ -10,8 +10,8 @@ A point in three dimensional space.
type Point3d = [number; 3] type Point3d = [number; 3]
``` ```
`Point3d` is an alias for a three-element array of [number](/docs/kcl/types/number)s. To write a value [`Point3d`](/docs/kcl/types/Point3d) is an alias for a three-element array of [number](/docs/kcl/types/number)s. To write a value
with type `Point3d`, use an array, e.g., `[0, 0, 0]` or `[5.0, 3.14, 6.8]`. with type [`Point3d`](/docs/kcl/types/Point3d), use an array, e.g., `[0, 0, 0]` or `[5.0, 3.14, 6.8]`.

View File

@ -17,7 +17,7 @@ mySketch = startSketchOn('XY')
|> close() |> close()
``` ```
The `mySketch` variable will be an executed `Sketch` object. Executed being past The `mySketch` variable will be an executed [`Sketch`](/docs/kcl/types/Sketch) object. Executed being past
tense, because the engine has already executed the commands to create the sketch. tense, because the engine has already executed the commands to create the sketch.
The previous sketch commands will never be executed again, in this case. The previous sketch commands will never be executed again, in this case.

View File

@ -18,7 +18,7 @@ myPart = startSketchOn('XY')
|> extrude(length = 6) |> extrude(length = 6)
``` ```
The `myPart` variable will be an executed `Solid` object. Executed being past The `myPart` variable will be an executed [`Solid`](/docs/kcl/types/Solid) object. Executed being past
tense, because the engine has already executed the commands to create the solid. tense, because the engine has already executed the commands to create the solid.
The previous solid commands will never be executed again, in this case. The previous solid commands will never be executed again, in this case.

View File

@ -6,6 +6,7 @@ import {
executorInputPath, executorInputPath,
getUtils, getUtils,
orRunWhenFullSuiteEnabled, orRunWhenFullSuiteEnabled,
runningOnWindows,
} from './test-utils' } from './test-utils'
import { join } from 'path' import { join } from 'path'
import { FILE_EXT } from 'lib/constants' import { FILE_EXT } from 'lib/constants'
@ -15,6 +16,9 @@ test.describe('integrations tests', () => {
'Creating a new file or switching file while in sketchMode should exit sketchMode', 'Creating a new file or switching file while in sketchMode should exit sketchMode',
{ tag: '@electron' }, { tag: '@electron' },
async ({ page, context, homePage, scene, editor, toolbar, cmdBar }) => { async ({ page, context, homePage, scene, editor, toolbar, cmdBar }) => {
if (runningOnWindows()) {
test.fixme(orRunWhenFullSuiteEnabled())
}
await context.folderSetupFn(async (dir) => { await context.folderSetupFn(async (dir) => {
const bracketDir = join(dir, 'test-sample') const bracketDir = join(dir, 'test-sample')
await fsp.mkdir(bracketDir, { recursive: true }) await fsp.mkdir(bracketDir, { recursive: true })

View File

@ -1071,7 +1071,7 @@ openSketch = startSketchOn(XY)
}) })
}) })
test('Helix point-and-click', async ({ test('Helix point-and-click on default axis', async ({
context, context,
page, page,
homePage, homePage,
@ -1082,29 +1082,26 @@ openSketch = startSketchOn(XY)
}) => { }) => {
// One dumb hardcoded screen pixel value // One dumb hardcoded screen pixel value
const testPoint = { x: 620, y: 257 } const testPoint = { x: 620, y: 257 }
const expectedOutput = `helix001 = helix( revolutions = 1, angleStart = 360, counterClockWise = false, radius = 5, axis = 'X', length = 5,)` const expectedOutput = `helix001 = helix( revolutions = 1, angleStart = 360, ccw = false, radius = 5, axis = 'X', length = 5,)`
const expectedLine = `revolutions=1,` const expectedLine = `revolutions=1,`
await homePage.goToModelingScene() await homePage.goToModelingScene()
// await test.step(`Look for the red of the default plane`, async () => {
// await scene.expectPixelColor([96, 52, 52], testPoint, 15)
// })
await test.step(`Go through the command bar flow`, async () => { await test.step(`Go through the command bar flow`, async () => {
await toolbar.helixButton.click() await toolbar.helixButton.click()
await cmdBar.expectState({ await cmdBar.expectState({
stage: 'arguments', stage: 'arguments',
currentArgKey: 'revolutions', currentArgKey: 'axisOrEdge',
currentArgValue: '1', currentArgValue: '',
headerArguments: { headerArguments: {
AngleStart: '', AngleStart: '',
Axis: '', AxisOrEdge: '',
CounterClockWise: '', CounterClockWise: '',
Length: '', Length: '',
Radius: '', Radius: '',
Revolutions: '', Revolutions: '',
}, },
highlightedHeaderArg: 'revolutions', highlightedHeaderArg: 'axisOrEdge',
commandName: 'Helix', commandName: 'Helix',
}) })
await cmdBar.progressCmdBar() await cmdBar.progressCmdBar()
@ -1114,6 +1111,7 @@ openSketch = startSketchOn(XY)
await cmdBar.progressCmdBar() await cmdBar.progressCmdBar()
await cmdBar.progressCmdBar() await cmdBar.progressCmdBar()
await cmdBar.progressCmdBar() await cmdBar.progressCmdBar()
await cmdBar.progressCmdBar()
}) })
await test.step(`Confirm code is added to the editor, scene has changed`, async () => { await test.step(`Confirm code is added to the editor, scene has changed`, async () => {
@ -1179,6 +1177,165 @@ openSketch = startSketchOn(XY)
}) })
}) })
const helixCases = [
{
selectionType: 'segment',
testPoint: { x: 513, y: 221 },
expectedOutput: `helix001 = helix( revolutions = 20, angleStart = 0, ccw = true, radius = 1, axis = seg01, length = 100,)`,
expectedEditedOutput: `helix001 = helix( revolutions = 20, angleStart = 0, ccw = true, radius = 1, axis = seg01, length = 50,)`,
},
{
selectionType: 'sweepEdge',
testPoint: { x: 564, y: 364 },
expectedOutput: `helix001 = helix( revolutions = 20, angleStart = 0, ccw = true, radius = 1, axis = getOppositeEdge(seg01), length = 100,)`,
expectedEditedOutput: `helix001 = helix( revolutions = 20, angleStart = 0, ccw = true, radius = 1, axis = getOppositeEdge(seg01), length = 50,)`,
},
]
helixCases.map(
({ selectionType, testPoint, expectedOutput, expectedEditedOutput }) => {
test(`Helix point-and-click around ${selectionType}`, async ({
context,
page,
homePage,
scene,
editor,
toolbar,
cmdBar,
}) => {
page.on('console', console.log)
const initialCode = `sketch001 = startSketchOn('XZ')
profile001 = startProfileAt([0, 0], sketch001)
|> yLine(length = 100)
|> line(endAbsolute = [100, 0])
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close()
extrude001 = extrude(profile001, length = 100)`
// One dumb hardcoded screen pixel value
const [clickOnEdge] = scene.makeMouseHelpers(testPoint.x, testPoint.y)
await context.addInitScript((initialCode) => {
localStorage.setItem('persistCode', initialCode)
}, initialCode)
await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene()
await test.step(`Go through the command bar flow`, async () => {
await toolbar.closePane('code')
await toolbar.helixButton.click()
await cmdBar.expectState({
stage: 'arguments',
currentArgKey: 'axisOrEdge',
currentArgValue: '',
headerArguments: {
AngleStart: '',
AxisOrEdge: '',
CounterClockWise: '',
Length: '',
Radius: '',
Revolutions: '',
},
highlightedHeaderArg: 'axisOrEdge',
commandName: 'Helix',
})
await cmdBar.selectOption({ name: 'Edge' }).click()
await clickOnEdge()
await cmdBar.progressCmdBar()
await cmdBar.argumentInput.focus()
await page.keyboard.insertText('20')
await cmdBar.progressCmdBar()
await page.keyboard.insertText('0')
await cmdBar.progressCmdBar()
await cmdBar.selectOption({ name: 'True' }).click()
await page.keyboard.insertText('1')
await cmdBar.progressCmdBar()
await page.keyboard.insertText('100')
await cmdBar.progressCmdBar()
await cmdBar.expectState({
stage: 'review',
headerArguments: {
AngleStart: '0',
AxisOrEdge: 'Edge',
Edge: `1 ${selectionType}`,
CounterClockWise: '',
Length: '100',
Radius: '1',
Revolutions: '20',
},
commandName: 'Helix',
})
await cmdBar.progressCmdBar()
})
await test.step(`Confirm code is added to the editor, scene has changed`, async () => {
await toolbar.openPane('code')
await editor.expectEditor.toContain(expectedOutput)
await toolbar.closePane('code')
})
await test.step(`Edit helix through the feature tree`, async () => {
await toolbar.openPane('feature-tree')
const operationButton = await toolbar.getFeatureTreeOperation(
'Helix',
0
)
await operationButton.dblclick()
const initialInput = '100'
const newInput = '50'
await cmdBar.expectState({
commandName: 'Helix',
stage: 'arguments',
currentArgKey: 'length',
currentArgValue: initialInput,
headerArguments: {
AngleStart: '0',
CounterClockWise: '',
Length: initialInput,
Radius: '1',
Revolutions: '20',
},
highlightedHeaderArg: 'length',
})
await expect(cmdBar.currentArgumentInput).toBeVisible()
await cmdBar.currentArgumentInput
.locator('.cm-content')
.fill(newInput)
await cmdBar.progressCmdBar()
await cmdBar.expectState({
stage: 'review',
headerArguments: {
AngleStart: '0',
CounterClockWise: '',
Length: newInput,
Radius: '1',
Revolutions: '20',
},
commandName: 'Helix',
})
await cmdBar.progressCmdBar()
await toolbar.closePane('feature-tree')
await toolbar.openPane('code')
await editor.expectEditor.toContain(expectedEditedOutput)
await toolbar.closePane('code')
})
await test.step('Delete helix via feature tree selection', async () => {
await toolbar.openPane('feature-tree')
const operationButton = await toolbar.getFeatureTreeOperation(
'Helix',
0
)
await operationButton.click({ button: 'left' })
await page.keyboard.press('Delete')
await editor.expectEditor.not.toContain(expectedEditedOutput)
await expect(
await toolbar.getFeatureTreeOperation('Helix', 0)
).not.toBeVisible()
})
})
}
)
const loftPointAndClickCases = [ const loftPointAndClickCases = [
{ shouldPreselect: true }, { shouldPreselect: true },
{ shouldPreselect: false }, { shouldPreselect: false },

View File

@ -191,7 +191,7 @@ test(
// error text on hover // error text on hover
await page.hover('.cm-lint-marker-error') await page.hover('.cm-lint-marker-error')
const crypticErrorText = `Expected a tag declarator` const crypticErrorText = `The arg tag was given, but it was the wrong type`
await expect(page.getByText(crypticErrorText).first()).toBeVisible() await expect(page.getByText(crypticErrorText).first()).toBeVisible()
// black pixel means the scene has been cleared. // black pixel means the scene has been cleared.
@ -409,7 +409,7 @@ test(
// error text on hover // error text on hover
await page.hover('.cm-lint-marker-error') await page.hover('.cm-lint-marker-error')
const crypticErrorText = `Expected a tag declarator` const crypticErrorText = `The arg tag was given, but it was the wrong type`
await expect(page.getByText(crypticErrorText).first()).toBeVisible() await expect(page.getByText(crypticErrorText).first()).toBeVisible()
// black pixel means the scene has been cleared. // black pixel means the scene has been cleared.
@ -453,7 +453,7 @@ test(
// error text on hover // error text on hover
await page.hover('.cm-lint-marker-error') await page.hover('.cm-lint-marker-error')
const crypticErrorText = `Expected a tag declarator` const crypticErrorText = `The arg tag was given, but it was the wrong type`
await expect(page.getByText(crypticErrorText).first()).toBeVisible() await expect(page.getByText(crypticErrorText).first()).toBeVisible()
} }
) )

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 143 KiB

After

Width:  |  Height:  |  Size: 143 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 68 KiB

View File

@ -29,5 +29,5 @@
} }
} }
], ],
"kcl_version": "0.2.52" "kcl_version": "0.2.53"
} }

View File

@ -0,0 +1,96 @@
import {
runningOnLinux,
runningOnMac,
runningOnWindows,
orRunWhenFullSuiteEnabled,
} from './test-utils'
describe('platform detection utilities', () => {
const originalPlatform = process.platform
afterAll(() => {
Object.defineProperty(process, 'platform', {
value: originalPlatform,
})
})
describe('runningOnLinux', () => {
it('returns true on Linux', () => {
Object.defineProperty(process, 'platform', {
value: 'linux',
})
expect(runningOnLinux()).toBe(true)
})
it('returns false on other platforms', () => {
Object.defineProperty(process, 'platform', {
value: 'darwin',
})
expect(runningOnLinux()).toBe(false)
})
})
describe('runningOnMac', () => {
it('returns true on Mac', () => {
Object.defineProperty(process, 'platform', {
value: 'darwin',
})
expect(runningOnMac()).toBe(true)
})
it('returns false on other platforms', () => {
Object.defineProperty(process, 'platform', {
value: 'linux',
})
expect(runningOnMac()).toBe(false)
})
})
describe('runningOnWindows', () => {
it('returns true on Windows', () => {
Object.defineProperty(process, 'platform', {
value: 'win32',
})
expect(runningOnWindows()).toBe(true)
})
it('returns false on other platforms', () => {
Object.defineProperty(process, 'platform', {
value: 'linux',
})
expect(runningOnWindows()).toBe(false)
})
})
})
describe('utility to bypass unreliable tests', () => {
const originalEnv = { ...process.env }
afterAll(() => {
process.env = { ...originalEnv }
})
it('always runs them on dedicated branch', () => {
process.env.GITHUB_EVENT_NAME = 'push'
process.env.GITHUB_REF = 'refs/heads/all-e2e'
process.env.GITHUB_HEAD_REF = ''
process.env.GITHUB_BASE_REF = ''
const condition = orRunWhenFullSuiteEnabled()
expect(condition).toBe(false)
})
it('skips them on the main branch', () => {
process.env.GITHUB_EVENT_NAME = 'push'
process.env.GITHUB_REF = 'refs/heads/main'
process.env.GITHUB_HEAD_REF = ''
process.env.GITHUB_BASE_REF = ''
const condition = orRunWhenFullSuiteEnabled()
expect(condition).toBe(true)
})
it('skips them on pull requests', () => {
process.env.GITHUB_EVENT_NAME = 'pull_request'
process.env.GITHUB_REF = 'refs/pull/5883/merge'
process.env.GITHUB_HEAD_REF = 'my-branch'
process.env.GITHUB_BASE_REF = 'main'
const condition = orRunWhenFullSuiteEnabled()
expect(condition).toBe(true)
})
})

View File

@ -55,8 +55,21 @@ export const commonPoints = {
export const editorSelector = '[role="textbox"][data-language="kcl"]' export const editorSelector = '[role="textbox"][data-language="kcl"]'
type PaneId = 'variables' | 'code' | 'files' | 'logs' type PaneId = 'variables' | 'code' | 'files' | 'logs'
export function runningOnLinux() {
return process.platform === 'linux'
}
export function runningOnMac() {
return process.platform === 'darwin'
}
export function runningOnWindows() {
return process.platform === 'win32'
}
export function orRunWhenFullSuiteEnabled() { export function orRunWhenFullSuiteEnabled() {
return process.env.GITHUB_REF !== 'all-e2e' const branch = process.env.GITHUB_REF?.replace('refs/heads/', '')
return branch !== 'all-e2e'
} }
async function waitForPageLoadWithRetry(page: Page) { async function waitForPageLoadWithRetry(page: Page) {

3
interface.d.ts vendored
View File

@ -44,6 +44,9 @@ export interface IElectronAPI {
rm: typeof fs.rm rm: typeof fs.rm
stat: (path: string) => ReturnType<fs.stat> stat: (path: string) => ReturnType<fs.stat>
statIsDirectory: (path: string) => Promise<boolean> statIsDirectory: (path: string) => Promise<boolean>
canReadWriteDirectory: (
path: string
) => Promise<{ value: boolean; error: unknown }>
path: typeof path path: typeof path
mkdir: typeof fs.mkdir mkdir: typeof fs.mkdir
join: typeof path.join join: typeof path.join

View File

@ -75,17 +75,14 @@ LabeledArgument { ArgumentLabel Equals expression }
ArgumentList { "(" commaSep<LabeledArgument | expression> ")" } ArgumentList { "(" commaSep<LabeledArgument | expression> ")" }
type[@isGroup=Type] { type[@isGroup=Type] {
@specialize[@name=PrimitiveType]< PrimitiveType { identifier } |
identifier,
"bool" | "number" | "string" | "tag" | "Sketch" | "SketchSurface" | "Solid" | "Plane"
> |
ArrayType { "[" type !member (";" Number "+"?)? "]" } | ArrayType { "[" type !member (";" Number "+"?)? "]" } |
ObjectType { "{" commaSep<ObjectProperty { PropertyName ":" type }> "}" } ObjectType { "{" commaSep<ObjectProperty { PropertyName ":" type }> "}" }
} }
VariableDefinition { identifier } VariableDefinition { identifier }
VariableName { identifier } VariableName { identifier ("::" identifier)*}
ArgumentLabel { identifier } ArgumentLabel { identifier }
@ -137,7 +134,7 @@ commaSep1NoTrailingComma<term> { term ("," term)* }
"(" ")" "(" ")"
"{" "}" "{" "}"
"[" "]" "[" "]"
"," "?" ":" "." ".." ";" "," "?" ":" "." ".." ";" "::"
} }
@external propSource kclHighlight from "./highlight" @external propSource kclHighlight from "./highlight"

View File

@ -18,7 +18,9 @@ const config = defineConfig({
environment: 'node', environment: 'node',
reporters: process.env.GITHUB_ACTIONS reporters: process.env.GITHUB_ACTIONS
? ['dot', 'github-actions'] ? ['dot', 'github-actions']
: ['verbose', 'hanging-process'], : // Gotcha: 'hanging-process' is very noisey, turn off by default on localhost
// : ['verbose', 'hanging-process'],
['verbose'],
testTimeout: 1000, testTimeout: 1000,
hookTimeout: 1000, hookTimeout: 1000,
teardownTimeout: 1000, teardownTimeout: 1000,

View File

@ -29,7 +29,7 @@
"vscode-uri": "^3.1.0" "vscode-uri": "^3.1.0"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^22.13.10", "@types/node": "^22.13.13",
"ts-node": "^10.9.2" "ts-node": "^10.9.2"
} }
} }

View File

@ -116,10 +116,10 @@
resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.4.tgz#0b92dcc0cc1c81f6f306a381f28e31b1a56536e9" resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.4.tgz#0b92dcc0cc1c81f6f306a381f28e31b1a56536e9"
integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA== integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==
"@types/node@^22.13.10": "@types/node@^22.13.13":
version "22.13.10" version "22.13.13"
resolved "https://registry.yarnpkg.com/@types/node/-/node-22.13.10.tgz#df9ea358c5ed991266becc3109dc2dc9125d77e4" resolved "https://registry.yarnpkg.com/@types/node/-/node-22.13.13.tgz#5e7d110fb509b0d4a43fbf48fa9d6e0f83e1b1e7"
integrity sha512-I6LPUvlRH+O6VRUqYOcMudhaIdUVWfsjnZavnsraHvpBwaEyMN29ry+0UVJhImYL16xsscu0aske3yA+uPOWfw== integrity sha512-ClsL5nMwKaBRwPcCvH8E7+nU4GxHVx1axNvMZTFHMEfNI7oahimt26P5zjVCRrjiIWj6YFXfE1v3dEp94wLcGQ==
dependencies: dependencies:
undici-types "~6.20.0" undici-types "~6.20.0"

View File

@ -12,6 +12,7 @@ import { defineConfig, devices } from '@playwright/test'
export default defineConfig({ export default defineConfig({
timeout: 120_000, // override the default 30s timeout timeout: 120_000, // override the default 30s timeout
testDir: './e2e/playwright', testDir: './e2e/playwright',
testIgnore: '*.test.ts', // ignore unit tests
/* Run tests in files in parallel */ /* Run tests in files in parallel */
fullyParallel: true, fullyParallel: true,
/* Fail the build on CI if you accidentally left test.only in the source code. */ /* Fail the build on CI if you accidentally left test.only in the source code. */

20
rust/Cargo.lock generated
View File

@ -1780,7 +1780,7 @@ dependencies = [
[[package]] [[package]]
name = "kcl-bumper" name = "kcl-bumper"
version = "0.1.53" version = "0.1.54"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"clap", "clap",
@ -1791,7 +1791,7 @@ dependencies = [
[[package]] [[package]]
name = "kcl-derive-docs" name = "kcl-derive-docs"
version = "0.1.53" version = "0.1.54"
dependencies = [ dependencies = [
"Inflector", "Inflector",
"anyhow", "anyhow",
@ -1810,7 +1810,7 @@ dependencies = [
[[package]] [[package]]
name = "kcl-directory-test-macro" name = "kcl-directory-test-macro"
version = "0.1.53" version = "0.1.54"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -1819,7 +1819,7 @@ dependencies = [
[[package]] [[package]]
name = "kcl-language-server" name = "kcl-language-server"
version = "0.2.53" version = "0.2.54"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"clap", "clap",
@ -1840,7 +1840,7 @@ dependencies = [
[[package]] [[package]]
name = "kcl-language-server-release" name = "kcl-language-server-release"
version = "0.1.53" version = "0.1.54"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"clap", "clap",
@ -1860,7 +1860,7 @@ dependencies = [
[[package]] [[package]]
name = "kcl-lib" name = "kcl-lib"
version = "0.2.53" version = "0.2.54"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"approx 0.5.1", "approx 0.5.1",
@ -1928,7 +1928,7 @@ dependencies = [
[[package]] [[package]]
name = "kcl-python-bindings" name = "kcl-python-bindings"
version = "0.3.53" version = "0.3.54"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"kcl-lib", "kcl-lib",
@ -1943,7 +1943,7 @@ dependencies = [
[[package]] [[package]]
name = "kcl-test-server" name = "kcl-test-server"
version = "0.1.53" version = "0.1.54"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"hyper 0.14.32", "hyper 0.14.32",
@ -1956,7 +1956,7 @@ dependencies = [
[[package]] [[package]]
name = "kcl-to-core" name = "kcl-to-core"
version = "0.1.53" version = "0.1.54"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",
@ -1970,7 +1970,7 @@ dependencies = [
[[package]] [[package]]
name = "kcl-wasm-lib" name = "kcl-wasm-lib"
version = "0.1.53" version = "0.1.54"
dependencies = [ dependencies = [
"bson", "bson",
"console_error_panic_hook", "console_error_panic_hook",

View File

@ -1,7 +1,7 @@
[package] [package]
name = "kcl-bumper" name = "kcl-bumper"
version = "0.1.53" version = "0.1.54"
edition = "2021" edition = "2021"
repository = "https://github.com/KittyCAD/modeling-api" repository = "https://github.com/KittyCAD/modeling-api"
rust-version = "1.76" rust-version = "1.76"

View File

@ -1,7 +1,7 @@
[package] [package]
name = "kcl-derive-docs" name = "kcl-derive-docs"
description = "A tool for generating documentation from Rust derive macros" description = "A tool for generating documentation from Rust derive macros"
version = "0.1.53" version = "0.1.54"
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
repository = "https://github.com/KittyCAD/modeling-app" repository = "https://github.com/KittyCAD/modeling-app"

View File

@ -1,7 +1,7 @@
[package] [package]
name = "kcl-directory-test-macro" name = "kcl-directory-test-macro"
description = "A tool for generating tests from a directory of kcl files" description = "A tool for generating tests from a directory of kcl files"
version = "0.1.53" version = "0.1.54"
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
repository = "https://github.com/KittyCAD/modeling-app" repository = "https://github.com/KittyCAD/modeling-app"

View File

@ -1,6 +1,6 @@
[package] [package]
name = "kcl-language-server-release" name = "kcl-language-server-release"
version = "0.1.53" version = "0.1.54"
edition = "2021" edition = "2021"
authors = ["KittyCAD Inc <kcl@kittycad.io>"] authors = ["KittyCAD Inc <kcl@kittycad.io>"]
publish = false publish = false

View File

@ -2,7 +2,7 @@
name = "kcl-language-server" name = "kcl-language-server"
description = "A language server for KCL." description = "A language server for KCL."
authors = ["KittyCAD Inc <kcl@kittycad.io>"] authors = ["KittyCAD Inc <kcl@kittycad.io>"]
version = "0.2.53" version = "0.2.54"
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View File

@ -135,13 +135,13 @@
"@types/mocha": "^10.0.10", "@types/mocha": "^10.0.10",
"@types/node": "^22.13.10", "@types/node": "^22.13.10",
"@types/vscode": "^1.97.0", "@types/vscode": "^1.97.0",
"@typescript-eslint/eslint-plugin": "^6.6.0", "@typescript-eslint/eslint-plugin": "^8.27.0",
"@typescript-eslint/parser": "^6.6.0", "@typescript-eslint/parser": "^8.27.0",
"@vscode/test-electron": "^2.4.1", "@vscode/test-electron": "^2.4.1",
"@vscode/vsce": "^2.30.0", "@vscode/vsce": "^3.3.0",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"esbuild": "^0.25.1", "esbuild": "^0.25.1",
"glob": "^10.4.3", "glob": "^11.0.1",
"mocha": "^11.1.0", "mocha": "^11.1.0",
"typescript": "^5.8.2" "typescript": "^5.8.2"
}, },

View File

@ -252,7 +252,7 @@
dependencies: dependencies:
eslint-visitor-keys "^3.4.3" eslint-visitor-keys "^3.4.3"
"@eslint-community/regexpp@^4.5.1": "@eslint-community/regexpp@^4.10.0":
version "4.12.1" version "4.12.1"
resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.1.tgz#cfc6cffe39df390a3841cde2abccf92eaa7ae0e0" resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.1.tgz#cfc6cffe39df390a3841cde2abccf92eaa7ae0e0"
integrity sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ== integrity sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==
@ -308,11 +308,6 @@
"@types/minimatch" "^5.1.2" "@types/minimatch" "^5.1.2"
"@types/node" "*" "@types/node" "*"
"@types/json-schema@^7.0.12":
version "7.0.15"
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841"
integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==
"@types/minimatch@^5.1.2": "@types/minimatch@^5.1.2":
version "5.1.2" version "5.1.2"
resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-5.1.2.tgz#07508b45797cb81ec3f273011b054cd0755eddca" resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-5.1.2.tgz#07508b45797cb81ec3f273011b054cd0755eddca"
@ -330,101 +325,91 @@
dependencies: dependencies:
undici-types "~6.20.0" undici-types "~6.20.0"
"@types/semver@^7.5.0":
version "7.5.8"
resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.8.tgz#8268a8c57a3e4abd25c165ecd36237db7948a55e"
integrity sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==
"@types/vscode@^1.97.0": "@types/vscode@^1.97.0":
version "1.97.0" version "1.97.0"
resolved "https://registry.yarnpkg.com/@types/vscode/-/vscode-1.97.0.tgz#62ce3a32243019aaa4fc20cee2a3de06bc71af4f" resolved "https://registry.yarnpkg.com/@types/vscode/-/vscode-1.97.0.tgz#62ce3a32243019aaa4fc20cee2a3de06bc71af4f"
integrity sha512-ueE73loeOTe7olaVyqP9mrRI54kVPJifUPjblZo9fYcv1CuVLPOEKEkqW0GkqPC454+nCEoigLWnC2Pp7prZ9w== integrity sha512-ueE73loeOTe7olaVyqP9mrRI54kVPJifUPjblZo9fYcv1CuVLPOEKEkqW0GkqPC454+nCEoigLWnC2Pp7prZ9w==
"@typescript-eslint/eslint-plugin@^6.6.0": "@typescript-eslint/eslint-plugin@^8.27.0":
version "6.21.0" version "8.27.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz#30830c1ca81fd5f3c2714e524c4303e0194f9cd3" resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.27.0.tgz#fbef10802365832ee1d1bd5d2117dcec82727a72"
integrity sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA== integrity sha512-4henw4zkePi5p252c8ncBLzLce52SEUz2Ebj8faDnuUXz2UuHEONYcJ+G0oaCF+bYCWVZtrGzq3FD7YXetmnSA==
dependencies: dependencies:
"@eslint-community/regexpp" "^4.5.1" "@eslint-community/regexpp" "^4.10.0"
"@typescript-eslint/scope-manager" "6.21.0" "@typescript-eslint/scope-manager" "8.27.0"
"@typescript-eslint/type-utils" "6.21.0" "@typescript-eslint/type-utils" "8.27.0"
"@typescript-eslint/utils" "6.21.0" "@typescript-eslint/utils" "8.27.0"
"@typescript-eslint/visitor-keys" "6.21.0" "@typescript-eslint/visitor-keys" "8.27.0"
debug "^4.3.4"
graphemer "^1.4.0" graphemer "^1.4.0"
ignore "^5.2.4" ignore "^5.3.1"
natural-compare "^1.4.0" natural-compare "^1.4.0"
semver "^7.5.4" ts-api-utils "^2.0.1"
ts-api-utils "^1.0.1"
"@typescript-eslint/parser@^6.6.0": "@typescript-eslint/parser@^8.27.0":
version "6.21.0" version "8.27.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-6.21.0.tgz#af8fcf66feee2edc86bc5d1cf45e33b0630bf35b" resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.27.0.tgz#3f6beaa83934467eda34ae82ef04090014df8227"
integrity sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ== integrity sha512-XGwIabPallYipmcOk45DpsBSgLC64A0yvdAkrwEzwZ2viqGqRUJ8eEYoPz0CWnutgAFbNMPdsGGvzjSmcWVlEA==
dependencies: dependencies:
"@typescript-eslint/scope-manager" "6.21.0" "@typescript-eslint/scope-manager" "8.27.0"
"@typescript-eslint/types" "6.21.0" "@typescript-eslint/types" "8.27.0"
"@typescript-eslint/typescript-estree" "6.21.0" "@typescript-eslint/typescript-estree" "8.27.0"
"@typescript-eslint/visitor-keys" "6.21.0" "@typescript-eslint/visitor-keys" "8.27.0"
debug "^4.3.4" debug "^4.3.4"
"@typescript-eslint/scope-manager@6.21.0": "@typescript-eslint/scope-manager@8.27.0":
version "6.21.0" version "8.27.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz#ea8a9bfc8f1504a6ac5d59a6df308d3a0630a2b1" resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.27.0.tgz#b51042927067d677fbfc471605cf40d1ffaee482"
integrity sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg== integrity sha512-8oI9GwPMQmBryaaxG1tOZdxXVeMDte6NyJA4i7/TWa4fBwgnAXYlIQP+uYOeqAaLJ2JRxlG9CAyL+C+YE9Xknw==
dependencies: dependencies:
"@typescript-eslint/types" "6.21.0" "@typescript-eslint/types" "8.27.0"
"@typescript-eslint/visitor-keys" "6.21.0" "@typescript-eslint/visitor-keys" "8.27.0"
"@typescript-eslint/type-utils@6.21.0": "@typescript-eslint/type-utils@8.27.0":
version "6.21.0" version "8.27.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz#6473281cfed4dacabe8004e8521cee0bd9d4c01e" resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.27.0.tgz#af3c4eefcb64455ee50aae2d7069918467af085c"
integrity sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag== integrity sha512-wVArTVcz1oJOIEJxui/nRhV0TXzD/zMSOYi/ggCfNq78EIszddXcJb7r4RCp/oBrjt8n9A0BSxRMKxHftpDxDA==
dependencies: dependencies:
"@typescript-eslint/typescript-estree" "6.21.0" "@typescript-eslint/typescript-estree" "8.27.0"
"@typescript-eslint/utils" "6.21.0" "@typescript-eslint/utils" "8.27.0"
debug "^4.3.4" debug "^4.3.4"
ts-api-utils "^1.0.1" ts-api-utils "^2.0.1"
"@typescript-eslint/types@6.21.0": "@typescript-eslint/types@8.27.0":
version "6.21.0" version "8.27.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-6.21.0.tgz#205724c5123a8fef7ecd195075fa6e85bac3436d" resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.27.0.tgz#3dd01ced4c81e798d1106fda0904f8d5c91051aa"
integrity sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg== integrity sha512-/6cp9yL72yUHAYq9g6DsAU+vVfvQmd1a8KyA81uvfDE21O2DwQ/qxlM4AR8TSdAu+kJLBDrEHKC5/W2/nxsY0A==
"@typescript-eslint/typescript-estree@6.21.0": "@typescript-eslint/typescript-estree@8.27.0":
version "6.21.0" version "8.27.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz#c47ae7901db3b8bddc3ecd73daff2d0895688c46" resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.27.0.tgz#4e02a1056454a84418cc9bce7c00b1c08b03567a"
integrity sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ== integrity sha512-BnKq8cqPVoMw71O38a1tEb6iebEgGA80icSxW7g+kndx0o6ot6696HjG7NdgfuAVmVEtwXUr3L8R9ZuVjoQL6A==
dependencies: dependencies:
"@typescript-eslint/types" "6.21.0" "@typescript-eslint/types" "8.27.0"
"@typescript-eslint/visitor-keys" "6.21.0" "@typescript-eslint/visitor-keys" "8.27.0"
debug "^4.3.4" debug "^4.3.4"
globby "^11.1.0" fast-glob "^3.3.2"
is-glob "^4.0.3" is-glob "^4.0.3"
minimatch "9.0.3" minimatch "^9.0.4"
semver "^7.5.4" semver "^7.6.0"
ts-api-utils "^1.0.1" ts-api-utils "^2.0.1"
"@typescript-eslint/utils@6.21.0": "@typescript-eslint/utils@8.27.0":
version "6.21.0" version "8.27.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-6.21.0.tgz#4714e7a6b39e773c1c8e97ec587f520840cd8134" resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.27.0.tgz#d9c2a4891c6a85b952a9d5f9656c379ab111cf6d"
integrity sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ== integrity sha512-njkodcwH1yvmo31YWgRHNb/x1Xhhq4/m81PhtvmRngD8iHPehxffz1SNCO+kwaePhATC+kOa/ggmvPoPza5i0Q==
dependencies: dependencies:
"@eslint-community/eslint-utils" "^4.4.0" "@eslint-community/eslint-utils" "^4.4.0"
"@types/json-schema" "^7.0.12" "@typescript-eslint/scope-manager" "8.27.0"
"@types/semver" "^7.5.0" "@typescript-eslint/types" "8.27.0"
"@typescript-eslint/scope-manager" "6.21.0" "@typescript-eslint/typescript-estree" "8.27.0"
"@typescript-eslint/types" "6.21.0"
"@typescript-eslint/typescript-estree" "6.21.0"
semver "^7.5.4"
"@typescript-eslint/visitor-keys@6.21.0": "@typescript-eslint/visitor-keys@8.27.0":
version "6.21.0" version "8.27.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz#87a99d077aa507e20e238b11d56cc26ade45fe47" resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.27.0.tgz#4a13e9d7ad7e311a07ea1b178b4c9f848ce11334"
integrity sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A== integrity sha512-WsXQwMkILJvffP6z4U3FYJPlbf/j07HIxmDjZpbNvBJkMfvwXj5ACRkkHwBDvLBbDbtX5TdU64/rcvKJ/vuInQ==
dependencies: dependencies:
"@typescript-eslint/types" "6.21.0" "@typescript-eslint/types" "8.27.0"
eslint-visitor-keys "^3.4.1" eslint-visitor-keys "^4.2.0"
"@vscode/test-electron@^2.4.1": "@vscode/test-electron@^2.4.1":
version "2.4.1" version "2.4.1"
@ -497,10 +482,10 @@
"@vscode/vsce-sign-win32-arm64" "2.0.2" "@vscode/vsce-sign-win32-arm64" "2.0.2"
"@vscode/vsce-sign-win32-x64" "2.0.2" "@vscode/vsce-sign-win32-x64" "2.0.2"
"@vscode/vsce@^2.30.0": "@vscode/vsce@^3.3.0":
version "2.30.0" version "3.3.0"
resolved "https://registry.yarnpkg.com/@vscode/vsce/-/vsce-2.30.0.tgz#7a0c16b20ef529fa291fe9d3c7fe51a2a613f773" resolved "https://registry.yarnpkg.com/@vscode/vsce/-/vsce-3.3.0.tgz#803e41368a95d35693ce049076503f34f89fde09"
integrity sha512-MBYpXdCY1SCdc2u/y11kmJuSODKFyZRpeRTQq5p4rSg05QSjSy5pz6h/BGLNdSahgXfKRBATEkjAcJFdJuDz8Q== integrity sha512-HA/pUyvh/TQWkc4wG7AudPIWUvsR8i4jiWZZgM/a69ncPi9Nm5FDogf/wVEk4EWJs4/UdxU7J6X18dfAwfPbxA==
dependencies: dependencies:
"@azure/identity" "^4.1.0" "@azure/identity" "^4.1.0"
"@vscode/vsce-sign" "^2.0.0" "@vscode/vsce-sign" "^2.0.0"
@ -508,19 +493,19 @@
chalk "^2.4.2" chalk "^2.4.2"
cheerio "^1.0.0-rc.9" cheerio "^1.0.0-rc.9"
cockatiel "^3.1.2" cockatiel "^3.1.2"
commander "^6.2.1" commander "^12.1.0"
form-data "^4.0.0" form-data "^4.0.0"
glob "^7.0.6" glob "^11.0.0"
hosted-git-info "^4.0.2" hosted-git-info "^4.0.2"
jsonc-parser "^3.2.0" jsonc-parser "^3.2.0"
leven "^3.1.0" leven "^3.1.0"
markdown-it "^12.3.2" markdown-it "^14.1.0"
mime "^1.3.4" mime "^1.3.4"
minimatch "^3.0.3" minimatch "^3.0.3"
parse-semver "^1.1.1" parse-semver "^1.1.1"
read "^1.0.7" read "^1.0.7"
semver "^7.5.2" semver "^7.5.2"
tmp "^0.2.1" tmp "^0.2.3"
typed-rest-client "^1.8.4" typed-rest-client "^1.8.4"
url-join "^4.0.1" url-join "^4.0.1"
xml2js "^0.5.0" xml2js "^0.5.0"
@ -583,11 +568,6 @@ argparse@^2.0.1:
resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
array-union@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d"
integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==
asynckit@^0.4.0: asynckit@^0.4.0:
version "0.4.0" version "0.4.0"
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
@ -832,10 +812,10 @@ combined-stream@^1.0.8:
dependencies: dependencies:
delayed-stream "~1.0.0" delayed-stream "~1.0.0"
commander@^6.2.1: commander@^12.1.0:
version "6.2.1" version "12.1.0"
resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c" resolved "https://registry.yarnpkg.com/commander/-/commander-12.1.0.tgz#01423b36f501259fdaac4d0e4d60c96c991585d3"
integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA== integrity sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==
concat-map@0.0.1: concat-map@0.0.1:
version "0.0.1" version "0.0.1"
@ -932,13 +912,6 @@ diff@^5.2.0:
resolved "https://registry.yarnpkg.com/diff/-/diff-5.2.0.tgz#26ded047cd1179b78b9537d5ef725503ce1ae531" resolved "https://registry.yarnpkg.com/diff/-/diff-5.2.0.tgz#26ded047cd1179b78b9537d5ef725503ce1ae531"
integrity sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A== integrity sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==
dir-glob@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f"
integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==
dependencies:
path-type "^4.0.0"
dom-serializer@^2.0.0: dom-serializer@^2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-2.0.0.tgz#e41b802e1eedf9f6cae183ce5e622d789d7d8e53" resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-2.0.0.tgz#e41b802e1eedf9f6cae183ce5e622d789d7d8e53"
@ -1008,11 +981,6 @@ entities@^4.2.0, entities@^4.4.0:
resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48" resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48"
integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==
entities@~2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/entities/-/entities-2.1.0.tgz#992d3129cf7df6870b96c57858c249a120f8b8b5"
integrity sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==
es-define-property@^1.0.0: es-define-property@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845" resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845"
@ -1071,11 +1039,16 @@ escape-string-regexp@^4.0.0:
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3: eslint-visitor-keys@^3.4.3:
version "3.4.3" version "3.4.3"
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800"
integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==
eslint-visitor-keys@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz#687bacb2af884fcdda8a6e7d65c606f46a14cd45"
integrity sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==
events@^3.0.0: events@^3.0.0:
version "3.3.0" version "3.3.0"
resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400"
@ -1086,7 +1059,7 @@ expand-template@^2.0.3:
resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-2.0.3.tgz#6e14b3fcee0f3a6340ecb57d2e8918692052a47c" resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-2.0.3.tgz#6e14b3fcee0f3a6340ecb57d2e8918692052a47c"
integrity sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg== integrity sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==
fast-glob@^3.2.9: fast-glob@^3.3.2:
version "3.3.3" version "3.3.3"
resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.3.tgz#d06d585ce8dba90a16b0505c543c3ccfb3aeb818" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.3.tgz#d06d585ce8dba90a16b0505c543c3ccfb3aeb818"
integrity sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg== integrity sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==
@ -1153,11 +1126,6 @@ fs-constants@^1.0.0:
resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad"
integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==
fs.realpath@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==
fsevents@~2.3.2: fsevents@~2.3.2:
version "2.3.3" version "2.3.3"
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6"
@ -1196,7 +1164,7 @@ glob-parent@^5.1.2, glob-parent@~5.1.2:
dependencies: dependencies:
is-glob "^4.0.1" is-glob "^4.0.1"
glob@^10.4.3, glob@^10.4.5: glob@^10.4.5:
version "10.4.5" version "10.4.5"
resolved "https://registry.yarnpkg.com/glob/-/glob-10.4.5.tgz#f4d9f0b90ffdbab09c9d77f5f29b4262517b0956" resolved "https://registry.yarnpkg.com/glob/-/glob-10.4.5.tgz#f4d9f0b90ffdbab09c9d77f5f29b4262517b0956"
integrity sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg== integrity sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==
@ -1208,29 +1176,17 @@ glob@^10.4.3, glob@^10.4.5:
package-json-from-dist "^1.0.0" package-json-from-dist "^1.0.0"
path-scurry "^1.11.1" path-scurry "^1.11.1"
glob@^7.0.6: glob@^11.0.0, glob@^11.0.1:
version "7.2.3" version "11.0.1"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" resolved "https://registry.yarnpkg.com/glob/-/glob-11.0.1.tgz#1c3aef9a59d680e611b53dcd24bb8639cef064d9"
integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== integrity sha512-zrQDm8XPnYEKawJScsnM0QzobJxlT/kHOOlRTio8IH/GrmxRE5fjllkzdaHclIuNjUQTJYH2xHNIGfdpJkDJUw==
dependencies: dependencies:
fs.realpath "^1.0.0" foreground-child "^3.1.0"
inflight "^1.0.4" jackspeak "^4.0.1"
inherits "2" minimatch "^10.0.0"
minimatch "^3.1.1" minipass "^7.1.2"
once "^1.3.0" package-json-from-dist "^1.0.0"
path-is-absolute "^1.0.0" path-scurry "^2.0.0"
globby@^11.1.0:
version "11.1.0"
resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b"
integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==
dependencies:
array-union "^2.1.0"
dir-glob "^3.0.1"
fast-glob "^3.2.9"
ignore "^5.2.0"
merge2 "^1.4.1"
slash "^3.0.0"
gopd@^1.0.1: gopd@^1.0.1:
version "1.0.1" version "1.0.1"
@ -1321,7 +1277,7 @@ ieee754@^1.1.13, ieee754@^1.2.1:
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
ignore@^5.2.0, ignore@^5.2.4: ignore@^5.3.1:
version "5.3.2" version "5.3.2"
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5"
integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==
@ -1331,15 +1287,7 @@ immediate@~3.0.5:
resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b" resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b"
integrity sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ== integrity sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==
inflight@^1.0.4: inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3:
version "1.0.6"
resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==
dependencies:
once "^1.3.0"
wrappy "1"
inherits@2, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3:
version "2.0.4" version "2.0.4"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
@ -1429,6 +1377,13 @@ jackspeak@^3.1.2:
optionalDependencies: optionalDependencies:
"@pkgjs/parseargs" "^0.11.0" "@pkgjs/parseargs" "^0.11.0"
jackspeak@^4.0.1:
version "4.1.0"
resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-4.1.0.tgz#c489c079f2b636dc4cbe9b0312a13ff1282e561b"
integrity sha512-9DDdhb5j6cpeitCbvLO7n7J4IxnbM6hoF6O1g4HQ5TfhvvKN8ywDM7668ZhMHRqVmxqhps/F6syWK2KcPxYlkw==
dependencies:
"@isaacs/cliui" "^8.0.2"
js-yaml@^4.1.0: js-yaml@^4.1.0:
version "4.1.0" version "4.1.0"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602"
@ -1521,12 +1476,12 @@ lie@~3.3.0:
dependencies: dependencies:
immediate "~3.0.5" immediate "~3.0.5"
linkify-it@^3.0.1: linkify-it@^5.0.0:
version "3.0.3" version "5.0.0"
resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-3.0.3.tgz#a98baf44ce45a550efb4d49c769d07524cc2fa2e" resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-5.0.0.tgz#9ef238bfa6dc70bd8e7f9572b52d369af569b421"
integrity sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ== integrity sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==
dependencies: dependencies:
uc.micro "^1.0.1" uc.micro "^2.0.0"
locate-path@^6.0.0: locate-path@^6.0.0:
version "6.0.0" version "6.0.0"
@ -1591,6 +1546,11 @@ lru-cache@^10.2.0:
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.2.2.tgz#48206bc114c1252940c41b25b41af5b545aca878" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.2.2.tgz#48206bc114c1252940c41b25b41af5b545aca878"
integrity sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ== integrity sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==
lru-cache@^11.0.0:
version "11.0.2"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-11.0.2.tgz#fbd8e7cf8211f5e7e5d91905c415a3f55755ca39"
integrity sha512-123qHRfJBmo2jXDbo/a5YOQrJoHF/GNQTLzQ5+IdK5pWpceK17yRc6ozlWd25FxvGKQbIUs91fDFkXmDHTKcyA==
lru-cache@^6.0.0: lru-cache@^6.0.0:
version "6.0.0" version "6.0.0"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94"
@ -1598,23 +1558,24 @@ lru-cache@^6.0.0:
dependencies: dependencies:
yallist "^4.0.0" yallist "^4.0.0"
markdown-it@^12.3.2: markdown-it@^14.1.0:
version "12.3.2" version "14.1.0"
resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-12.3.2.tgz#bf92ac92283fe983fe4de8ff8abfb5ad72cd0c90" resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-14.1.0.tgz#3c3c5992883c633db4714ccb4d7b5935d98b7d45"
integrity sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg== integrity sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==
dependencies: dependencies:
argparse "^2.0.1" argparse "^2.0.1"
entities "~2.1.0" entities "^4.4.0"
linkify-it "^3.0.1" linkify-it "^5.0.0"
mdurl "^1.0.1" mdurl "^2.0.0"
uc.micro "^1.0.5" punycode.js "^2.3.1"
uc.micro "^2.1.0"
mdurl@^1.0.1: mdurl@^2.0.0:
version "1.0.1" version "2.0.0"
resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e" resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-2.0.0.tgz#80676ec0433025dd3e17ee983d0fe8de5a2237e0"
integrity sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g== integrity sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==
merge2@^1.3.0, merge2@^1.4.1: merge2@^1.3.0:
version "1.4.1" version "1.4.1"
resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae"
integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==
@ -1654,14 +1615,14 @@ mimic-response@^3.1.0:
resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9"
integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==
minimatch@9.0.3: minimatch@^10.0.0:
version "9.0.3" version "10.0.1"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.3.tgz#a6e00c3de44c3a542bfaae70abfc22420a6da825" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-10.0.1.tgz#ce0521856b453c86e25f2c4c0d03e6ff7ddc440b"
integrity sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg== integrity sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==
dependencies: dependencies:
brace-expansion "^2.0.1" brace-expansion "^2.0.1"
minimatch@^3.0.3, minimatch@^3.1.1: minimatch@^3.0.3:
version "3.1.2" version "3.1.2"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
@ -1777,7 +1738,7 @@ object-inspect@^1.13.1:
resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.1.tgz#b96c6109324ccfef6b12216a956ca4dc2ff94bc2" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.1.tgz#b96c6109324ccfef6b12216a956ca4dc2ff94bc2"
integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ== integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==
once@^1.3.0, once@^1.3.1, once@^1.4.0: once@^1.3.1, once@^1.4.0:
version "1.4.0" version "1.4.0"
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==
@ -1866,11 +1827,6 @@ path-exists@^4.0.0:
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3"
integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==
path-is-absolute@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==
path-key@^3.1.0: path-key@^3.1.0:
version "3.1.1" version "3.1.1"
resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375"
@ -1884,10 +1840,13 @@ path-scurry@^1.11.1:
lru-cache "^10.2.0" lru-cache "^10.2.0"
minipass "^5.0.0 || ^6.0.2 || ^7.0.0" minipass "^5.0.0 || ^6.0.2 || ^7.0.0"
path-type@^4.0.0: path-scurry@^2.0.0:
version "4.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-2.0.0.tgz#9f052289f23ad8bf9397a2a0425e7b8615c58580"
integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== integrity sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==
dependencies:
lru-cache "^11.0.0"
minipass "^7.1.2"
pend@~1.2.0: pend@~1.2.0:
version "1.2.0" version "1.2.0"
@ -1930,6 +1889,11 @@ pump@^3.0.0:
end-of-stream "^1.1.0" end-of-stream "^1.1.0"
once "^1.3.1" once "^1.3.1"
punycode.js@^2.3.1:
version "2.3.1"
resolved "https://registry.yarnpkg.com/punycode.js/-/punycode.js-2.3.1.tgz#6b53e56ad75588234e79f4affa90972c7dd8cdb7"
integrity sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==
qs@^6.9.1: qs@^6.9.1:
version "6.12.1" version "6.12.1"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.12.1.tgz#39422111ca7cbdb70425541cba20c7d7b216599a" resolved "https://registry.yarnpkg.com/qs/-/qs-6.12.1.tgz#39422111ca7cbdb70425541cba20c7d7b216599a"
@ -2040,10 +2004,10 @@ semver@^5.1.0:
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8"
integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==
semver@^7.3.5, semver@^7.3.7, semver@^7.5.2, semver@^7.5.4, semver@^7.6.2: semver@^7.3.5, semver@^7.3.7, semver@^7.5.2, semver@^7.5.4, semver@^7.6.0, semver@^7.6.2:
version "7.6.2" version "7.7.1"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.2.tgz#1e3b34759f896e8f14d6134732ce798aeb0c6e13" resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.1.tgz#abd5098d82b18c6c81f6074ff2647fd3e7220c9f"
integrity sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w== integrity sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==
serialize-javascript@^6.0.2: serialize-javascript@^6.0.2:
version "6.0.2" version "6.0.2"
@ -2115,11 +2079,6 @@ simple-get@^4.0.0:
once "^1.3.1" once "^1.3.1"
simple-concat "^1.0.0" simple-concat "^1.0.0"
slash@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"
integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==
stdin-discarder@^0.1.0: stdin-discarder@^0.1.0:
version "0.1.0" version "0.1.0"
resolved "https://registry.yarnpkg.com/stdin-discarder/-/stdin-discarder-0.1.0.tgz#22b3e400393a8e28ebf53f9958f3880622efde21" resolved "https://registry.yarnpkg.com/stdin-discarder/-/stdin-discarder-0.1.0.tgz#22b3e400393a8e28ebf53f9958f3880622efde21"
@ -2255,7 +2214,7 @@ tar-stream@^2.1.4:
inherits "^2.0.3" inherits "^2.0.3"
readable-stream "^3.1.1" readable-stream "^3.1.1"
tmp@^0.2.1: tmp@^0.2.3:
version "0.2.3" version "0.2.3"
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.3.tgz#eb783cc22bc1e8bebd0671476d46ea4eb32a79ae" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.3.tgz#eb783cc22bc1e8bebd0671476d46ea4eb32a79ae"
integrity sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w== integrity sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==
@ -2267,10 +2226,10 @@ to-regex-range@^5.0.1:
dependencies: dependencies:
is-number "^7.0.0" is-number "^7.0.0"
ts-api-utils@^1.0.1: ts-api-utils@^2.0.1:
version "1.4.3" version "2.1.0"
resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.4.3.tgz#bfc2215fe6528fecab2b0fba570a2e8a4263b064" resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-2.1.0.tgz#595f7094e46eed364c13fd23e75f9513d29baf91"
integrity sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw== integrity sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==
tslib@^2.2.0, tslib@^2.6.2: tslib@^2.2.0, tslib@^2.6.2:
version "2.6.2" version "2.6.2"
@ -2303,10 +2262,10 @@ typescript@^5.8.2:
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.8.2.tgz#8170b3702f74b79db2e5a96207c15e65807999e4" resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.8.2.tgz#8170b3702f74b79db2e5a96207c15e65807999e4"
integrity sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ== integrity sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==
uc.micro@^1.0.1, uc.micro@^1.0.5: uc.micro@^2.0.0, uc.micro@^2.1.0:
version "1.0.6" version "2.1.0"
resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac" resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-2.1.0.tgz#f8d3f7d0ec4c3dea35a7e3c8efa4cb8b45c9e7ee"
integrity sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA== integrity sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==
underscore@^1.12.1: underscore@^1.12.1:
version "1.13.6" version "1.13.6"

View File

@ -1,7 +1,7 @@
[package] [package]
name = "kcl-lib" name = "kcl-lib"
description = "KittyCAD Language implementation and tools" description = "KittyCAD Language implementation and tools"
version = "0.2.53" version = "0.2.54"
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
repository = "https://github.com/KittyCAD/modeling-app" repository = "https://github.com/KittyCAD/modeling-app"

View File

@ -837,10 +837,10 @@ holeIndex = 6
// Create the mounting plate extrusion, holes, and fillets // Create the mounting plate extrusion, holes, and fillets
part = rectShape([0, 0], 20, 20) part = rectShape([0, 0], 20, 20)
|> hole(circle(XY, [-holeIndex, holeIndex], holeRadius), %) |> hole(circle('XY', center = [-holeIndex, holeIndex], radius = holeRadius), %)
|> hole(circle(XY, [holeIndex, holeIndex], holeRadius), %) |> hole(circle('XY', center = [holeIndex, holeIndex], radius = holeRadius), %)
|> hole(circle(XY, [-holeIndex, -holeIndex], holeRadius), %) |> hole(circle('XY', center = [-holeIndex, -holeIndex], radius = holeRadius), %)
|> hole(circle(XY, [holeIndex, -holeIndex], holeRadius), %) |> hole(circle('XY', center = [holeIndex, -holeIndex], radius = holeRadius), %)
|> extrude(length = 2) |> extrude(length = 2)
|> fillet( |> fillet(
radius = 4, radius = 4,
@ -860,7 +860,7 @@ part = rectShape([0, 0], 20, 20)
}; };
assert_eq!( assert_eq!(
err.error.message(), err.error.message(),
"This function requires a keyword argument 'center'" "The input argument of std::sketch::circle requires a value with type `Sketch | Plane | Face`, but found string (text)"
); );
} }
@ -1347,7 +1347,7 @@ secondSketch = startSketchOn(part001, '')
assert!(result.is_err()); assert!(result.is_err());
assert_eq!( assert_eq!(
result.err().unwrap().to_string(), result.err().unwrap().to_string(),
r#"semantic: KclErrorDetails { source_ranges: [SourceRange([297, 299, 0])], message: "Argument at index 1 was supposed to be type Option<kcl_lib::std::sketch::FaceTag> but found string (text)" }"# r#"semantic: KclErrorDetails { source_ranges: [SourceRange([297, 299, 0])], message: "Argument at index 1 was supposed to be type Option<FaceTag> but found string (text)" }"#
); );
} }
@ -1983,7 +1983,7 @@ someFunction('INVALID')
assert!(result.is_err()); assert!(result.is_err());
assert_eq!( assert_eq!(
result.err().unwrap().to_string(), result.err().unwrap().to_string(),
r#"semantic: KclErrorDetails { source_ranges: [SourceRange([51, 60, 0]), SourceRange([65, 88, 0])], message: "Argument at index 0 was supposed to be type kcl_lib::std::sketch::SketchData but found string (text)" }"# r#"semantic: KclErrorDetails { source_ranges: [SourceRange([51, 60, 0]), SourceRange([65, 88, 0])], message: "Argument at index 0 was supposed to be type SketchData but found string (text)" }"#
); );
} }
@ -2004,7 +2004,7 @@ someFunction('INVALID')
assert!(result.is_err()); assert!(result.is_err());
assert_eq!( assert_eq!(
result.err().unwrap().to_string(), result.err().unwrap().to_string(),
r#"semantic: KclErrorDetails { source_ranges: [SourceRange([103, 113, 0]), SourceRange([126, 155, 0]), SourceRange([159, 182, 0])], message: "Argument at index 0 was supposed to be type kcl_lib::std::sketch::SketchData but found string (text)" }"# r#"semantic: KclErrorDetails { source_ranges: [SourceRange([103, 113, 0]), SourceRange([126, 155, 0]), SourceRange([159, 182, 0])], message: "Argument at index 0 was supposed to be type SketchData but found string (text)" }"#
); );
} }

View File

@ -467,6 +467,10 @@ fn generate_type_from_kcl(ty: &TyData, file_name: String, example_name: String)
}); });
let output = hbs.render("kclType", &data)?; let output = hbs.render("kclType", &data)?;
let output = cleanup_type_links(
&output,
ty.referenced_types.iter().filter(|t| !DECLARED_TYPES.contains(&&***t)),
);
expectorate::assert_contents(format!("../../docs/kcl/{}.md", file_name), &output); expectorate::assert_contents(format!("../../docs/kcl/{}.md", file_name), &output);
Ok(()) Ok(())
@ -514,6 +518,13 @@ fn generate_function_from_kcl(function: &FnData, file_name: String) -> Result<()
}); });
let output = hbs.render("function", &data)?; let output = hbs.render("function", &data)?;
let output = cleanup_type_links(
&output,
function
.referenced_types
.iter()
.filter(|t| !DECLARED_TYPES.contains(&&***t)),
);
expectorate::assert_contents(format!("../../docs/kcl/{}.md", file_name), &output); expectorate::assert_contents(format!("../../docs/kcl/{}.md", file_name), &output);
Ok(()) Ok(())
@ -677,6 +688,12 @@ fn cleanup_type_links<'a>(output: &str, types: impl Iterator<Item = &'a String>)
} }
} }
// TODO handle union types generically rather than special casing them.
cleaned_output = cleaned_output.replace(
"`Sketch | Plane | Face`",
"[`Sketch`](/docs/kcl/types/Sketch) `|` [`Plane`](/docs/kcl/types/Face) `|` [`Plane`](/docs/kcl/types/Face)",
);
cleanup_static_links(&cleaned_output) cleanup_static_links(&cleaned_output)
} }

View File

@ -1,4 +1,4 @@
use std::str::FromStr; use std::{collections::HashSet, str::FromStr};
use regex::Regex; use regex::Regex;
use tower_lsp::lsp_types::{ use tower_lsp::lsp_types::{
@ -9,7 +9,7 @@ use tower_lsp::lsp_types::{
use crate::{ use crate::{
execution::annotations, execution::annotations,
parsing::{ parsing::{
ast::types::{Annotation, Node, NonCodeNode, VariableKind}, ast::types::{Annotation, Node, PrimitiveType, Type, VariableKind},
token::NumericSuffix, token::NumericSuffix,
}, },
ModuleId, ModuleId,
@ -59,7 +59,6 @@ impl CollectionVisitor {
format!("std::{}::", self.name) format!("std::{}::", self.name)
}; };
let mut dd = match var.kind { let mut dd = match var.kind {
// TODO metadata for args
VariableKind::Fn => DocData::Fn(FnData::from_ast(var, qual_name)), VariableKind::Fn => DocData::Fn(FnData::from_ast(var, qual_name)),
VariableKind::Const => DocData::Const(ConstData::from_ast(var, qual_name)), VariableKind::Const => DocData::Const(ConstData::from_ast(var, qual_name)),
}; };
@ -322,6 +321,8 @@ pub struct FnData {
/// Code examples. /// Code examples.
/// These are tested and we know they compile and execute. /// These are tested and we know they compile and execute.
pub examples: Vec<(String, ExampleProperties)>, pub examples: Vec<(String, ExampleProperties)>,
#[allow(dead_code)]
pub referenced_types: Vec<String>,
} }
impl FnData { impl FnData {
@ -332,6 +333,17 @@ impl FnData {
}; };
let name = var.declaration.id.name.clone(); let name = var.declaration.id.name.clone();
qual_name.push_str(&name); qual_name.push_str(&name);
let mut referenced_types = HashSet::new();
if let Some(t) = &expr.return_type {
collect_type_names(&mut referenced_types, t);
}
for p in &expr.params {
if let Some(t) = &p.type_ {
collect_type_names(&mut referenced_types, t);
}
}
FnData { FnData {
name, name,
qual_name, qual_name,
@ -346,6 +358,7 @@ impl FnData {
summary: None, summary: None,
description: None, description: None,
examples: Vec::new(), examples: Vec::new(),
referenced_types: referenced_types.into_iter().collect(),
} }
} }
@ -414,7 +427,7 @@ impl FnData {
} }
#[allow(clippy::literal_string_with_formatting_args)] #[allow(clippy::literal_string_with_formatting_args)]
fn to_autocomplete_snippet(&self) -> String { pub(super) fn to_autocomplete_snippet(&self) -> String {
if self.name == "loft" { if self.name == "loft" {
return "loft([${0:sketch000}, ${1:sketch001}])${}".to_owned(); return "loft([${0:sketch000}, ${1:sketch001}])${}".to_owned();
} else if self.name == "hole" { } else if self.name == "hole" {
@ -480,12 +493,12 @@ pub struct ArgData {
/// If the argument is required. /// If the argument is required.
pub kind: ArgKind, pub kind: ArgKind,
/// Additional information that could be used instead of the type's description. /// Additional information that could be used instead of the type's description.
/// This is helpful if the type is really basic, like "u32" -- that won't tell the user much about /// This is helpful if the type is really basic, like "number" -- that won't tell the user much about
/// how this argument is meant to be used. /// how this argument is meant to be used.
pub docs: Option<String>, pub docs: Option<String>,
} }
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy, PartialEq)]
pub enum ArgKind { pub enum ArgKind {
Special, Special,
// Parameter is whether the arg is optional. // Parameter is whether the arg is optional.
@ -495,38 +508,47 @@ pub enum ArgKind {
impl ArgData { impl ArgData {
fn from_ast(arg: &crate::parsing::ast::types::Parameter) -> Self { fn from_ast(arg: &crate::parsing::ast::types::Parameter) -> Self {
ArgData { let mut result = ArgData {
name: arg.identifier.name.clone(), name: arg.identifier.name.clone(),
ty: arg.type_.as_ref().map(|t| t.to_string()), ty: arg.type_.as_ref().map(|t| t.to_string()),
// Doc comments are not yet supported on parameters.
docs: None, docs: None,
kind: if arg.labeled { kind: if arg.labeled {
ArgKind::Labelled(arg.optional()) ArgKind::Labelled(arg.optional())
} else { } else {
ArgKind::Special ArgKind::Special
}, },
} };
}
fn _with_meta(&mut self, _meta: &[Node<NonCodeNode>]) { result.with_comments(&arg.identifier.pre_comments);
// TODO use comments for docs (we can't currently get the comments for an argument) result
} }
pub fn get_autocomplete_snippet(&self, index: usize) -> Option<(usize, String)> { pub fn get_autocomplete_snippet(&self, index: usize) -> Option<(usize, String)> {
match &self.ty { let label = if self.kind == ArgKind::Special {
Some(s) String::new()
if [ } else {
"Sketch", format!("{} = ", self.name)
"SketchSet", };
"Solid", match self.ty.as_deref() {
"SolidSet", Some(s) if ["Sketch", "Solid", "Plane | Face", "Sketch | Plane | Face"].contains(&s) => {
"SketchSurface", Some((index, format!("{label}${{{}:{}}}", index, "%")))
"SketchOrSurface",
]
.contains(&&**s) =>
{
Some((index, format!("${{{}:{}}}", index, "%")))
} }
Some("number") if self.kind.required() => Some((index, format!(r#"{label}${{{}:3.14}}"#, index))),
Some("Point2d") if self.kind.required() => Some((
index + 1,
format!(r#"{label}[${{{}:3.14}}, ${{{}:3.14}}]"#, index, index + 1),
)),
Some("Point3d") if self.kind.required() => Some((
index + 2,
format!(
r#"{label}[${{{}:3.14}}, ${{{}:3.14}}, ${{{}:3.14}}]"#,
index,
index + 1,
index + 2
),
)),
Some("string") if self.kind.required() => Some((index, format!(r#"{label}${{{}:"string"}}"#, index))),
Some("bool") if self.kind.required() => Some((index, format!(r#"{label}${{{}:false}}"#, index))),
_ => None, _ => None,
} }
} }
@ -570,12 +592,19 @@ pub struct TyData {
/// Code examples. /// Code examples.
/// These are tested and we know they compile and execute. /// These are tested and we know they compile and execute.
pub examples: Vec<(String, ExampleProperties)>, pub examples: Vec<(String, ExampleProperties)>,
#[allow(dead_code)]
pub referenced_types: Vec<String>,
} }
impl TyData { impl TyData {
fn from_ast(ty: &crate::parsing::ast::types::TypeDeclaration, mut qual_name: String) -> Self { fn from_ast(ty: &crate::parsing::ast::types::TypeDeclaration, mut qual_name: String) -> Self {
let name = ty.name.name.clone(); let name = ty.name.name.clone();
qual_name.push_str(&name); qual_name.push_str(&name);
let mut referenced_types = HashSet::new();
if let Some(t) = &ty.alias {
collect_type_names(&mut referenced_types, t);
}
TyData { TyData {
name, name,
qual_name, qual_name,
@ -589,6 +618,7 @@ impl TyData {
summary: None, summary: None,
description: None, description: None,
examples: Vec::new(), examples: Vec::new(),
referenced_types: referenced_types.into_iter().collect(),
} }
} }
@ -852,6 +882,66 @@ impl ApplyMeta for TyData {
} }
} }
impl ApplyMeta for ArgData {
fn apply_docs(
&mut self,
summary: Option<String>,
description: Option<String>,
_examples: Vec<(String, ExampleProperties)>,
) {
let Some(mut docs) = summary else {
return;
};
if let Some(desc) = description {
docs.push_str("\n\n");
docs.push_str(&desc);
}
self.docs = Some(docs);
}
fn deprecated(&mut self, _deprecated: bool) {
unreachable!();
}
fn doc_hidden(&mut self, _doc_hidden: bool) {
unreachable!();
}
fn impl_kind(&mut self, _impl_kind: annotations::Impl) {
unreachable!();
}
}
fn collect_type_names(acc: &mut HashSet<String>, ty: &Type) {
match ty {
Type::Primitive(primitive_type) => {
acc.insert(collect_type_names_from_primitive(primitive_type));
}
Type::Array { ty, .. } => {
acc.insert(collect_type_names_from_primitive(ty));
}
Type::Union { tys } => tys.iter().for_each(|t| {
acc.insert(collect_type_names_from_primitive(t));
}),
Type::Object { properties } => properties.iter().for_each(|p| {
if let Some(t) = &p.type_ {
collect_type_names(acc, t)
}
}),
}
}
fn collect_type_names_from_primitive(ty: &PrimitiveType) -> String {
match ty {
PrimitiveType::String => "string".to_owned(),
PrimitiveType::Number(_) => "number".to_owned(),
PrimitiveType::Boolean => "bool".to_owned(),
PrimitiveType::Tag => "tag".to_owned(),
PrimitiveType::Named(id) => id.name.clone(),
}
}
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;

View File

@ -927,6 +927,8 @@ fn get_autocomplete_string_from_schema(schema: &schemars::schema::Schema) -> Res
mod tests { mod tests {
use pretty_assertions::assert_eq; use pretty_assertions::assert_eq;
use crate::docs::kcl_doc::{self, DocData};
use super::StdLibFn; use super::StdLibFn;
#[test] #[test]
@ -1007,8 +1009,11 @@ mod tests {
#[test] #[test]
#[allow(clippy::literal_string_with_formatting_args)] #[allow(clippy::literal_string_with_formatting_args)]
fn get_autocomplete_snippet_circle() { fn get_autocomplete_snippet_circle() {
let circle_fn: Box<dyn StdLibFn> = Box::new(crate::std::shapes::Circle); let data = kcl_doc::walk_prelude();
let snippet = circle_fn.to_autocomplete_snippet().unwrap(); let DocData::Fn(circle_fn) = data.into_iter().find(|d| d.name() == "circle").unwrap() else {
panic!();
};
let snippet = circle_fn.to_autocomplete_snippet();
assert_eq!( assert_eq!(
snippet, snippet,
r#"circle(${0:%}, center = [${1:3.14}, ${2:3.14}], radius = ${3:3.14})${}"# r#"circle(${0:%}, center = [${1:3.14}, ${2:3.14}], radius = ${3:3.14})${}"#

View File

@ -78,15 +78,18 @@ pub(super) fn expect_properties<'a>(
} }
pub(super) fn expect_ident(expr: &Expr) -> Result<&str, KclError> { pub(super) fn expect_ident(expr: &Expr) -> Result<&str, KclError> {
match expr { if let Expr::Name(name) = expr {
Expr::Identifier(id) => Ok(&id.name), if let Some(name) = name.local_ident() {
e => Err(KclError::Semantic(KclErrorDetails { return Ok(*name);
message: "Unexpected settings value, expected a simple name, e.g., `mm`".to_owned(),
source_ranges: vec![e.into()],
})),
} }
} }
Err(KclError::Semantic(KclErrorDetails {
message: "Unexpected settings value, expected a simple name, e.g., `mm`".to_owned(),
source_ranges: vec![expr.into()],
}))
}
pub(super) fn get_impl(annotations: &[Node<Annotation>], source_range: SourceRange) -> Result<Option<Impl>, KclError> { pub(super) fn get_impl(annotations: &[Node<Annotation>], source_range: SourceRange) -> Result<Option<Impl>, KclError> {
for attr in annotations { for attr in annotations {
if attr.name.is_some() || attr.properties.is_none() { if attr.name.is_some() || attr.properties.is_none() {

View File

@ -1,6 +1,7 @@
use std::collections::HashMap; use std::collections::HashMap;
use async_recursion::async_recursion; use async_recursion::async_recursion;
use indexmap::IndexMap;
use crate::{ use crate::{
engine::ExecutionKind, engine::ExecutionKind,
@ -19,7 +20,7 @@ use crate::{
parsing::ast::types::{ parsing::ast::types::{
Annotation, ArrayExpression, ArrayRangeExpression, BinaryExpression, BinaryOperator, BinaryPart, BodyItem, Annotation, ArrayExpression, ArrayRangeExpression, BinaryExpression, BinaryOperator, BinaryPart, BodyItem,
CallExpression, CallExpressionKw, Expr, FunctionExpression, IfExpression, ImportPath, ImportSelector, CallExpression, CallExpressionKw, Expr, FunctionExpression, IfExpression, ImportPath, ImportSelector,
ItemVisibility, LiteralIdentifier, LiteralValue, MemberExpression, MemberObject, Node, NodeRef, ItemVisibility, LiteralIdentifier, LiteralValue, MemberExpression, MemberObject, Name, Node, NodeRef,
ObjectExpression, PipeExpression, Program, TagDeclarator, Type, UnaryExpression, UnaryOperator, ObjectExpression, PipeExpression, Program, TagDeclarator, Type, UnaryExpression, UnaryOperator,
}, },
source_range::SourceRange, source_range::SourceRange,
@ -514,15 +515,23 @@ impl ExecutorContext {
source_range: SourceRange, source_range: SourceRange,
) -> Result<Option<KclValue>, KclError> { ) -> Result<Option<KclValue>, KclError> {
let path = exec_state.global.module_infos[&module_id].path.clone(); let path = exec_state.global.module_infos[&module_id].path.clone();
let repr = exec_state.global.module_infos[&module_id].take_repr(); let mut repr = exec_state.global.module_infos[&module_id].take_repr();
// DON'T EARLY RETURN! We need to restore the module repr // DON'T EARLY RETURN! We need to restore the module repr
let result = match &repr { let result = match &mut repr {
ModuleRepr::Root => Err(exec_state.circular_import_error(&path, source_range)), ModuleRepr::Root => Err(exec_state.circular_import_error(&path, source_range)),
ModuleRepr::Kcl(program, _) => self ModuleRepr::Kcl(program, cached_items) => {
let result = self
.exec_module_from_ast(program, module_id, &path, exec_state, exec_kind, source_range) .exec_module_from_ast(program, module_id, &path, exec_state, exec_kind, source_range)
.await .await;
.map(|(val, _, _)| val), match result {
Ok((val, env, items)) => {
*cached_items = Some((env, items));
Ok(val)
}
Err(e) => Err(e),
}
}
ModuleRepr::Foreign(geom) => super::import::send_to_engine(geom.clone(), self) ModuleRepr::Foreign(geom) => super::import::send_to_engine(geom.clone(), self)
.await .await
.map(|geom| Some(KclValue::ImportedGeometry(geom))), .map(|geom| Some(KclValue::ImportedGeometry(geom))),
@ -578,8 +587,8 @@ impl ExecutorContext {
Expr::None(none) => KclValue::from(none), Expr::None(none) => KclValue::from(none),
Expr::Literal(literal) => KclValue::from_literal((**literal).clone(), &exec_state.mod_local.settings), Expr::Literal(literal) => KclValue::from_literal((**literal).clone(), &exec_state.mod_local.settings),
Expr::TagDeclarator(tag) => tag.execute(exec_state).await?, Expr::TagDeclarator(tag) => tag.execute(exec_state).await?,
Expr::Identifier(identifier) => { Expr::Name(name) => {
let value = exec_state.stack().get(&identifier.name, identifier.into())?.clone(); let value = name.get_result(exec_state, self).await?.clone();
if let KclValue::Module { value: module_id, meta } = value { if let KclValue::Module { value: module_id, meta } = value {
self.exec_module_for_result(module_id, exec_state, ExecutionKind::Normal, metadata.source_range) self.exec_module_for_result(module_id, exec_state, ExecutionKind::Normal, metadata.source_range)
.await? .await?
@ -610,7 +619,11 @@ impl ExecutorContext {
if let Some(std_path) = &exec_state.mod_local.settings.std_path { if let Some(std_path) = &exec_state.mod_local.settings.std_path {
let (func, props) = crate::std::std_fn(std_path, statement_kind.expect_name()); let (func, props) = crate::std::std_fn(std_path, statement_kind.expect_name());
KclValue::Function { KclValue::Function {
value: FunctionSource::Std { func, props }, value: FunctionSource::Std {
func,
props,
ast: function_expression.clone(),
},
meta: vec![metadata.to_owned()], meta: vec![metadata.to_owned()],
} }
} else { } else {
@ -710,10 +723,7 @@ impl BinaryPart {
(**literal).clone(), (**literal).clone(),
&exec_state.mod_local.settings, &exec_state.mod_local.settings,
)), )),
BinaryPart::Identifier(identifier) => { BinaryPart::Name(name) => name.get_result(exec_state, ctx).await.cloned(),
let value = exec_state.stack().get(&identifier.name, identifier.into())?;
Ok(value.clone())
}
BinaryPart::BinaryExpression(binary_expression) => binary_expression.get_result(exec_state, ctx).await, BinaryPart::BinaryExpression(binary_expression) => binary_expression.get_result(exec_state, ctx).await,
BinaryPart::CallExpression(call_expression) => call_expression.execute(exec_state, ctx).await, BinaryPart::CallExpression(call_expression) => call_expression.execute(exec_state, ctx).await,
BinaryPart::CallExpressionKw(call_expression) => call_expression.execute(exec_state, ctx).await, BinaryPart::CallExpressionKw(call_expression) => call_expression.execute(exec_state, ctx).await,
@ -724,6 +734,73 @@ impl BinaryPart {
} }
} }
impl Node<Name> {
async fn get_result<'a>(
&self,
exec_state: &'a mut ExecState,
ctx: &ExecutorContext,
) -> Result<&'a KclValue, KclError> {
if self.abs_path {
return Err(KclError::Semantic(KclErrorDetails {
message: "Absolute paths (names beginning with `::` are not yet supported)".to_owned(),
source_ranges: self.as_source_ranges(),
}));
}
if self.path.is_empty() {
return exec_state.stack().get(&self.name.name, self.into());
}
let mut mem_spec: Option<(EnvironmentRef, Vec<String>)> = None;
for p in &self.path {
let value = match mem_spec {
Some((env, exports)) => {
if !exports.contains(&p.name) {
return Err(KclError::Semantic(KclErrorDetails {
message: format!("Item {} not found in module's exported items", p.name),
source_ranges: p.as_source_ranges(),
}));
}
exec_state
.stack()
.memory
.get_from(&p.name, env, p.as_source_range(), 0)?
}
None => exec_state.stack().get(&p.name, self.into())?,
};
let KclValue::Module { value: module_id, .. } = value else {
return Err(KclError::Semantic(KclErrorDetails {
message: format!(
"Identifier in path must refer to a module, found {}",
value.human_friendly_type()
),
source_ranges: p.as_source_ranges(),
}));
};
mem_spec = Some(
ctx.exec_module_for_items(*module_id, exec_state, ExecutionKind::Normal, p.as_source_range())
.await?,
);
}
let (env, exports) = mem_spec.unwrap();
if !exports.contains(&self.name.name) {
return Err(KclError::Semantic(KclErrorDetails {
message: format!("Item {} not found in module's exported items", self.name.name),
source_ranges: self.name.as_source_ranges(),
}));
}
exec_state
.stack()
.memory
.get_from(&self.name.name, env, self.name.as_source_range(), 0)
}
}
impl Node<MemberExpression> { impl Node<MemberExpression> {
fn get_result(&self, exec_state: &mut ExecState) -> Result<KclValue, KclError> { fn get_result(&self, exec_state: &mut ExecState) -> Result<KclValue, KclError> {
let property = Property::try_from(self.computed, self.property.clone(), exec_state, self.into())?; let property = Property::try_from(self.computed, self.property.clone(), exec_state, self.into())?;
@ -1054,11 +1131,11 @@ async fn inner_execute_pipe_body(
impl Node<CallExpressionKw> { impl Node<CallExpressionKw> {
#[async_recursion] #[async_recursion]
pub async fn execute(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> { pub async fn execute(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> {
let fn_name = &self.callee.name; let fn_name = &self.callee;
let callsite: SourceRange = self.into(); let callsite: SourceRange = self.into();
// Build a hashmap from argument labels to the final evaluated values. // Build a hashmap from argument labels to the final evaluated values.
let mut fn_args = HashMap::with_capacity(self.arguments.len()); let mut fn_args = IndexMap::with_capacity(self.arguments.len());
for arg_expr in &self.arguments { for arg_expr in &self.arguments {
let source_range = SourceRange::from(arg_expr.arg.clone()); let source_range = SourceRange::from(arg_expr.arg.clone());
let metadata = Metadata { source_range }; let metadata = Metadata { source_range };
@ -1098,6 +1175,7 @@ impl Node<CallExpressionKw> {
format!("`{fn_name}` is deprecated, see the docs for a recommended replacement"), format!("`{fn_name}` is deprecated, see the docs for a recommended replacement"),
)); ));
} }
let op = if func.feature_tree_operation() { let op = if func.feature_tree_operation() {
let op_labeled_args = args let op_labeled_args = args
.kw_args .kw_args
@ -1118,10 +1196,34 @@ impl Node<CallExpressionKw> {
None None
}; };
let formals = func.args(false);
for (label, arg) in &args.kw_args.labeled {
match formals.iter().find(|p| &p.name == label) {
Some(p) => {
if !p.label_required {
exec_state.err(CompilationError::err(
arg.source_range,
format!(
"The function `{fn_name}` expects an unlabeled first parameter (`{label}`), but it is labelled in the call"
),
));
}
}
None => {
exec_state.err(CompilationError::err(
arg.source_range,
format!("`{label}` is not an argument of `{fn_name}`"),
));
}
}
}
// Attempt to call the function. // Attempt to call the function.
let mut return_value = { let mut return_value = {
// Don't early-return in this block. // Don't early-return in this block.
exec_state.mut_stack().push_new_env_for_rust_call();
let result = func.std_lib_fn()(exec_state, args).await; let result = func.std_lib_fn()(exec_state, args).await;
exec_state.mut_stack().pop_env();
if let Some(mut op) = op { if let Some(mut op) = op {
op.set_std_lib_call_is_error(result.is_err()); op.set_std_lib_call_is_error(result.is_err());
@ -1140,10 +1242,9 @@ impl Node<CallExpressionKw> {
Ok(return_value) Ok(return_value)
} }
FunctionKind::UserDefined => { FunctionKind::UserDefined => {
let source_range = SourceRange::from(self);
// Clone the function so that we can use a mutable reference to // Clone the function so that we can use a mutable reference to
// exec_state. // exec_state.
let func = exec_state.stack().get(fn_name, source_range)?.clone(); let func = fn_name.get_result(exec_state, ctx).await?.clone();
// Track call operation. // Track call operation.
let op_labeled_args = args let op_labeled_args = args
@ -1153,7 +1254,7 @@ impl Node<CallExpressionKw> {
.map(|(k, arg)| (k.clone(), OpArg::new(OpKclValue::from(&arg.value), arg.source_range))) .map(|(k, arg)| (k.clone(), OpArg::new(OpKclValue::from(&arg.value), arg.source_range)))
.collect(); .collect();
exec_state.global.operations.push(Operation::UserDefinedFunctionCall { exec_state.global.operations.push(Operation::UserDefinedFunctionCall {
name: Some(fn_name.clone()), name: Some(fn_name.to_string()),
function_source_range: func.function_def_source_range().unwrap_or_default(), function_source_range: func.function_def_source_range().unwrap_or_default(),
unlabeled_arg: args unlabeled_arg: args
.kw_args .kw_args
@ -1164,17 +1265,21 @@ impl Node<CallExpressionKw> {
source_range: callsite, source_range: callsite,
}); });
let return_value = func let Some(fn_src) = func.as_fn() else {
.call_fn_kw(args, exec_state, ctx.clone(), callsite) return Err(KclError::Semantic(KclErrorDetails {
.await message: "cannot call this because it isn't a function".to_string(),
.map_err(|e| { source_ranges: vec![callsite],
}));
};
let return_value = fn_src.call_kw(exec_state, ctx, args, callsite).await.map_err(|e| {
// Add the call expression to the source ranges. // Add the call expression to the source ranges.
// TODO currently ignored by the frontend // TODO currently ignored by the frontend
e.add_source_ranges(vec![source_range]) e.add_source_ranges(vec![callsite])
})?; })?;
let result = return_value.ok_or_else(move || { let result = return_value.ok_or_else(move || {
let mut source_ranges: Vec<SourceRange> = vec![source_range]; let mut source_ranges: Vec<SourceRange> = vec![callsite];
// We want to send the source range of the original function. // We want to send the source range of the original function.
if let KclValue::Function { meta, .. } = func { if let KclValue::Function { meta, .. } = func {
source_ranges = meta.iter().map(|m| m.source_range).collect(); source_ranges = meta.iter().map(|m| m.source_range).collect();
@ -1197,7 +1302,7 @@ impl Node<CallExpressionKw> {
impl Node<CallExpression> { impl Node<CallExpression> {
#[async_recursion] #[async_recursion]
pub async fn execute(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> { pub async fn execute(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> {
let fn_name = &self.callee.name; let fn_name = &self.callee;
let callsite = SourceRange::from(self); let callsite = SourceRange::from(self);
let mut fn_args: Vec<Arg> = Vec::with_capacity(self.arguments.len()); let mut fn_args: Vec<Arg> = Vec::with_capacity(self.arguments.len());
@ -1279,11 +1384,11 @@ impl Node<CallExpression> {
let source_range = SourceRange::from(self); let source_range = SourceRange::from(self);
// Clone the function so that we can use a mutable reference to // Clone the function so that we can use a mutable reference to
// exec_state. // exec_state.
let func = exec_state.stack().get(fn_name, source_range)?.clone(); let func = fn_name.get_result(exec_state, ctx).await?.clone();
// Track call operation. // Track call operation.
exec_state.global.operations.push(Operation::UserDefinedFunctionCall { exec_state.global.operations.push(Operation::UserDefinedFunctionCall {
name: Some(fn_name.clone()), name: Some(fn_name.to_string()),
function_source_range: func.function_def_source_range().unwrap_or_default(), function_source_range: func.function_def_source_range().unwrap_or_default(),
unlabeled_arg: None, unlabeled_arg: None,
// TODO: Add the arguments for legacy positional parameters. // TODO: Add the arguments for legacy positional parameters.
@ -1291,10 +1396,13 @@ impl Node<CallExpression> {
source_range: callsite, source_range: callsite,
}); });
let return_value = func let Some(fn_src) = func.as_fn() else {
.call_fn(fn_args, exec_state, ctx.clone(), source_range) return Err(KclError::Semantic(KclErrorDetails {
.await message: "cannot call this because it isn't a function".to_string(),
.map_err(|e| { source_ranges: vec![source_range],
}));
};
let return_value = fn_src.call(exec_state, ctx, fn_args, source_range).await.map_err(|e| {
// Add the call expression to the source ranges. // Add the call expression to the source ranges.
// TODO currently ignored by the frontend // TODO currently ignored by the frontend
e.add_source_ranges(vec![source_range]) e.add_source_ranges(vec![source_range])
@ -1784,6 +1892,27 @@ fn assign_args_to_params_kw(
mut args: crate::std::args::KwArgs, mut args: crate::std::args::KwArgs,
exec_state: &mut ExecState, exec_state: &mut ExecState,
) -> Result<(), KclError> { ) -> Result<(), KclError> {
for (label, arg) in &args.labeled {
match function_expression.params.iter().find(|p| &p.identifier.name == label) {
Some(p) => {
if !p.labeled {
exec_state.err(CompilationError::err(
arg.source_range,
format!(
"This function expects an unlabeled first parameter (`{label}`), but it is labelled in the call"
),
));
}
}
None => {
exec_state.err(CompilationError::err(
arg.source_range,
format!("`{label}` is not an argument of this function"),
));
}
}
}
// Add the arguments to the memory. A new call frame should have already // Add the arguments to the memory. A new call frame should have already
// been created. // been created.
let source_ranges = vec![function_expression.into()]; let source_ranges = vec![function_expression.into()];
@ -1832,10 +1961,11 @@ fn assign_args_to_params_kw(
)?; )?;
} }
} }
Ok(()) Ok(())
} }
pub(crate) async fn call_user_defined_function( async fn call_user_defined_function(
args: Vec<Arg>, args: Vec<Arg>,
memory: EnvironmentRef, memory: EnvironmentRef,
function_expression: NodeRef<'_, FunctionExpression>, function_expression: NodeRef<'_, FunctionExpression>,
@ -1868,7 +1998,7 @@ pub(crate) async fn call_user_defined_function(
result result
} }
pub(crate) async fn call_user_defined_function_kw( async fn call_user_defined_function_kw(
args: crate::std::args::KwArgs, args: crate::std::args::KwArgs,
memory: EnvironmentRef, memory: EnvironmentRef,
function_expression: NodeRef<'_, FunctionExpression>, function_expression: NodeRef<'_, FunctionExpression>,
@ -1906,35 +2036,138 @@ impl FunctionSource {
&self, &self,
exec_state: &mut ExecState, exec_state: &mut ExecState,
ctx: &ExecutorContext, ctx: &ExecutorContext,
args: Vec<Arg>, mut args: Vec<Arg>,
source_range: SourceRange, callsite: SourceRange,
) -> Result<Option<KclValue>, KclError> { ) -> Result<Option<KclValue>, KclError> {
match self { match self {
FunctionSource::Std { func, props } => { FunctionSource::Std { props, .. } => {
if args.len() <= 1 {
let args = crate::std::Args::new_kw(
KwArgs {
unlabeled: args.pop(),
labeled: IndexMap::new(),
},
callsite,
ctx.clone(),
exec_state.mod_local.pipe_value.clone().map(|v| Arg::new(v, callsite)),
);
self.call_kw(exec_state, ctx, args, callsite).await
} else {
Err(KclError::Semantic(KclErrorDetails {
message: format!("{} requires its arguments to be labelled", props.name),
source_ranges: vec![callsite],
}))
}
}
FunctionSource::User { ast, memory, .. } => {
call_user_defined_function(args, *memory, ast, exec_state, ctx).await
}
FunctionSource::None => unreachable!(),
}
}
pub async fn call_kw(
&self,
exec_state: &mut ExecState,
ctx: &ExecutorContext,
mut args: crate::std::Args,
callsite: SourceRange,
) -> Result<Option<KclValue>, KclError> {
match self {
FunctionSource::Std { func, ast, props } => {
if props.deprecated { if props.deprecated {
exec_state.warn(CompilationError::err( exec_state.warn(CompilationError::err(
source_range, callsite,
format!( format!(
"`{}` is deprecated, see the docs for a recommended replacement", "`{}` is deprecated, see the docs for a recommended replacement",
props.name props.name
), ),
)); ));
} }
let args = crate::std::Args::new(
args,
source_range,
ctx.clone(),
exec_state
.mod_local
.pipe_value
.clone()
.map(|v| Arg::new(v, source_range)),
);
func(exec_state, args).await.map(Some) for (label, arg) in &mut args.kw_args.labeled {
match ast.params.iter().find(|p| &p.identifier.name == label) {
Some(p) => {
if !p.labeled {
exec_state.err(CompilationError::err(
arg.source_range,
format!(
"The function `{}` expects an unlabeled first parameter (`{label}`), but it is labelled in the call",
props.name
),
));
}
if let Some(ty) = &p.type_ {
arg.value = arg
.value
.coerce(
&RuntimeType::from_parsed(ty.inner.clone(), exec_state, arg.source_range)
.unwrap(),
exec_state,
)
.ok_or_else(|| {
KclError::Semantic(KclErrorDetails {
message: format!(
"{label} requires a value with type `{}`, but found {}",
ty.inner,
arg.value.human_friendly_type()
),
source_ranges: vec![callsite],
})
})?;
}
}
None => {
exec_state.err(CompilationError::err(
arg.source_range,
format!("`{label}` is not an argument of `{}`", props.name),
));
}
}
}
if let Some(arg) = &mut args.kw_args.unlabeled {
if let Some(p) = ast.params.iter().find(|p| !p.labeled) {
if let Some(ty) = &p.type_ {
arg.value = arg
.value
.coerce(
&RuntimeType::from_parsed(ty.inner.clone(), exec_state, arg.source_range).unwrap(),
exec_state,
)
.ok_or_else(|| {
KclError::Semantic(KclErrorDetails {
message: format!(
"The input argument of {} requires a value with type `{}`, but found {}",
props.name,
ty.inner,
arg.value.human_friendly_type()
),
source_ranges: vec![callsite],
})
})?;
}
}
}
// Attempt to call the function.
exec_state.mut_stack().push_new_env_for_rust_call();
let mut result = {
// Don't early-return in this block.
let result = func(exec_state, args).await;
exec_state.mut_stack().pop_env();
// TODO support recording op into the feature tree
result
}?;
update_memory_for_tags_of_geometry(&mut result, exec_state)?;
Ok(Some(result))
} }
FunctionSource::User { ast, memory, .. } => { FunctionSource::User { ast, memory, .. } => {
call_user_defined_function(args, *memory, ast, exec_state, ctx).await call_user_defined_function_kw(args.kw_args, *memory, ast, exec_state, ctx).await
} }
FunctionSource::None => unreachable!(), FunctionSource::None => unreachable!(),
} }

View File

@ -9,13 +9,13 @@ use crate::{
errors::KclErrorDetails, errors::KclErrorDetails,
execution::{ execution::{
types::{NumericType, PrimitiveType, RuntimeType}, types::{NumericType, PrimitiveType, RuntimeType},
ExecState, ExecutorContext, Face, Helix, ImportedGeometry, Metadata, Plane, Sketch, Solid, TagIdentifier, Face, Helix, ImportedGeometry, Metadata, Plane, Sketch, Solid, TagIdentifier,
}, },
parsing::ast::types::{ parsing::ast::types::{
DefaultParamVal, FunctionExpression, KclNone, Literal, LiteralValue, Node, TagDeclarator, TagNode, DefaultParamVal, FunctionExpression, KclNone, Literal, LiteralValue, Node, TagDeclarator, TagNode,
}, },
std::{args::Arg, StdFnProps}, std::StdFnProps,
CompilationError, KclError, ModuleId, SourceRange, KclError, ModuleId, SourceRange,
}; };
pub type KclObjectFields = HashMap<String, KclValue>; pub type KclObjectFields = HashMap<String, KclValue>;
@ -113,6 +113,7 @@ pub enum FunctionSource {
None, None,
Std { Std {
func: crate::std::StdFn, func: crate::std::StdFn,
ast: crate::parsing::ast::types::BoxNode<FunctionExpression>,
props: StdFnProps, props: StdFnProps,
}, },
User { User {
@ -550,91 +551,10 @@ impl KclValue {
Ok(*b) Ok(*b)
} }
/// If this memory item is a function, call it with the given arguments, return its val as Ok. pub fn as_fn(&self) -> Option<&FunctionSource> {
/// If it's not a function, return Err.
pub async fn call_fn(
&self,
args: Vec<Arg>,
exec_state: &mut ExecState,
ctx: ExecutorContext,
source_range: SourceRange,
) -> Result<Option<KclValue>, KclError> {
match self { match self {
KclValue::Function { KclValue::Function { value, .. } => Some(value),
value: FunctionSource::Std { func, props }, _ => None,
..
} => {
if props.deprecated {
exec_state.warn(CompilationError::err(
source_range,
format!(
"`{}` is deprecated, see the docs for a recommended replacement",
props.name
),
));
}
exec_state.mut_stack().push_new_env_for_rust_call();
let args = crate::std::Args::new(
args,
source_range,
ctx.clone(),
exec_state
.mod_local
.pipe_value
.clone()
.map(|v| Arg::new(v, source_range)),
);
let result = func(exec_state, args).await.map(Some);
exec_state.mut_stack().pop_env();
result
}
KclValue::Function {
value: FunctionSource::User { ast, memory, .. },
..
} => crate::execution::exec_ast::call_user_defined_function(args, *memory, ast, exec_state, &ctx).await,
_ => Err(KclError::Semantic(KclErrorDetails {
message: "cannot call this because it isn't a function".to_string(),
source_ranges: vec![source_range],
})),
}
}
/// If this is a function, call it by applying keyword arguments.
/// If it's not a function, returns an error.
pub async fn call_fn_kw(
&self,
args: crate::std::Args,
exec_state: &mut ExecState,
ctx: ExecutorContext,
callsite: SourceRange,
) -> Result<Option<KclValue>, KclError> {
match self {
KclValue::Function {
value: FunctionSource::Std { func: _, props },
..
} => {
if props.deprecated {
exec_state.warn(CompilationError::err(
callsite,
format!(
"`{}` is deprecated, see the docs for a recommended replacement",
props.name
),
));
}
todo!("Implement KCL stdlib fns with keyword args");
}
KclValue::Function {
value: FunctionSource::User { ast, memory, .. },
..
} => {
crate::execution::exec_ast::call_user_defined_function_kw(args.kw_args, *memory, ast, exec_state, &ctx)
.await
}
_ => Err(KclError::Semantic(KclErrorDetails {
message: "cannot call this because it isn't a function".to_string(),
source_ranges: vec![callsite],
})),
} }
} }

View File

@ -31,7 +31,7 @@ pub fn lint_should_be_offset_plane(node: Node) -> Result<Vec<Discovered>> {
return Ok(vec![]); return Ok(vec![]);
}; };
if call.inner.callee.inner.name != "startSketchOn" { if call.inner.callee.inner.name.name != "startSketchOn" {
return Ok(vec![]); return Ok(vec![]);
} }

View File

@ -62,7 +62,7 @@ pub fn lint_call_expressions(exp: Node) -> Result<Vec<Discovered>> {
return Ok(vec![]); return Ok(vec![]);
}; };
match stdlib.get_either(&exp.callee.name) { match stdlib.get_either(&exp.callee) {
FunctionKind::Core(func) => lint_too_many_args_std_lib_function(func, exp), FunctionKind::Core(func) => lint_too_many_args_std_lib_function(func, exp),
_ => Ok(vec![]), _ => Ok(vec![]),
} }

View File

@ -105,16 +105,19 @@ impl Expr {
// TODO: LSP hover information for values/types. https://github.com/KittyCAD/modeling-app/issues/1126 // TODO: LSP hover information for values/types. https://github.com/KittyCAD/modeling-app/issues/1126
Expr::None(_) => None, Expr::None(_) => None,
Expr::Literal(_) => None, Expr::Literal(_) => None,
Expr::Identifier(id) => { Expr::Name(name) => {
if id.contains(pos) { if name.contains(pos) {
let name = id.name.clone(); let ty = if let Some(name) = name.local_ident() {
Some(Hover::Variable { opts.vars
ty: opts
.vars
.as_ref() .as_ref()
.and_then(|vars| vars.get(&name).and_then(Clone::clone)), .and_then(|vars| vars.get(&**name).and_then(Clone::clone))
name, } else {
range: id.as_source_range().to_lsp_range(code), None
};
Some(Hover::Variable {
ty,
name: name.to_string(),
range: name.as_source_range().to_lsp_range(code),
}) })
} else { } else {
None None
@ -137,7 +140,7 @@ impl BinaryPart {
fn get_hover_value_for_position(&self, pos: usize, code: &str, opts: &HoverOpts) -> Option<Hover> { fn get_hover_value_for_position(&self, pos: usize, code: &str, opts: &HoverOpts) -> Option<Hover> {
match self { match self {
BinaryPart::Literal(_literal) => None, BinaryPart::Literal(_literal) => None,
BinaryPart::Identifier(_identifier) => None, BinaryPart::Name(_identifier) => None,
BinaryPart::BinaryExpression(binary_expression) => { BinaryPart::BinaryExpression(binary_expression) => {
binary_expression.get_hover_value_for_position(pos, code, opts) binary_expression.get_hover_value_for_position(pos, code, opts)
} }
@ -163,7 +166,7 @@ impl CallExpression {
let callee_source_range: SourceRange = self.callee.clone().into(); let callee_source_range: SourceRange = self.callee.clone().into();
if callee_source_range.contains(pos) { if callee_source_range.contains(pos) {
return Some(Hover::Function { return Some(Hover::Function {
name: self.callee.name.clone(), name: self.callee.to_string(),
range: callee_source_range.to_lsp_range(code), range: callee_source_range.to_lsp_range(code),
}); });
} }
@ -173,7 +176,7 @@ impl CallExpression {
if source_range.contains(pos) { if source_range.contains(pos) {
return if opts.prefer_sig { return if opts.prefer_sig {
Some(Hover::Signature { Some(Hover::Signature {
name: self.callee.name.clone(), name: self.callee.to_string(),
parameter_index: index as u32, parameter_index: index as u32,
range: source_range.to_lsp_range(code), range: source_range.to_lsp_range(code),
}) })
@ -192,7 +195,7 @@ impl CallExpressionKw {
let callee_source_range: SourceRange = self.callee.clone().into(); let callee_source_range: SourceRange = self.callee.clone().into();
if callee_source_range.contains(pos) { if callee_source_range.contains(pos) {
return Some(Hover::Function { return Some(Hover::Function {
name: self.callee.name.clone(), name: self.callee.to_string(),
range: callee_source_range.to_lsp_range(code), range: callee_source_range.to_lsp_range(code),
}); });
} }
@ -202,7 +205,7 @@ impl CallExpressionKw {
if source_range.contains(pos) { if source_range.contains(pos) {
return if opts.prefer_sig { return if opts.prefer_sig {
Some(Hover::Signature { Some(Hover::Signature {
name: self.callee.name.clone(), name: self.callee.to_string(),
parameter_index: index as u32, parameter_index: index as u32,
range: source_range.to_lsp_range(code), range: source_range.to_lsp_range(code),
}) })
@ -215,7 +218,7 @@ impl CallExpressionKw {
if id.as_source_range().contains(pos) { if id.as_source_range().contains(pos) {
return Some(Hover::KwArg { return Some(Hover::KwArg {
name: id.name.clone(), name: id.name.clone(),
callee_name: self.callee.name.clone(), callee_name: self.callee.to_string(),
range: id.as_source_range().to_lsp_range(code), range: id.as_source_range().to_lsp_range(code),
}); });
} }

View File

@ -523,7 +523,7 @@ impl Backend {
None => token_type_index, None => token_type_index,
}; };
if self.stdlib_completions.contains_key(&call_expr.callee.name) { if self.stdlib_completions.contains_key(&call_expr.callee.name.name) {
// This is a stdlib function. // This is a stdlib function.
return get_modifier(vec![SemanticTokenModifier::DEFAULT_LIBRARY]); return get_modifier(vec![SemanticTokenModifier::DEFAULT_LIBRARY]);
} }

View File

@ -87,6 +87,7 @@ pub(crate) fn read_std(mod_name: &str) -> Option<&'static str> {
match mod_name { match mod_name {
"prelude" => Some(include_str!("../std/prelude.kcl")), "prelude" => Some(include_str!("../std/prelude.kcl")),
"math" => Some(include_str!("../std/math.kcl")), "math" => Some(include_str!("../std/math.kcl")),
"sketch" => Some(include_str!("../std/sketch.kcl")),
_ => None, _ => None,
} }
} }

View File

@ -4,7 +4,7 @@ use crate::parsing::ast::types::{
Annotation, ArrayExpression, ArrayRangeExpression, Ascription, BinaryExpression, BinaryPart, BodyItem, Annotation, ArrayExpression, ArrayRangeExpression, Ascription, BinaryExpression, BinaryPart, BodyItem,
CallExpression, CallExpressionKw, DefaultParamVal, ElseIf, Expr, ExpressionStatement, FunctionExpression, CallExpression, CallExpressionKw, DefaultParamVal, ElseIf, Expr, ExpressionStatement, FunctionExpression,
Identifier, IfExpression, ImportItem, ImportSelector, ImportStatement, ItemVisibility, KclNone, LabelledExpression, Identifier, IfExpression, ImportItem, ImportSelector, ImportStatement, ItemVisibility, KclNone, LabelledExpression,
Literal, LiteralIdentifier, LiteralValue, MemberExpression, MemberObject, ObjectExpression, ObjectProperty, Literal, LiteralIdentifier, LiteralValue, MemberExpression, MemberObject, Name, ObjectExpression, ObjectProperty,
Parameter, PipeExpression, PipeSubstitution, PrimitiveType, Program, ReturnStatement, TagDeclarator, Type, Parameter, PipeExpression, PipeSubstitution, PrimitiveType, Program, ReturnStatement, TagDeclarator, Type,
TypeDeclaration, UnaryExpression, VariableDeclaration, VariableDeclarator, VariableKind, TypeDeclaration, UnaryExpression, VariableDeclaration, VariableDeclarator, VariableKind,
}; };
@ -128,7 +128,7 @@ impl Expr {
pub fn compute_digest(&mut self) -> Digest { pub fn compute_digest(&mut self) -> Digest {
match self { match self {
Expr::Literal(lit) => lit.compute_digest(), Expr::Literal(lit) => lit.compute_digest(),
Expr::Identifier(id) => id.compute_digest(), Expr::Name(id) => id.compute_digest(),
Expr::TagDeclarator(tag) => tag.compute_digest(), Expr::TagDeclarator(tag) => tag.compute_digest(),
Expr::BinaryExpression(be) => be.compute_digest(), Expr::BinaryExpression(be) => be.compute_digest(),
Expr::FunctionExpression(fe) => fe.compute_digest(), Expr::FunctionExpression(fe) => fe.compute_digest(),
@ -157,7 +157,7 @@ impl BinaryPart {
pub fn compute_digest(&mut self) -> Digest { pub fn compute_digest(&mut self) -> Digest {
match self { match self {
BinaryPart::Literal(lit) => lit.compute_digest(), BinaryPart::Literal(lit) => lit.compute_digest(),
BinaryPart::Identifier(id) => id.compute_digest(), BinaryPart::Name(id) => id.compute_digest(),
BinaryPart::BinaryExpression(be) => be.compute_digest(), BinaryPart::BinaryExpression(be) => be.compute_digest(),
BinaryPart::CallExpression(ce) => ce.compute_digest(), BinaryPart::CallExpression(ce) => ce.compute_digest(),
BinaryPart::CallExpressionKw(ce) => ce.compute_digest(), BinaryPart::CallExpressionKw(ce) => ce.compute_digest(),
@ -377,6 +377,17 @@ impl Identifier {
}); });
} }
impl Name {
compute_digest!(|slf, hasher| {
hasher.update(slf.name.compute_digest());
for p in &mut slf.path {
hasher.update(p.compute_digest());
}
if slf.abs_path {
hasher.update([1]);
}
});
}
impl TagDeclarator { impl TagDeclarator {
compute_digest!(|slf, hasher| { compute_digest!(|slf, hasher| {
let name = slf.name.as_bytes(); let name = slf.name.as_bytes();

View File

@ -23,7 +23,7 @@ impl Expr {
pub fn module_id(&self) -> ModuleId { pub fn module_id(&self) -> ModuleId {
match self { match self {
Expr::Literal(literal) => literal.module_id, Expr::Literal(literal) => literal.module_id,
Expr::Identifier(identifier) => identifier.module_id, Expr::Name(identifier) => identifier.module_id,
Expr::TagDeclarator(tag) => tag.module_id, Expr::TagDeclarator(tag) => tag.module_id,
Expr::BinaryExpression(binary_expression) => binary_expression.module_id, Expr::BinaryExpression(binary_expression) => binary_expression.module_id,
Expr::FunctionExpression(function_expression) => function_expression.module_id, Expr::FunctionExpression(function_expression) => function_expression.module_id,
@ -48,7 +48,7 @@ impl BinaryPart {
pub fn module_id(&self) -> ModuleId { pub fn module_id(&self) -> ModuleId {
match self { match self {
BinaryPart::Literal(literal) => literal.module_id, BinaryPart::Literal(literal) => literal.module_id,
BinaryPart::Identifier(identifier) => identifier.module_id, BinaryPart::Name(identifier) => identifier.module_id,
BinaryPart::BinaryExpression(binary_expression) => binary_expression.module_id, BinaryPart::BinaryExpression(binary_expression) => binary_expression.module_id,
BinaryPart::CallExpression(call_expression) => call_expression.module_id, BinaryPart::CallExpression(call_expression) => call_expression.module_id,
BinaryPart::CallExpressionKw(call_expression) => call_expression.module_id, BinaryPart::CallExpressionKw(call_expression) => call_expression.module_id,

View File

@ -35,6 +35,7 @@ mod condition;
mod literal_value; mod literal_value;
mod none; mod none;
#[derive(Debug)]
pub enum Definition<'a> { pub enum Definition<'a> {
Variable(&'a VariableDeclarator), Variable(&'a VariableDeclarator),
Import(NodeRef<'a, ImportStatement>), Import(NodeRef<'a, ImportStatement>),
@ -166,6 +167,18 @@ impl<T> Node<T> {
self.pre_comments = comments; self.pre_comments = comments;
self.comment_start = start; self.comment_start = start;
} }
pub fn map_ref<'a, U: 'a>(&'a self, f: fn(&'a T) -> U) -> Node<U> {
Node {
inner: f(&self.inner),
start: self.start,
end: self.end,
module_id: self.module_id,
outer_attrs: self.outer_attrs.clone(),
pre_comments: self.pre_comments.clone(),
comment_start: self.start,
}
}
} }
impl<T> Deref for Node<T> { impl<T> Deref for Node<T> {
@ -762,7 +775,7 @@ impl From<&BodyItem> for SourceRange {
#[allow(clippy::large_enum_variant)] #[allow(clippy::large_enum_variant)]
pub enum Expr { pub enum Expr {
Literal(BoxNode<Literal>), Literal(BoxNode<Literal>),
Identifier(BoxNode<Identifier>), Name(BoxNode<Name>),
TagDeclarator(BoxNode<TagDeclarator>), TagDeclarator(BoxNode<TagDeclarator>),
BinaryExpression(BoxNode<BinaryExpression>), BinaryExpression(BoxNode<BinaryExpression>),
FunctionExpression(BoxNode<FunctionExpression>), FunctionExpression(BoxNode<FunctionExpression>),
@ -814,7 +827,7 @@ impl Expr {
Expr::FunctionExpression(_func_exp) => None, Expr::FunctionExpression(_func_exp) => None,
Expr::CallExpression(_call_exp) => None, Expr::CallExpression(_call_exp) => None,
Expr::CallExpressionKw(_call_exp) => None, Expr::CallExpressionKw(_call_exp) => None,
Expr::Identifier(_ident) => None, Expr::Name(_ident) => None,
Expr::TagDeclarator(_tag) => None, Expr::TagDeclarator(_tag) => None,
Expr::PipeExpression(pipe_exp) => Some(&pipe_exp.non_code_meta), Expr::PipeExpression(pipe_exp) => Some(&pipe_exp.non_code_meta),
Expr::UnaryExpression(_unary_exp) => None, Expr::UnaryExpression(_unary_exp) => None,
@ -842,7 +855,7 @@ impl Expr {
Expr::FunctionExpression(ref mut func_exp) => func_exp.replace_value(source_range, new_value), Expr::FunctionExpression(ref mut func_exp) => func_exp.replace_value(source_range, new_value),
Expr::CallExpression(ref mut call_exp) => call_exp.replace_value(source_range, new_value), Expr::CallExpression(ref mut call_exp) => call_exp.replace_value(source_range, new_value),
Expr::CallExpressionKw(ref mut call_exp) => call_exp.replace_value(source_range, new_value), Expr::CallExpressionKw(ref mut call_exp) => call_exp.replace_value(source_range, new_value),
Expr::Identifier(_) => {} Expr::Name(_) => {}
Expr::TagDeclarator(_) => {} Expr::TagDeclarator(_) => {}
Expr::PipeExpression(ref mut pipe_exp) => pipe_exp.replace_value(source_range, new_value), Expr::PipeExpression(ref mut pipe_exp) => pipe_exp.replace_value(source_range, new_value),
Expr::UnaryExpression(ref mut unary_exp) => unary_exp.replace_value(source_range, new_value), Expr::UnaryExpression(ref mut unary_exp) => unary_exp.replace_value(source_range, new_value),
@ -857,7 +870,7 @@ impl Expr {
pub fn start(&self) -> usize { pub fn start(&self) -> usize {
match self { match self {
Expr::Literal(literal) => literal.start, Expr::Literal(literal) => literal.start,
Expr::Identifier(identifier) => identifier.start, Expr::Name(identifier) => identifier.start,
Expr::TagDeclarator(tag) => tag.start, Expr::TagDeclarator(tag) => tag.start,
Expr::BinaryExpression(binary_expression) => binary_expression.start, Expr::BinaryExpression(binary_expression) => binary_expression.start,
Expr::FunctionExpression(function_expression) => function_expression.start, Expr::FunctionExpression(function_expression) => function_expression.start,
@ -880,7 +893,7 @@ impl Expr {
pub fn end(&self) -> usize { pub fn end(&self) -> usize {
match self { match self {
Expr::Literal(literal) => literal.end, Expr::Literal(literal) => literal.end,
Expr::Identifier(identifier) => identifier.end, Expr::Name(identifier) => identifier.end,
Expr::TagDeclarator(tag) => tag.end, Expr::TagDeclarator(tag) => tag.end,
Expr::BinaryExpression(binary_expression) => binary_expression.end, Expr::BinaryExpression(binary_expression) => binary_expression.end,
Expr::FunctionExpression(function_expression) => function_expression.end, Expr::FunctionExpression(function_expression) => function_expression.end,
@ -904,7 +917,7 @@ impl Expr {
fn rename_identifiers(&mut self, old_name: &str, new_name: &str) { fn rename_identifiers(&mut self, old_name: &str, new_name: &str) {
match self { match self {
Expr::Literal(_literal) => {} Expr::Literal(_literal) => {}
Expr::Identifier(ref mut identifier) => identifier.rename(old_name, new_name), Expr::Name(ref mut identifier) => identifier.rename(old_name, new_name),
Expr::TagDeclarator(ref mut tag) => tag.rename(old_name, new_name), Expr::TagDeclarator(ref mut tag) => tag.rename(old_name, new_name),
Expr::BinaryExpression(ref mut binary_expression) => { Expr::BinaryExpression(ref mut binary_expression) => {
binary_expression.rename_identifiers(old_name, new_name) binary_expression.rename_identifiers(old_name, new_name)
@ -934,7 +947,7 @@ impl Expr {
pub fn get_constraint_level(&self) -> ConstraintLevel { pub fn get_constraint_level(&self) -> ConstraintLevel {
match self { match self {
Expr::Literal(literal) => literal.get_constraint_level(), Expr::Literal(literal) => literal.get_constraint_level(),
Expr::Identifier(identifier) => identifier.get_constraint_level(), Expr::Name(identifier) => identifier.get_constraint_level(),
Expr::TagDeclarator(tag) => tag.get_constraint_level(), Expr::TagDeclarator(tag) => tag.get_constraint_level(),
Expr::BinaryExpression(binary_expression) => binary_expression.get_constraint_level(), Expr::BinaryExpression(binary_expression) => binary_expression.get_constraint_level(),
@ -967,35 +980,6 @@ impl Expr {
} }
} }
/// Describe this expression's type for a human, for typechecking.
/// This is a best-effort function, it's OK to give a shitty string here (but we should work on improving it)
pub fn human_friendly_type(&self) -> &'static str {
match self {
Expr::Literal(node) => match node.inner.value {
LiteralValue::Number { .. } => "number",
LiteralValue::String(_) => "string (text)",
LiteralValue::Bool(_) => "boolean (true/false value)",
},
Expr::Identifier(_) => "named constant",
Expr::TagDeclarator(_) => "tag declarator",
Expr::BinaryExpression(_) => "expression",
Expr::FunctionExpression(_) => "function definition",
Expr::CallExpression(_) => "function call",
Expr::CallExpressionKw(_) => "function call",
Expr::PipeExpression(_) => "pipeline of function calls",
Expr::PipeSubstitution(_) => "left-hand side of a |> pipeline",
Expr::ArrayExpression(_) => "array",
Expr::ArrayRangeExpression(_) => "array",
Expr::ObjectExpression(_) => "object",
Expr::MemberExpression(_) => "property of an object/array",
Expr::UnaryExpression(_) => "expression",
Expr::IfExpression(_) => "if expression",
Expr::LabelledExpression(_) => "labelled expression",
Expr::AscribedExpression(_) => "type-ascribed expression",
Expr::None(_) => "none",
}
}
pub fn literal_bool(&self) -> Option<bool> { pub fn literal_bool(&self) -> Option<bool> {
match self { match self {
Expr::Literal(lit) => match lit.value { Expr::Literal(lit) => match lit.value {
@ -1028,7 +1012,7 @@ impl Expr {
pub fn ident_name(&self) -> Option<&str> { pub fn ident_name(&self) -> Option<&str> {
match self { match self {
Expr::Identifier(ident) => Some(&ident.name), Expr::Name(ident) => Some(&ident.name.name),
_ => None, _ => None,
} }
} }
@ -1102,7 +1086,7 @@ impl Ascription {
#[serde(tag = "type")] #[serde(tag = "type")]
pub enum BinaryPart { pub enum BinaryPart {
Literal(BoxNode<Literal>), Literal(BoxNode<Literal>),
Identifier(BoxNode<Identifier>), Name(BoxNode<Name>),
BinaryExpression(BoxNode<BinaryExpression>), BinaryExpression(BoxNode<BinaryExpression>),
CallExpression(BoxNode<CallExpression>), CallExpression(BoxNode<CallExpression>),
CallExpressionKw(BoxNode<CallExpressionKw>), CallExpressionKw(BoxNode<CallExpressionKw>),
@ -1128,7 +1112,7 @@ impl BinaryPart {
pub fn get_constraint_level(&self) -> ConstraintLevel { pub fn get_constraint_level(&self) -> ConstraintLevel {
match self { match self {
BinaryPart::Literal(literal) => literal.get_constraint_level(), BinaryPart::Literal(literal) => literal.get_constraint_level(),
BinaryPart::Identifier(identifier) => identifier.get_constraint_level(), BinaryPart::Name(identifier) => identifier.get_constraint_level(),
BinaryPart::BinaryExpression(binary_expression) => binary_expression.get_constraint_level(), BinaryPart::BinaryExpression(binary_expression) => binary_expression.get_constraint_level(),
BinaryPart::CallExpression(call_expression) => call_expression.get_constraint_level(), BinaryPart::CallExpression(call_expression) => call_expression.get_constraint_level(),
BinaryPart::CallExpressionKw(call_expression) => call_expression.get_constraint_level(), BinaryPart::CallExpressionKw(call_expression) => call_expression.get_constraint_level(),
@ -1141,7 +1125,7 @@ impl BinaryPart {
pub fn replace_value(&mut self, source_range: SourceRange, new_value: Expr) { pub fn replace_value(&mut self, source_range: SourceRange, new_value: Expr) {
match self { match self {
BinaryPart::Literal(_) => {} BinaryPart::Literal(_) => {}
BinaryPart::Identifier(_) => {} BinaryPart::Name(_) => {}
BinaryPart::BinaryExpression(ref mut binary_expression) => { BinaryPart::BinaryExpression(ref mut binary_expression) => {
binary_expression.replace_value(source_range, new_value) binary_expression.replace_value(source_range, new_value)
} }
@ -1162,7 +1146,7 @@ impl BinaryPart {
pub fn start(&self) -> usize { pub fn start(&self) -> usize {
match self { match self {
BinaryPart::Literal(literal) => literal.start, BinaryPart::Literal(literal) => literal.start,
BinaryPart::Identifier(identifier) => identifier.start, BinaryPart::Name(identifier) => identifier.start,
BinaryPart::BinaryExpression(binary_expression) => binary_expression.start, BinaryPart::BinaryExpression(binary_expression) => binary_expression.start,
BinaryPart::CallExpression(call_expression) => call_expression.start, BinaryPart::CallExpression(call_expression) => call_expression.start,
BinaryPart::CallExpressionKw(call_expression) => call_expression.start, BinaryPart::CallExpressionKw(call_expression) => call_expression.start,
@ -1175,7 +1159,7 @@ impl BinaryPart {
pub fn end(&self) -> usize { pub fn end(&self) -> usize {
match self { match self {
BinaryPart::Literal(literal) => literal.end, BinaryPart::Literal(literal) => literal.end,
BinaryPart::Identifier(identifier) => identifier.end, BinaryPart::Name(identifier) => identifier.end,
BinaryPart::BinaryExpression(binary_expression) => binary_expression.end, BinaryPart::BinaryExpression(binary_expression) => binary_expression.end,
BinaryPart::CallExpression(call_expression) => call_expression.end, BinaryPart::CallExpression(call_expression) => call_expression.end,
BinaryPart::CallExpressionKw(call_expression) => call_expression.end, BinaryPart::CallExpressionKw(call_expression) => call_expression.end,
@ -1189,7 +1173,7 @@ impl BinaryPart {
fn rename_identifiers(&mut self, old_name: &str, new_name: &str) { fn rename_identifiers(&mut self, old_name: &str, new_name: &str) {
match self { match self {
BinaryPart::Literal(_literal) => {} BinaryPart::Literal(_literal) => {}
BinaryPart::Identifier(ref mut identifier) => identifier.rename(old_name, new_name), BinaryPart::Name(ref mut identifier) => identifier.rename(old_name, new_name),
BinaryPart::BinaryExpression(ref mut binary_expression) => { BinaryPart::BinaryExpression(ref mut binary_expression) => {
binary_expression.rename_identifiers(old_name, new_name) binary_expression.rename_identifiers(old_name, new_name)
} }
@ -1418,13 +1402,13 @@ impl Annotation {
pub fn new_from_meta_settings(settings: &crate::execution::MetaSettings) -> Annotation { pub fn new_from_meta_settings(settings: &crate::execution::MetaSettings) -> Annotation {
let mut properties: Vec<Node<ObjectProperty>> = vec![ObjectProperty::new( let mut properties: Vec<Node<ObjectProperty>> = vec![ObjectProperty::new(
Identifier::new(annotations::SETTINGS_UNIT_LENGTH), Identifier::new(annotations::SETTINGS_UNIT_LENGTH),
Expr::Identifier(Box::new(Identifier::new(&settings.default_length_units.to_string()))), Expr::Name(Box::new(Name::new(&settings.default_length_units.to_string()))),
)]; )];
if settings.default_angle_units != Default::default() { if settings.default_angle_units != Default::default() {
properties.push(ObjectProperty::new( properties.push(ObjectProperty::new(
Identifier::new(annotations::SETTINGS_UNIT_ANGLE), Identifier::new(annotations::SETTINGS_UNIT_ANGLE),
Expr::Identifier(Box::new(Identifier::new(&settings.default_angle_units.to_string()))), Expr::Name(Box::new(Name::new(&settings.default_angle_units.to_string()))),
)); ));
} }
Annotation { Annotation {
@ -1689,7 +1673,7 @@ pub struct ExpressionStatement {
#[ts(export)] #[ts(export)]
#[serde(tag = "type")] #[serde(tag = "type")]
pub struct CallExpression { pub struct CallExpression {
pub callee: Node<Identifier>, pub callee: Node<Name>,
pub arguments: Vec<Expr>, pub arguments: Vec<Expr>,
#[serde(default, skip_serializing_if = "Option::is_none")] #[serde(default, skip_serializing_if = "Option::is_none")]
@ -1701,7 +1685,7 @@ pub struct CallExpression {
#[ts(export)] #[ts(export)]
#[serde(rename_all = "camelCase", tag = "type")] #[serde(rename_all = "camelCase", tag = "type")]
pub struct CallExpressionKw { pub struct CallExpressionKw {
pub callee: Node<Identifier>, pub callee: Node<Name>,
pub unlabeled: Option<Expr>, pub unlabeled: Option<Expr>,
#[serde(default, skip_serializing_if = "Vec::is_empty")] #[serde(default, skip_serializing_if = "Vec::is_empty")]
pub arguments: Vec<LabeledArg>, pub arguments: Vec<LabeledArg>,
@ -1775,7 +1759,7 @@ impl Node<CallExpressionKw> {
impl CallExpression { impl CallExpression {
pub fn new(name: &str, arguments: Vec<Expr>) -> Result<Node<Self>, KclError> { pub fn new(name: &str, arguments: Vec<Expr>) -> Result<Node<Self>, KclError> {
Ok(Node::no_src(Self { Ok(Node::no_src(Self {
callee: Identifier::new(name), callee: Name::new(name),
arguments, arguments,
digest: None, digest: None,
})) }))
@ -1807,7 +1791,7 @@ impl CallExpression {
impl CallExpressionKw { impl CallExpressionKw {
pub fn new(name: &str, unlabeled: Option<Expr>, arguments: Vec<LabeledArg>) -> Result<Node<Self>, KclError> { pub fn new(name: &str, unlabeled: Option<Expr>, arguments: Vec<LabeledArg>) -> Result<Node<Self>, KclError> {
Ok(Node::no_src(Self { Ok(Node::no_src(Self {
callee: Identifier::new(name), callee: Name::new(name),
unlabeled, unlabeled,
arguments, arguments,
digest: None, digest: None,
@ -2192,13 +2176,8 @@ impl Node<Identifier> {
/// Get the constraint level for this identifier. /// Get the constraint level for this identifier.
/// Identifier are always fully constrained. /// Identifier are always fully constrained.
pub fn get_constraint_level(&self) -> ConstraintLevel { pub fn get_constraint_level(&self) -> ConstraintLevel {
match &*self.name { ConstraintLevel::Full {
"XY" | "XZ" | "YZ" => ConstraintLevel::None {
source_ranges: vec![self.into()], source_ranges: vec![self.into()],
},
_ => ConstraintLevel::Full {
source_ranges: vec![self.into()],
},
} }
} }
} }
@ -2223,6 +2202,99 @@ impl Identifier {
} }
} }
/// A qualified name, e.g., `foo`, `bar::foo`, or `::bar::foo`.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
#[ts(export)]
#[serde(tag = "type")]
pub struct Name {
pub name: Node<Identifier>,
// The qualifying parts of the name.
pub path: NodeList<Identifier>,
// The path starts with `::`.
pub abs_path: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[ts(optional)]
pub digest: Option<Digest>,
}
impl Node<Name> {
pub fn get_constraint_level(&self) -> ConstraintLevel {
match &*self.name.name {
"XY" | "XZ" | "YZ" => ConstraintLevel::None {
source_ranges: vec![self.into()],
},
_ => ConstraintLevel::Full {
source_ranges: vec![self.into()],
},
}
}
}
impl Name {
pub fn new(name: &str) -> Node<Self> {
Node::no_src(Name {
name: Node::no_src(Identifier {
name: name.to_string(),
digest: None,
}),
path: Vec::new(),
abs_path: false,
digest: None,
})
}
pub fn local_ident(&self) -> Option<Node<&str>> {
if self.path.is_empty() && !self.abs_path {
Some(self.name.map_ref(|n| &n.name))
} else {
None
}
}
/// Rename all identifiers that have the old name to the new given name.
fn rename(&mut self, old_name: &str, new_name: &str) {
if let Some(n) = self.local_ident() {
if n.inner == old_name {
self.name.name = new_name.to_owned();
}
}
}
}
impl From<Node<Identifier>> for Node<Name> {
fn from(value: Node<Identifier>) -> Self {
let start = value.start;
let end = value.end;
let mod_id = value.module_id;
Node::new(
Name {
name: value,
path: Vec::new(),
abs_path: false,
digest: None,
},
start,
end,
mod_id,
)
}
}
impl fmt::Display for Name {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.abs_path {
f.write_str("::")?;
}
for p in &self.path {
f.write_str(&p.name)?;
f.write_str("::")?;
}
f.write_str(&self.name.name)
}
}
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, Eq)] #[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, Eq)]
#[ts(export)] #[ts(export)]
#[serde(tag = "type")] #[serde(tag = "type")]

View File

@ -60,11 +60,8 @@ pub fn parse_tokens(mut tokens: TokenStream) -> ParseResult {
return Node::<Program>::default().into(); return Node::<Program>::default().into();
} }
// Check all the tokens are whitespace or comments. // Check all the tokens are whitespace.
if tokens if tokens.iter().all(|t| t.token_type.is_whitespace()) {
.iter()
.all(|t| t.token_type.is_whitespace() || t.token_type.is_comment())
{
return Node::<Program>::default().into(); return Node::<Program>::default().into();
} }

View File

@ -18,7 +18,6 @@ use super::{
DeprecationKind, DeprecationKind,
}; };
use crate::{ use crate::{
docs::StdLibFn,
errors::{CompilationError, Severity, Tag}, errors::{CompilationError, Severity, Tag},
execution::types::ArrayLen, execution::types::ArrayLen,
parsing::{ parsing::{
@ -27,9 +26,9 @@ use crate::{
BoxNode, CallExpression, CallExpressionKw, CommentStyle, DefaultParamVal, ElseIf, Expr, BoxNode, CallExpression, CallExpressionKw, CommentStyle, DefaultParamVal, ElseIf, Expr,
ExpressionStatement, FunctionExpression, Identifier, IfExpression, ImportItem, ImportSelector, ExpressionStatement, FunctionExpression, Identifier, IfExpression, ImportItem, ImportSelector,
ImportStatement, ItemVisibility, LabeledArg, Literal, LiteralIdentifier, LiteralValue, MemberExpression, ImportStatement, ItemVisibility, LabeledArg, Literal, LiteralIdentifier, LiteralValue, MemberExpression,
MemberObject, Node, NodeList, NonCodeMeta, NonCodeNode, NonCodeValue, ObjectExpression, ObjectProperty, MemberObject, Name, Node, NodeList, NonCodeMeta, NonCodeNode, NonCodeValue, ObjectExpression,
Parameter, PipeExpression, PipeSubstitution, PrimitiveType, Program, ReturnStatement, Shebang, ObjectProperty, Parameter, PipeExpression, PipeSubstitution, PrimitiveType, Program, ReturnStatement,
TagDeclarator, Type, TypeDeclaration, UnaryExpression, UnaryOperator, VariableDeclaration, Shebang, TagDeclarator, Type, TypeDeclaration, UnaryExpression, UnaryOperator, VariableDeclaration,
VariableDeclarator, VariableKind, VariableDeclarator, VariableKind,
}, },
math::BinaryExpressionToken, math::BinaryExpressionToken,
@ -631,7 +630,7 @@ fn operand(i: &mut TokenSlice) -> PResult<BinaryPart> {
} }
Expr::UnaryExpression(x) => BinaryPart::UnaryExpression(x), Expr::UnaryExpression(x) => BinaryPart::UnaryExpression(x),
Expr::Literal(x) => BinaryPart::Literal(x), Expr::Literal(x) => BinaryPart::Literal(x),
Expr::Identifier(x) => BinaryPart::Identifier(x), Expr::Name(x) => BinaryPart::Name(x),
Expr::BinaryExpression(x) => BinaryPart::BinaryExpression(x), Expr::BinaryExpression(x) => BinaryPart::BinaryExpression(x),
Expr::CallExpression(x) => BinaryPart::CallExpression(x), Expr::CallExpression(x) => BinaryPart::CallExpression(x),
Expr::CallExpressionKw(x) => BinaryPart::CallExpressionKw(x), Expr::CallExpressionKw(x) => BinaryPart::CallExpressionKw(x),
@ -891,7 +890,7 @@ fn object_property_same_key_and_val(i: &mut TokenSlice) -> PResult<Node<ObjectPr
key.end, key.end,
key.module_id, key.module_id,
ObjectProperty { ObjectProperty {
value: Expr::Identifier(Box::new(key.clone())), value: Expr::Name(Box::new(key.clone().into())),
key, key,
digest: None, digest: None,
}, },
@ -2069,7 +2068,7 @@ fn expr_allowed_in_pipe_expr(i: &mut TokenSlice) -> PResult<Expr> {
literal.map(Expr::Literal), literal.map(Expr::Literal),
fn_call.map(Box::new).map(Expr::CallExpression), fn_call.map(Box::new).map(Expr::CallExpression),
fn_call_kw.map(Box::new).map(Expr::CallExpressionKw), fn_call_kw.map(Box::new).map(Expr::CallExpressionKw),
nameable_identifier.map(Box::new).map(Expr::Identifier), name.map(Box::new).map(Expr::Name),
array, array,
object.map(Box::new).map(Expr::ObjectExpression), object.map(Box::new).map(Expr::ObjectExpression),
pipe_sub.map(Box::new).map(Expr::PipeSubstitution), pipe_sub.map(Box::new).map(Expr::PipeSubstitution),
@ -2088,7 +2087,7 @@ fn possible_operands(i: &mut TokenSlice) -> PResult<Expr> {
member_expression.map(Box::new).map(Expr::MemberExpression), member_expression.map(Box::new).map(Expr::MemberExpression),
literal.map(Expr::Literal), literal.map(Expr::Literal),
fn_call.map(Box::new).map(Expr::CallExpression), fn_call.map(Box::new).map(Expr::CallExpression),
nameable_identifier.map(Box::new).map(Expr::Identifier), name.map(Box::new).map(Expr::Name),
binary_expr_in_parens.map(Box::new).map(Expr::BinaryExpression), binary_expr_in_parens.map(Box::new).map(Expr::BinaryExpression),
unnecessarily_bracketed, unnecessarily_bracketed,
)) ))
@ -2360,6 +2359,35 @@ fn nameable_identifier(i: &mut TokenSlice) -> PResult<Node<Identifier>> {
Ok(result) Ok(result)
} }
fn name(i: &mut TokenSlice) -> PResult<Node<Name>> {
let abs_path = opt(double_colon).parse_next(i)?;
let mut idents: NodeList<Identifier> = separated(1.., nameable_identifier, double_colon)
.parse_next(i)
.map_err(|e| e.backtrack())?;
let mut start = idents[0].start;
if let Some(abs_path) = &abs_path {
start = abs_path.start;
}
let abs_path = abs_path.is_some();
let name = idents.pop().unwrap();
let end = name.end;
let module_id = name.module_id;
Ok(Node::new(
Name {
name,
path: idents,
abs_path,
digest: None,
},
start,
end,
module_id,
))
}
impl TryFrom<Token> for Node<TagDeclarator> { impl TryFrom<Token> for Node<TagDeclarator> {
type Error = CompilationError; type Error = CompilationError;
@ -2671,6 +2699,10 @@ fn plus(i: &mut TokenSlice) -> PResult<Token> {
one_of((TokenType::Operator, "+")).parse_next(i) one_of((TokenType::Operator, "+")).parse_next(i)
} }
fn double_colon(i: &mut TokenSlice) -> PResult<Token> {
TokenType::DoubleColon.parse_from(i)
}
fn equals(i: &mut TokenSlice) -> PResult<Token> { fn equals(i: &mut TokenSlice) -> PResult<Token> {
one_of((TokenType::Operator, "=")) one_of((TokenType::Operator, "="))
.context(expected("the equals operator, =")) .context(expected("the equals operator, ="))
@ -2851,15 +2883,40 @@ fn uom_for_type(i: &mut TokenSlice) -> PResult<NumericSuffix> {
any.try_map(|t: Token| t.value.parse()).parse_next(i) any.try_map(|t: Token| t.value.parse()).parse_next(i)
} }
fn comment(i: &mut TokenSlice) -> PResult<Node<String>> {
any.verify_map(|token: Token| {
let value = match token.token_type {
TokenType::LineComment => token.value,
TokenType::BlockComment => token.value,
_ => return None,
};
Some(Node::new(value, token.start, token.end, token.module_id))
})
.context(expected("Comment"))
.parse_next(i)
}
fn comments(i: &mut TokenSlice) -> PResult<Node<Vec<String>>> {
let comments: Vec<Node<String>> = repeat(1.., (comment, opt(whitespace)).map(|(c, _)| c)).parse_next(i)?;
let start = comments[0].start;
let module_id = comments[0].module_id;
let end = comments.last().unwrap().end;
let inner = comments.into_iter().map(|n| n.inner).collect();
Ok(Node::new(inner, start, end, module_id))
}
struct ParamDescription { struct ParamDescription {
labeled: bool, labeled: bool,
arg_name: Token, arg_name: Token,
type_: std::option::Option<Node<Type>>, type_: std::option::Option<Node<Type>>,
default_value: Option<DefaultParamVal>, default_value: Option<DefaultParamVal>,
comments: Option<Node<Vec<String>>>,
} }
fn parameter(i: &mut TokenSlice) -> PResult<ParamDescription> { fn parameter(i: &mut TokenSlice) -> PResult<ParamDescription> {
let (found_at_sign, arg_name, question_mark, _, type_, _ws, default_literal) = ( let (_, comments, found_at_sign, arg_name, question_mark, _, type_, _ws, default_literal) = (
opt(whitespace),
opt(comments),
opt(at_sign), opt(at_sign),
any.verify(|token: &Token| !matches!(token.token_type, TokenType::Brace) || token.value != ")"), any.verify(|token: &Token| !matches!(token.token_type, TokenType::Brace) || token.value != ")"),
opt(question_mark), opt(question_mark),
@ -2869,6 +2926,7 @@ fn parameter(i: &mut TokenSlice) -> PResult<ParamDescription> {
opt((equals, opt(whitespace), literal).map(|(_, _, literal)| literal)), opt((equals, opt(whitespace), literal).map(|(_, _, literal)| literal)),
) )
.parse_next(i)?; .parse_next(i)?;
Ok(ParamDescription { Ok(ParamDescription {
labeled: found_at_sign.is_none(), labeled: found_at_sign.is_none(),
arg_name, arg_name,
@ -2883,6 +2941,7 @@ fn parameter(i: &mut TokenSlice) -> PResult<ParamDescription> {
return Err(ErrMode::Backtrack(ContextError::from(e))); return Err(ErrMode::Backtrack(ContextError::from(e)));
} }
}, },
comments,
}) })
} }
@ -2892,6 +2951,7 @@ fn parameters(i: &mut TokenSlice) -> PResult<Vec<Parameter>> {
let candidates: Vec<_> = separated(0.., parameter, comma_sep) let candidates: Vec<_> = separated(0.., parameter, comma_sep)
.context(expected("function parameters")) .context(expected("function parameters"))
.parse_next(i)?; .parse_next(i)?;
opt(comma_sep).parse_next(i)?;
// Make sure all those tokens are valid parameters. // Make sure all those tokens are valid parameters.
let params: Vec<Parameter> = candidates let params: Vec<Parameter> = candidates
@ -2902,8 +2962,13 @@ fn parameters(i: &mut TokenSlice) -> PResult<Vec<Parameter>> {
arg_name, arg_name,
type_, type_,
default_value, default_value,
comments,
}| { }| {
let identifier = Node::<Identifier>::try_from(arg_name)?; let mut identifier = Node::<Identifier>::try_from(arg_name)?;
if let Some(comments) = comments {
identifier.comment_start = comments.start;
identifier.pre_comments = comments.inner;
}
Ok(Parameter { Ok(Parameter {
identifier, identifier,
@ -2957,76 +3022,6 @@ fn binding_name(i: &mut TokenSlice) -> PResult<Node<Identifier>> {
.parse_next(i) .parse_next(i)
} }
/// Typecheck the arguments in a keyword fn call.
fn typecheck_all_kw(std_fn: Box<dyn StdLibFn>, args: &[&LabeledArg]) -> PResult<()> {
for arg in args {
let label = &arg.label;
let expr = &arg.arg;
if let Some(spec_arg) = std_fn.args(false).iter().find(|spec_arg| spec_arg.name == label.name) {
typecheck(spec_arg, &expr)?;
}
}
Ok(())
}
/// Type check the arguments in a positional fn call.
fn typecheck_all_positional(std_fn: Box<dyn StdLibFn>, args: &[&Expr]) -> PResult<()> {
for (i, spec_arg) in std_fn.args(false).iter().enumerate() {
let Some(arg) = &args.get(i) else {
// The executor checks the number of arguments, so we don't need to check it here.
continue;
};
typecheck(spec_arg, arg)?;
}
Ok(())
}
fn typecheck(spec_arg: &crate::docs::StdLibFnArg, arg: &&Expr) -> PResult<()> {
match spec_arg.type_.as_ref() {
"TagNode" => match &arg {
Expr::Identifier(_) => {
// These are fine since we want someone to be able to map a variable to a tag declarator.
}
Expr::TagDeclarator(tag) => {
// TODO: Remove this check. It should be redundant.
tag.clone()
.into_valid_binding_name()
.map_err(|e| ErrMode::Cut(ContextError::from(e)))?;
}
e => {
return Err(ErrMode::Cut(
CompilationError::fatal(
SourceRange::from(*arg),
format!(
"Expected a tag declarator like `$name`, found {}",
e.human_friendly_type()
),
)
.into(),
));
}
},
"TagIdentifier" => match &arg {
Expr::Identifier(_) => {}
Expr::MemberExpression(_) => {}
e => {
return Err(ErrMode::Cut(
CompilationError::fatal(
SourceRange::from(*arg),
format!(
"Expected a tag identifier like `tagName`, found {}",
e.human_friendly_type()
),
)
.into(),
));
}
},
_ => {}
}
Ok(())
}
/// Either a positional or keyword function call. /// Either a positional or keyword function call.
fn fn_call_pos_or_kw(i: &mut TokenSlice) -> PResult<Expr> { fn fn_call_pos_or_kw(i: &mut TokenSlice) -> PResult<Expr> {
alt(( alt((
@ -3047,15 +3042,10 @@ fn labelled_fn_call(i: &mut TokenSlice) -> PResult<Expr> {
} }
fn fn_call(i: &mut TokenSlice) -> PResult<Node<CallExpression>> { fn fn_call(i: &mut TokenSlice) -> PResult<Node<CallExpression>> {
let fn_name = nameable_identifier(i)?; let fn_name = name(i)?;
opt(whitespace).parse_next(i)?; opt(whitespace).parse_next(i)?;
let _ = terminated(open_paren, opt(whitespace)).parse_next(i)?; let _ = terminated(open_paren, opt(whitespace)).parse_next(i)?;
let args = arguments(i)?; let args = arguments(i)?;
if let Some(std_fn) = crate::std::get_stdlib_fn(&fn_name.name) {
let just_args: Vec<_> = args.iter().collect();
typecheck_all_positional(std_fn, &just_args)?;
}
let end = preceded(opt(whitespace), close_paren).parse_next(i)?.end; let end = preceded(opt(whitespace), close_paren).parse_next(i)?.end;
let result = Node::new_node( let result = Node::new_node(
@ -3069,17 +3059,15 @@ fn fn_call(i: &mut TokenSlice) -> PResult<Node<CallExpression>> {
}, },
); );
if let Some(suggestion) = super::deprecation(&result.callee.name, DeprecationKind::Function) { let callee_str = result.callee.name.name.to_string();
if let Some(suggestion) = super::deprecation(&callee_str, DeprecationKind::Function) {
ParseContext::warn( ParseContext::warn(
CompilationError::err( CompilationError::err(
result.as_source_range(), result.as_source_range(),
format!( format!("Calling `{}` is deprecated, prefer using `{}`.", callee_str, suggestion),
"Calling `{}` is deprecated, prefer using `{}`.",
result.callee.name, suggestion
),
) )
.with_suggestion( .with_suggestion(
format!("Replace `{}` with `{}`", result.callee.name, suggestion), format!("Replace `{}` with `{}`", callee_str, suggestion),
suggestion, suggestion,
None, None,
Tag::Deprecated, Tag::Deprecated,
@ -3091,7 +3079,7 @@ fn fn_call(i: &mut TokenSlice) -> PResult<Node<CallExpression>> {
} }
fn fn_call_kw(i: &mut TokenSlice) -> PResult<Node<CallExpressionKw>> { fn fn_call_kw(i: &mut TokenSlice) -> PResult<Node<CallExpressionKw>> {
let fn_name = nameable_identifier(i)?; let fn_name = name(i)?;
opt(whitespace).parse_next(i)?; opt(whitespace).parse_next(i)?;
let _ = open_paren.parse_next(i)?; let _ = open_paren.parse_next(i)?;
ignore_whitespace(i); ignore_whitespace(i);
@ -3147,10 +3135,6 @@ fn fn_call_kw(i: &mut TokenSlice) -> PResult<Node<CallExpressionKw>> {
Ok((args, non_code_nodes)) Ok((args, non_code_nodes))
}, },
)?; )?;
if let Some(std_fn) = crate::std::get_stdlib_fn(&fn_name.name) {
let just_args: Vec<_> = args.iter().collect();
typecheck_all_kw(std_fn, &just_args)?;
}
ignore_whitespace(i); ignore_whitespace(i);
opt(comma_sep).parse_next(i)?; opt(comma_sep).parse_next(i)?;
let end = close_paren.parse_next(i)?.end; let end = close_paren.parse_next(i)?.end;
@ -3172,17 +3156,15 @@ fn fn_call_kw(i: &mut TokenSlice) -> PResult<Node<CallExpressionKw>> {
}, },
); );
if let Some(suggestion) = super::deprecation(&result.callee.name, DeprecationKind::Function) { let callee_str = result.callee.name.name.to_string();
if let Some(suggestion) = super::deprecation(&callee_str, DeprecationKind::Function) {
ParseContext::warn( ParseContext::warn(
CompilationError::err( CompilationError::err(
result.as_source_range(), result.as_source_range(),
format!( format!("Calling `{}` is deprecated, prefer using `{}`.", callee_str, suggestion),
"Calling `{}` is deprecated, prefer using `{}`.",
result.callee.name, suggestion
),
) )
.with_suggestion( .with_suggestion(
format!("Replace `{}` with `{}`", result.callee.name, suggestion), format!("Replace `{}` with `{}`", callee_str, suggestion),
suggestion, suggestion,
None, None,
Tag::Deprecated, Tag::Deprecated,
@ -3244,6 +3226,17 @@ mod tests {
} }
} }
#[test]
fn parse_names() {
for (test, expected_len) in [("someVar", 0), ("::foo", 0), ("foo::bar::baz", 2)] {
let tokens = crate::parsing::token::lex(test, ModuleId::default()).unwrap();
match name.parse(tokens.as_slice()) {
Ok(n) => assert_eq!(n.path.len(), expected_len, "Could not parse name from `{test}`: {n:?}"),
Err(e) => panic!("Could not parse name from `{test}`: {e:?}"),
}
}
}
#[test] #[test]
fn weird_program_unclosed_paren() { fn weird_program_unclosed_paren() {
let tokens = crate::parsing::token::lex("fn firstPrime(", ModuleId::default()).unwrap(); let tokens = crate::parsing::token::lex("fn firstPrime(", ModuleId::default()).unwrap();

View File

@ -18,33 +18,57 @@ expression: actual
"type": "BinaryExpression", "type": "BinaryExpression",
"operator": "*", "operator": "*",
"left": { "left": {
"type": "Identifier", "type": "Name",
"type": "Name",
"name": {
"type": "Identifier", "type": "Identifier",
"name": "distance", "name": "distance",
"start": 0, "start": 0,
"end": 8, "end": 8,
"commentStart": 0 "commentStart": 0
}, },
"path": [],
"abs_path": false,
"start": 0,
"end": 8,
"commentStart": 0
},
"right": { "right": {
"type": "Identifier", "type": "Name",
"type": "Name",
"name": {
"type": "Identifier", "type": "Identifier",
"name": "p", "name": "p",
"start": 11, "start": 11,
"end": 12, "end": 12,
"commentStart": 11 "commentStart": 11
}, },
"path": [],
"abs_path": false,
"start": 11,
"end": 12,
"commentStart": 11
},
"start": 0, "start": 0,
"end": 12, "end": 12,
"commentStart": 0 "commentStart": 0
}, },
"right": { "right": {
"type": "Identifier", "type": "Name",
"type": "Name",
"name": {
"type": "Identifier", "type": "Identifier",
"name": "FOS", "name": "FOS",
"start": 15, "start": 15,
"end": 18, "end": 18,
"commentStart": 15 "commentStart": 15
}, },
"path": [],
"abs_path": false,
"start": 15,
"end": 18,
"commentStart": 15
},
"start": 0, "start": 0,
"end": 18, "end": 18,
"commentStart": 0 "commentStart": 0
@ -70,21 +94,37 @@ expression: actual
"type": "BinaryExpression", "type": "BinaryExpression",
"operator": "*", "operator": "*",
"left": { "left": {
"type": "Identifier", "type": "Name",
"type": "Name",
"name": {
"type": "Identifier", "type": "Identifier",
"name": "sigmaAllow", "name": "sigmaAllow",
"start": 26, "start": 26,
"end": 36, "end": 36,
"commentStart": 26 "commentStart": 26
}, },
"path": [],
"abs_path": false,
"start": 26,
"end": 36,
"commentStart": 26
},
"right": { "right": {
"type": "Identifier", "type": "Name",
"type": "Name",
"name": {
"type": "Identifier", "type": "Identifier",
"name": "width", "name": "width",
"start": 39, "start": 39,
"end": 44, "end": 44,
"commentStart": 39 "commentStart": 39
}, },
"path": [],
"abs_path": false,
"start": 39,
"end": 44,
"commentStart": 39
},
"start": 26, "start": 26,
"end": 44, "end": 44,
"commentStart": 26 "commentStart": 26

View File

@ -21,21 +21,37 @@ expression: actual
{ {
"arguments": [ "arguments": [
{ {
"abs_path": false,
"commentStart": 26,
"end": 28,
"name": {
"commentStart": 26, "commentStart": 26,
"end": 28, "end": 28,
"name": "XY", "name": "XY",
"start": 26, "start": 26,
"type": "Identifier",
"type": "Identifier" "type": "Identifier"
},
"path": [],
"start": 26,
"type": "Name",
"type": "Name"
} }
], ],
"callee": { "callee": {
"abs_path": false,
"commentStart": 12,
"end": 25,
"name": {
"commentStart": 12, "commentStart": 12,
"end": 25, "end": 25,
"name": "startSketchOn", "name": "startSketchOn",
"start": 12, "start": 12,
"type": "Identifier" "type": "Identifier"
}, },
"path": [],
"start": 12,
"type": "Name"
},
"commentStart": 12, "commentStart": 12,
"end": 29, "end": 29,
"start": 12, "start": 12,
@ -86,12 +102,20 @@ expression: actual
} }
], ],
"callee": { "callee": {
"abs_path": false,
"commentStart": 37,
"end": 51,
"name": {
"commentStart": 37, "commentStart": 37,
"end": 51, "end": 51,
"name": "startProfileAt", "name": "startProfileAt",
"start": 37, "start": 37,
"type": "Identifier" "type": "Identifier"
}, },
"path": [],
"start": 37,
"type": "Name"
},
"commentStart": 37, "commentStart": 37,
"end": 62, "end": 62,
"start": 37, "start": 37,
@ -142,12 +166,20 @@ expression: actual
} }
], ],
"callee": { "callee": {
"abs_path": false,
"commentStart": 70,
"end": 74,
"name": {
"commentStart": 70, "commentStart": 70,
"end": 74, "end": 74,
"name": "line", "name": "line",
"start": 70, "start": 70,
"type": "Identifier" "type": "Identifier"
}, },
"path": [],
"start": 70,
"type": "Name"
},
"commentStart": 70, "commentStart": 70,
"end": 86, "end": 86,
"start": 70, "start": 70,
@ -206,12 +238,20 @@ expression: actual
} }
], ],
"callee": { "callee": {
"abs_path": false,
"commentStart": 94,
"end": 107,
"name": {
"commentStart": 94, "commentStart": 94,
"end": 107, "end": 107,
"name": "tangentialArc", "name": "tangentialArc",
"start": 94, "start": 94,
"type": "Identifier" "type": "Identifier"
}, },
"path": [],
"start": 94,
"type": "Name"
},
"commentStart": 94, "commentStart": 94,
"end": 119, "end": 119,
"start": 94, "start": 94,
@ -270,12 +310,20 @@ expression: actual
} }
], ],
"callee": { "callee": {
"abs_path": false,
"commentStart": 127,
"end": 131,
"name": {
"commentStart": 127, "commentStart": 127,
"end": 131, "end": 131,
"name": "line", "name": "line",
"start": 127, "start": 127,
"type": "Identifier" "type": "Identifier"
}, },
"path": [],
"start": 127,
"type": "Name"
},
"commentStart": 127, "commentStart": 127,
"end": 144, "end": 144,
"start": 127, "start": 127,
@ -308,12 +356,20 @@ expression: actual
} }
], ],
"callee": { "callee": {
"abs_path": false,
"commentStart": 152,
"end": 159,
"name": {
"commentStart": 152, "commentStart": 152,
"end": 159, "end": 159,
"name": "extrude", "name": "extrude",
"start": 152, "start": 152,
"type": "Identifier" "type": "Identifier"
}, },
"path": [],
"start": 152,
"type": "Name"
},
"commentStart": 152, "commentStart": 152,
"end": 170, "end": 170,
"start": 152, "start": 152,

View File

@ -18,13 +18,21 @@ expression: actual
}, },
"init": { "init": {
"argument": { "argument": {
"abs_path": false,
"commentStart": 6,
"end": 11,
"name": {
"commentStart": 6, "commentStart": 6,
"end": 11, "end": 11,
"name": "scale", "name": "scale",
"start": 6, "start": 6,
"type": "Identifier",
"type": "Identifier" "type": "Identifier"
}, },
"path": [],
"start": 6,
"type": "Name",
"type": "Name"
},
"commentStart": 5, "commentStart": 5,
"end": 11, "end": 11,
"operator": "-", "operator": "-",

View File

@ -62,12 +62,20 @@ expression: actual
} }
], ],
"callee": { "callee": {
"abs_path": false,
"commentStart": 0,
"end": 4,
"name": {
"commentStart": 0, "commentStart": 0,
"end": 4, "end": 4,
"name": "line", "name": "line",
"start": 0, "start": 0,
"type": "Identifier" "type": "Identifier"
}, },
"path": [],
"start": 0,
"type": "Name"
},
"commentStart": 0, "commentStart": 0,
"end": 27, "end": 27,
"start": 0, "start": 0,

View File

@ -65,12 +65,20 @@ expression: actual
"expression": { "expression": {
"arguments": [], "arguments": [],
"callee": { "callee": {
"abs_path": false,
"commentStart": 62,
"end": 78,
"name": {
"commentStart": 62, "commentStart": 62,
"end": 78, "end": 78,
"name": "firstPrimeNumber", "name": "firstPrimeNumber",
"start": 62, "start": 62,
"type": "Identifier" "type": "Identifier"
}, },
"path": [],
"start": 62,
"type": "Name"
},
"commentStart": 62, "commentStart": 62,
"end": 80, "end": 80,
"start": 62, "start": 62,

View File

@ -83,12 +83,20 @@ expression: actual
} }
], ],
"callee": { "callee": {
"abs_path": false,
"commentStart": 54,
"end": 59,
"name": {
"commentStart": 54, "commentStart": 54,
"end": 59, "end": 59,
"name": "thing", "name": "thing",
"start": 54, "start": 54,
"type": "Identifier" "type": "Identifier"
}, },
"path": [],
"start": 54,
"type": "Name"
},
"commentStart": 54, "commentStart": 54,
"end": 66, "end": 66,
"start": 54, "start": 54,

View File

@ -21,21 +21,37 @@ expression: actual
{ {
"arguments": [ "arguments": [
{ {
"abs_path": false,
"commentStart": 25,
"end": 27,
"name": {
"commentStart": 25, "commentStart": 25,
"end": 27, "end": 27,
"name": "XY", "name": "XY",
"start": 25, "start": 25,
"type": "Identifier",
"type": "Identifier" "type": "Identifier"
},
"path": [],
"start": 25,
"type": "Name",
"type": "Name"
} }
], ],
"callee": { "callee": {
"abs_path": false,
"commentStart": 11,
"end": 24,
"name": {
"commentStart": 11, "commentStart": 11,
"end": 24, "end": 24,
"name": "startSketchOn", "name": "startSketchOn",
"start": 11, "start": 11,
"type": "Identifier" "type": "Identifier"
}, },
"path": [],
"start": 11,
"type": "Name"
},
"commentStart": 11, "commentStart": 11,
"end": 28, "end": 28,
"start": 11, "start": 11,
@ -86,12 +102,20 @@ expression: actual
} }
], ],
"callee": { "callee": {
"abs_path": false,
"commentStart": 40,
"end": 54,
"name": {
"commentStart": 40, "commentStart": 40,
"end": 54, "end": 54,
"name": "startProfileAt", "name": "startProfileAt",
"start": 40, "start": 40,
"type": "Identifier" "type": "Identifier"
}, },
"path": [],
"start": 40,
"type": "Name"
},
"commentStart": 40, "commentStart": 40,
"end": 64, "end": 64,
"start": 40, "start": 40,
@ -163,12 +187,20 @@ expression: actual
} }
], ],
"callee": { "callee": {
"abs_path": false,
"commentStart": 76,
"end": 80,
"name": {
"commentStart": 76, "commentStart": 76,
"end": 80, "end": 80,
"name": "line", "name": "line",
"start": 76, "start": 76,
"type": "Identifier" "type": "Identifier"
}, },
"path": [],
"start": 76,
"type": "Name"
},
"commentStart": 76, "commentStart": 76,
"end": 117, "end": 117,
"start": 76, "start": 76,
@ -223,12 +255,20 @@ expression: actual
} }
], ],
"callee": { "callee": {
"abs_path": false,
"commentStart": 129,
"end": 133,
"name": {
"commentStart": 129, "commentStart": 129,
"end": 133, "end": 133,
"name": "line", "name": "line",
"start": 129, "start": 129,
"type": "Identifier" "type": "Identifier"
}, },
"path": [],
"start": 129,
"type": "Name"
},
"commentStart": 129, "commentStart": 129,
"end": 155, "end": 155,
"start": 129, "start": 129,
@ -301,12 +341,20 @@ expression: actual
} }
], ],
"callee": { "callee": {
"abs_path": false,
"commentStart": 167,
"end": 171,
"name": {
"commentStart": 167, "commentStart": 167,
"end": 171, "end": 171,
"name": "line", "name": "line",
"start": 167, "start": 167,
"type": "Identifier" "type": "Identifier"
}, },
"path": [],
"start": 167,
"type": "Name"
},
"commentStart": 167, "commentStart": 167,
"end": 211, "end": 211,
"start": 167, "start": 167,
@ -317,12 +365,20 @@ expression: actual
{ {
"arguments": [], "arguments": [],
"callee": { "callee": {
"abs_path": false,
"commentStart": 223,
"end": 228,
"name": {
"commentStart": 223, "commentStart": 223,
"end": 228, "end": 228,
"name": "close", "name": "close",
"start": 223, "start": 223,
"type": "Identifier" "type": "Identifier"
}, },
"path": [],
"start": 223,
"type": "Name"
},
"commentStart": 223, "commentStart": 223,
"end": 230, "end": 230,
"start": 223, "start": 223,

View File

@ -21,21 +21,37 @@ expression: actual
{ {
"arguments": [ "arguments": [
{ {
"abs_path": false,
"commentStart": 25,
"end": 27,
"name": {
"commentStart": 25, "commentStart": 25,
"end": 27, "end": 27,
"name": "XY", "name": "XY",
"start": 25, "start": 25,
"type": "Identifier",
"type": "Identifier" "type": "Identifier"
},
"path": [],
"start": 25,
"type": "Name",
"type": "Name"
} }
], ],
"callee": { "callee": {
"abs_path": false,
"commentStart": 11,
"end": 24,
"name": {
"commentStart": 11, "commentStart": 11,
"end": 24, "end": 24,
"name": "startSketchOn", "name": "startSketchOn",
"start": 11, "start": 11,
"type": "Identifier" "type": "Identifier"
}, },
"path": [],
"start": 11,
"type": "Name"
},
"commentStart": 11, "commentStart": 11,
"end": 28, "end": 28,
"start": 11, "start": 11,
@ -86,12 +102,20 @@ expression: actual
} }
], ],
"callee": { "callee": {
"abs_path": false,
"commentStart": 32,
"end": 46,
"name": {
"commentStart": 32, "commentStart": 32,
"end": 46, "end": 46,
"name": "startProfileAt", "name": "startProfileAt",
"start": 32, "start": 32,
"type": "Identifier" "type": "Identifier"
}, },
"path": [],
"start": 32,
"type": "Name"
},
"commentStart": 32, "commentStart": 32,
"end": 56, "end": 56,
"start": 32, "start": 32,
@ -145,12 +169,20 @@ expression: actual
} }
], ],
"callee": { "callee": {
"abs_path": false,
"commentStart": 60,
"end": 64,
"name": {
"commentStart": 60, "commentStart": 60,
"end": 64, "end": 64,
"name": "line", "name": "line",
"start": 60, "start": 60,
"type": "Identifier" "type": "Identifier"
}, },
"path": [],
"start": 60,
"type": "Name"
},
"commentStart": 60, "commentStart": 60,
"end": 86, "end": 86,
"start": 60, "start": 60,
@ -161,12 +193,20 @@ expression: actual
{ {
"arguments": [], "arguments": [],
"callee": { "callee": {
"abs_path": false,
"commentStart": 90,
"end": 95,
"name": {
"commentStart": 90, "commentStart": 90,
"end": 95, "end": 95,
"name": "close", "name": "close",
"start": 90, "start": 90,
"type": "Identifier" "type": "Identifier"
}, },
"path": [],
"start": 90,
"type": "Name"
},
"commentStart": 90, "commentStart": 90,
"end": 97, "end": 97,
"start": 90, "start": 90,

View File

@ -21,21 +21,37 @@ expression: actual
{ {
"arguments": [ "arguments": [
{ {
"abs_path": false,
"commentStart": 22,
"end": 24,
"name": {
"commentStart": 22, "commentStart": 22,
"end": 24, "end": 24,
"name": "XY", "name": "XY",
"start": 22, "start": 22,
"type": "Identifier",
"type": "Identifier" "type": "Identifier"
},
"path": [],
"start": 22,
"type": "Name",
"type": "Name"
} }
], ],
"callee": { "callee": {
"abs_path": false,
"commentStart": 8,
"end": 21,
"name": {
"commentStart": 8, "commentStart": 8,
"end": 21, "end": 21,
"name": "startSketchOn", "name": "startSketchOn",
"start": 8, "start": 8,
"type": "Identifier" "type": "Identifier"
}, },
"path": [],
"start": 8,
"type": "Name"
},
"commentStart": 8, "commentStart": 8,
"end": 25, "end": 25,
"start": 8, "start": 8,
@ -45,13 +61,21 @@ expression: actual
{ {
"arguments": [ "arguments": [
{ {
"abs_path": false,
"commentStart": 44,
"end": 45,
"name": {
"commentStart": 44, "commentStart": 44,
"end": 45, "end": 45,
"name": "p", "name": "p",
"start": 44, "start": 44,
"type": "Identifier",
"type": "Identifier" "type": "Identifier"
}, },
"path": [],
"start": 44,
"type": "Name",
"type": "Name"
},
{ {
"commentStart": 47, "commentStart": 47,
"end": 48, "end": 48,
@ -61,12 +85,20 @@ expression: actual
} }
], ],
"callee": { "callee": {
"abs_path": false,
"commentStart": 29,
"end": 43,
"name": {
"commentStart": 29, "commentStart": 29,
"end": 43, "end": 43,
"name": "startProfileAt", "name": "startProfileAt",
"start": 29, "start": 29,
"type": "Identifier" "type": "Identifier"
}, },
"path": [],
"start": 29,
"type": "Name"
},
"commentStart": 29, "commentStart": 29,
"end": 49, "end": 49,
"start": 29, "start": 29,

View File

@ -34,12 +34,20 @@ expression: actual
} }
], ],
"callee": { "callee": {
"abs_path": false,
"commentStart": 8,
"end": 9,
"name": {
"commentStart": 8, "commentStart": 8,
"end": 9, "end": 9,
"name": "f", "name": "f",
"start": 8, "start": 8,
"type": "Identifier" "type": "Identifier"
}, },
"path": [],
"start": 8,
"type": "Name"
},
"commentStart": 8, "commentStart": 8,
"end": 12, "end": 12,
"start": 8, "start": 8,
@ -69,12 +77,20 @@ expression: actual
} }
], ],
"callee": { "callee": {
"abs_path": false,
"commentStart": 16,
"end": 17,
"name": {
"commentStart": 16, "commentStart": 16,
"end": 17, "end": 17,
"name": "g", "name": "g",
"start": 16, "start": 16,
"type": "Identifier" "type": "Identifier"
}, },
"path": [],
"start": 16,
"type": "Name"
},
"commentStart": 16, "commentStart": 16,
"end": 23, "end": 23,
"start": 16, "start": 16,

View File

@ -21,21 +21,37 @@ expression: actual
{ {
"arguments": [ "arguments": [
{ {
"abs_path": false,
"commentStart": 22,
"end": 24,
"name": {
"commentStart": 22, "commentStart": 22,
"end": 24, "end": 24,
"name": "XY", "name": "XY",
"start": 22, "start": 22,
"type": "Identifier",
"type": "Identifier" "type": "Identifier"
},
"path": [],
"start": 22,
"type": "Name",
"type": "Name"
} }
], ],
"callee": { "callee": {
"abs_path": false,
"commentStart": 8,
"end": 21,
"name": {
"commentStart": 8, "commentStart": 8,
"end": 21, "end": 21,
"name": "startSketchOn", "name": "startSketchOn",
"start": 8, "start": 8,
"type": "Identifier" "type": "Identifier"
}, },
"path": [],
"start": 8,
"type": "Name"
},
"commentStart": 8, "commentStart": 8,
"end": 25, "end": 25,
"start": 8, "start": 8,
@ -45,13 +61,21 @@ expression: actual
{ {
"arguments": [ "arguments": [
{ {
"abs_path": false,
"commentStart": 44,
"end": 45,
"name": {
"commentStart": 44, "commentStart": 44,
"end": 45, "end": 45,
"name": "p", "name": "p",
"start": 44, "start": 44,
"type": "Identifier",
"type": "Identifier" "type": "Identifier"
}, },
"path": [],
"start": 44,
"type": "Name",
"type": "Name"
},
{ {
"commentStart": 47, "commentStart": 47,
"end": 48, "end": 48,
@ -61,12 +85,20 @@ expression: actual
} }
], ],
"callee": { "callee": {
"abs_path": false,
"commentStart": 29,
"end": 43,
"name": {
"commentStart": 29, "commentStart": 29,
"end": 43, "end": 43,
"name": "startProfileAt", "name": "startProfileAt",
"start": 29, "start": 29,
"type": "Identifier" "type": "Identifier"
}, },
"path": [],
"start": 29,
"type": "Name"
},
"commentStart": 29, "commentStart": 29,
"end": 49, "end": 49,
"start": 29, "start": 29,
@ -100,12 +132,20 @@ expression: actual
} }
}, },
{ {
"abs_path": false,
"commentStart": 68,
"end": 69,
"name": {
"commentStart": 68, "commentStart": 68,
"end": 69, "end": 69,
"name": "l", "name": "l",
"start": 68, "start": 68,
"type": "Identifier",
"type": "Identifier" "type": "Identifier"
},
"path": [],
"start": 68,
"type": "Name",
"type": "Name"
} }
], ],
"end": 70, "end": 70,
@ -116,12 +156,20 @@ expression: actual
} }
], ],
"callee": { "callee": {
"abs_path": false,
"commentStart": 53,
"end": 57,
"name": {
"commentStart": 53, "commentStart": 53,
"end": 57, "end": 57,
"name": "line", "name": "line",
"start": 53, "start": 53,
"type": "Identifier" "type": "Identifier"
}, },
"path": [],
"start": 53,
"type": "Name"
},
"commentStart": 53, "commentStart": 53,
"end": 71, "end": 71,
"start": 53, "start": 53,

View File

@ -54,12 +54,20 @@ expression: actual
} }
], ],
"callee": { "callee": {
"abs_path": false,
"commentStart": 0,
"end": 4,
"name": {
"commentStart": 0, "commentStart": 0,
"end": 4, "end": 4,
"name": "line", "name": "line",
"start": 0, "start": 0,
"type": "Identifier" "type": "Identifier"
}, },
"path": [],
"start": 0,
"type": "Name"
},
"commentStart": 0, "commentStart": 0,
"end": 26, "end": 26,
"start": 0, "start": 0,

View File

@ -21,21 +21,37 @@ expression: actual
{ {
"arguments": [ "arguments": [
{ {
"abs_path": false,
"commentStart": 25,
"end": 27,
"name": {
"commentStart": 25, "commentStart": 25,
"end": 27, "end": 27,
"name": "XY", "name": "XY",
"start": 25, "start": 25,
"type": "Identifier",
"type": "Identifier" "type": "Identifier"
},
"path": [],
"start": 25,
"type": "Name",
"type": "Name"
} }
], ],
"callee": { "callee": {
"abs_path": false,
"commentStart": 11,
"end": 24,
"name": {
"commentStart": 11, "commentStart": 11,
"end": 24, "end": 24,
"name": "startSketchOn", "name": "startSketchOn",
"start": 11, "start": 11,
"type": "Identifier" "type": "Identifier"
}, },
"path": [],
"start": 11,
"type": "Name"
},
"commentStart": 11, "commentStart": 11,
"end": 28, "end": 28,
"start": 11, "start": 11,
@ -86,12 +102,20 @@ expression: actual
} }
], ],
"callee": { "callee": {
"abs_path": false,
"commentStart": 32,
"end": 46,
"name": {
"commentStart": 32, "commentStart": 32,
"end": 46, "end": 46,
"name": "startProfileAt", "name": "startProfileAt",
"start": 32, "start": 32,
"type": "Identifier" "type": "Identifier"
}, },
"path": [],
"start": 32,
"type": "Name"
},
"commentStart": 32, "commentStart": 32,
"end": 56, "end": 56,
"start": 32, "start": 32,

View File

@ -31,21 +31,37 @@ expression: actual
"value": "hello" "value": "hello"
}, },
{ {
"abs_path": false,
"commentStart": 16,
"end": 27,
"name": {
"commentStart": 16, "commentStart": 16,
"end": 27, "end": 27,
"name": "aIdentifier", "name": "aIdentifier",
"start": 16, "start": 16,
"type": "Identifier",
"type": "Identifier" "type": "Identifier"
},
"path": [],
"start": 16,
"type": "Name",
"type": "Name"
} }
], ],
"callee": { "callee": {
"abs_path": false,
"commentStart": 0,
"end": 3,
"name": {
"commentStart": 0, "commentStart": 0,
"end": 3, "end": 3,
"name": "log", "name": "log",
"start": 0, "start": 0,
"type": "Identifier" "type": "Identifier"
}, },
"path": [],
"start": 0,
"type": "Name"
},
"commentStart": 0, "commentStart": 0,
"end": 28, "end": 28,
"start": 0, "start": 0,

View File

@ -25,12 +25,20 @@ expression: actual
} }
}, },
{ {
"abs_path": false,
"commentStart": 9,
"end": 10,
"name": {
"commentStart": 9, "commentStart": 9,
"end": 10, "end": 10,
"name": "l", "name": "l",
"start": 9, "start": 9,
"type": "Identifier",
"type": "Identifier" "type": "Identifier"
},
"path": [],
"start": 9,
"type": "Name",
"type": "Name"
} }
], ],
"end": 11, "end": 11,
@ -47,12 +55,20 @@ expression: actual
} }
], ],
"callee": { "callee": {
"abs_path": false,
"commentStart": 0,
"end": 4,
"name": {
"commentStart": 0, "commentStart": 0,
"end": 4, "end": 4,
"name": "line", "name": "line",
"start": 0, "start": 0,
"type": "Identifier" "type": "Identifier"
}, },
"path": [],
"start": 0,
"type": "Name"
},
"commentStart": 0, "commentStart": 0,
"end": 15, "end": 15,
"start": 0, "start": 0,

View File

@ -21,21 +21,37 @@ expression: actual
{ {
"arguments": [ "arguments": [
{ {
"abs_path": false,
"commentStart": 31,
"end": 33,
"name": {
"commentStart": 31, "commentStart": 31,
"end": 33, "end": 33,
"name": "XY", "name": "XY",
"start": 31, "start": 31,
"type": "Identifier",
"type": "Identifier" "type": "Identifier"
},
"path": [],
"start": 31,
"type": "Name",
"type": "Name"
} }
], ],
"callee": { "callee": {
"abs_path": false,
"commentStart": 17,
"end": 30,
"name": {
"commentStart": 17, "commentStart": 17,
"end": 30, "end": 30,
"name": "startSketchOn", "name": "startSketchOn",
"start": 17, "start": 17,
"type": "Identifier" "type": "Identifier"
}, },
"path": [],
"start": 17,
"type": "Name"
},
"commentStart": 17, "commentStart": 17,
"end": 34, "end": 34,
"start": 17, "start": 17,
@ -111,12 +127,20 @@ expression: actual
} }
], ],
"callee": { "callee": {
"abs_path": false,
"commentStart": 42,
"end": 48,
"name": {
"commentStart": 42, "commentStart": 42,
"end": 48, "end": 48,
"name": "circle", "name": "circle",
"start": 42, "start": 42,
"type": "Identifier" "type": "Identifier"
}, },
"path": [],
"start": 42,
"type": "Name"
},
"commentStart": 42, "commentStart": 42,
"end": 76, "end": 76,
"start": 42, "start": 42,
@ -150,12 +174,20 @@ expression: actual
} }
], ],
"callee": { "callee": {
"abs_path": false,
"commentStart": 84,
"end": 91,
"name": {
"commentStart": 84, "commentStart": 84,
"end": 91, "end": 91,
"name": "extrude", "name": "extrude",
"start": 84, "start": 84,
"type": "Identifier" "type": "Identifier"
}, },
"path": [],
"start": 84,
"type": "Name"
},
"commentStart": 84, "commentStart": 84,
"end": 104, "end": 104,
"start": 84, "start": 84,

View File

@ -23,13 +23,21 @@ expression: actual
"argument": { "argument": {
"arguments": [ "arguments": [
{ {
"abs_path": false,
"commentStart": 36,
"end": 41,
"name": {
"commentStart": 36, "commentStart": 36,
"end": 41, "end": 41,
"name": "angle", "name": "angle",
"start": 36, "start": 36,
"type": "Identifier",
"type": "Identifier" "type": "Identifier"
}, },
"path": [],
"start": 36,
"type": "Name",
"type": "Name"
},
{ {
"commentStart": 43, "commentStart": 43,
"end": 46, "end": 46,
@ -44,12 +52,20 @@ expression: actual
} }
], ],
"callee": { "callee": {
"abs_path": false,
"commentStart": 28,
"end": 35,
"name": {
"commentStart": 28, "commentStart": 28,
"end": 35, "end": 35,
"name": "default", "name": "default",
"start": 28, "start": 28,
"type": "Identifier" "type": "Identifier"
}, },
"path": [],
"start": 28,
"type": "Name"
},
"commentStart": 28, "commentStart": 28,
"end": 47, "end": 47,
"start": 28, "start": 28,

View File

@ -59,12 +59,20 @@ expression: actual
} }
], ],
"callee": { "callee": {
"abs_path": false,
"commentStart": 17,
"end": 23,
"name": {
"commentStart": 17, "commentStart": 17,
"end": 23, "end": 23,
"name": "legLen", "name": "legLen",
"start": 17, "start": 17,
"type": "Identifier" "type": "Identifier"
}, },
"path": [],
"start": 17,
"type": "Name"
},
"commentStart": 17, "commentStart": 17,
"end": 29, "end": 29,
"start": 17, "start": 17,
@ -80,12 +88,20 @@ expression: actual
} }
], ],
"callee": { "callee": {
"abs_path": false,
"commentStart": 8,
"end": 11,
"name": {
"commentStart": 8, "commentStart": 8,
"end": 11, "end": 11,
"name": "min", "name": "min",
"start": 8, "start": 8,
"type": "Identifier" "type": "Identifier"
}, },
"path": [],
"start": 8,
"type": "Name"
},
"commentStart": 8, "commentStart": 8,
"end": 30, "end": 30,
"start": 8, "start": 8,

View File

@ -31,12 +31,20 @@ expression: actual
} }
], ],
"callee": { "callee": {
"abs_path": false,
"commentStart": 13,
"end": 26,
"name": {
"commentStart": 13, "commentStart": 13,
"end": 26, "end": 26,
"name": "startSketchOn", "name": "startSketchOn",
"start": 13, "start": 13,
"type": "Identifier" "type": "Identifier"
}, },
"path": [],
"start": 13,
"type": "Name"
},
"commentStart": 13, "commentStart": 13,
"end": 32, "end": 32,
"start": 13, "start": 13,
@ -54,12 +62,20 @@ expression: actual
} }
], ],
"callee": { "callee": {
"abs_path": false,
"commentStart": 109,
"end": 123,
"name": {
"commentStart": 109, "commentStart": 109,
"end": 123, "end": 123,
"name": "startProfileAt", "name": "startProfileAt",
"start": 109, "start": 109,
"type": "Identifier" "type": "Identifier"
}, },
"path": [],
"start": 109,
"type": "Name"
},
"commentStart": 109, "commentStart": 109,
"end": 126, "end": 126,
"start": 109, "start": 109,

View File

@ -34,21 +34,37 @@ expression: actual
"cond": { "cond": {
"arguments": [ "arguments": [
{ {
"abs_path": false,
"commentStart": 51,
"end": 57,
"name": {
"commentStart": 51, "commentStart": 51,
"end": 57, "end": 57,
"name": "radius", "name": "radius",
"start": 51, "start": 51,
"type": "Identifier",
"type": "Identifier" "type": "Identifier"
},
"path": [],
"start": 51,
"type": "Name",
"type": "Name"
} }
], ],
"callee": { "callee": {
"abs_path": false,
"commentStart": 46,
"end": 50,
"name": {
"commentStart": 46, "commentStart": 46,
"end": 50, "end": 50,
"name": "func", "name": "func",
"start": 46, "start": 46,
"type": "Identifier" "type": "Identifier"
}, },
"path": [],
"start": 46,
"type": "Name"
},
"commentStart": 46, "commentStart": 46,
"end": 58, "end": 58,
"start": 46, "start": 46,

View File

@ -66,12 +66,20 @@ expression: actual
"start": 22, "start": 22,
"type": "ObjectProperty", "type": "ObjectProperty",
"value": { "value": {
"abs_path": false,
"commentStart": 22,
"end": 23,
"name": {
"commentStart": 22, "commentStart": 22,
"end": 23, "end": 23,
"name": "x", "name": "x",
"start": 22, "start": 22,
"type": "Identifier",
"type": "Identifier" "type": "Identifier"
},
"path": [],
"start": 22,
"type": "Name",
"type": "Name"
} }
}, },
{ {

View File

@ -8,13 +8,21 @@ expression: actual
"commentStart": 0, "commentStart": 0,
"end": 5, "end": 5,
"expression": { "expression": {
"abs_path": false,
"commentStart": 0,
"end": 5,
"name": {
"commentStart": 0, "commentStart": 0,
"end": 5, "end": 5,
"name": "truee", "name": "truee",
"start": 0, "start": 0,
"type": "Identifier",
"type": "Identifier" "type": "Identifier"
}, },
"path": [],
"start": 0,
"type": "Name",
"type": "Name"
},
"start": 0, "start": 0,
"type": "ExpressionStatement", "type": "ExpressionStatement",
"type": "ExpressionStatement" "type": "ExpressionStatement"

View File

@ -47,12 +47,20 @@ expression: actual
} }
], ],
"callee": { "callee": {
"abs_path": false,
"commentStart": 13,
"end": 19,
"name": {
"commentStart": 13, "commentStart": 13,
"end": 19, "end": 19,
"name": "legLen", "name": "legLen",
"start": 13, "start": 13,
"type": "Identifier" "type": "Identifier"
}, },
"path": [],
"start": 13,
"type": "Name"
},
"commentStart": 13, "commentStart": 13,
"end": 25, "end": 25,
"start": 13, "start": 13,
@ -80,12 +88,20 @@ expression: actual
} }
], ],
"callee": { "callee": {
"abs_path": false,
"commentStart": 8,
"end": 11,
"name": {
"commentStart": 8, "commentStart": 8,
"end": 11, "end": 11,
"name": "min", "name": "min",
"start": 8, "start": 8,
"type": "Identifier" "type": "Identifier"
}, },
"path": [],
"start": 8,
"type": "Name"
},
"commentStart": 8, "commentStart": 8,
"end": 29, "end": 29,
"start": 8, "start": 8,

View File

@ -73,12 +73,20 @@ expression: actual
} }
], ],
"callee": { "callee": {
"abs_path": false,
"commentStart": 17,
"end": 23,
"name": {
"commentStart": 17, "commentStart": 17,
"end": 23, "end": 23,
"name": "myFunc", "name": "myFunc",
"start": 17, "start": 17,
"type": "Identifier" "type": "Identifier"
}, },
"path": [],
"start": 17,
"type": "Name"
},
"commentStart": 17, "commentStart": 17,
"end": 30, "end": 30,
"start": 17, "start": 17,

View File

@ -21,13 +21,21 @@ expression: actual
"end": 21, "end": 21,
"left": { "left": {
"argument": { "argument": {
"abs_path": false,
"commentStart": 5,
"end": 9,
"name": {
"commentStart": 5, "commentStart": 5,
"end": 9, "end": 9,
"name": "leg2", "name": "leg2",
"start": 5, "start": 5,
"type": "Identifier",
"type": "Identifier" "type": "Identifier"
}, },
"path": [],
"start": 5,
"type": "Name",
"type": "Name"
},
"commentStart": 4, "commentStart": 4,
"end": 9, "end": 9,
"operator": "-", "operator": "-",
@ -37,13 +45,21 @@ expression: actual
}, },
"operator": "+", "operator": "+",
"right": { "right": {
"abs_path": false,
"commentStart": 12,
"end": 21,
"name": {
"commentStart": 12, "commentStart": 12,
"end": 21, "end": 21,
"name": "thickness", "name": "thickness",
"start": 12, "start": 12,
"type": "Identifier",
"type": "Identifier" "type": "Identifier"
}, },
"path": [],
"start": 12,
"type": "Name",
"type": "Name"
},
"start": 4, "start": 4,
"type": "BinaryExpression", "type": "BinaryExpression",
"type": "BinaryExpression" "type": "BinaryExpression"

View File

@ -21,13 +21,21 @@ expression: actual
"body": [ "body": [
{ {
"argument": { "argument": {
"abs_path": false,
"commentStart": 30,
"end": 32,
"name": {
"commentStart": 30, "commentStart": 30,
"end": 32, "end": 32,
"name": "sg", "name": "sg",
"start": 30, "start": 30,
"type": "Identifier",
"type": "Identifier" "type": "Identifier"
}, },
"path": [],
"start": 30,
"type": "Name",
"type": "Name"
},
"commentStart": 23, "commentStart": 23,
"end": 32, "end": 32,
"start": 23, "start": 23,
@ -36,13 +44,21 @@ expression: actual
}, },
{ {
"argument": { "argument": {
"abs_path": false,
"commentStart": 48,
"end": 50,
"name": {
"commentStart": 48, "commentStart": 48,
"end": 50, "end": 50,
"name": "sg", "name": "sg",
"start": 48, "start": 48,
"type": "Identifier",
"type": "Identifier" "type": "Identifier"
}, },
"path": [],
"start": 48,
"type": "Name",
"type": "Name"
},
"commentStart": 41, "commentStart": 41,
"end": 50, "end": 50,
"start": 41, "start": 41,

View File

@ -28,12 +28,20 @@ expression: actual
"type": "Identifier" "type": "Identifier"
}, },
"arg": { "arg": {
"abs_path": false,
"commentStart": 14,
"end": 15,
"name": {
"commentStart": 14, "commentStart": 14,
"end": 15, "end": 15,
"name": "a", "name": "a",
"start": 14, "start": 14,
"type": "Identifier",
"type": "Identifier" "type": "Identifier"
},
"path": [],
"start": 14,
"type": "Name",
"type": "Name"
} }
}, },
{ {
@ -46,22 +54,38 @@ expression: actual
"type": "Identifier" "type": "Identifier"
}, },
"arg": { "arg": {
"abs_path": false,
"commentStart": 21,
"end": 22,
"name": {
"commentStart": 21, "commentStart": 21,
"end": 22, "end": 22,
"name": "b", "name": "b",
"start": 21, "start": 21,
"type": "Identifier",
"type": "Identifier" "type": "Identifier"
},
"path": [],
"start": 21,
"type": "Name",
"type": "Name"
} }
} }
], ],
"callee": { "callee": {
"abs_path": false,
"commentStart": 6,
"end": 9,
"name": {
"commentStart": 6, "commentStart": 6,
"end": 9, "end": 9,
"name": "foo", "name": "foo",
"start": 6, "start": 6,
"type": "Identifier" "type": "Identifier"
}, },
"path": [],
"start": 6,
"type": "Name"
},
"commentStart": 6, "commentStart": 6,
"end": 23, "end": 23,
"start": 6, "start": 6,

View File

@ -42,22 +42,38 @@ expression: actual
"type": "Identifier" "type": "Identifier"
}, },
"arg": { "arg": {
"abs_path": false,
"commentStart": 19,
"end": 20,
"name": {
"commentStart": 19, "commentStart": 19,
"end": 20, "end": 20,
"name": "x", "name": "x",
"start": 19, "start": 19,
"type": "Identifier",
"type": "Identifier" "type": "Identifier"
},
"path": [],
"start": 19,
"type": "Name",
"type": "Name"
} }
} }
], ],
"callee": { "callee": {
"abs_path": false,
"commentStart": 11,
"end": 12,
"name": {
"commentStart": 11, "commentStart": 11,
"end": 12, "end": 12,
"name": "f", "name": "f",
"start": 11, "start": 11,
"type": "Identifier" "type": "Identifier"
}, },
"path": [],
"start": 11,
"type": "Name"
},
"commentStart": 11, "commentStart": 11,
"end": 21, "end": 21,
"start": 11, "start": 11,

View File

@ -28,12 +28,20 @@ expression: actual
"type": "Identifier" "type": "Identifier"
}, },
"arg": { "arg": {
"abs_path": false,
"commentStart": 28,
"end": 29,
"name": {
"commentStart": 28, "commentStart": 28,
"end": 29, "end": 29,
"name": "x", "name": "x",
"start": 28, "start": 28,
"type": "Identifier",
"type": "Identifier" "type": "Identifier"
},
"path": [],
"start": 28,
"type": "Name",
"type": "Name"
} }
}, },
{ {
@ -46,12 +54,20 @@ expression: actual
"type": "Identifier" "type": "Identifier"
}, },
"arg": { "arg": {
"abs_path": false,
"commentStart": 50,
"end": 51,
"name": {
"commentStart": 50, "commentStart": 50,
"end": 51, "end": 51,
"name": "x", "name": "x",
"start": 50, "start": 50,
"type": "Identifier",
"type": "Identifier" "type": "Identifier"
},
"path": [],
"start": 50,
"type": "Name",
"type": "Name"
} }
}, },
{ {
@ -64,22 +80,38 @@ expression: actual
"type": "Identifier" "type": "Identifier"
}, },
"arg": { "arg": {
"abs_path": false,
"commentStart": 72,
"end": 73,
"name": {
"commentStart": 72, "commentStart": 72,
"end": 73, "end": 73,
"name": "x", "name": "x",
"start": 72, "start": 72,
"type": "Identifier",
"type": "Identifier" "type": "Identifier"
},
"path": [],
"start": 72,
"type": "Name",
"type": "Name"
} }
} }
], ],
"callee": { "callee": {
"abs_path": false,
"commentStart": 6,
"end": 7,
"name": {
"commentStart": 6, "commentStart": 6,
"end": 7, "end": 7,
"name": "f", "name": "f",
"start": 6, "start": 6,
"type": "Identifier" "type": "Identifier"
}, },
"path": [],
"start": 6,
"type": "Name"
},
"commentStart": 6, "commentStart": 6,
"end": 87, "end": 87,
"start": 6, "start": 6,

View File

@ -28,12 +28,20 @@ expression: actual
"type": "Identifier" "type": "Identifier"
}, },
"arg": { "arg": {
"abs_path": false,
"commentStart": 28,
"end": 29,
"name": {
"commentStart": 28, "commentStart": 28,
"end": 29, "end": 29,
"name": "x", "name": "x",
"start": 28, "start": 28,
"type": "Identifier",
"type": "Identifier" "type": "Identifier"
},
"path": [],
"start": 28,
"type": "Name",
"type": "Name"
} }
}, },
{ {
@ -46,22 +54,38 @@ expression: actual
"type": "Identifier" "type": "Identifier"
}, },
"arg": { "arg": {
"abs_path": false,
"commentStart": 75,
"end": 76,
"name": {
"commentStart": 75, "commentStart": 75,
"end": 76, "end": 76,
"name": "x", "name": "x",
"start": 75, "start": 75,
"type": "Identifier",
"type": "Identifier" "type": "Identifier"
},
"path": [],
"start": 75,
"type": "Name",
"type": "Name"
} }
} }
], ],
"callee": { "callee": {
"abs_path": false,
"commentStart": 6,
"end": 7,
"name": {
"commentStart": 6, "commentStart": 6,
"end": 7, "end": 7,
"name": "f", "name": "f",
"start": 6, "start": 6,
"type": "Identifier" "type": "Identifier"
}, },
"path": [],
"start": 6,
"type": "Name"
},
"commentStart": 6, "commentStart": 6,
"end": 90, "end": 90,
"nonCodeMeta": { "nonCodeMeta": {

View File

@ -28,34 +28,58 @@ expression: actual
"type": "Identifier" "type": "Identifier"
}, },
"arg": { "arg": {
"abs_path": false,
"commentStart": 17,
"end": 18,
"name": {
"commentStart": 17, "commentStart": 17,
"end": 18, "end": 18,
"name": "z", "name": "z",
"start": 17, "start": 17,
"type": "Identifier",
"type": "Identifier" "type": "Identifier"
},
"path": [],
"start": 17,
"type": "Name",
"type": "Name"
} }
} }
], ],
"callee": { "callee": {
"abs_path": false,
"commentStart": 6,
"end": 9,
"name": {
"commentStart": 6, "commentStart": 6,
"end": 9, "end": 9,
"name": "foo", "name": "foo",
"start": 6, "start": 6,
"type": "Identifier" "type": "Identifier"
}, },
"path": [],
"start": 6,
"type": "Name"
},
"commentStart": 6, "commentStart": 6,
"end": 19, "end": 19,
"start": 6, "start": 6,
"type": "CallExpressionKw", "type": "CallExpressionKw",
"type": "CallExpressionKw", "type": "CallExpressionKw",
"unlabeled": { "unlabeled": {
"abs_path": false,
"commentStart": 10,
"end": 11,
"name": {
"commentStart": 10, "commentStart": 10,
"end": 11, "end": 11,
"name": "x", "name": "x",
"start": 10, "start": 10,
"type": "Identifier",
"type": "Identifier" "type": "Identifier"
},
"path": [],
"start": 10,
"type": "Name",
"type": "Name"
} }
}, },
"start": 0, "start": 0,

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