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 .

View File

@ -1,7 +1,9 @@
name: E2E Tests
on:
push:
branches: [ main ]
branches:
- main
- all-e2e # this bypasses `fixme()` using `orRunWhenFullSuiteEnabled()`
pull_request:
schedule:
- cron: 0 * * * * # hourly
@ -15,7 +17,6 @@ permissions:
pull-requests: write
actions: read
jobs:
conditions:
@ -67,14 +68,16 @@ jobs:
- name: Display conditions
shell: bash
run: |
# For debugging purposes.
# For debugging purposes
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 "should-run: ${{ steps.should-run.outputs.should-run }}"
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
needs: conditions
steps:
@ -159,7 +162,6 @@ jobs:
name: prepared-wasm
path: |
rust/kcl-wasm-lib/pkg/kcl_wasm_lib*
snapshots:
name: playwright:snapshots:ubuntu
@ -243,7 +245,7 @@ jobs:
retention-days: 30
overwrite: true
- name: check for changes
- name: Check for changes
if: ${{ needs.conditions.outputs.should-run == 'true' && github.ref != 'refs/heads/main' }}
shell: bash
id: git-check

View File

@ -141,13 +141,13 @@ jobs:
if: ${{ github.event_name != 'release' && github.event_name != 'schedule' }}
run: yarn playwright install chromium --with-deps
- name: run unit tests
- name: Run unit tests
if: ${{ github.event_name != 'release' && github.event_name != 'schedule' }}
run: xvfb-run -a yarn test:unit
env:
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' }}
id: git-check
run: |

View File

@ -14,10 +14,19 @@ permissions:
jobs:
update-branch:
runs-on: ubuntu-latest
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
- shell: bash
with:
token: ${{ steps.app-token.outputs.token }}
- name: Sync with main
run: |
# checkout our branch
git checkout all-e2e || git checkout -b all-e2e
@ -26,4 +35,5 @@ jobs:
# reset to main
git reset --hard origin/main
# 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

View File

@ -20,8 +20,12 @@ $(WASM_PACK):
###############################################################################
# BUILD
RUST_SOURCES := $(wildcard rust/*) $(wildcard rust/**/*)
TYPESCRIPT_SOURCES := $(wildcard src/**/*.tsx) $(wildcard src/**/*.ts)
CARGO_SOURCES := rust/.cargo/config.toml $(wildcard rust/Cargo.*) $(wildcard rust/**/Cargo.*)
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
build: build-web build-desktop
@ -32,13 +36,13 @@ build-web: public/kcl_wasm_lib_bg.wasm build/index.html
.PHONY: build-desktop
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
build/index.html: $(TYPESCRIPT_SOURCES)
build/index.html: $(REACT_SOURCES) $(TYPESCRIPT_SOURCES) $(VITE_SOURCES)
yarn build:local
.vite/build/main.js: $(TYPESCRIPT_SOURCES)
.vite/build/main.js: $(REACT_SOURCES) $(TYPESCRIPT_SOURCES) $(VITE_SOURCES)
yarn tronb:vite:dev
###############################################################################
@ -73,19 +77,29 @@ run-desktop: install build-desktop ## Start the desktop app
###############################################################################
# TEST
GREP ?= ""
E2E_WORKERS ?= 1
E2E_FAILURES ?= 1
E2E_GREP ?= ""
.PHONY: test
test: test-unit test-e2e
.PHONY: test-unit
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
.PHONY: test-e2e
test-e2e: install build-desktop ## Run the e2e tests
yarn test:playwright:electron --workers=1 --grep=$(GREP)
test-e2e: test-e2e-desktop
.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

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
circleThreePoint(
sketchSurfaceOrGroup: SketchOrSurface,
p1: [number],
p2: [number],
p3: [number],
sketchSurfaceOrGroup: SketchOrSurface,
tag?: TagDeclarator,
): Sketch
```
@ -23,10 +23,10 @@ circleThreePoint(
| 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 |
| `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 |
| `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 |
### Returns

View File

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

View File

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

View File

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

File diff suppressed because one or more lines are too long

View File

@ -61,7 +61,6 @@ layout: manual
* [`bezierCurve`](kcl/bezierCurve)
* [`ceil`](kcl/ceil)
* [`chamfer`](kcl/chamfer)
* [`circle`](kcl/circle)
* [`circleThreePoint`](kcl/circleThreePoint)
* [`close`](kcl/close)
* [`cm`](kcl/cm)
@ -145,3 +144,5 @@ layout: manual
* [`cos`](kcl/std-math-cos)
* [`sin`](kcl/std-math-sin)
* [`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
cos(num: number(rad)): number(_)
cos(@num: number(rad)): number(_)
```
@ -27,7 +27,7 @@ cos(num: number(rad)): number(_)
### Examples
```js
exampleSketch = startSketchOn("XZ")
exampleSketch = startSketchOn(XZ)
|> startProfileAt([0, 0], %)
|> angledLine({
angle = 30,

View File

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

View File

@ -9,7 +9,7 @@ Compute the tangent of a number (in radians).
```js
tan(num: number(rad)): number(_)
tan(@num: number(rad)): number(_)
```
@ -27,7 +27,7 @@ tan(num: number(rad)): number(_)
### Examples
```js
exampleSketch = startSketchOn("XZ")
exampleSketch = startSketchOn(XZ)
|> startProfileAt([0, 0], %)
|> angledLine({
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]
```
`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]`.
[`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`](/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]
```
`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]`.
[`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`](/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()
```
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.
The previous sketch commands will never be executed again, in this case.

View File

@ -18,7 +18,7 @@ myPart = startSketchOn('XY')
|> 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.
The previous solid commands will never be executed again, in this case.

View File

@ -6,6 +6,7 @@ import {
executorInputPath,
getUtils,
orRunWhenFullSuiteEnabled,
runningOnWindows,
} from './test-utils'
import { join } from 'path'
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',
{ tag: '@electron' },
async ({ page, context, homePage, scene, editor, toolbar, cmdBar }) => {
if (runningOnWindows()) {
test.fixme(orRunWhenFullSuiteEnabled())
}
await context.folderSetupFn(async (dir) => {
const bracketDir = join(dir, 'test-sample')
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,
page,
homePage,
@ -1082,29 +1082,26 @@ openSketch = startSketchOn(XY)
}) => {
// One dumb hardcoded screen pixel value
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,`
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 toolbar.helixButton.click()
await cmdBar.expectState({
stage: 'arguments',
currentArgKey: 'revolutions',
currentArgValue: '1',
currentArgKey: 'axisOrEdge',
currentArgValue: '',
headerArguments: {
AngleStart: '',
Axis: '',
AxisOrEdge: '',
CounterClockWise: '',
Length: '',
Radius: '',
Revolutions: '',
},
highlightedHeaderArg: 'revolutions',
highlightedHeaderArg: 'axisOrEdge',
commandName: 'Helix',
})
await cmdBar.progressCmdBar()
@ -1114,6 +1111,7 @@ openSketch = startSketchOn(XY)
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 () => {
@ -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 = [
{ shouldPreselect: true },
{ shouldPreselect: false },

View File

@ -191,7 +191,7 @@ test(
// error text on hover
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()
// black pixel means the scene has been cleared.
@ -409,7 +409,7 @@ test(
// error text on hover
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()
// black pixel means the scene has been cleared.
@ -453,7 +453,7 @@ test(
// error text on hover
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()
}
)

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

@ -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"]'
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() {
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) {

3
interface.d.ts vendored
View File

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

View File

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

View File

@ -18,7 +18,9 @@ const config = defineConfig({
environment: 'node',
reporters: process.env.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,
hookTimeout: 1000,
teardownTimeout: 1000,

View File

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

View File

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

View File

@ -12,6 +12,7 @@ import { defineConfig, devices } from '@playwright/test'
export default defineConfig({
timeout: 120_000, // override the default 30s timeout
testDir: './e2e/playwright',
testIgnore: '*.test.ts', // ignore unit tests
/* Run tests in files in parallel */
fullyParallel: true,
/* 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]]
name = "kcl-bumper"
version = "0.1.53"
version = "0.1.54"
dependencies = [
"anyhow",
"clap",
@ -1791,7 +1791,7 @@ dependencies = [
[[package]]
name = "kcl-derive-docs"
version = "0.1.53"
version = "0.1.54"
dependencies = [
"Inflector",
"anyhow",
@ -1810,7 +1810,7 @@ dependencies = [
[[package]]
name = "kcl-directory-test-macro"
version = "0.1.53"
version = "0.1.54"
dependencies = [
"proc-macro2",
"quote",
@ -1819,7 +1819,7 @@ dependencies = [
[[package]]
name = "kcl-language-server"
version = "0.2.53"
version = "0.2.54"
dependencies = [
"anyhow",
"clap",
@ -1840,7 +1840,7 @@ dependencies = [
[[package]]
name = "kcl-language-server-release"
version = "0.1.53"
version = "0.1.54"
dependencies = [
"anyhow",
"clap",
@ -1860,7 +1860,7 @@ dependencies = [
[[package]]
name = "kcl-lib"
version = "0.2.53"
version = "0.2.54"
dependencies = [
"anyhow",
"approx 0.5.1",
@ -1928,7 +1928,7 @@ dependencies = [
[[package]]
name = "kcl-python-bindings"
version = "0.3.53"
version = "0.3.54"
dependencies = [
"anyhow",
"kcl-lib",
@ -1943,7 +1943,7 @@ dependencies = [
[[package]]
name = "kcl-test-server"
version = "0.1.53"
version = "0.1.54"
dependencies = [
"anyhow",
"hyper 0.14.32",
@ -1956,7 +1956,7 @@ dependencies = [
[[package]]
name = "kcl-to-core"
version = "0.1.53"
version = "0.1.54"
dependencies = [
"anyhow",
"async-trait",
@ -1970,7 +1970,7 @@ dependencies = [
[[package]]
name = "kcl-wasm-lib"
version = "0.1.53"
version = "0.1.54"
dependencies = [
"bson",
"console_error_panic_hook",

View File

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

View File

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

View File

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

View File

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

View File

@ -2,7 +2,7 @@
name = "kcl-language-server"
description = "A language server for KCL."
authors = ["KittyCAD Inc <kcl@kittycad.io>"]
version = "0.2.53"
version = "0.2.54"
edition = "2021"
license = "MIT"
# 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/node": "^22.13.10",
"@types/vscode": "^1.97.0",
"@typescript-eslint/eslint-plugin": "^6.6.0",
"@typescript-eslint/parser": "^6.6.0",
"@typescript-eslint/eslint-plugin": "^8.27.0",
"@typescript-eslint/parser": "^8.27.0",
"@vscode/test-electron": "^2.4.1",
"@vscode/vsce": "^2.30.0",
"@vscode/vsce": "^3.3.0",
"cross-env": "^7.0.3",
"esbuild": "^0.25.1",
"glob": "^10.4.3",
"glob": "^11.0.1",
"mocha": "^11.1.0",
"typescript": "^5.8.2"
},

View File

@ -252,7 +252,7 @@
dependencies:
eslint-visitor-keys "^3.4.3"
"@eslint-community/regexpp@^4.5.1":
"@eslint-community/regexpp@^4.10.0":
version "4.12.1"
resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.1.tgz#cfc6cffe39df390a3841cde2abccf92eaa7ae0e0"
integrity sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==
@ -308,11 +308,6 @@
"@types/minimatch" "^5.1.2"
"@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":
version "5.1.2"
resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-5.1.2.tgz#07508b45797cb81ec3f273011b054cd0755eddca"
@ -330,101 +325,91 @@
dependencies:
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":
version "1.97.0"
resolved "https://registry.yarnpkg.com/@types/vscode/-/vscode-1.97.0.tgz#62ce3a32243019aaa4fc20cee2a3de06bc71af4f"
integrity sha512-ueE73loeOTe7olaVyqP9mrRI54kVPJifUPjblZo9fYcv1CuVLPOEKEkqW0GkqPC454+nCEoigLWnC2Pp7prZ9w==
"@typescript-eslint/eslint-plugin@^6.6.0":
version "6.21.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz#30830c1ca81fd5f3c2714e524c4303e0194f9cd3"
integrity sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==
"@typescript-eslint/eslint-plugin@^8.27.0":
version "8.27.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.27.0.tgz#fbef10802365832ee1d1bd5d2117dcec82727a72"
integrity sha512-4henw4zkePi5p252c8ncBLzLce52SEUz2Ebj8faDnuUXz2UuHEONYcJ+G0oaCF+bYCWVZtrGzq3FD7YXetmnSA==
dependencies:
"@eslint-community/regexpp" "^4.5.1"
"@typescript-eslint/scope-manager" "6.21.0"
"@typescript-eslint/type-utils" "6.21.0"
"@typescript-eslint/utils" "6.21.0"
"@typescript-eslint/visitor-keys" "6.21.0"
debug "^4.3.4"
"@eslint-community/regexpp" "^4.10.0"
"@typescript-eslint/scope-manager" "8.27.0"
"@typescript-eslint/type-utils" "8.27.0"
"@typescript-eslint/utils" "8.27.0"
"@typescript-eslint/visitor-keys" "8.27.0"
graphemer "^1.4.0"
ignore "^5.2.4"
ignore "^5.3.1"
natural-compare "^1.4.0"
semver "^7.5.4"
ts-api-utils "^1.0.1"
ts-api-utils "^2.0.1"
"@typescript-eslint/parser@^6.6.0":
version "6.21.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-6.21.0.tgz#af8fcf66feee2edc86bc5d1cf45e33b0630bf35b"
integrity sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==
"@typescript-eslint/parser@^8.27.0":
version "8.27.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.27.0.tgz#3f6beaa83934467eda34ae82ef04090014df8227"
integrity sha512-XGwIabPallYipmcOk45DpsBSgLC64A0yvdAkrwEzwZ2viqGqRUJ8eEYoPz0CWnutgAFbNMPdsGGvzjSmcWVlEA==
dependencies:
"@typescript-eslint/scope-manager" "6.21.0"
"@typescript-eslint/types" "6.21.0"
"@typescript-eslint/typescript-estree" "6.21.0"
"@typescript-eslint/visitor-keys" "6.21.0"
"@typescript-eslint/scope-manager" "8.27.0"
"@typescript-eslint/types" "8.27.0"
"@typescript-eslint/typescript-estree" "8.27.0"
"@typescript-eslint/visitor-keys" "8.27.0"
debug "^4.3.4"
"@typescript-eslint/scope-manager@6.21.0":
version "6.21.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz#ea8a9bfc8f1504a6ac5d59a6df308d3a0630a2b1"
integrity sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==
"@typescript-eslint/scope-manager@8.27.0":
version "8.27.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.27.0.tgz#b51042927067d677fbfc471605cf40d1ffaee482"
integrity sha512-8oI9GwPMQmBryaaxG1tOZdxXVeMDte6NyJA4i7/TWa4fBwgnAXYlIQP+uYOeqAaLJ2JRxlG9CAyL+C+YE9Xknw==
dependencies:
"@typescript-eslint/types" "6.21.0"
"@typescript-eslint/visitor-keys" "6.21.0"
"@typescript-eslint/types" "8.27.0"
"@typescript-eslint/visitor-keys" "8.27.0"
"@typescript-eslint/type-utils@6.21.0":
version "6.21.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz#6473281cfed4dacabe8004e8521cee0bd9d4c01e"
integrity sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==
"@typescript-eslint/type-utils@8.27.0":
version "8.27.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.27.0.tgz#af3c4eefcb64455ee50aae2d7069918467af085c"
integrity sha512-wVArTVcz1oJOIEJxui/nRhV0TXzD/zMSOYi/ggCfNq78EIszddXcJb7r4RCp/oBrjt8n9A0BSxRMKxHftpDxDA==
dependencies:
"@typescript-eslint/typescript-estree" "6.21.0"
"@typescript-eslint/utils" "6.21.0"
"@typescript-eslint/typescript-estree" "8.27.0"
"@typescript-eslint/utils" "8.27.0"
debug "^4.3.4"
ts-api-utils "^1.0.1"
ts-api-utils "^2.0.1"
"@typescript-eslint/types@6.21.0":
version "6.21.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-6.21.0.tgz#205724c5123a8fef7ecd195075fa6e85bac3436d"
integrity sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==
"@typescript-eslint/types@8.27.0":
version "8.27.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.27.0.tgz#3dd01ced4c81e798d1106fda0904f8d5c91051aa"
integrity sha512-/6cp9yL72yUHAYq9g6DsAU+vVfvQmd1a8KyA81uvfDE21O2DwQ/qxlM4AR8TSdAu+kJLBDrEHKC5/W2/nxsY0A==
"@typescript-eslint/typescript-estree@6.21.0":
version "6.21.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz#c47ae7901db3b8bddc3ecd73daff2d0895688c46"
integrity sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==
"@typescript-eslint/typescript-estree@8.27.0":
version "8.27.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.27.0.tgz#4e02a1056454a84418cc9bce7c00b1c08b03567a"
integrity sha512-BnKq8cqPVoMw71O38a1tEb6iebEgGA80icSxW7g+kndx0o6ot6696HjG7NdgfuAVmVEtwXUr3L8R9ZuVjoQL6A==
dependencies:
"@typescript-eslint/types" "6.21.0"
"@typescript-eslint/visitor-keys" "6.21.0"
"@typescript-eslint/types" "8.27.0"
"@typescript-eslint/visitor-keys" "8.27.0"
debug "^4.3.4"
globby "^11.1.0"
fast-glob "^3.3.2"
is-glob "^4.0.3"
minimatch "9.0.3"
semver "^7.5.4"
ts-api-utils "^1.0.1"
minimatch "^9.0.4"
semver "^7.6.0"
ts-api-utils "^2.0.1"
"@typescript-eslint/utils@6.21.0":
version "6.21.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-6.21.0.tgz#4714e7a6b39e773c1c8e97ec587f520840cd8134"
integrity sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==
"@typescript-eslint/utils@8.27.0":
version "8.27.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.27.0.tgz#d9c2a4891c6a85b952a9d5f9656c379ab111cf6d"
integrity sha512-njkodcwH1yvmo31YWgRHNb/x1Xhhq4/m81PhtvmRngD8iHPehxffz1SNCO+kwaePhATC+kOa/ggmvPoPza5i0Q==
dependencies:
"@eslint-community/eslint-utils" "^4.4.0"
"@types/json-schema" "^7.0.12"
"@types/semver" "^7.5.0"
"@typescript-eslint/scope-manager" "6.21.0"
"@typescript-eslint/types" "6.21.0"
"@typescript-eslint/typescript-estree" "6.21.0"
semver "^7.5.4"
"@typescript-eslint/scope-manager" "8.27.0"
"@typescript-eslint/types" "8.27.0"
"@typescript-eslint/typescript-estree" "8.27.0"
"@typescript-eslint/visitor-keys@6.21.0":
version "6.21.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz#87a99d077aa507e20e238b11d56cc26ade45fe47"
integrity sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==
"@typescript-eslint/visitor-keys@8.27.0":
version "8.27.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.27.0.tgz#4a13e9d7ad7e311a07ea1b178b4c9f848ce11334"
integrity sha512-WsXQwMkILJvffP6z4U3FYJPlbf/j07HIxmDjZpbNvBJkMfvwXj5ACRkkHwBDvLBbDbtX5TdU64/rcvKJ/vuInQ==
dependencies:
"@typescript-eslint/types" "6.21.0"
eslint-visitor-keys "^3.4.1"
"@typescript-eslint/types" "8.27.0"
eslint-visitor-keys "^4.2.0"
"@vscode/test-electron@^2.4.1":
version "2.4.1"
@ -497,10 +482,10 @@
"@vscode/vsce-sign-win32-arm64" "2.0.2"
"@vscode/vsce-sign-win32-x64" "2.0.2"
"@vscode/vsce@^2.30.0":
version "2.30.0"
resolved "https://registry.yarnpkg.com/@vscode/vsce/-/vsce-2.30.0.tgz#7a0c16b20ef529fa291fe9d3c7fe51a2a613f773"
integrity sha512-MBYpXdCY1SCdc2u/y11kmJuSODKFyZRpeRTQq5p4rSg05QSjSy5pz6h/BGLNdSahgXfKRBATEkjAcJFdJuDz8Q==
"@vscode/vsce@^3.3.0":
version "3.3.0"
resolved "https://registry.yarnpkg.com/@vscode/vsce/-/vsce-3.3.0.tgz#803e41368a95d35693ce049076503f34f89fde09"
integrity sha512-HA/pUyvh/TQWkc4wG7AudPIWUvsR8i4jiWZZgM/a69ncPi9Nm5FDogf/wVEk4EWJs4/UdxU7J6X18dfAwfPbxA==
dependencies:
"@azure/identity" "^4.1.0"
"@vscode/vsce-sign" "^2.0.0"
@ -508,19 +493,19 @@
chalk "^2.4.2"
cheerio "^1.0.0-rc.9"
cockatiel "^3.1.2"
commander "^6.2.1"
commander "^12.1.0"
form-data "^4.0.0"
glob "^7.0.6"
glob "^11.0.0"
hosted-git-info "^4.0.2"
jsonc-parser "^3.2.0"
leven "^3.1.0"
markdown-it "^12.3.2"
markdown-it "^14.1.0"
mime "^1.3.4"
minimatch "^3.0.3"
parse-semver "^1.1.1"
read "^1.0.7"
semver "^7.5.2"
tmp "^0.2.1"
tmp "^0.2.3"
typed-rest-client "^1.8.4"
url-join "^4.0.1"
xml2js "^0.5.0"
@ -583,11 +568,6 @@ argparse@^2.0.1:
resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
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:
version "0.4.0"
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
@ -832,10 +812,10 @@ combined-stream@^1.0.8:
dependencies:
delayed-stream "~1.0.0"
commander@^6.2.1:
version "6.2.1"
resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c"
integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==
commander@^12.1.0:
version "12.1.0"
resolved "https://registry.yarnpkg.com/commander/-/commander-12.1.0.tgz#01423b36f501259fdaac4d0e4d60c96c991585d3"
integrity sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==
concat-map@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"
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:
version "2.0.0"
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"
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:
version "1.0.0"
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"
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"
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==
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:
version "3.3.0"
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"
integrity sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==
fast-glob@^3.2.9:
fast-glob@^3.3.2:
version "3.3.3"
resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.3.tgz#d06d585ce8dba90a16b0505c543c3ccfb3aeb818"
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"
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:
version "2.3.3"
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:
is-glob "^4.0.1"
glob@^10.4.3, glob@^10.4.5:
glob@^10.4.5:
version "10.4.5"
resolved "https://registry.yarnpkg.com/glob/-/glob-10.4.5.tgz#f4d9f0b90ffdbab09c9d77f5f29b4262517b0956"
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"
path-scurry "^1.11.1"
glob@^7.0.6:
version "7.2.3"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b"
integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==
glob@^11.0.0, glob@^11.0.1:
version "11.0.1"
resolved "https://registry.yarnpkg.com/glob/-/glob-11.0.1.tgz#1c3aef9a59d680e611b53dcd24bb8639cef064d9"
integrity sha512-zrQDm8XPnYEKawJScsnM0QzobJxlT/kHOOlRTio8IH/GrmxRE5fjllkzdaHclIuNjUQTJYH2xHNIGfdpJkDJUw==
dependencies:
fs.realpath "^1.0.0"
inflight "^1.0.4"
inherits "2"
minimatch "^3.1.1"
once "^1.3.0"
path-is-absolute "^1.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"
foreground-child "^3.1.0"
jackspeak "^4.0.1"
minimatch "^10.0.0"
minipass "^7.1.2"
package-json-from-dist "^1.0.0"
path-scurry "^2.0.0"
gopd@^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"
integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
ignore@^5.2.0, ignore@^5.2.4:
ignore@^5.3.1:
version "5.3.2"
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5"
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"
integrity sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==
inflight@^1.0.4:
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:
inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3:
version "2.0.4"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
@ -1429,6 +1377,13 @@ jackspeak@^3.1.2:
optionalDependencies:
"@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:
version "4.1.0"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602"
@ -1521,12 +1476,12 @@ lie@~3.3.0:
dependencies:
immediate "~3.0.5"
linkify-it@^3.0.1:
version "3.0.3"
resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-3.0.3.tgz#a98baf44ce45a550efb4d49c769d07524cc2fa2e"
integrity sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==
linkify-it@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-5.0.0.tgz#9ef238bfa6dc70bd8e7f9572b52d369af569b421"
integrity sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==
dependencies:
uc.micro "^1.0.1"
uc.micro "^2.0.0"
locate-path@^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"
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:
version "6.0.0"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94"
@ -1598,23 +1558,24 @@ lru-cache@^6.0.0:
dependencies:
yallist "^4.0.0"
markdown-it@^12.3.2:
version "12.3.2"
resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-12.3.2.tgz#bf92ac92283fe983fe4de8ff8abfb5ad72cd0c90"
integrity sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==
markdown-it@^14.1.0:
version "14.1.0"
resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-14.1.0.tgz#3c3c5992883c633db4714ccb4d7b5935d98b7d45"
integrity sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==
dependencies:
argparse "^2.0.1"
entities "~2.1.0"
linkify-it "^3.0.1"
mdurl "^1.0.1"
uc.micro "^1.0.5"
entities "^4.4.0"
linkify-it "^5.0.0"
mdurl "^2.0.0"
punycode.js "^2.3.1"
uc.micro "^2.1.0"
mdurl@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e"
integrity sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==
mdurl@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-2.0.0.tgz#80676ec0433025dd3e17ee983d0fe8de5a2237e0"
integrity sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==
merge2@^1.3.0, merge2@^1.4.1:
merge2@^1.3.0:
version "1.4.1"
resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae"
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"
integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==
minimatch@9.0.3:
version "9.0.3"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.3.tgz#a6e00c3de44c3a542bfaae70abfc22420a6da825"
integrity sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==
minimatch@^10.0.0:
version "10.0.1"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-10.0.1.tgz#ce0521856b453c86e25f2c4c0d03e6ff7ddc440b"
integrity sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==
dependencies:
brace-expansion "^2.0.1"
minimatch@^3.0.3, minimatch@^3.1.1:
minimatch@^3.0.3:
version "3.1.2"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
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"
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"
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
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"
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:
version "3.1.1"
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"
minipass "^5.0.0 || ^6.0.2 || ^7.0.0"
path-type@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
path-scurry@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-2.0.0.tgz#9f052289f23ad8bf9397a2a0425e7b8615c58580"
integrity sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==
dependencies:
lru-cache "^11.0.0"
minipass "^7.1.2"
pend@~1.2.0:
version "1.2.0"
@ -1930,6 +1889,11 @@ pump@^3.0.0:
end-of-stream "^1.1.0"
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:
version "6.12.1"
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"
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:
version "7.6.2"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.2.tgz#1e3b34759f896e8f14d6134732ce798aeb0c6e13"
integrity sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==
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.7.1"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.1.tgz#abd5098d82b18c6c81f6074ff2647fd3e7220c9f"
integrity sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==
serialize-javascript@^6.0.2:
version "6.0.2"
@ -2115,11 +2079,6 @@ simple-get@^4.0.0:
once "^1.3.1"
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:
version "0.1.0"
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"
readable-stream "^3.1.1"
tmp@^0.2.1:
tmp@^0.2.3:
version "0.2.3"
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.3.tgz#eb783cc22bc1e8bebd0671476d46ea4eb32a79ae"
integrity sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==
@ -2267,10 +2226,10 @@ to-regex-range@^5.0.1:
dependencies:
is-number "^7.0.0"
ts-api-utils@^1.0.1:
version "1.4.3"
resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.4.3.tgz#bfc2215fe6528fecab2b0fba570a2e8a4263b064"
integrity sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==
ts-api-utils@^2.0.1:
version "2.1.0"
resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-2.1.0.tgz#595f7094e46eed364c13fd23e75f9513d29baf91"
integrity sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==
tslib@^2.2.0, tslib@^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"
integrity sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==
uc.micro@^1.0.1, uc.micro@^1.0.5:
version "1.0.6"
resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac"
integrity sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==
uc.micro@^2.0.0, uc.micro@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-2.1.0.tgz#f8d3f7d0ec4c3dea35a7e3c8efa4cb8b45c9e7ee"
integrity sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==
underscore@^1.12.1:
version "1.13.6"

View File

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

View File

@ -837,10 +837,10 @@ holeIndex = 6
// Create the mounting plate extrusion, holes, and fillets
part = rectShape([0, 0], 20, 20)
|> hole(circle(XY, [-holeIndex, holeIndex], holeRadius), %)
|> hole(circle(XY, [holeIndex, holeIndex], holeRadius), %)
|> hole(circle(XY, [-holeIndex, -holeIndex], holeRadius), %)
|> hole(circle(XY, [holeIndex, -holeIndex], holeRadius), %)
|> hole(circle('XY', center = [-holeIndex, holeIndex], radius = holeRadius), %)
|> hole(circle('XY', center = [holeIndex, holeIndex], radius = holeRadius), %)
|> hole(circle('XY', center = [-holeIndex, -holeIndex], radius = holeRadius), %)
|> hole(circle('XY', center = [holeIndex, -holeIndex], radius = holeRadius), %)
|> extrude(length = 2)
|> fillet(
radius = 4,
@ -860,7 +860,7 @@ part = rectShape([0, 0], 20, 20)
};
assert_eq!(
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_eq!(
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_eq!(
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_eq!(
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 = cleanup_type_links(
&output,
ty.referenced_types.iter().filter(|t| !DECLARED_TYPES.contains(&&***t)),
);
expectorate::assert_contents(format!("../../docs/kcl/{}.md", file_name), &output);
Ok(())
@ -514,6 +518,13 @@ fn generate_function_from_kcl(function: &FnData, file_name: String) -> Result<()
});
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);
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)
}

View File

@ -1,4 +1,4 @@
use std::str::FromStr;
use std::{collections::HashSet, str::FromStr};
use regex::Regex;
use tower_lsp::lsp_types::{
@ -9,7 +9,7 @@ use tower_lsp::lsp_types::{
use crate::{
execution::annotations,
parsing::{
ast::types::{Annotation, Node, NonCodeNode, VariableKind},
ast::types::{Annotation, Node, PrimitiveType, Type, VariableKind},
token::NumericSuffix,
},
ModuleId,
@ -59,7 +59,6 @@ impl CollectionVisitor {
format!("std::{}::", self.name)
};
let mut dd = match var.kind {
// TODO metadata for args
VariableKind::Fn => DocData::Fn(FnData::from_ast(var, qual_name)),
VariableKind::Const => DocData::Const(ConstData::from_ast(var, qual_name)),
};
@ -322,6 +321,8 @@ pub struct FnData {
/// Code examples.
/// These are tested and we know they compile and execute.
pub examples: Vec<(String, ExampleProperties)>,
#[allow(dead_code)]
pub referenced_types: Vec<String>,
}
impl FnData {
@ -332,6 +333,17 @@ impl FnData {
};
let name = var.declaration.id.name.clone();
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 {
name,
qual_name,
@ -346,6 +358,7 @@ impl FnData {
summary: None,
description: None,
examples: Vec::new(),
referenced_types: referenced_types.into_iter().collect(),
}
}
@ -414,7 +427,7 @@ impl FnData {
}
#[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" {
return "loft([${0:sketch000}, ${1:sketch001}])${}".to_owned();
} else if self.name == "hole" {
@ -480,12 +493,12 @@ pub struct ArgData {
/// If the argument is required.
pub kind: ArgKind,
/// 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.
pub docs: Option<String>,
}
#[derive(Debug, Clone, Copy)]
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ArgKind {
Special,
// Parameter is whether the arg is optional.
@ -495,38 +508,47 @@ pub enum ArgKind {
impl ArgData {
fn from_ast(arg: &crate::parsing::ast::types::Parameter) -> Self {
ArgData {
let mut result = ArgData {
name: arg.identifier.name.clone(),
ty: arg.type_.as_ref().map(|t| t.to_string()),
// Doc comments are not yet supported on parameters.
docs: None,
kind: if arg.labeled {
ArgKind::Labelled(arg.optional())
} else {
ArgKind::Special
},
}
}
};
fn _with_meta(&mut self, _meta: &[Node<NonCodeNode>]) {
// TODO use comments for docs (we can't currently get the comments for an argument)
result.with_comments(&arg.identifier.pre_comments);
result
}
pub fn get_autocomplete_snippet(&self, index: usize) -> Option<(usize, String)> {
match &self.ty {
Some(s)
if [
"Sketch",
"SketchSet",
"Solid",
"SolidSet",
"SketchSurface",
"SketchOrSurface",
]
.contains(&&**s) =>
{
Some((index, format!("${{{}:{}}}", index, "%")))
let label = if self.kind == ArgKind::Special {
String::new()
} else {
format!("{} = ", self.name)
};
match self.ty.as_deref() {
Some(s) if ["Sketch", "Solid", "Plane | Face", "Sketch | Plane | Face"].contains(&s) => {
Some((index, format!("{label}${{{}:{}}}", 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,
}
}
@ -570,12 +592,19 @@ pub struct TyData {
/// Code examples.
/// These are tested and we know they compile and execute.
pub examples: Vec<(String, ExampleProperties)>,
#[allow(dead_code)]
pub referenced_types: Vec<String>,
}
impl TyData {
fn from_ast(ty: &crate::parsing::ast::types::TypeDeclaration, mut qual_name: String) -> Self {
let name = ty.name.name.clone();
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 {
name,
qual_name,
@ -589,6 +618,7 @@ impl TyData {
summary: None,
description: None,
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)]
mod test {
use super::*;

View File

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

View File

@ -78,13 +78,16 @@ pub(super) fn expect_properties<'a>(
}
pub(super) fn expect_ident(expr: &Expr) -> Result<&str, KclError> {
match expr {
Expr::Identifier(id) => Ok(&id.name),
e => Err(KclError::Semantic(KclErrorDetails {
message: "Unexpected settings value, expected a simple name, e.g., `mm`".to_owned(),
source_ranges: vec![e.into()],
})),
if let Expr::Name(name) = expr {
if let Some(name) = name.local_ident() {
return Ok(*name);
}
}
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> {

View File

@ -1,6 +1,7 @@
use std::collections::HashMap;
use async_recursion::async_recursion;
use indexmap::IndexMap;
use crate::{
engine::ExecutionKind,
@ -19,7 +20,7 @@ use crate::{
parsing::ast::types::{
Annotation, ArrayExpression, ArrayRangeExpression, BinaryExpression, BinaryOperator, BinaryPart, BodyItem,
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,
},
source_range::SourceRange,
@ -514,15 +515,23 @@ impl ExecutorContext {
source_range: SourceRange,
) -> Result<Option<KclValue>, KclError> {
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
let result = match &repr {
let result = match &mut repr {
ModuleRepr::Root => Err(exec_state.circular_import_error(&path, source_range)),
ModuleRepr::Kcl(program, _) => self
.exec_module_from_ast(program, module_id, &path, exec_state, exec_kind, source_range)
.await
.map(|(val, _, _)| val),
ModuleRepr::Kcl(program, cached_items) => {
let result = self
.exec_module_from_ast(program, module_id, &path, exec_state, exec_kind, source_range)
.await;
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)
.await
.map(|geom| Some(KclValue::ImportedGeometry(geom))),
@ -578,8 +587,8 @@ impl ExecutorContext {
Expr::None(none) => KclValue::from(none),
Expr::Literal(literal) => KclValue::from_literal((**literal).clone(), &exec_state.mod_local.settings),
Expr::TagDeclarator(tag) => tag.execute(exec_state).await?,
Expr::Identifier(identifier) => {
let value = exec_state.stack().get(&identifier.name, identifier.into())?.clone();
Expr::Name(name) => {
let value = name.get_result(exec_state, self).await?.clone();
if let KclValue::Module { value: module_id, meta } = value {
self.exec_module_for_result(module_id, exec_state, ExecutionKind::Normal, metadata.source_range)
.await?
@ -610,7 +619,11 @@ impl ExecutorContext {
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());
KclValue::Function {
value: FunctionSource::Std { func, props },
value: FunctionSource::Std {
func,
props,
ast: function_expression.clone(),
},
meta: vec![metadata.to_owned()],
}
} else {
@ -622,7 +635,7 @@ impl ExecutorContext {
}
} else {
// Snapshotting memory here is crucial for semantics so that we close
// over variables. Variables defined lexically later shouldn't
// over variables. Variables defined lexically later shouldn't
// be available to the function body.
KclValue::Function {
value: FunctionSource::User {
@ -710,10 +723,7 @@ impl BinaryPart {
(**literal).clone(),
&exec_state.mod_local.settings,
)),
BinaryPart::Identifier(identifier) => {
let value = exec_state.stack().get(&identifier.name, identifier.into())?;
Ok(value.clone())
}
BinaryPart::Name(name) => name.get_result(exec_state, ctx).await.cloned(),
BinaryPart::BinaryExpression(binary_expression) => binary_expression.get_result(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,
@ -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> {
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())?;
@ -1054,11 +1131,11 @@ async fn inner_execute_pipe_body(
impl Node<CallExpressionKw> {
#[async_recursion]
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();
// 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 {
let source_range = SourceRange::from(arg_expr.arg.clone());
let metadata = Metadata { source_range };
@ -1098,6 +1175,7 @@ impl Node<CallExpressionKw> {
format!("`{fn_name}` is deprecated, see the docs for a recommended replacement"),
));
}
let op = if func.feature_tree_operation() {
let op_labeled_args = args
.kw_args
@ -1118,10 +1196,34 @@ impl Node<CallExpressionKw> {
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.
let mut return_value = {
// 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;
exec_state.mut_stack().pop_env();
if let Some(mut op) = op {
op.set_std_lib_call_is_error(result.is_err());
@ -1140,10 +1242,9 @@ impl Node<CallExpressionKw> {
Ok(return_value)
}
FunctionKind::UserDefined => {
let source_range = SourceRange::from(self);
// Clone the function so that we can use a mutable reference to
// 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.
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)))
.collect();
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(),
unlabeled_arg: args
.kw_args
@ -1164,17 +1265,21 @@ impl Node<CallExpressionKw> {
source_range: callsite,
});
let return_value = func
.call_fn_kw(args, exec_state, ctx.clone(), callsite)
.await
.map_err(|e| {
// Add the call expression to the source ranges.
// TODO currently ignored by the frontend
e.add_source_ranges(vec![source_range])
})?;
let Some(fn_src) = func.as_fn() else {
return Err(KclError::Semantic(KclErrorDetails {
message: "cannot call this because it isn't a function".to_string(),
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.
// TODO currently ignored by the frontend
e.add_source_ranges(vec![callsite])
})?;
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.
if let KclValue::Function { meta, .. } = func {
source_ranges = meta.iter().map(|m| m.source_range).collect();
@ -1197,7 +1302,7 @@ impl Node<CallExpressionKw> {
impl Node<CallExpression> {
#[async_recursion]
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 mut fn_args: Vec<Arg> = Vec::with_capacity(self.arguments.len());
@ -1279,11 +1384,11 @@ impl Node<CallExpression> {
let source_range = SourceRange::from(self);
// Clone the function so that we can use a mutable reference to
// 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.
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(),
unlabeled_arg: None,
// TODO: Add the arguments for legacy positional parameters.
@ -1291,14 +1396,17 @@ impl Node<CallExpression> {
source_range: callsite,
});
let return_value = func
.call_fn(fn_args, exec_state, ctx.clone(), source_range)
.await
.map_err(|e| {
// Add the call expression to the source ranges.
// TODO currently ignored by the frontend
e.add_source_ranges(vec![source_range])
})?;
let Some(fn_src) = func.as_fn() else {
return Err(KclError::Semantic(KclErrorDetails {
message: "cannot call this because it isn't a function".to_string(),
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.
// TODO currently ignored by the frontend
e.add_source_ranges(vec![source_range])
})?;
let result = return_value.ok_or_else(move || {
let mut source_ranges: Vec<SourceRange> = vec![source_range];
@ -1784,6 +1892,27 @@ fn assign_args_to_params_kw(
mut args: crate::std::args::KwArgs,
exec_state: &mut ExecState,
) -> 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
// been created.
let source_ranges = vec![function_expression.into()];
@ -1832,10 +1961,11 @@ fn assign_args_to_params_kw(
)?;
}
}
Ok(())
}
pub(crate) async fn call_user_defined_function(
async fn call_user_defined_function(
args: Vec<Arg>,
memory: EnvironmentRef,
function_expression: NodeRef<'_, FunctionExpression>,
@ -1868,7 +1998,7 @@ pub(crate) async fn call_user_defined_function(
result
}
pub(crate) async fn call_user_defined_function_kw(
async fn call_user_defined_function_kw(
args: crate::std::args::KwArgs,
memory: EnvironmentRef,
function_expression: NodeRef<'_, FunctionExpression>,
@ -1906,35 +2036,138 @@ impl FunctionSource {
&self,
exec_state: &mut ExecState,
ctx: &ExecutorContext,
args: Vec<Arg>,
source_range: SourceRange,
mut args: Vec<Arg>,
callsite: SourceRange,
) -> Result<Option<KclValue>, KclError> {
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 {
exec_state.warn(CompilationError::err(
source_range,
callsite,
format!(
"`{}` is deprecated, see the docs for a recommended replacement",
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, .. } => {
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!(),
}

View File

@ -9,13 +9,13 @@ use crate::{
errors::KclErrorDetails,
execution::{
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::{
DefaultParamVal, FunctionExpression, KclNone, Literal, LiteralValue, Node, TagDeclarator, TagNode,
},
std::{args::Arg, StdFnProps},
CompilationError, KclError, ModuleId, SourceRange,
std::StdFnProps,
KclError, ModuleId, SourceRange,
};
pub type KclObjectFields = HashMap<String, KclValue>;
@ -113,6 +113,7 @@ pub enum FunctionSource {
None,
Std {
func: crate::std::StdFn,
ast: crate::parsing::ast::types::BoxNode<FunctionExpression>,
props: StdFnProps,
},
User {
@ -550,91 +551,10 @@ impl KclValue {
Ok(*b)
}
/// If this memory item is a function, call it with the given arguments, return its val as Ok.
/// 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> {
pub fn as_fn(&self) -> Option<&FunctionSource> {
match self {
KclValue::Function {
value: FunctionSource::Std { func, props },
..
} => {
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],
})),
KclValue::Function { value, .. } => Some(value),
_ => None,
}
}

View File

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

View File

@ -62,7 +62,7 @@ pub fn lint_call_expressions(exp: Node) -> Result<Vec<Discovered>> {
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),
_ => 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
Expr::None(_) => None,
Expr::Literal(_) => None,
Expr::Identifier(id) => {
if id.contains(pos) {
let name = id.name.clone();
Some(Hover::Variable {
ty: opts
.vars
Expr::Name(name) => {
if name.contains(pos) {
let ty = if let Some(name) = name.local_ident() {
opts.vars
.as_ref()
.and_then(|vars| vars.get(&name).and_then(Clone::clone)),
name,
range: id.as_source_range().to_lsp_range(code),
.and_then(|vars| vars.get(&**name).and_then(Clone::clone))
} else {
None
};
Some(Hover::Variable {
ty,
name: name.to_string(),
range: name.as_source_range().to_lsp_range(code),
})
} else {
None
@ -137,7 +140,7 @@ impl BinaryPart {
fn get_hover_value_for_position(&self, pos: usize, code: &str, opts: &HoverOpts) -> Option<Hover> {
match self {
BinaryPart::Literal(_literal) => None,
BinaryPart::Identifier(_identifier) => None,
BinaryPart::Name(_identifier) => None,
BinaryPart::BinaryExpression(binary_expression) => {
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();
if callee_source_range.contains(pos) {
return Some(Hover::Function {
name: self.callee.name.clone(),
name: self.callee.to_string(),
range: callee_source_range.to_lsp_range(code),
});
}
@ -173,7 +176,7 @@ impl CallExpression {
if source_range.contains(pos) {
return if opts.prefer_sig {
Some(Hover::Signature {
name: self.callee.name.clone(),
name: self.callee.to_string(),
parameter_index: index as u32,
range: source_range.to_lsp_range(code),
})
@ -192,7 +195,7 @@ impl CallExpressionKw {
let callee_source_range: SourceRange = self.callee.clone().into();
if callee_source_range.contains(pos) {
return Some(Hover::Function {
name: self.callee.name.clone(),
name: self.callee.to_string(),
range: callee_source_range.to_lsp_range(code),
});
}
@ -202,7 +205,7 @@ impl CallExpressionKw {
if source_range.contains(pos) {
return if opts.prefer_sig {
Some(Hover::Signature {
name: self.callee.name.clone(),
name: self.callee.to_string(),
parameter_index: index as u32,
range: source_range.to_lsp_range(code),
})
@ -215,7 +218,7 @@ impl CallExpressionKw {
if id.as_source_range().contains(pos) {
return Some(Hover::KwArg {
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),
});
}

View File

@ -523,7 +523,7 @@ impl Backend {
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.
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 {
"prelude" => Some(include_str!("../std/prelude.kcl")),
"math" => Some(include_str!("../std/math.kcl")),
"sketch" => Some(include_str!("../std/sketch.kcl")),
_ => None,
}
}

View File

@ -4,7 +4,7 @@ use crate::parsing::ast::types::{
Annotation, ArrayExpression, ArrayRangeExpression, Ascription, BinaryExpression, BinaryPart, BodyItem,
CallExpression, CallExpressionKw, DefaultParamVal, ElseIf, Expr, ExpressionStatement, FunctionExpression,
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,
TypeDeclaration, UnaryExpression, VariableDeclaration, VariableDeclarator, VariableKind,
};
@ -128,7 +128,7 @@ impl Expr {
pub fn compute_digest(&mut self) -> Digest {
match self {
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::BinaryExpression(be) => be.compute_digest(),
Expr::FunctionExpression(fe) => fe.compute_digest(),
@ -157,7 +157,7 @@ impl BinaryPart {
pub fn compute_digest(&mut self) -> Digest {
match self {
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::CallExpression(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 {
compute_digest!(|slf, hasher| {
let name = slf.name.as_bytes();

View File

@ -23,7 +23,7 @@ impl Expr {
pub fn module_id(&self) -> ModuleId {
match self {
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::BinaryExpression(binary_expression) => binary_expression.module_id,
Expr::FunctionExpression(function_expression) => function_expression.module_id,
@ -48,7 +48,7 @@ impl BinaryPart {
pub fn module_id(&self) -> ModuleId {
match self {
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::CallExpression(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 none;
#[derive(Debug)]
pub enum Definition<'a> {
Variable(&'a VariableDeclarator),
Import(NodeRef<'a, ImportStatement>),
@ -166,6 +167,18 @@ impl<T> Node<T> {
self.pre_comments = comments;
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> {
@ -762,7 +775,7 @@ impl From<&BodyItem> for SourceRange {
#[allow(clippy::large_enum_variant)]
pub enum Expr {
Literal(BoxNode<Literal>),
Identifier(BoxNode<Identifier>),
Name(BoxNode<Name>),
TagDeclarator(BoxNode<TagDeclarator>),
BinaryExpression(BoxNode<BinaryExpression>),
FunctionExpression(BoxNode<FunctionExpression>),
@ -814,7 +827,7 @@ impl Expr {
Expr::FunctionExpression(_func_exp) => None,
Expr::CallExpression(_call_exp) => None,
Expr::CallExpressionKw(_call_exp) => None,
Expr::Identifier(_ident) => None,
Expr::Name(_ident) => None,
Expr::TagDeclarator(_tag) => None,
Expr::PipeExpression(pipe_exp) => Some(&pipe_exp.non_code_meta),
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::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::Identifier(_) => {}
Expr::Name(_) => {}
Expr::TagDeclarator(_) => {}
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),
@ -857,7 +870,7 @@ impl Expr {
pub fn start(&self) -> usize {
match self {
Expr::Literal(literal) => literal.start,
Expr::Identifier(identifier) => identifier.start,
Expr::Name(identifier) => identifier.start,
Expr::TagDeclarator(tag) => tag.start,
Expr::BinaryExpression(binary_expression) => binary_expression.start,
Expr::FunctionExpression(function_expression) => function_expression.start,
@ -880,7 +893,7 @@ impl Expr {
pub fn end(&self) -> usize {
match self {
Expr::Literal(literal) => literal.end,
Expr::Identifier(identifier) => identifier.end,
Expr::Name(identifier) => identifier.end,
Expr::TagDeclarator(tag) => tag.end,
Expr::BinaryExpression(binary_expression) => binary_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) {
match self {
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::BinaryExpression(ref mut binary_expression) => {
binary_expression.rename_identifiers(old_name, new_name)
@ -934,7 +947,7 @@ impl Expr {
pub fn get_constraint_level(&self) -> ConstraintLevel {
match self {
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::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> {
match self {
Expr::Literal(lit) => match lit.value {
@ -1028,7 +1012,7 @@ impl Expr {
pub fn ident_name(&self) -> Option<&str> {
match self {
Expr::Identifier(ident) => Some(&ident.name),
Expr::Name(ident) => Some(&ident.name.name),
_ => None,
}
}
@ -1102,7 +1086,7 @@ impl Ascription {
#[serde(tag = "type")]
pub enum BinaryPart {
Literal(BoxNode<Literal>),
Identifier(BoxNode<Identifier>),
Name(BoxNode<Name>),
BinaryExpression(BoxNode<BinaryExpression>),
CallExpression(BoxNode<CallExpression>),
CallExpressionKw(BoxNode<CallExpressionKw>),
@ -1128,7 +1112,7 @@ impl BinaryPart {
pub fn get_constraint_level(&self) -> ConstraintLevel {
match self {
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::CallExpression(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) {
match self {
BinaryPart::Literal(_) => {}
BinaryPart::Identifier(_) => {}
BinaryPart::Name(_) => {}
BinaryPart::BinaryExpression(ref mut binary_expression) => {
binary_expression.replace_value(source_range, new_value)
}
@ -1162,7 +1146,7 @@ impl BinaryPart {
pub fn start(&self) -> usize {
match self {
BinaryPart::Literal(literal) => literal.start,
BinaryPart::Identifier(identifier) => identifier.start,
BinaryPart::Name(identifier) => identifier.start,
BinaryPart::BinaryExpression(binary_expression) => binary_expression.start,
BinaryPart::CallExpression(call_expression) => call_expression.start,
BinaryPart::CallExpressionKw(call_expression) => call_expression.start,
@ -1175,7 +1159,7 @@ impl BinaryPart {
pub fn end(&self) -> usize {
match self {
BinaryPart::Literal(literal) => literal.end,
BinaryPart::Identifier(identifier) => identifier.end,
BinaryPart::Name(identifier) => identifier.end,
BinaryPart::BinaryExpression(binary_expression) => binary_expression.end,
BinaryPart::CallExpression(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) {
match self {
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) => {
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 {
let mut properties: Vec<Node<ObjectProperty>> = vec![ObjectProperty::new(
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() {
properties.push(ObjectProperty::new(
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 {
@ -1689,7 +1673,7 @@ pub struct ExpressionStatement {
#[ts(export)]
#[serde(tag = "type")]
pub struct CallExpression {
pub callee: Node<Identifier>,
pub callee: Node<Name>,
pub arguments: Vec<Expr>,
#[serde(default, skip_serializing_if = "Option::is_none")]
@ -1701,7 +1685,7 @@ pub struct CallExpression {
#[ts(export)]
#[serde(rename_all = "camelCase", tag = "type")]
pub struct CallExpressionKw {
pub callee: Node<Identifier>,
pub callee: Node<Name>,
pub unlabeled: Option<Expr>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub arguments: Vec<LabeledArg>,
@ -1775,7 +1759,7 @@ impl Node<CallExpressionKw> {
impl CallExpression {
pub fn new(name: &str, arguments: Vec<Expr>) -> Result<Node<Self>, KclError> {
Ok(Node::no_src(Self {
callee: Identifier::new(name),
callee: Name::new(name),
arguments,
digest: None,
}))
@ -1807,7 +1791,7 @@ impl CallExpression {
impl CallExpressionKw {
pub fn new(name: &str, unlabeled: Option<Expr>, arguments: Vec<LabeledArg>) -> Result<Node<Self>, KclError> {
Ok(Node::no_src(Self {
callee: Identifier::new(name),
callee: Name::new(name),
unlabeled,
arguments,
digest: None,
@ -2192,13 +2176,8 @@ impl Node<Identifier> {
/// Get the constraint level for this identifier.
/// Identifier are always fully constrained.
pub fn get_constraint_level(&self) -> ConstraintLevel {
match &*self.name {
"XY" | "XZ" | "YZ" => ConstraintLevel::None {
source_ranges: vec![self.into()],
},
_ => ConstraintLevel::Full {
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)]
#[ts(export)]
#[serde(tag = "type")]

View File

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

View File

@ -18,7 +18,6 @@ use super::{
DeprecationKind,
};
use crate::{
docs::StdLibFn,
errors::{CompilationError, Severity, Tag},
execution::types::ArrayLen,
parsing::{
@ -27,9 +26,9 @@ use crate::{
BoxNode, CallExpression, CallExpressionKw, CommentStyle, DefaultParamVal, ElseIf, Expr,
ExpressionStatement, FunctionExpression, Identifier, IfExpression, ImportItem, ImportSelector,
ImportStatement, ItemVisibility, LabeledArg, Literal, LiteralIdentifier, LiteralValue, MemberExpression,
MemberObject, Node, NodeList, NonCodeMeta, NonCodeNode, NonCodeValue, ObjectExpression, ObjectProperty,
Parameter, PipeExpression, PipeSubstitution, PrimitiveType, Program, ReturnStatement, Shebang,
TagDeclarator, Type, TypeDeclaration, UnaryExpression, UnaryOperator, VariableDeclaration,
MemberObject, Name, Node, NodeList, NonCodeMeta, NonCodeNode, NonCodeValue, ObjectExpression,
ObjectProperty, Parameter, PipeExpression, PipeSubstitution, PrimitiveType, Program, ReturnStatement,
Shebang, TagDeclarator, Type, TypeDeclaration, UnaryExpression, UnaryOperator, VariableDeclaration,
VariableDeclarator, VariableKind,
},
math::BinaryExpressionToken,
@ -631,7 +630,7 @@ fn operand(i: &mut TokenSlice) -> PResult<BinaryPart> {
}
Expr::UnaryExpression(x) => BinaryPart::UnaryExpression(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::CallExpression(x) => BinaryPart::CallExpression(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.module_id,
ObjectProperty {
value: Expr::Identifier(Box::new(key.clone())),
value: Expr::Name(Box::new(key.clone().into())),
key,
digest: None,
},
@ -2069,7 +2068,7 @@ fn expr_allowed_in_pipe_expr(i: &mut TokenSlice) -> PResult<Expr> {
literal.map(Expr::Literal),
fn_call.map(Box::new).map(Expr::CallExpression),
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,
object.map(Box::new).map(Expr::ObjectExpression),
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),
literal.map(Expr::Literal),
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),
unnecessarily_bracketed,
))
@ -2360,6 +2359,35 @@ fn nameable_identifier(i: &mut TokenSlice) -> PResult<Node<Identifier>> {
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> {
type Error = CompilationError;
@ -2671,6 +2699,10 @@ fn plus(i: &mut TokenSlice) -> PResult<Token> {
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> {
one_of((TokenType::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)
}
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 {
labeled: bool,
arg_name: Token,
type_: std::option::Option<Node<Type>>,
default_value: Option<DefaultParamVal>,
comments: Option<Node<Vec<String>>>,
}
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),
any.verify(|token: &Token| !matches!(token.token_type, TokenType::Brace) || token.value != ")"),
opt(question_mark),
@ -2869,6 +2926,7 @@ fn parameter(i: &mut TokenSlice) -> PResult<ParamDescription> {
opt((equals, opt(whitespace), literal).map(|(_, _, literal)| literal)),
)
.parse_next(i)?;
Ok(ParamDescription {
labeled: found_at_sign.is_none(),
arg_name,
@ -2883,6 +2941,7 @@ fn parameter(i: &mut TokenSlice) -> PResult<ParamDescription> {
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)
.context(expected("function parameters"))
.parse_next(i)?;
opt(comma_sep).parse_next(i)?;
// Make sure all those tokens are valid parameters.
let params: Vec<Parameter> = candidates
@ -2902,8 +2962,13 @@ fn parameters(i: &mut TokenSlice) -> PResult<Vec<Parameter>> {
arg_name,
type_,
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 {
identifier,
@ -2957,76 +3022,6 @@ fn binding_name(i: &mut TokenSlice) -> PResult<Node<Identifier>> {
.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.
fn fn_call_pos_or_kw(i: &mut TokenSlice) -> PResult<Expr> {
alt((
@ -3047,15 +3042,10 @@ fn labelled_fn_call(i: &mut TokenSlice) -> PResult<Expr> {
}
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)?;
let _ = terminated(open_paren, opt(whitespace)).parse_next(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 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(
CompilationError::err(
result.as_source_range(),
format!(
"Calling `{}` is deprecated, prefer using `{}`.",
result.callee.name, suggestion
),
format!("Calling `{}` is deprecated, prefer using `{}`.", callee_str, suggestion),
)
.with_suggestion(
format!("Replace `{}` with `{}`", result.callee.name, suggestion),
format!("Replace `{}` with `{}`", callee_str, suggestion),
suggestion,
None,
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>> {
let fn_name = nameable_identifier(i)?;
let fn_name = name(i)?;
opt(whitespace).parse_next(i)?;
let _ = open_paren.parse_next(i)?;
ignore_whitespace(i);
@ -3147,10 +3135,6 @@ fn fn_call_kw(i: &mut TokenSlice) -> PResult<Node<CallExpressionKw>> {
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);
opt(comma_sep).parse_next(i)?;
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(
CompilationError::err(
result.as_source_range(),
format!(
"Calling `{}` is deprecated, prefer using `{}`.",
result.callee.name, suggestion
),
format!("Calling `{}` is deprecated, prefer using `{}`.", callee_str, suggestion),
)
.with_suggestion(
format!("Replace `{}` with `{}`", result.callee.name, suggestion),
format!("Replace `{}` with `{}`", callee_str, suggestion),
suggestion,
None,
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]
fn weird_program_unclosed_paren() {
let tokens = crate::parsing::token::lex("fn firstPrime(", ModuleId::default()).unwrap();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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