Compare commits

...

63 Commits

Author SHA1 Message Date
b17e61d963 Cut release v0.10.0 (#803)
Co-authored-by: Frank Noirot <frank@kittycad.io>
2023-10-06 11:09:54 -04:00
d31d07d9c8 Make "Replay Onboarding" button available on home settings page (#804)
* Fix unrelated bug, settings button in the home sidebar
doesn't go to the home settings after my previous fixes to routes

* Turn on "Replay Onboarding" button in home settings

* Use ONBOARDING_PROJECT_NAME in both places

* Fix formatting
2023-10-06 10:00:35 -04:00
7aa2d63c21 Hide planes (#797)
* hide planes in one go

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* update hide;

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fixes

Signed-off-by: Jess Frazelle <github@jessfraz.com>

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2023-10-05 19:54:31 -07:00
e1081b0ee6 turning back on all planes (#720)
* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

tests

Signed-off-by: Jess Frazelle <github@jessfraz.com>

fix more tests

Signed-off-by: Jess Frazelle <github@jessfraz.com>

fixes

Signed-off-by: Jess Frazelle <github@jessfraz.com>

fix stdlib

Signed-off-by: Jess Frazelle <github@jessfraz.com>

fix tests

Signed-off-by: Jess Frazelle <github@jessfraz.com>

fixes

Signed-off-by: Jess Frazelle <github@jessfraz.com>

updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

compile

Signed-off-by: Jess Frazelle <github@jessfraz.com>

update sample code

Signed-off-by: Jess Frazelle <github@jessfraz.com>

re-enable the planes

Signed-off-by: Jess Frazelle <github@jessfraz.com>

fix tests

Signed-off-by: Jess Frazelle <github@jessfraz.com>

fix tests

Signed-off-by: Jess Frazelle <github@jessfraz.com>

fix tests

Signed-off-by: Jess Frazelle <github@jessfraz.com>

fix tests

Signed-off-by: Jess Frazelle <github@jessfraz.com>

fix tests

Signed-off-by: Jess Frazelle <github@jessfraz.com>

fix tests

Signed-off-by: Jess Frazelle <github@jessfraz.com>

fix tests

Signed-off-by: Jess Frazelle <github@jessfraz.com>

updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

fix all tests

Signed-off-by: Jess Frazelle <github@jessfraz.com>

updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

boilerplate

Signed-off-by: Jess Frazelle <github@jessfraz.com>

Cut release v0.9.2 (#714)

rust make default planes

Signed-off-by: Jess Frazelle <github@jessfraz.com>

updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

use the planes from engine

Signed-off-by: Jess Frazelle <github@jessfraz.com>

fixups

Signed-off-by: Jess Frazelle <github@jessfraz.com>

updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

fixes

Signed-off-by: Jess Frazelle <github@jessfraz.com>

updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

fixes

Signed-off-by: Jess Frazelle <github@jessfraz.com>

fixes

Signed-off-by: Jess Frazelle <github@jessfraz.com>

negative args

Signed-off-by: Jess Frazelle <github@jessfraz.com>

diable camera

Signed-off-by: Jess Frazelle <github@jessfraz.com>

hide planes

Signed-off-by: Jess Frazelle <github@jessfraz.com>

updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

updatress

Signed-off-by: Jess Frazelle <github@jessfraz.com>

fmt

Signed-off-by: Jess Frazelle <github@jessfraz.com>

Update src/hooks/useAppMode.ts

Co-authored-by: Kurt Hutten <k.hutten@protonmail.ch>

Update src/hooks/useAppMode.ts

Co-authored-by: Kurt Hutten <k.hutten@protonmail.ch>

cleanups

Signed-off-by: Jess Frazelle <github@jessfraz.com>

Bump kittycad from 0.2.26 to 0.2.27 in /src-tauri (#726)

Bumps [kittycad](https://github.com/KittyCAD/kittycad.rs) from 0.2.26 to 0.2.27.
- [Release notes](https://github.com/KittyCAD/kittycad.rs/releases)
- [Commits](https://github.com/KittyCAD/kittycad.rs/compare/v0.2.26...v0.2.27)

---
updated-dependencies:
- dependency-name: kittycad
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

Bump tauri-plugin-fs-extra from `b04bde3` to `6c7a4c0` in /src-tauri (#725)

Bumps [tauri-plugin-fs-extra](https://github.com/tauri-apps/plugins-workspace) from `b04bde3` to `6c7a4c0`.
- [Release notes](https://github.com/tauri-apps/plugins-workspace/releases)
- [Commits](b04bde3461...6c7a4c0984)

---
updated-dependencies:
- dependency-name: tauri-plugin-fs-extra
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

Bump toml from 0.8.0 to 0.8.1 in /src-tauri (#724)

Bumps [toml](https://github.com/toml-rs/toml) from 0.8.0 to 0.8.1.
- [Commits](https://github.com/toml-rs/toml/compare/toml-v0.8.0...toml-v0.8.1)

---
updated-dependencies:
- dependency-name: toml
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

Bump openapitor from `61a1605` to `d3e98c4` in /src/wasm-lib (#723)

Bumps [openapitor](https://github.com/KittyCAD/kittycad.rs) from `61a1605` to `d3e98c4`.
- [Release notes](https://github.com/KittyCAD/kittycad.rs/releases)
- [Commits](61a16059b3...d3e98c4ec0)

---
updated-dependencies:
- dependency-name: openapitor
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

Bump kittycad from 0.2.26 to 0.2.27 in /src/wasm-lib (#722)

Bumps [kittycad](https://github.com/KittyCAD/kittycad.rs) from 0.2.26 to 0.2.27.
- [Release notes](https://github.com/KittyCAD/kittycad.rs/releases)
- [Commits](https://github.com/KittyCAD/kittycad.rs/compare/v0.2.26...v0.2.27)

---
updated-dependencies:
- dependency-name: kittycad
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

Bump thiserror from 1.0.48 to 1.0.49 in /src/wasm-lib (#721)

Bumps [thiserror](https://github.com/dtolnay/thiserror) from 1.0.48 to 1.0.49.
- [Release notes](https://github.com/dtolnay/thiserror/releases)
- [Commits](https://github.com/dtolnay/thiserror/compare/1.0.48...1.0.49)

---
updated-dependencies:
- dependency-name: thiserror
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

Bump expectorate from 1.0.7 to 1.1.0 in /src/wasm-lib (#712)

Bumps [expectorate](https://github.com/oxidecomputer/expectorate) from 1.0.7 to 1.1.0.
- [Release notes](https://github.com/oxidecomputer/expectorate/releases)
- [Commits](https://github.com/oxidecomputer/expectorate/compare/v1.0.7...v1.1.0)

---
updated-dependencies:
- dependency-name: expectorate
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

Bump clap from 4.4.4 to 4.4.5 in /src/wasm-lib (#711)

Bumps [clap](https://github.com/clap-rs/clap) from 4.4.4 to 4.4.5.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.4.4...v4.4.5)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

refactor cleanup

Signed-off-by: Jess Frazelle <github@jessfraz.com>

updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

type improvements

* use new sketchmode no camera

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* js working better

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* start of negative planes

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* tests and neg

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* images

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* norma;s

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* better initial load of planes

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* ts

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fixes

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fixes

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fix tsc

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fix edit sketch

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* add regression test for 2d solid issue

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* show planes

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fix clippy

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fix tests

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* canecel in progress

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fix ci as well

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2023-10-05 14:27:48 -07:00
59223279b7 Type Error: Type error engineConnection.ts (#786)
* TypeError Fix

* removed not needed console log

* took into account adamchalmers feedback and fixed cl type error

* pretty up

* Irev-dev feedback fixes issue
2023-10-05 16:18:50 +11:00
8a4e717565 Use absolute URLs to settings to avoid relative URL edge cases (#781)
* Create useAbsoluteFilePath hook

* Fix "report bug" link on Error page

* Replace relative URL to settings with absolute URL

* Replace other absolute file URLs to use common hook

* Use named const for default browser file name

* Fix UI tests that now rely on useRouteLoaderData()

Signed-off-by: Frank Noirot <frank@kittycad.io>

---------

Signed-off-by: Frank Noirot <frank@kittycad.io>
2023-10-04 18:00:55 -04:00
80b542ca18 Allow Vertical Scroll Wheel to affect Horizontal Scroll for Toolbar (#780) 2023-10-04 09:35:50 -07:00
e4bfc863ea Bump re-resizable from 6.9.9 to 6.9.11 (#777)
Bumps [re-resizable](https://github.com/bokuweb/react-resizable-box) from 6.9.9 to 6.9.11.
- [Release notes](https://github.com/bokuweb/react-resizable-box/releases)
- [Changelog](https://github.com/bokuweb/re-resizable/blob/master/CHANGELOG.md)
- [Commits](https://github.com/bokuweb/react-resizable-box/commits)

---
updated-dependencies:
- dependency-name: re-resizable
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-04 10:08:10 -05:00
77ef255de4 Bump tauri from 1.5.0 to 1.5.1 in /src-tauri (#761)
Bumps [tauri](https://github.com/tauri-apps/tauri) from 1.5.0 to 1.5.1.
- [Release notes](https://github.com/tauri-apps/tauri/releases)
- [Commits](https://github.com/tauri-apps/tauri/compare/tauri-v1.5...tauri-v1.5.1)

---
updated-dependencies:
- dependency-name: tauri
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-04 09:21:58 -05:00
64c3841079 Bump tauri-plugin-fs-extra from 9b96996 to fa32d1a in /src-tauri (#776)
Bumps [tauri-plugin-fs-extra](https://github.com/tauri-apps/plugins-workspace) from `9b96996` to `fa32d1a`.
- [Release notes](https://github.com/tauri-apps/plugins-workspace/releases)
- [Commits](9b96996b5a...fa32d1afa9)

---
updated-dependencies:
- dependency-name: tauri-plugin-fs-extra
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-03 23:23:43 -05:00
c7bb6bc845 Bump reqwest from 0.11.21 to 0.11.22 in /src/wasm-lib (#775)
Bumps [reqwest](https://github.com/seanmonstar/reqwest) from 0.11.21 to 0.11.22.
- [Release notes](https://github.com/seanmonstar/reqwest/releases)
- [Changelog](https://github.com/seanmonstar/reqwest/blob/master/CHANGELOG.md)
- [Commits](https://github.com/seanmonstar/reqwest/compare/v0.11.21...v0.11.22)

---
updated-dependencies:
- dependency-name: reqwest
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-03 23:23:18 -05:00
1af8a8c64f Cut release v0.9.5 (#772) 2023-10-03 17:35:16 -07:00
eb4776826b disable pip for firefox (#770)
Signed-off-by: Jess Frazelle <github@jessfraz.com>
2023-10-03 15:11:44 -07:00
f3dd0469d5 re-execute when we remount the app (#769)
Signed-off-by: Jess Frazelle <github@jessfraz.com>
2023-10-03 15:07:09 -07:00
deea74754d save actual file contents (#768)
Signed-off-by: Jess Frazelle <github@jessfraz.com>
2023-10-03 15:06:45 -07:00
3fd798c704 Bump @adobe/css-tools from 4.2.0 to 4.3.1 (#353)
Bumps [@adobe/css-tools](https://github.com/adobe/css-tools) from 4.2.0 to 4.3.1.
- [Changelog](https://github.com/adobe/css-tools/blob/main/History.md)
- [Commits](https://github.com/adobe/css-tools/commits)

---
updated-dependencies:
- dependency-name: "@adobe/css-tools"
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Pierre Jacquier <pierrejacquier39@gmail.com>
2023-10-03 14:12:58 -05:00
cc9eaf2991 Bump get-func-name from 2.0.0 to 2.0.2 (#730)
Bumps [get-func-name](https://github.com/chaijs/get-func-name) from 2.0.0 to 2.0.2.
- [Release notes](https://github.com/chaijs/get-func-name/releases)
- [Commits](https://github.com/chaijs/get-func-name/commits/v2.0.2)

---
updated-dependencies:
- dependency-name: get-func-name
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-03 14:12:46 -05:00
6f24031220 add mit (#765)
Signed-off-by: Jess Frazelle <github@jessfraz.com>
2023-10-03 11:59:57 -07:00
672bcd297f Bump toml from 0.8.1 to 0.8.2 in /src-tauri (#760)
Bumps [toml](https://github.com/toml-rs/toml) from 0.8.1 to 0.8.2.
- [Commits](https://github.com/toml-rs/toml/compare/toml-v0.8.1...toml-v0.8.2)

---
updated-dependencies:
- dependency-name: toml
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-03 07:20:21 -07:00
3bc182fe16 Bump reqwest from 0.11.20 to 0.11.21 in /src/wasm-lib (#762)
Bumps [reqwest](https://github.com/seanmonstar/reqwest) from 0.11.20 to 0.11.21.
- [Release notes](https://github.com/seanmonstar/reqwest/releases)
- [Changelog](https://github.com/seanmonstar/reqwest/blob/master/CHANGELOG.md)
- [Commits](https://github.com/seanmonstar/reqwest/compare/v0.11.20...v0.11.21)

---
updated-dependencies:
- dependency-name: reqwest
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-03 07:20:08 -07:00
589cd39eec Cut release v0.9.4 (#759)
Co-authored-by: Jess Frazelle <jessfraz@users.noreply.github.com>
2023-10-02 20:09:53 -04:00
63feebef5c add report a bug button (#758)
* add report a bug button

* format
2023-10-02 16:33:33 -07:00
65037abd9a new sample code w tangental arc (#756)
* new sample code w tangental arc

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* line 6

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* make sure sample code works in test

Signed-off-by: Jess Frazelle <github@jessfraz.com>

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2023-10-02 16:14:09 -07:00
97bc339a62 Bump tauri-plugin-fs-extra from 9af4c37 to 9b96996 in /src-tauri (#751)
Bumps [tauri-plugin-fs-extra](https://github.com/tauri-apps/plugins-workspace) from `9af4c37` to `9b96996`.
- [Release notes](https://github.com/tauri-apps/plugins-workspace/releases)
- [Commits](9af4c3727c...9b96996b5a)

---
updated-dependencies:
- dependency-name: tauri-plugin-fs-extra
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-02 12:27:24 -07:00
4e9a6375a5 Bump tauri-build from 1.4.1 to 1.5.0 in /src-tauri (#752)
Bumps [tauri-build](https://github.com/tauri-apps/tauri) from 1.4.1 to 1.5.0.
- [Release notes](https://github.com/tauri-apps/tauri/releases)
- [Commits](https://github.com/tauri-apps/tauri/compare/tauri-build-v1.4.1...tauri-build-v1.5)

---
updated-dependencies:
- dependency-name: tauri-build
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-02 12:27:16 -07:00
3d19dfb800 Cut release v0.9.3 (#753) 2023-10-02 14:14:00 -04:00
d2a7b84292 Request a feature should open externally (#737)
Fixes #659
2023-10-02 13:26:23 -04:00
9e02bab155 Explicitly close the Engine Connection when ICE fails (#732)
This will avoid letting the stack think its ready until it times out,
and will hopefully help with faster retry logic in the future

Signed-off-by: Paul Tagliamonte <paul@kittycad.io>
2023-10-02 09:50:49 -04:00
7352de5a70 Bump vitest from 0.34.1 to 0.34.6 (#747)
Bumps [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest) from 0.34.1 to 0.34.6.
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Commits](https://github.com/vitest-dev/vitest/commits/v0.34.6/packages/vitest)

---
updated-dependencies:
- dependency-name: vitest
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-30 12:24:39 +10:00
9797d0cb81 Fix pipe expression start (#746) 2023-09-29 22:19:20 -04:00
83907fa9db adds tangentalArc and tangentalArcTo to the stdlib (#748)
* start of tangental arc

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* update docs

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fixes

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2023-09-29 14:41:14 -07:00
a367be4e2b add gizmo (#738)
* add gizmo

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fixups

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fix force ast execution

Signed-off-by: Jess Frazelle <github@jessfraz.com>

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2023-09-29 12:41:58 -07:00
056fa00adc Bump tauri from 1.4.1 to 1.5.0 in /src-tauri (#742)
Bumps [tauri](https://github.com/tauri-apps/tauri) from 1.4.1 to 1.5.0.
- [Release notes](https://github.com/tauri-apps/tauri/releases)
- [Commits](https://github.com/tauri-apps/tauri/compare/tauri-v1.4.1...tauri-v1.5.0)

---
updated-dependencies:
- dependency-name: tauri
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-29 12:03:35 -07:00
4759fb2e6f Bump postcss from 8.4.27 to 8.4.31 (#743)
Bumps [postcss](https://github.com/postcss/postcss) from 8.4.27 to 8.4.31.
- [Release notes](https://github.com/postcss/postcss/releases)
- [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/postcss/postcss/compare/8.4.27...8.4.31)

---
updated-dependencies:
- dependency-name: postcss
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-29 11:12:08 -07:00
45f497d9cd Bump tauri-build from 1.4.0 to 1.4.1 in /src-tauri (#741)
Bumps [tauri-build](https://github.com/tauri-apps/tauri) from 1.4.0 to 1.4.1.
- [Release notes](https://github.com/tauri-apps/tauri/releases)
- [Commits](https://github.com/tauri-apps/tauri/compare/tauri-build-v1.4...tauri-build-v1.4.1)

---
updated-dependencies:
- dependency-name: tauri-build
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-29 11:11:57 -07:00
dc61bdebdf Bump tauri-plugin-fs-extra from 3d27909 to 9af4c37 in /src-tauri (#740)
Bumps [tauri-plugin-fs-extra](https://github.com/tauri-apps/plugins-workspace) from `3d27909` to `9af4c37`.
- [Release notes](https://github.com/tauri-apps/plugins-workspace/releases)
- [Commits](3d279094d4...9af4c3727c)

---
updated-dependencies:
- dependency-name: tauri-plugin-fs-extra
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-29 11:11:42 -07:00
61943055e5 refactor a bit before more rust port (#739)
* partially there

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* cleanup

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fixes

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fixups

Signed-off-by: Jess Frazelle <github@jessfraz.com>

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2023-09-29 11:11:01 -07:00
416fe0f644 Bump clap from 4.4.5 to 4.4.6 in /src/wasm-lib (#744)
Bumps [clap](https://github.com/clap-rs/clap) from 4.4.5 to 4.4.6.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.4.5...v4.4.6)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-29 12:41:22 -04:00
708465d818 Bump kittycad from 0.2.27 to 0.2.28 in /src-tauri (#734)
Bumps [kittycad](https://github.com/KittyCAD/kittycad.rs) from 0.2.27 to 0.2.28.
- [Release notes](https://github.com/KittyCAD/kittycad.rs/releases)
- [Commits](https://github.com/KittyCAD/kittycad.rs/compare/v0.2.27...v0.2.28)

---
updated-dependencies:
- dependency-name: kittycad
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-28 18:09:56 -07:00
e706fb02d6 Bump openapitor from d3e98c4 to fa0345c in /src/wasm-lib (#733)
Bumps [openapitor](https://github.com/KittyCAD/kittycad.rs) from `d3e98c4` to `fa0345c`.
- [Release notes](https://github.com/KittyCAD/kittycad.rs/releases)
- [Commits](d3e98c4ec0...fa0345c514)

---
updated-dependencies:
- dependency-name: openapitor
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-28 18:09:46 -07:00
1bf7daa474 Bump tauri-plugin-fs-extra from 6c7a4c0 to 3d27909 in /src-tauri (#735)
Bumps [tauri-plugin-fs-extra](https://github.com/tauri-apps/plugins-workspace) from `6c7a4c0` to `3d27909`.
- [Release notes](https://github.com/tauri-apps/plugins-workspace/releases)
- [Commits](6c7a4c0984...3d279094d4)

---
updated-dependencies:
- dependency-name: tauri-plugin-fs-extra
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-28 18:09:31 -07:00
ffc47f8f40 actually use the units the user sets (#727)
* actually use the units the user sets

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* update kittycad lib

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* next

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fix tests

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fix bug

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fix types

Signed-off-by: Jess Frazelle <github@jessfraz.com>

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2023-09-27 15:42:16 -07:00
768aaa84f6 Bump clap from 4.4.4 to 4.4.5 in /src/wasm-lib (#711)
Bumps [clap](https://github.com/clap-rs/clap) from 4.4.4 to 4.4.5.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.4.4...v4.4.5)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-27 09:10:07 -07:00
f3a700eec8 Bump expectorate from 1.0.7 to 1.1.0 in /src/wasm-lib (#712)
Bumps [expectorate](https://github.com/oxidecomputer/expectorate) from 1.0.7 to 1.1.0.
- [Release notes](https://github.com/oxidecomputer/expectorate/releases)
- [Commits](https://github.com/oxidecomputer/expectorate/compare/v1.0.7...v1.1.0)

---
updated-dependencies:
- dependency-name: expectorate
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-27 08:39:16 -07:00
c853637a9a Bump thiserror from 1.0.48 to 1.0.49 in /src/wasm-lib (#721)
Bumps [thiserror](https://github.com/dtolnay/thiserror) from 1.0.48 to 1.0.49.
- [Release notes](https://github.com/dtolnay/thiserror/releases)
- [Commits](https://github.com/dtolnay/thiserror/compare/1.0.48...1.0.49)

---
updated-dependencies:
- dependency-name: thiserror
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-27 08:39:03 -07:00
9af30d9ef6 Bump kittycad from 0.2.26 to 0.2.27 in /src/wasm-lib (#722)
Bumps [kittycad](https://github.com/KittyCAD/kittycad.rs) from 0.2.26 to 0.2.27.
- [Release notes](https://github.com/KittyCAD/kittycad.rs/releases)
- [Commits](https://github.com/KittyCAD/kittycad.rs/compare/v0.2.26...v0.2.27)

---
updated-dependencies:
- dependency-name: kittycad
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-27 08:38:49 -07:00
6164714a6b Bump openapitor from 61a1605 to d3e98c4 in /src/wasm-lib (#723)
Bumps [openapitor](https://github.com/KittyCAD/kittycad.rs) from `61a1605` to `d3e98c4`.
- [Release notes](https://github.com/KittyCAD/kittycad.rs/releases)
- [Commits](61a16059b3...d3e98c4ec0)

---
updated-dependencies:
- dependency-name: openapitor
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-27 08:38:37 -07:00
64ceb98eba Bump toml from 0.8.0 to 0.8.1 in /src-tauri (#724)
Bumps [toml](https://github.com/toml-rs/toml) from 0.8.0 to 0.8.1.
- [Commits](https://github.com/toml-rs/toml/compare/toml-v0.8.0...toml-v0.8.1)

---
updated-dependencies:
- dependency-name: toml
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-27 08:38:25 -07:00
2cbf260900 Bump tauri-plugin-fs-extra from b04bde3 to 6c7a4c0 in /src-tauri (#725)
Bumps [tauri-plugin-fs-extra](https://github.com/tauri-apps/plugins-workspace) from `b04bde3` to `6c7a4c0`.
- [Release notes](https://github.com/tauri-apps/plugins-workspace/releases)
- [Commits](b04bde3461...6c7a4c0984)

---
updated-dependencies:
- dependency-name: tauri-plugin-fs-extra
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-27 08:38:14 -07:00
cfaaedf602 Bump kittycad from 0.2.26 to 0.2.27 in /src-tauri (#726)
Bumps [kittycad](https://github.com/KittyCAD/kittycad.rs) from 0.2.26 to 0.2.27.
- [Release notes](https://github.com/KittyCAD/kittycad.rs/releases)
- [Commits](https://github.com/KittyCAD/kittycad.rs/compare/v0.2.26...v0.2.27)

---
updated-dependencies:
- dependency-name: kittycad
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-27 08:37:54 -07:00
12b3717eb5 Cut release v0.9.2 (#714) 2023-09-26 20:39:05 -04:00
0bc685b0c4 Bump tungstenite from 0.20.0 to 0.20.1 in /src/wasm-lib/kcl/fuzz (#709)
Bumps [tungstenite](https://github.com/snapview/tungstenite-rs) from 0.20.0 to 0.20.1.
- [Changelog](https://github.com/snapview/tungstenite-rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/snapview/tungstenite-rs/compare/v0.20.0...v0.20.1)

---
updated-dependencies:
- dependency-name: tungstenite
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-25 20:49:16 -07:00
9ee032771a unused dep (#710) 2023-09-26 03:22:05 +00:00
c307ddd1b1 resize (#706)
* start of resize

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* refactor

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* check if 0

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* will work w new lib

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* new types

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* handle resize effect

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
Co-authored-by: Kurt Hutten Irev-Dev <k.hutten@protonmail.ch>
2023-09-25 19:49:53 -07:00
a30818ff2b fixes negative args in function (#707)
* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fixes

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fixes

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fixes

Signed-off-by: Jess Frazelle <github@jessfraz.com>

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2023-09-25 15:25:58 -07:00
53e763d938 fix close arc (#704)
* fix close arc

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* much bigger radius

Signed-off-by: Jess Frazelle <github@jessfraz.com>

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2023-09-25 12:14:41 -07:00
8f74cd1d0c Bump tauri-plugin-fs-extra from 0190f68 to b04bde3 in /src-tauri (#702)
Bumps [tauri-plugin-fs-extra](https://github.com/tauri-apps/plugins-workspace) from `0190f68` to `b04bde3`.
- [Release notes](https://github.com/tauri-apps/plugins-workspace/releases)
- [Commits](0190f68f1d...b04bde3461)

---
updated-dependencies:
- dependency-name: tauri-plugin-fs-extra
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-25 09:33:53 -07:00
c271942897 remove errors (#703) 2023-09-25 07:28:03 +00:00
a03d09b41d Restructure tokenizer module (#700)
* Remove duplicated tests

These tests already were copied to tokeniser2.rs, so removing them doesn't affect code coverage.

* Move tokeniser to its own module

Now there's a module for tokens, and the tokenizer/lexer implementation is private within the token module.
2023-09-24 20:01:17 -05:00
2971b7752b Bump rust websocket libraries (#701)
Changelog: https://github.com/snapview/tokio-tungstenite/blob/master/CHANGELOG.md#0201
2023-09-24 21:34:31 +00:00
70e99eb00b Refactor is_code_token into a method (#699)
* Refactor is_code_token into a method

* Fix typos, use Parser as it was imported
2023-09-24 21:11:36 +00:00
5c66af59d2 New tokenizer based on winnow (#697)
* New tokenizer, using Winnow instead of regexes.

Between 1.3x and 4.4x speedup on lexer benchmarks :)

* Use dispatch instead of alt

Most of the time, if you know the first character of a token, you can narrow down its potential possible token types, instead of just trying each token type until one succeeds.

This further speeds up the lexer. Compared to main, this branch is now between 3x and 12x faster than main.
2023-09-22 21:57:39 -05:00
6dda6daeef Use separate benchmarks for lexing and parsing (#698) 2023-09-23 02:01:18 +00:00
125 changed files with 8606 additions and 2654 deletions

View File

@ -15,6 +15,9 @@ on:
- '**/Cargo.lock'
- '**/rust-toolchain.toml'
- .github/workflows/cargo-build.yml
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
name: cargo build
jobs:
cargobuild:

View File

@ -15,6 +15,9 @@ on:
- '**/rust-toolchain.toml'
- '**.rs'
- .github/workflows/cargo-build.yml
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
name: cargo clippy
jobs:
cargoclippy:

View File

@ -17,6 +17,9 @@ on:
- .github/workflows/cargo-criterion.yml
workflow_dispatch:
permissions: read-all
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
name: cargo criterion
jobs:
cargocriterion:

View File

@ -18,6 +18,9 @@ on:
permissions:
packages: read
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
name: cargo fmt
jobs:
cargofmt:

View File

@ -17,6 +17,9 @@ on:
- .github/workflows/cargo-test.yml
workflow_dispatch:
permissions: read-all
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
name: cargo test
jobs:
cargotest:

View File

@ -8,6 +8,9 @@ on:
release:
types: [published]
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
check-format:
runs-on: 'ubuntu-20.04'

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2023 The KittyCAD Authors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

File diff suppressed because it is too large Load Diff

View File

@ -48,8 +48,12 @@
* [`show`](#show)
* [`sin`](#sin)
* [`sqrt`](#sqrt)
* [`startProfileAt`](#startProfileAt)
* [`startSketchAt`](#startSketchAt)
* [`startSketchOn`](#startSketchOn)
* [`tan`](#tan)
* [`tangentalArc`](#tangentalArc)
* [`tangentalArcTo`](#tangentalArcTo)
* [`tau`](#tau)
* [`xLine`](#xLine)
* [`xLineTo`](#xLineTo)
@ -118,6 +122,8 @@ angleToMatchLengthX(segment_name: string, to: number, sketch_group: SketchGroup)
{
// The id of the sketch group.
id: uuid,
// The plane id of the sketch group.
planeId: uuid,
// The position of the sketch group.
position: [number],
// The rotation of the sketch group.
@ -202,6 +208,8 @@ angleToMatchLengthY(segment_name: string, to: number, sketch_group: SketchGroup)
{
// The id of the sketch group.
id: uuid,
// The plane id of the sketch group.
planeId: uuid,
// The position of the sketch group.
position: [number],
// The rotation of the sketch group.
@ -296,6 +304,8 @@ angledLine(data: AngledLineData, sketch_group: SketchGroup) -> SketchGroup
{
// The id of the sketch group.
id: uuid,
// The plane id of the sketch group.
planeId: uuid,
// The position of the sketch group.
position: [number],
// The rotation of the sketch group.
@ -362,6 +372,8 @@ angledLine(data: AngledLineData, sketch_group: SketchGroup) -> SketchGroup
{
// The id of the sketch group.
id: uuid,
// The plane id of the sketch group.
planeId: uuid,
// The position of the sketch group.
position: [number],
// The rotation of the sketch group.
@ -452,6 +464,8 @@ angledLineOfXLength(data: AngledLineData, sketch_group: SketchGroup) -> SketchGr
{
// The id of the sketch group.
id: uuid,
// The plane id of the sketch group.
planeId: uuid,
// The position of the sketch group.
position: [number],
// The rotation of the sketch group.
@ -518,6 +532,8 @@ angledLineOfXLength(data: AngledLineData, sketch_group: SketchGroup) -> SketchGr
{
// The id of the sketch group.
id: uuid,
// The plane id of the sketch group.
planeId: uuid,
// The position of the sketch group.
position: [number],
// The rotation of the sketch group.
@ -608,6 +624,8 @@ angledLineOfYLength(data: AngledLineData, sketch_group: SketchGroup) -> SketchGr
{
// The id of the sketch group.
id: uuid,
// The plane id of the sketch group.
planeId: uuid,
// The position of the sketch group.
position: [number],
// The rotation of the sketch group.
@ -674,6 +692,8 @@ angledLineOfYLength(data: AngledLineData, sketch_group: SketchGroup) -> SketchGr
{
// The id of the sketch group.
id: uuid,
// The plane id of the sketch group.
planeId: uuid,
// The position of the sketch group.
position: [number],
// The rotation of the sketch group.
@ -765,6 +785,8 @@ angledLineThatIntersects(data: AngeledLineThatIntersectsData, sketch_group: Sket
{
// The id of the sketch group.
id: uuid,
// The plane id of the sketch group.
planeId: uuid,
// The position of the sketch group.
position: [number],
// The rotation of the sketch group.
@ -831,6 +853,8 @@ angledLineThatIntersects(data: AngeledLineThatIntersectsData, sketch_group: Sket
{
// The id of the sketch group.
id: uuid,
// The plane id of the sketch group.
planeId: uuid,
// The position of the sketch group.
position: [number],
// The rotation of the sketch group.
@ -921,6 +945,8 @@ angledLineToX(data: AngledLineToData, sketch_group: SketchGroup) -> SketchGroup
{
// The id of the sketch group.
id: uuid,
// The plane id of the sketch group.
planeId: uuid,
// The position of the sketch group.
position: [number],
// The rotation of the sketch group.
@ -987,6 +1013,8 @@ angledLineToX(data: AngledLineToData, sketch_group: SketchGroup) -> SketchGroup
{
// The id of the sketch group.
id: uuid,
// The plane id of the sketch group.
planeId: uuid,
// The position of the sketch group.
position: [number],
// The rotation of the sketch group.
@ -1077,6 +1105,8 @@ angledLineToY(data: AngledLineToData, sketch_group: SketchGroup) -> SketchGroup
{
// The id of the sketch group.
id: uuid,
// The plane id of the sketch group.
planeId: uuid,
// The position of the sketch group.
position: [number],
// The rotation of the sketch group.
@ -1143,6 +1173,8 @@ angledLineToY(data: AngledLineToData, sketch_group: SketchGroup) -> SketchGroup
{
// The id of the sketch group.
id: uuid,
// The plane id of the sketch group.
planeId: uuid,
// The position of the sketch group.
position: [number],
// The rotation of the sketch group.
@ -1260,6 +1292,8 @@ arc(data: ArcData, sketch_group: SketchGroup) -> SketchGroup
{
// The id of the sketch group.
id: uuid,
// The plane id of the sketch group.
planeId: uuid,
// The position of the sketch group.
position: [number],
// The rotation of the sketch group.
@ -1326,6 +1360,8 @@ arc(data: ArcData, sketch_group: SketchGroup) -> SketchGroup
{
// The id of the sketch group.
id: uuid,
// The plane id of the sketch group.
planeId: uuid,
// The position of the sketch group.
position: [number],
// The rotation of the sketch group.
@ -1465,6 +1501,8 @@ bezierCurve(data: BezierData, sketch_group: SketchGroup) -> SketchGroup
{
// The id of the sketch group.
id: uuid,
// The plane id of the sketch group.
planeId: uuid,
// The position of the sketch group.
position: [number],
// The rotation of the sketch group.
@ -1531,6 +1569,8 @@ bezierCurve(data: BezierData, sketch_group: SketchGroup) -> SketchGroup
{
// The id of the sketch group.
id: uuid,
// The plane id of the sketch group.
planeId: uuid,
// The position of the sketch group.
position: [number],
// The rotation of the sketch group.
@ -1629,6 +1669,8 @@ close(sketch_group: SketchGroup) -> SketchGroup
{
// The id of the sketch group.
id: uuid,
// The plane id of the sketch group.
planeId: uuid,
// The position of the sketch group.
position: [number],
// The rotation of the sketch group.
@ -1695,6 +1737,8 @@ close(sketch_group: SketchGroup) -> SketchGroup
{
// The id of the sketch group.
id: uuid,
// The plane id of the sketch group.
planeId: uuid,
// The position of the sketch group.
position: [number],
// The rotation of the sketch group.
@ -1813,6 +1857,8 @@ extrude(length: number, sketch_group: SketchGroup) -> ExtrudeGroup
{
// The id of the sketch group.
id: uuid,
// The plane id of the sketch group.
planeId: uuid,
// The position of the sketch group.
position: [number],
// The rotation of the sketch group.
@ -1994,6 +2040,8 @@ lastSegX(sketch_group: SketchGroup) -> number
{
// The id of the sketch group.
id: uuid,
// The plane id of the sketch group.
planeId: uuid,
// The position of the sketch group.
position: [number],
// The rotation of the sketch group.
@ -2076,6 +2124,8 @@ lastSegY(sketch_group: SketchGroup) -> number
{
// The id of the sketch group.
id: uuid,
// The plane id of the sketch group.
planeId: uuid,
// The position of the sketch group.
position: [number],
// The rotation of the sketch group.
@ -2231,6 +2281,8 @@ line(data: LineData, sketch_group: SketchGroup) -> SketchGroup
{
// The id of the sketch group.
id: uuid,
// The plane id of the sketch group.
planeId: uuid,
// The position of the sketch group.
position: [number],
// The rotation of the sketch group.
@ -2297,6 +2349,8 @@ line(data: LineData, sketch_group: SketchGroup) -> SketchGroup
{
// The id of the sketch group.
id: uuid,
// The plane id of the sketch group.
planeId: uuid,
// The position of the sketch group.
position: [number],
// The rotation of the sketch group.
@ -2385,6 +2439,8 @@ lineTo(data: LineToData, sketch_group: SketchGroup) -> SketchGroup
{
// The id of the sketch group.
id: uuid,
// The plane id of the sketch group.
planeId: uuid,
// The position of the sketch group.
position: [number],
// The rotation of the sketch group.
@ -2451,6 +2507,8 @@ lineTo(data: LineToData, sketch_group: SketchGroup) -> SketchGroup
{
// The id of the sketch group.
id: uuid,
// The plane id of the sketch group.
planeId: uuid,
// The position of the sketch group.
position: [number],
// The rotation of the sketch group.
@ -2691,6 +2749,8 @@ segAng(segment_name: string, sketch_group: SketchGroup) -> number
{
// The id of the sketch group.
id: uuid,
// The plane id of the sketch group.
planeId: uuid,
// The position of the sketch group.
position: [number],
// The rotation of the sketch group.
@ -2774,6 +2834,8 @@ segEndX(segment_name: string, sketch_group: SketchGroup) -> number
{
// The id of the sketch group.
id: uuid,
// The plane id of the sketch group.
planeId: uuid,
// The position of the sketch group.
position: [number],
// The rotation of the sketch group.
@ -2857,6 +2919,8 @@ segEndY(segment_name: string, sketch_group: SketchGroup) -> number
{
// The id of the sketch group.
id: uuid,
// The plane id of the sketch group.
planeId: uuid,
// The position of the sketch group.
position: [number],
// The rotation of the sketch group.
@ -2940,6 +3004,8 @@ segLen(segment_name: string, sketch_group: SketchGroup) -> number
{
// The id of the sketch group.
id: uuid,
// The plane id of the sketch group.
planeId: uuid,
// The position of the sketch group.
position: [number],
// The rotation of the sketch group.
@ -3022,6 +3088,8 @@ show(sketch: SketchGroup)
{
// The id of the sketch group.
id: uuid,
// The plane id of the sketch group.
planeId: uuid,
// The position of the sketch group.
position: [number],
// The rotation of the sketch group.
@ -3123,14 +3191,14 @@ sqrt(num: number) -> number
### startSketchAt
### startProfileAt
Start a sketch at a given point.
Start a profile at a given point.
```
startSketchAt(data: LineData) -> SketchGroup
startProfileAt(data: LineData, plane: Plane) -> SketchGroup
```
#### Arguments
@ -3145,6 +3213,40 @@ startSketchAt(data: LineData) -> SketchGroup
} |
[number]
```
* `plane`: `Plane` - A plane.
```
{
// The id of the plane.
id: uuid,
// Origin of the plane.
origin: {
x: number,
y: number,
z: number,
},
// Type for a plane.
value: string |
string,
// What should the planes X axis be?
xAxis: {
x: number,
y: number,
z: number,
},
// What should the planes Y axis be?
yAxis: {
x: number,
y: number,
z: number,
},
// The z-axis (normal).
zAxis: {
x: number,
y: number,
z: number,
},
}
```
#### Returns
@ -3153,6 +3255,8 @@ startSketchAt(data: LineData) -> SketchGroup
{
// The id of the sketch group.
id: uuid,
// The plane id of the sketch group.
planeId: uuid,
// The position of the sketch group.
position: [number],
// The rotation of the sketch group.
@ -3214,6 +3318,188 @@ startSketchAt(data: LineData) -> SketchGroup
### startSketchAt
Start a sketch at a given point on the 'XY' plane.
```
startSketchAt(data: LineData) -> SketchGroup
```
#### Arguments
* `data`: `LineData` - Data to draw a line.
```
{
// The tag.
tag: string,
// The to point.
to: [number],
} |
[number]
```
#### Returns
* `SketchGroup` - A sketch group is a collection of paths.
```
{
// The id of the sketch group.
id: uuid,
// The plane id of the sketch group.
planeId: uuid,
// The position of the sketch group.
position: [number],
// The rotation of the sketch group.
rotation: [number],
// The starting path.
start: {
// The from point.
from: [number],
// The name of the path.
name: string,
// The to point.
to: [number],
},
// The paths in the sketch group.
value: [{
// The from point.
from: [number],
// The name of the path.
name: string,
// The to point.
to: [number],
type: string,
} |
{
// The from point.
from: [number],
// The name of the path.
name: string,
// The to point.
to: [number],
type: string,
// The x coordinate.
x: number,
} |
{
// The from point.
from: [number],
// The name of the path.
name: string,
// The to point.
to: [number],
type: string,
// The x coordinate.
x: number,
// The y coordinate.
y: number,
} |
{
// The from point.
from: [number],
// The name of the path.
name: string,
// The to point.
to: [number],
type: string,
}],
}
```
### startSketchOn
Start a sketch at a given point.
```
startSketchOn(data: PlaneData) -> Plane
```
#### Arguments
* `data`: `PlaneData` - Data for a plane.
```
string |
string |
string |
string |
string |
string |
{
plane: {
// Origin of the plane.
origin: {
x: number,
y: number,
z: number,
},
// What should the planes X axis be?
x_axis: {
x: number,
y: number,
z: number,
},
// What should the planes Y axis be?
y_axis: {
x: number,
y: number,
z: number,
},
// The z-axis (normal).
z_axis: {
x: number,
y: number,
z: number,
},
},
}
```
#### Returns
* `Plane` - A plane.
```
{
// The id of the plane.
id: uuid,
// Origin of the plane.
origin: {
x: number,
y: number,
z: number,
},
// Type for a plane.
value: string |
string,
// What should the planes X axis be?
xAxis: {
x: number,
y: number,
z: number,
},
// What should the planes Y axis be?
yAxis: {
x: number,
y: number,
z: number,
},
// The z-axis (normal).
zAxis: {
x: number,
y: number,
z: number,
},
}
```
### tan
Computes the tangent of a number (in radians).
@ -3234,6 +3520,328 @@ tan(num: number) -> number
### tangentalArc
Draw an arc.
```
tangentalArc(data: TangentalArcData, sketch_group: SketchGroup) -> SketchGroup
```
#### Arguments
* `data`: `TangentalArcData` - Data to draw a tangental arc.
```
{
// Offset of the arc, in degrees.
offset: number,
// Radius of the arc. Not to be confused with Raiders of the Lost Ark.
radius: number,
} |
{
// The tag.
tag: string,
// Where the arc should end. Must lie in the same plane as the current path pen position. Must not be colinear with current path pen position.
to: [number],
} |
[number]
```
* `sketch_group`: `SketchGroup` - A sketch group is a collection of paths.
```
{
// The id of the sketch group.
id: uuid,
// The plane id of the sketch group.
planeId: uuid,
// The position of the sketch group.
position: [number],
// The rotation of the sketch group.
rotation: [number],
// The starting path.
start: {
// The from point.
from: [number],
// The name of the path.
name: string,
// The to point.
to: [number],
},
// The paths in the sketch group.
value: [{
// The from point.
from: [number],
// The name of the path.
name: string,
// The to point.
to: [number],
type: string,
} |
{
// The from point.
from: [number],
// The name of the path.
name: string,
// The to point.
to: [number],
type: string,
// The x coordinate.
x: number,
} |
{
// The from point.
from: [number],
// The name of the path.
name: string,
// The to point.
to: [number],
type: string,
// The x coordinate.
x: number,
// The y coordinate.
y: number,
} |
{
// The from point.
from: [number],
// The name of the path.
name: string,
// The to point.
to: [number],
type: string,
}],
}
```
#### Returns
* `SketchGroup` - A sketch group is a collection of paths.
```
{
// The id of the sketch group.
id: uuid,
// The plane id of the sketch group.
planeId: uuid,
// The position of the sketch group.
position: [number],
// The rotation of the sketch group.
rotation: [number],
// The starting path.
start: {
// The from point.
from: [number],
// The name of the path.
name: string,
// The to point.
to: [number],
},
// The paths in the sketch group.
value: [{
// The from point.
from: [number],
// The name of the path.
name: string,
// The to point.
to: [number],
type: string,
} |
{
// The from point.
from: [number],
// The name of the path.
name: string,
// The to point.
to: [number],
type: string,
// The x coordinate.
x: number,
} |
{
// The from point.
from: [number],
// The name of the path.
name: string,
// The to point.
to: [number],
type: string,
// The x coordinate.
x: number,
// The y coordinate.
y: number,
} |
{
// The from point.
from: [number],
// The name of the path.
name: string,
// The to point.
to: [number],
type: string,
}],
}
```
### tangentalArcTo
Draw an arc.
```
tangentalArcTo(data: TangentalArcToData, sketch_group: SketchGroup) -> SketchGroup
```
#### Arguments
* `data`: `TangentalArcToData` - Data to draw a tangental arc to a specific point.
```
{
// The tag.
tag: string,
// Where the arc should end. Must lie in the same plane as the current path pen position. Must not be colinear with current path pen position.
to: [number],
} |
[number]
```
* `sketch_group`: `SketchGroup` - A sketch group is a collection of paths.
```
{
// The id of the sketch group.
id: uuid,
// The plane id of the sketch group.
planeId: uuid,
// The position of the sketch group.
position: [number],
// The rotation of the sketch group.
rotation: [number],
// The starting path.
start: {
// The from point.
from: [number],
// The name of the path.
name: string,
// The to point.
to: [number],
},
// The paths in the sketch group.
value: [{
// The from point.
from: [number],
// The name of the path.
name: string,
// The to point.
to: [number],
type: string,
} |
{
// The from point.
from: [number],
// The name of the path.
name: string,
// The to point.
to: [number],
type: string,
// The x coordinate.
x: number,
} |
{
// The from point.
from: [number],
// The name of the path.
name: string,
// The to point.
to: [number],
type: string,
// The x coordinate.
x: number,
// The y coordinate.
y: number,
} |
{
// The from point.
from: [number],
// The name of the path.
name: string,
// The to point.
to: [number],
type: string,
}],
}
```
#### Returns
* `SketchGroup` - A sketch group is a collection of paths.
```
{
// The id of the sketch group.
id: uuid,
// The plane id of the sketch group.
planeId: uuid,
// The position of the sketch group.
position: [number],
// The rotation of the sketch group.
rotation: [number],
// The starting path.
start: {
// The from point.
from: [number],
// The name of the path.
name: string,
// The to point.
to: [number],
},
// The paths in the sketch group.
value: [{
// The from point.
from: [number],
// The name of the path.
name: string,
// The to point.
to: [number],
type: string,
} |
{
// The from point.
from: [number],
// The name of the path.
name: string,
// The to point.
to: [number],
type: string,
// The x coordinate.
x: number,
} |
{
// The from point.
from: [number],
// The name of the path.
name: string,
// The to point.
to: [number],
type: string,
// The x coordinate.
x: number,
// The y coordinate.
y: number,
} |
{
// The from point.
from: [number],
// The name of the path.
name: string,
// The to point.
to: [number],
type: string,
}],
}
```
### tau
Return the value of `tau`. The full circle constant (τ). Equal to 2π.
@ -3280,6 +3888,8 @@ number
{
// The id of the sketch group.
id: uuid,
// The plane id of the sketch group.
planeId: uuid,
// The position of the sketch group.
position: [number],
// The rotation of the sketch group.
@ -3346,6 +3956,8 @@ number
{
// The id of the sketch group.
id: uuid,
// The plane id of the sketch group.
planeId: uuid,
// The position of the sketch group.
position: [number],
// The rotation of the sketch group.
@ -3434,6 +4046,8 @@ number
{
// The id of the sketch group.
id: uuid,
// The plane id of the sketch group.
planeId: uuid,
// The position of the sketch group.
position: [number],
// The rotation of the sketch group.
@ -3500,6 +4114,8 @@ number
{
// The id of the sketch group.
id: uuid,
// The plane id of the sketch group.
planeId: uuid,
// The position of the sketch group.
position: [number],
// The rotation of the sketch group.
@ -3588,6 +4204,8 @@ number
{
// The id of the sketch group.
id: uuid,
// The plane id of the sketch group.
planeId: uuid,
// The position of the sketch group.
position: [number],
// The rotation of the sketch group.
@ -3654,6 +4272,8 @@ number
{
// The id of the sketch group.
id: uuid,
// The plane id of the sketch group.
planeId: uuid,
// The position of the sketch group.
position: [number],
// The rotation of the sketch group.
@ -3742,6 +4362,8 @@ number
{
// The id of the sketch group.
id: uuid,
// The plane id of the sketch group.
planeId: uuid,
// The position of the sketch group.
position: [number],
// The rotation of the sketch group.
@ -3808,6 +4430,8 @@ number
{
// The id of the sketch group.
id: uuid,
// The plane id of the sketch group.
planeId: uuid,
// The position of the sketch group.
position: [number],
// The rotation of the sketch group.

View File

@ -1,6 +1,6 @@
{
"name": "untitled-app",
"version": "0.9.1",
"version": "0.10.0",
"private": true,
"dependencies": {
"@codemirror/autocomplete": "^6.9.0",
@ -10,7 +10,7 @@
"@fortawesome/react-fontawesome": "^0.2.0",
"@headlessui/react": "^1.7.13",
"@headlessui/tailwindcss": "^0.2.0",
"@kittycad/lib": "^0.0.38",
"@kittycad/lib": "^0.0.43",
"@lezer/javascript": "^1.4.7",
"@open-rpc/client-js": "^1.8.1",
"@react-hook/resize-observer": "^1.2.6",
@ -32,7 +32,7 @@
"fuse.js": "^6.6.2",
"http-server": "^14.1.1",
"json-rpc-2.0": "^1.6.0",
"re-resizable": "^6.9.9",
"re-resizable": "^6.9.11",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-hot-toast": "^2.4.1",
@ -48,7 +48,7 @@
"ts-node": "^10.9.1",
"typescript": "^4.4.2",
"uuid": "^9.0.0",
"vitest": "^0.34.1",
"vitest": "^0.34.6",
"vscode-jsonrpc": "^8.1.0",
"vscode-languageserver-protocol": "^3.17.3",
"wasm-pack": "^0.12.1",
@ -102,7 +102,6 @@
"@babel/preset-env": "^7.22.9",
"@tauri-apps/cli": "^1.3.1",
"@types/crypto-js": "^4.1.1",
"@types/debounce": "^1.2.1",
"@types/debounce-promise": "^3.1.6",
"@types/isomorphic-fetch": "^0.0.36",
"@types/react-modal": "^3.16.0",
@ -117,7 +116,7 @@
"eslint-plugin-css-modules": "^2.11.0",
"happy-dom": "^10.8.0",
"husky": "^8.0.3",
"postcss": "^8.4.19",
"postcss": "^8.4.31",
"prettier": "^2.8.0",
"setimmediate": "^1.0.5",
"tailwindcss": "^3.2.4",

122
src-tauri/Cargo.lock generated
View File

@ -84,7 +84,7 @@ dependencies = [
"tauri-build",
"tauri-plugin-fs-extra",
"tokio",
"toml 0.8.0",
"toml 0.8.2",
]
[[package]]
@ -1309,7 +1309,21 @@ checksum = "e5c13fb08e5d4dfc151ee5e88bae63f7773d61852f3bdc73c9f4b9e1bde03148"
dependencies = [
"log",
"mac",
"markup5ever",
"markup5ever 0.10.1",
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "html5ever"
version = "0.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bea68cab48b8459f17cf1c944c67ddc572d272d9f2b274140f223ecb1da4a3b7"
dependencies = [
"log",
"mac",
"markup5ever 0.11.0",
"proc-macro2",
"quote",
"syn 1.0.109",
@ -1644,9 +1658,9 @@ dependencies = [
[[package]]
name = "kittycad"
version = "0.2.26"
version = "0.2.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2623ee601ce203476229df3f9d3a14664cb43e3f7455e9ac8ed91aacaa6163d"
checksum = "35b2f9302648dbb06fd7121687f9505fc3179eba84111a06d76b246e3158f5dc"
dependencies = [
"anyhow",
"async-trait",
@ -1686,7 +1700,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ea8e9c6e031377cff82ee3001dc8026cdf431ed4e2e6b51f98ab8c73484a358"
dependencies = [
"cssparser",
"html5ever",
"html5ever 0.25.2",
"matches",
"selectors",
]
[[package]]
name = "kuchikiki"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f29e4755b7b995046f510a7520c42b2fed58b77bd94d5a87a8eb43d2fd126da8"
dependencies = [
"cssparser",
"html5ever 0.26.0",
"indexmap 1.9.3",
"matches",
"selectors",
]
@ -1748,9 +1775,9 @@ dependencies = [
[[package]]
name = "log"
version = "0.4.18"
version = "0.4.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "518ef76f2f87365916b142844c16d8fefd85039bc5699050210a7778ee1cd1de"
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
dependencies = [
"serde",
]
@ -1802,7 +1829,21 @@ checksum = "a24f40fb03852d1cdd84330cddcaf98e9ec08a7b7768e952fad3b4cf048ec8fd"
dependencies = [
"log",
"phf 0.8.0",
"phf_codegen",
"phf_codegen 0.8.0",
"string_cache",
"string_cache_codegen",
"tendril",
]
[[package]]
name = "markup5ever"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a2629bb1404f3d34c2e921f21fd34ba00b206124c81f65c50b43b6aaefeb016"
dependencies = [
"log",
"phf 0.10.1",
"phf_codegen 0.10.0",
"string_cache",
"string_cache_codegen",
"tendril",
@ -2374,6 +2415,16 @@ dependencies = [
"phf_shared 0.8.0",
]
[[package]]
name = "phf_codegen"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd"
dependencies = [
"phf_generator 0.10.0",
"phf_shared 0.10.0",
]
[[package]]
name = "phf_generator"
version = "0.8.0"
@ -3139,7 +3190,7 @@ dependencies = [
"log",
"matches",
"phf 0.8.0",
"phf_codegen",
"phf_codegen 0.8.0",
"precomputed-hash",
"servo_arc",
"smallvec",
@ -3661,9 +3712,9 @@ dependencies = [
[[package]]
name = "tauri"
version = "1.4.1"
version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fbe522898e35407a8e60dc3870f7579fea2fc262a6a6072eccdd37ae1e1d91e"
checksum = "0238c5063bf9613054149a1b6bce4935922e532b7d8211f36989a490a79806be"
dependencies = [
"anyhow",
"base64 0.21.2",
@ -3717,12 +3768,13 @@ dependencies = [
[[package]]
name = "tauri-build"
version = "1.4.0"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d2edd6a259b5591c8efdeb9d5702cb53515b82a6affebd55c7fd6d3a27b7d1b"
checksum = "defbfc551bd38ab997e5f8e458f87396d2559d05ce32095076ad6c30f7fc5f9c"
dependencies = [
"anyhow",
"cargo_toml",
"dirs-next",
"heck 0.4.1",
"json-patch",
"semver",
@ -3730,13 +3782,14 @@ dependencies = [
"serde_json",
"tauri-utils",
"tauri-winres",
"walkdir",
]
[[package]]
name = "tauri-codegen"
version = "1.4.0"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54ad2d49fdeab4a08717f5b49a163bdc72efc3b1950b6758245fcde79b645e1a"
checksum = "7b3475e55acec0b4a50fb96435f19631fb58cbcd31923e1a213de5c382536bbb"
dependencies = [
"base64 0.21.2",
"brotli",
@ -3760,9 +3813,9 @@ dependencies = [
[[package]]
name = "tauri-macros"
version = "1.4.0"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8eb12a2454e747896929338d93b0642144bb51e0dddbb36e579035731f0d76b7"
checksum = "613740228de92d9196b795ac455091d3a5fbdac2654abb8bb07d010b62ab43af"
dependencies = [
"heck 0.4.1",
"proc-macro2",
@ -3775,7 +3828,7 @@ dependencies = [
[[package]]
name = "tauri-plugin-fs-extra"
version = "0.0.0"
source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v1#0190f68f1dff80576595a1b79e31338a3e9ebba1"
source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v1#fa32d1afa97f52f74d814c5619b8d95da3268e3e"
dependencies = [
"log",
"serde",
@ -3786,9 +3839,9 @@ dependencies = [
[[package]]
name = "tauri-runtime"
version = "0.14.0"
version = "0.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "108683199cb18f96d2d4134187bb789964143c845d2d154848dda209191fd769"
checksum = "07f8e9e53e00e9f41212c115749e87d5cd2a9eebccafca77a19722eeecd56d43"
dependencies = [
"gtk",
"http",
@ -3807,9 +3860,9 @@ dependencies = [
[[package]]
name = "tauri-runtime-wry"
version = "0.14.0"
version = "0.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b7aa256a1407a3a091b5d843eccc1a5042289baf0a43d1179d9f0fcfea37c1b"
checksum = "8141d72b6b65f2008911e9ef5b98a68d1e3413b7a1464e8f85eb3673bb19a895"
dependencies = [
"cocoa",
"gtk",
@ -3827,19 +3880,20 @@ dependencies = [
[[package]]
name = "tauri-utils"
version = "1.4.0"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03fc02bb6072bb397e1d473c6f76c953cda48b4a2d0cce605df284aa74a12e84"
checksum = "34d55e185904a84a419308d523c2c6891d5e2dbcee740c4997eb42e75a7b0f46"
dependencies = [
"brotli",
"ctor",
"dunce",
"glob",
"heck 0.4.1",
"html5ever",
"html5ever 0.26.0",
"infer",
"json-patch",
"kuchiki",
"kuchikiki",
"log",
"memchr",
"phf 0.10.1",
"proc-macro2",
@ -4024,14 +4078,14 @@ dependencies = [
[[package]]
name = "toml"
version = "0.8.0"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c226a7bba6d859b63c92c4b4fe69c5b6b72d0cb897dbc8e6012298e6154cb56e"
checksum = "185d8ab0dfbb35cf1399a6344d8484209c088f75f8f68230da55d48d95d43e3d"
dependencies = [
"serde",
"serde_spanned",
"toml_datetime",
"toml_edit 0.20.0",
"toml_edit 0.20.2",
]
[[package]]
@ -4058,9 +4112,9 @@ dependencies = [
[[package]]
name = "toml_edit"
version = "0.20.0"
version = "0.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ff63e60a958cefbb518ae1fd6566af80d9d4be430a33f3723dfc47d1d411d95"
checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338"
dependencies = [
"indexmap 2.0.0",
"serde",
@ -4855,9 +4909,9 @@ dependencies = [
[[package]]
name = "wry"
version = "0.24.3"
version = "0.24.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33748f35413c8a98d45f7a08832d848c0c5915501803d1faade5a4ebcd258cea"
checksum = "88ef04bdad49eba2e01f06e53688c8413bd6a87b0bc14b72284465cf96e3578e"
dependencies = [
"base64 0.13.1",
"block",
@ -4869,7 +4923,7 @@ dependencies = [
"gio",
"glib",
"gtk",
"html5ever",
"html5ever 0.25.2",
"http",
"kuchiki",
"libc",

View File

@ -12,18 +12,18 @@ rust-version = "1.60"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[build-dependencies]
tauri-build = { version = "1.4.0", features = [] }
tauri-build = { version = "1.5.0", features = [] }
[dependencies]
anyhow = "1"
kittycad = "0.2.26"
kittycad = "0.2.28"
oauth2 = "4.4.2"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
tauri = { version = "1.4.1", features = [ "os-all", "dialog-all", "fs-all", "http-request", "path-all", "shell-open", "shell-open-api", "updater", "devtools"] }
tauri = { version = "1.5.1", features = [ "os-all", "dialog-all", "fs-all", "http-request", "path-all", "shell-open", "shell-open-api", "updater", "devtools"] }
tauri-plugin-fs-extra = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" }
tokio = { version = "1.32.0", features = ["time"] }
toml = "0.8.0"
toml = "0.8.2"
[features]
# this feature is used for production builds or when `devPath` points to the filesystem and the built-in dev server is disabled.

View File

@ -8,7 +8,7 @@
},
"package": {
"productName": "kittycad-modeling",
"version": "0.9.1"
"version": "0.10.0"
},
"tauri": {
"allowlist": {

View File

@ -1,9 +1,15 @@
import { render, screen } from '@testing-library/react'
import { App } from './App'
import { describe, test, vi } from 'vitest'
import { BrowserRouter } from 'react-router-dom'
import {
Route,
RouterProvider,
createMemoryRouter,
createRoutesFromElements,
} from 'react-router-dom'
import { GlobalStateProvider } from './components/GlobalStateProvider'
import CommandBarProvider from 'components/CommandBar'
import { BROWSER_FILE_NAME } from 'Router'
let listener: ((rect: any) => void) | undefined = undefined
;(global as any).ResizeObserver = class ResizeObserver {
@ -24,7 +30,7 @@ describe('App tests', () => {
>
return {
...actual,
useParams: () => ({ id: 'new' }),
useParams: () => ({ id: BROWSER_FILE_NAME }),
useLoaderData: () => ({ code: null }),
}
})
@ -41,12 +47,24 @@ describe('App tests', () => {
})
function TestWrap({ children }: { children: React.ReactNode }) {
// wrap in router and xState context
return (
<BrowserRouter>
// We have to use a memory router in the testing environment,
// and we have to use the createMemoryRouter function instead of <MemoryRouter /> as of react-router v6.4:
// https://reactrouter.com/en/6.16.0/routers/picking-a-router#using-v64-data-apis
const router = createMemoryRouter(
createRoutesFromElements(
<Route
path="/file/:id"
element={
<CommandBarProvider>
<GlobalStateProvider>{children}</GlobalStateProvider>
</CommandBarProvider>
</BrowserRouter>
)
}
/>
),
{
initialEntries: ['/file/new'],
initialIndex: 0,
}
)
return <RouterProvider router={router} />
}

View File

@ -31,6 +31,7 @@ import { TextEditor } from 'components/TextEditor'
import { Themes, getSystemTheme } from 'lib/theme'
import { useSetupEngineManager } from 'hooks/useSetupEngineManager'
import { useEngineConnectionSubscriptions } from 'hooks/useEngineConnectionSubscriptions'
import { engineCommandManager } from './lang/std/engineConnection'
export function App() {
const { code: loadedCode, project } = useLoaderData() as IndexLoaderData
@ -39,7 +40,6 @@ export function App() {
useHotKeyListener()
const {
setCode,
engineCommandManager,
buttonDownInStream,
openPanes,
setOpenPanes,
@ -52,7 +52,6 @@ export function App() {
guiMode: s.guiMode,
setGuiMode: s.setGuiMode,
setCode: s.setCode,
engineCommandManager: s.engineCommandManager,
buttonDownInStream: s.buttonDownInStream,
openPanes: s.openPanes,
setOpenPanes: s.setOpenPanes,
@ -91,12 +90,12 @@ export function App() {
if (guiMode.sketchMode === 'sketchEdit') {
// TODO: share this with Toolbar's "Exit sketch" button
// exiting sketch should be done consistently across all exits
engineCommandManager?.sendSceneCommand({
engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: { type: 'edit_mode_exit' },
})
engineCommandManager?.sendSceneCommand({
engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: { type: 'default_camera_disable_sketch_mode' },
@ -107,7 +106,7 @@ export function App() {
// when exiting sketch mode in the future
executeAst()
} else {
engineCommandManager?.sendSceneCommand({
engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: {
@ -156,7 +155,7 @@ export function App() {
useEngineConnectionSubscriptions()
const debounceSocketSend = throttle<EngineCommand>((message) => {
engineCommandManager?.sendSceneCommand(message)
engineCommandManager.sendSceneCommand(message)
}, 16)
const handleMouseMove: MouseEventHandler<HTMLDivElement> = (e) => {
e.nativeEvent.preventDefault()
@ -216,7 +215,6 @@ export function App() {
} else if (interactionGuards.zoom.dragCallback(eWithButton)) {
interaction = 'zoom'
} else {
console.log('none')
return
}

View File

@ -94,6 +94,8 @@ export const paths = {
) as typeof onboardingPaths,
}
export const BROWSER_FILE_NAME = 'new'
export type IndexLoaderData = {
code: string | null
project?: ProjectWithEntryPointMetadata
@ -129,7 +131,9 @@ const router = createBrowserRouter(
{
path: paths.INDEX,
loader: () =>
isTauri() ? redirect(paths.HOME) : redirect(paths.FILE + '/new'),
isTauri()
? redirect(paths.HOME)
: redirect(paths.FILE + '/' + BROWSER_FILE_NAME),
errorElement: <ErrorPage />,
},
{
@ -167,7 +171,7 @@ const router = createBrowserRouter(
)
}
if (params.id && params.id !== 'new') {
if (params.id && params.id !== BROWSER_FILE_NAME) {
// Note that PROJECT_ENTRYPOINT is hardcoded until we support multiple files
const code = await readTextFile(params.id + '/' + PROJECT_ENTRYPOINT)
const entrypoint_metadata = await metadata(
@ -212,7 +216,7 @@ const router = createBrowserRouter(
),
loader: async () => {
if (!isTauri()) {
return redirect(paths.FILE + '/new')
return redirect(paths.FILE + '/' + BROWSER_FILE_NAME)
}
const fetchedStorage = localStorage?.getItem(SETTINGS_PERSIST_KEY)
const persistedSettings = JSON.parse(fetchedStorage || '{}') as Partial<

View File

@ -10,7 +10,7 @@ import { SetHorzVertDistance } from './components/Toolbar/SetHorzVertDistance'
import { SetAngleLength } from './components/Toolbar/setAngleLength'
import { SetAbsDistance } from './components/Toolbar/SetAbsDistance'
import { SetAngleBetween } from './components/Toolbar/SetAngleBetween'
import { Fragment, useEffect } from 'react'
import { Fragment, WheelEvent, useRef } from 'react'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faSearch, faX } from '@fortawesome/free-solid-svg-icons'
import { Popover, Transition } from '@headlessui/react'
@ -18,6 +18,7 @@ import styles from './Toolbar.module.css'
import { v4 as uuidv4 } from 'uuid'
import { useAppMode } from 'hooks/useAppMode'
import { ActionIcon } from 'components/ActionIcon'
import { engineCommandManager } from './lang/std/engineConnection'
export const sketchButtonClassnames = {
background:
@ -50,7 +51,6 @@ export const Toolbar = () => {
ast,
updateAst,
programMemory,
engineCommandManager,
executeAst,
} = useStore((s) => ({
guiMode: s.guiMode,
@ -59,18 +59,27 @@ export const Toolbar = () => {
ast: s.ast,
updateAst: s.updateAst,
programMemory: s.programMemory,
engineCommandManager: s.engineCommandManager,
executeAst: s.executeAst,
}))
useAppMode()
const toolbarButtonsRef = useRef<HTMLSpanElement>(null)
useEffect(() => {
console.log('guiMode', guiMode)
}, [guiMode])
function handleToolbarButtonsWheelEvent(ev: WheelEvent<HTMLSpanElement>) {
const span = toolbarButtonsRef.current
if (!span) {
return
}
span.scrollLeft = span.scrollLeft += ev.deltaY
}
function ToolbarButtons({ className }: React.HTMLAttributes<HTMLElement>) {
return (
<span className={styles.toolbarButtons + ' ' + className}>
<span
ref={toolbarButtonsRef}
onWheel={handleToolbarButtonsWheelEvent}
className={styles.toolbarButtons + ' ' + className}
>
{guiMode.mode === 'default' && (
<button
onClick={() => {
@ -173,12 +182,12 @@ export const Toolbar = () => {
{guiMode.mode === 'sketch' && (
<button
onClick={() => {
engineCommandManager?.sendSceneCommand({
engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: { type: 'edit_mode_exit' },
})
engineCommandManager?.sendSceneCommand({
engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: { type: 'default_camera_disable_sketch_mode' },
@ -214,7 +223,7 @@ export const Toolbar = () => {
<button
key={sketchFnName}
onClick={() => {
engineCommandManager?.sendSceneCommand({
engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: {

View File

@ -23,10 +23,7 @@ type ActionButtonAsLink = BaseActionButtonProps &
}
type ActionButtonAsExternal = BaseActionButtonProps &
Omit<
React.AnchorHTMLAttributes<HTMLAnchorElement>,
keyof BaseActionButtonProps
> & {
Omit<LinkProps, keyof BaseActionButtonProps> & {
Element: 'externalLink'
}
@ -69,12 +66,17 @@ export const ActionButton = (props: ActionButtonProps) => {
)
}
case 'externalLink': {
const { Element, icon, children, className, ...rest } = props
const { Element, to, icon, children, className, ...rest } = props
return (
<a className={classNames} {...rest}>
<Link
to={to || paths.INDEX}
className={classNames}
{...rest}
target="_blank"
>
{icon && <ActionIcon {...icon} />}
{children}
</a>
</Link>
)
}
default: {

View File

@ -1,7 +1,5 @@
import { useEffect, useState, useRef } from 'react'
import { parser_wasm } from '../lang/abstractSyntaxTree'
import { BinaryPart, Value } from '../lang/abstractSyntaxTreeTypes'
import { executor } from '../lang/executor'
import { parse, BinaryPart, Value, executor } from '../lang/wasm'
import {
createIdentifier,
createLiteral,
@ -10,6 +8,7 @@ import {
} from '../lang/modifyAst'
import { findAllPreviousVariables, PrevVariable } from '../lang/queryAst'
import { useStore } from '../useStore'
import { engineCommandManager } from '../lang/std/engineConnection'
export const AvailableVars = ({
onVarClick,
@ -92,12 +91,12 @@ export function useCalc({
newVariableInsertIndex: number
setNewVariableName: (a: string) => void
} {
const { ast, programMemory, selectionRange, engineCommandManager } = useStore(
const { ast, programMemory, selectionRange, defaultPlanes } = useStore(
(s) => ({
ast: s.ast,
programMemory: s.programMemory,
selectionRange: s.selectionRanges.codeBasedSelections[0].range,
engineCommandManager: s.engineCommandManager,
defaultPlanes: s.defaultPlanes,
})
)
const inputRef = useRef<HTMLInputElement>(null)
@ -140,15 +139,16 @@ export function useCalc({
}, [ast, programMemory, selectionRange])
useEffect(() => {
if (!engineCommandManager) return
try {
const code = `const __result__ = ${value}\nshow(__result__)`
const ast = parser_wasm(code)
const ast = parse(code)
const _programMem: any = { root: {}, return: null }
availableVarInfo.variables.forEach(({ key, value }) => {
_programMem.root[key] = { type: 'userVal', value, __meta: [] }
})
executor(ast, _programMem, engineCommandManager).then((programMemory) => {
if (!defaultPlanes) return
executor(ast, _programMem, engineCommandManager, defaultPlanes!).then(
(programMemory) => {
const resultDeclaration = ast.body.find(
(a) =>
a.type === 'VariableDeclaration' &&
@ -160,7 +160,8 @@ export function useCalc({
const result = programMemory?.root?.__result__?.value
setCalcResult(typeof result === 'number' ? String(result) : 'NAN')
init && setValueNode(init)
})
}
)
} catch (e) {
setCalcResult('NAN')
setValueNode(null)

View File

@ -1,5 +1,4 @@
import { CollapsiblePanel, CollapsiblePanelProps } from './CollapsiblePanel'
import { useStore } from '../useStore'
import { v4 as uuidv4 } from 'uuid'
import { EngineCommand } from '../lang/std/engineConnection'
import { useState } from 'react'
@ -7,6 +6,7 @@ import { ActionButton } from '../components/ActionButton'
import { faCheck } from '@fortawesome/free-solid-svg-icons'
import { isReducedMotion } from 'lang/util'
import { AstExplorer } from './AstExplorer'
import { engineCommandManager } from '../lang/std/engineConnection'
type SketchModeCmd = Extract<
Extract<EngineCommand, { type: 'modeling_cmd_req' }>['cmd'],
@ -14,9 +14,6 @@ type SketchModeCmd = Extract<
>
export const DebugPanel = ({ className, ...props }: CollapsiblePanelProps) => {
const { engineCommandManager } = useStore((s) => ({
engineCommandManager: s.engineCommandManager,
}))
const [sketchModeCmd, setSketchModeCmd] = useState<SketchModeCmd>({
type: 'default_camera_enable_sketch_mode',
origin: { x: 0, y: 0, z: 0 },
@ -70,19 +67,18 @@ export const DebugPanel = ({ className, ...props }: CollapsiblePanelProps) => {
className="w-16"
type="checkbox"
checked={sketchModeCmd.ortho}
onChange={(a) => {
console.log(a, (a as any).checked)
onChange={(a) =>
setSketchModeCmd({
...sketchModeCmd,
ortho: a.target.checked,
})
}}
}
/>
</div>
<ActionButton
Element="button"
onClick={() => {
engineCommandManager?.sendSceneCommand({
engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req',
cmd: sketchModeCmd,
cmd_id: uuidv4(),

View File

@ -47,11 +47,9 @@ export const ErrorPage = () => {
Clear storage
</ActionButton>
<ActionButton
Element="link"
Element="externalLink"
icon={{ icon: faBug }}
target="_blank"
rel="noopener noreferrer"
to="https://discord.com/channels/915388055236509727/1138967922614743060"
to="https://github.com/KittyCAD/modeling-app/issues/new"
>
Report Bug
</ActionButton>

View File

@ -1,13 +1,17 @@
import { v4 as uuidv4 } from 'uuid'
import { useStore } from '../useStore'
import { faFileExport, faXmark } from '@fortawesome/free-solid-svg-icons'
import { ActionButton } from './ActionButton'
import Modal from 'react-modal'
import React from 'react'
import { useFormik } from 'formik'
import { Models } from '@kittycad/lib'
import { engineCommandManager } from '../lang/std/engineConnection'
import { useGlobalStateContext } from 'hooks/useGlobalStateContext'
type OutputFormat = Models['OutputFormat_type']
type OutputTypeKey = OutputFormat['type']
type ExtractStorageTypes<T> = T extends { storage: infer U } ? U : never
type StorageUnion = ExtractStorageTypes<OutputFormat>
interface ExportButtonProps extends React.PropsWithChildren {
className?: {
@ -18,14 +22,19 @@ interface ExportButtonProps extends React.PropsWithChildren {
}
export const ExportButton = ({ children, className }: ExportButtonProps) => {
const { engineCommandManager } = useStore((s) => ({
engineCommandManager: s.engineCommandManager,
}))
const [modalIsOpen, setIsOpen] = React.useState(false)
const {
settings: {
state: {
context: { baseUnit },
},
},
} = useGlobalStateContext()
const defaultType = 'gltf'
const [type, setType] = React.useState(defaultType)
const [type, setType] = React.useState<OutputTypeKey>(defaultType)
const defaultStorage = 'embedded'
const [storage, setStorage] = React.useState<StorageUnion>(defaultStorage)
function openModal() {
setIsOpen(true)
@ -38,7 +47,7 @@ export const ExportButton = ({ children, className }: ExportButtonProps) => {
// Default to gltf and embedded.
const initialValues: OutputFormat = {
type: defaultType,
storage: 'embedded',
storage: defaultStorage,
presentation: 'pretty',
}
const formik = useFormik({
@ -66,7 +75,18 @@ export const ExportButton = ({ children, className }: ExportButtonProps) => {
},
}
}
engineCommandManager?.sendSceneCommand({
if (values.type === 'obj' || values.type === 'stl') {
values.units = baseUnit
}
if (
values.type === 'ply' ||
values.type === 'stl' ||
values.type === 'gltf'
) {
// Set the storage type.
values.storage = storage
}
engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req',
cmd: {
type: 'export',
@ -75,6 +95,7 @@ export const ExportButton = ({ children, className }: ExportButtonProps) => {
// in the scene to export. In that case, you'd pass the IDs thru here.
entity_ids: [],
format: values,
source_unit: baseUnit,
},
cmd_id: uuidv4(),
})
@ -109,7 +130,17 @@ export const ExportButton = ({ children, className }: ExportButtonProps) => {
id="type"
name="type"
onChange={(e) => {
setType(e.target.value)
setType(e.target.value as OutputTypeKey)
if (e.target.value === 'gltf') {
// Set default to embedded.
setStorage('embedded')
} else if (e.target.value === 'ply') {
// Set default to ascii.
setStorage('ascii')
} else if (e.target.value === 'stl') {
// Set default to ascii.
setStorage('ascii')
}
formik.handleChange(e)
}}
className="bg-chalkboard-20 dark:bg-chalkboard-90 w-full"
@ -127,10 +158,10 @@ export const ExportButton = ({ children, className }: ExportButtonProps) => {
<select
id="storage"
name="storage"
onChange={formik.handleChange}
value={
'storage' in formik.values ? formik.values.storage : ''
}
onChange={(e) => {
setStorage(e.target.value as StorageUnion)
formik.handleChange(e)
}}
className="bg-chalkboard-20 dark:bg-chalkboard-90 w-full"
>
{type === 'gltf' && (

View File

@ -1,7 +1,6 @@
import { processMemory } from './MemoryPanel'
import { parser_wasm } from '../lang/abstractSyntaxTree'
import { enginelessExecutor } from '../lib/testHelpers'
import { initPromise } from '../lang/rust'
import { initPromise, parse } from '../lang/wasm'
beforeAll(() => initPromise)
@ -15,18 +14,20 @@ describe('processMemory', () => {
}
const otherVar = myFn(5)
const theExtrude = startSketchAt([0, 0])
const theExtrude = startSketchOn('XY')
|> startProfileAt([0, 0], %)
|> lineTo([-2.4, myVar], %)
|> lineTo([-0.76, otherVar], %)
|> extrude(4, %)
const theSketch = startSketchAt([0, 0])
const theSketch = startSketchOn('XY')
|> startProfileAt([0, 0], %)
|> lineTo([-3.35, 0.17], %)
|> lineTo([0.98, 5.16], %)
|> lineTo([2.15, 4.32], %)
// |> rx(90, %)
show(theExtrude, theSketch)`
const ast = parser_wasm(code)
const ast = parse(code)
const programMemory = await enginelessExecutor(ast, {
root: {},
return: null,

View File

@ -2,7 +2,7 @@ import ReactJson from 'react-json-view'
import { CollapsiblePanel, CollapsiblePanelProps } from './CollapsiblePanel'
import { useStore } from '../useStore'
import { useMemo } from 'react'
import { ProgramMemory, Path, ExtrudeSurface } from '../lang/executor'
import { ProgramMemory, Path, ExtrudeSurface } from '../lang/wasm'
import { Themes } from '../lib/theme'
interface MemoryPanelProps extends CollapsiblePanelProps {

View File

@ -2,6 +2,8 @@ import { fireEvent, render, screen } from '@testing-library/react'
import { BrowserRouter } from 'react-router-dom'
import ProjectSidebarMenu from './ProjectSidebarMenu'
import { ProjectWithEntryPointMetadata } from '../Router'
import { GlobalStateProvider } from './GlobalStateProvider'
import CommandBarProvider from './CommandBar'
const now = new Date()
const projectWellFormed = {
@ -38,7 +40,11 @@ describe('ProjectSidebarMenu tests', () => {
test('Renders the project name', () => {
render(
<BrowserRouter>
<CommandBarProvider>
<GlobalStateProvider>
<ProjectSidebarMenu project={projectWellFormed} />
</GlobalStateProvider>
</CommandBarProvider>
</BrowserRouter>
)
@ -55,7 +61,11 @@ describe('ProjectSidebarMenu tests', () => {
test('Renders app name if given no project', () => {
render(
<BrowserRouter>
<CommandBarProvider>
<GlobalStateProvider>
<ProjectSidebarMenu />
</GlobalStateProvider>
</CommandBarProvider>
</BrowserRouter>
)
@ -69,7 +79,14 @@ describe('ProjectSidebarMenu tests', () => {
test('Renders as a link if set to do so', () => {
render(
<BrowserRouter>
<ProjectSidebarMenu project={projectWellFormed} renderAsLink={true} />
<CommandBarProvider>
<GlobalStateProvider>
<ProjectSidebarMenu
project={projectWellFormed}
renderAsLink={true}
/>
</GlobalStateProvider>
</CommandBarProvider>
</BrowserRouter>
)

View File

@ -1,6 +1,6 @@
import { Dialog, Transition } from '@headlessui/react'
import { Fragment, useState } from 'react'
import { Value } from '../lang/abstractSyntaxTreeTypes'
import { Value } from '../lang/wasm'
import {
AvailableVars,
addToInputHelper,

View File

@ -1,6 +1,6 @@
import { Dialog, Transition } from '@headlessui/react'
import { Fragment, useState } from 'react'
import { Value } from '../lang/abstractSyntaxTreeTypes'
import { Value } from '../lang/wasm'
import {
AvailableVars,
addToInputHelper,

View File

@ -20,11 +20,15 @@ import {
compareVec2Epsilon,
} from 'lang/std/sketch'
import { getNodeFromPath } from 'lang/queryAst'
import { Program, VariableDeclarator } from 'lang/abstractSyntaxTreeTypes'
import { modify_ast_for_sketch } from '../wasm-lib/pkg/wasm_lib'
import {
Program,
VariableDeclarator,
rangeTypeFix,
modifyAstForSketch,
} from 'lang/wasm'
import { KCLError } from 'lang/errors'
import { KclError as RustKclError } from '../wasm-lib/kcl/bindings/KclError'
import { rangeTypeFix } from 'lang/abstractSyntaxTree'
import { engineCommandManager } from '../lang/std/engineConnection'
export const Stream = ({ className = '' }) => {
const [isLoading, setIsLoading] = useState(true)
@ -32,7 +36,6 @@ export const Stream = ({ className = '' }) => {
const videoRef = useRef<HTMLVideoElement>(null)
const {
mediaStream,
engineCommandManager,
setButtonDownInStream,
didDragInStream,
setDidDragInStream,
@ -43,9 +46,10 @@ export const Stream = ({ className = '' }) => {
updateAst,
setGuiMode,
programMemory,
defaultPlanes,
currentPlane,
} = useStore((s) => ({
mediaStream: s.mediaStream,
engineCommandManager: s.engineCommandManager,
setButtonDownInStream: s.setButtonDownInStream,
fileId: s.fileId,
didDragInStream: s.didDragInStream,
@ -57,6 +61,8 @@ export const Stream = ({ className = '' }) => {
updateAst: s.updateAst,
setGuiMode: s.setGuiMode,
programMemory: s.programMemory,
defaultPlanes: s.defaultPlanes,
currentPlane: s.currentPlane,
}))
const {
settings: {
@ -73,7 +79,7 @@ export const Stream = ({ className = '' }) => {
if (!videoRef.current) return
if (!mediaStream) return
videoRef.current.srcObject = mediaStream
}, [mediaStream, engineCommandManager])
}, [mediaStream])
const handleMouseDown: MouseEventHandler<HTMLVideoElement> = (e) => {
if (!videoRef.current) return
@ -107,7 +113,7 @@ export const Stream = ({ className = '' }) => {
}
if (guiMode.mode === 'sketch' && guiMode.sketchMode === ('move' as any)) {
engineCommandManager?.sendSceneCommand({
engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req',
cmd: {
type: 'handle_mouse_drag_start',
@ -121,7 +127,7 @@ export const Stream = ({ className = '' }) => {
guiMode.sketchMode === ('sketch_line' as any)
)
) {
engineCommandManager?.sendSceneCommand({
engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req',
cmd: {
type: 'camera_drag_start',
@ -139,7 +145,7 @@ export const Stream = ({ className = '' }) => {
const handleScroll: WheelEventHandler<HTMLVideoElement> = (e) => {
if (!cameraMouseDragGuards[cameraControls].zoom.scrollCallback(e)) return
engineCommandManager?.sendSceneCommand({
engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req',
cmd: {
type: 'default_camera_zoom',
@ -177,7 +183,7 @@ export const Stream = ({ className = '' }) => {
}
if (!didDragInStream) {
engineCommandManager?.sendSceneCommand({
engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req',
cmd: {
type: 'select_with_point',
@ -214,7 +220,7 @@ export const Stream = ({ className = '' }) => {
window: { x, y },
}
}
engineCommandManager?.sendSceneCommand(command).then(async (resp) => {
engineCommandManager.sendSceneCommand(command).then(async (resp) => {
if (!(guiMode.mode === 'sketch')) return
if (guiMode.sketchMode === 'selectFace') return
@ -240,9 +246,6 @@ export const Stream = ({ className = '' }) => {
) {
// Let's get the updated ast.
if (sketchGroupId === '') return
console.log('guiMode.pathId', guiMode.pathId)
// We have a problem if we do not have an id for the sketch group.
if (
guiMode.pathId === undefined ||
@ -253,26 +256,30 @@ export const Stream = ({ className = '' }) => {
let engineId = guiMode.pathId
try {
const updatedAst: Program = await modify_ast_for_sketch(
// Get the current plane string for plane we are on.
let currentPlaneString = ''
if (currentPlane === defaultPlanes?.xy) {
currentPlaneString = 'XY'
} else if (currentPlane === defaultPlanes?.yz) {
currentPlaneString = 'YZ'
} else if (currentPlane === defaultPlanes?.xz) {
currentPlaneString = 'XZ'
}
// Do not supporting editing/moving lines on a non-default plane.
// Eventually we can support this but for now we will just throw an
// error.
if (currentPlaneString === '') return
const updatedAst: Program = await modifyAstForSketch(
engineCommandManager,
JSON.stringify(ast),
ast,
variableName,
currentPlaneString,
engineId
)
updateAst(updatedAst, false)
} catch (e: any) {
const parsed: RustKclError = JSON.parse(e.toString())
const kclError = new KCLError(
parsed.kind,
parsed.msg,
rangeTypeFix(parsed.sourceRanges)
)
console.log(kclError)
throw kclError
}
return
}
@ -285,7 +292,7 @@ export const Stream = ({ className = '' }) => {
guiMode.waitingFirstClick &&
!isEditingExistingSketch
) {
const curve = await engineCommandManager?.sendSceneCommand({
const curve = await engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: {
@ -295,8 +302,46 @@ export const Stream = ({ className = '' }) => {
})
const coords: { x: number; y: number }[] =
curve.data.data.control_points
// We need the normal for the plane we are on.
const plane = await engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: {
type: 'get_sketch_mode_plane',
},
})
const z_axis = plane.data.data.z_axis
// Get the current axis.
let currentAxis: 'xy' | 'xz' | 'yz' | '-xy' | '-xz' | '-yz' | null =
null
if (currentPlane === defaultPlanes?.xy) {
if (z_axis.z === -1) {
currentAxis = '-xy'
} else {
currentAxis = 'xy'
}
} else if (currentPlane === defaultPlanes?.yz) {
if (z_axis.x === -1) {
currentAxis = '-yz'
} else {
currentAxis = 'yz'
}
} else if (currentPlane === defaultPlanes?.xz) {
if (z_axis.y === -1) {
currentAxis = '-xz'
} else {
currentAxis = 'xz'
}
}
// Do not support starting a new sketch on a non-default plane.
if (!currentAxis) return
const _addStartSketch = addStartSketch(
ast,
currentAxis,
[roundOff(coords[0].x), roundOff(coords[0].y)],
[
roundOff(coords[1].x - coords[0].x),
@ -326,7 +371,7 @@ export const Stream = ({ className = '' }) => {
resp?.data?.data?.entities_modified?.length &&
(!guiMode.waitingFirstClick || isEditingExistingSketch)
) {
const curve = await engineCommandManager?.sendSceneCommand({
const curve = await engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: {
@ -371,12 +416,12 @@ export const Stream = ({ className = '' }) => {
setGuiMode({
mode: 'default',
})
engineCommandManager?.sendSceneCommand({
engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: { type: 'edit_mode_exit' },
})
engineCommandManager?.sendSceneCommand({
engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: { type: 'default_camera_disable_sketch_mode' },
@ -415,6 +460,7 @@ export const Stream = ({ className = '' }) => {
onWheel={handleScroll}
onPlay={() => setIsLoading(false)}
onMouseMoveCapture={handleMouseMove}
disablePictureInPicture
className={`w-full h-full ${isExecuting && 'blur-md'}`}
style={{ transitionDuration: '200ms', transitionProperty: 'filter' }}
/>

View File

@ -30,6 +30,7 @@ import { isOverlap, roundOff } from 'lib/utils'
import { kclErrToDiagnostic } from 'lang/errors'
import { CSSRuleObject } from 'tailwindcss/types/config'
import interact from '@replit/codemirror-interact'
import { engineCommandManager } from '../lang/std/engineConnection'
export const editorShortcutMeta = {
formatCode: {
@ -52,7 +53,6 @@ export const TextEditor = ({
code,
deferredSetCode,
editorView,
engineCommandManager,
formatCode,
isLSPServerReady,
selectionRanges,
@ -64,7 +64,6 @@ export const TextEditor = ({
code: s.code,
deferredSetCode: s.deferredSetCode,
editorView: s.editorView,
engineCommandManager: s.engineCommandManager,
formatCode: s.formatCode,
isLSPServerReady: s.isLSPServerReady,
selectionRanges: s.selectionRanges,
@ -173,7 +172,7 @@ export const TextEditor = ({
const idBasedSelections = codeBasedSelections
.map(({ type, range }) => {
const hasOverlap = Object.entries(
engineCommandManager?.sourceRangeMap || {}
engineCommandManager.sourceRangeMap || {}
).filter(([_, sourceRange]) => {
return isOverlap(sourceRange, range)
})
@ -186,7 +185,7 @@ export const TextEditor = ({
})
.filter(Boolean) as any
engineCommandManager?.cusorsSelected({
engineCommandManager.cusorsSelected({
otherSelections: [],
idBasedSelections,
})

View File

@ -1,6 +1,6 @@
import { useState, useEffect } from 'react'
import { toolTips, useStore } from '../../useStore'
import { Value, VariableDeclarator } from '../../lang/abstractSyntaxTreeTypes'
import { Value, VariableDeclarator } from '../../lang/wasm'
import {
getNodePathFromSourceRange,
getNodeFromPath,

View File

@ -1,6 +1,6 @@
import { useState, useEffect } from 'react'
import { toolTips, useStore } from '../../useStore'
import { Value, VariableDeclarator } from '../../lang/abstractSyntaxTreeTypes'
import { Value, VariableDeclarator } from '../../lang/wasm'
import {
getNodePathFromSourceRange,
getNodeFromPath,

View File

@ -1,6 +1,6 @@
import { useState, useEffect } from 'react'
import { toolTips, useStore } from '../../useStore'
import { Value } from '../../lang/abstractSyntaxTreeTypes'
import { Value } from '../../lang/wasm'
import {
getNodePathFromSourceRange,
getNodeFromPath,

View File

@ -1,11 +1,7 @@
import { useState, useEffect } from 'react'
import { create } from 'react-modal-promise'
import { toolTips, useStore } from '../../useStore'
import {
BinaryPart,
Value,
VariableDeclarator,
} from '../../lang/abstractSyntaxTreeTypes'
import { BinaryPart, Value, VariableDeclarator } from '../../lang/wasm'
import {
getNodePathFromSourceRange,
getNodeFromPath,

View File

@ -1,6 +1,6 @@
import { useState, useEffect } from 'react'
import { toolTips, useStore } from '../../useStore'
import { Value } from '../../lang/abstractSyntaxTreeTypes'
import { Value } from '../../lang/wasm'
import {
getNodePathFromSourceRange,
getNodeFromPath,

View File

@ -1,7 +1,7 @@
import { useState, useEffect } from 'react'
import { create } from 'react-modal-promise'
import { toolTips, useStore } from '../../useStore'
import { Value } from '../../lang/abstractSyntaxTreeTypes'
import { Value } from '../../lang/wasm'
import {
getNodePathFromSourceRange,
getNodeFromPath,
@ -133,7 +133,7 @@ export const SetAbsDistance = ({ buttonType }: { buttonType: ButtonType }) => {
callBack: updateCursors(setCursor, selectionRanges, pathToNodeMap),
})
} catch (e) {
console.log('e', e)
console.log('error', e)
}
}}
disabled={!enableAngLen}

View File

@ -1,11 +1,7 @@
import { useState, useEffect } from 'react'
import { create } from 'react-modal-promise'
import { toolTips, useStore } from '../../useStore'
import {
BinaryPart,
Value,
VariableDeclarator,
} from '../../lang/abstractSyntaxTreeTypes'
import { BinaryPart, Value, VariableDeclarator } from '../../lang/wasm'
import {
getNodePathFromSourceRange,
getNodeFromPath,

View File

@ -1,11 +1,7 @@
import { useState, useEffect } from 'react'
import { create } from 'react-modal-promise'
import { toolTips, useStore } from '../../useStore'
import {
BinaryPart,
Value,
VariableDeclarator,
} from '../../lang/abstractSyntaxTreeTypes'
import { BinaryPart, Value, VariableDeclarator } from '../../lang/wasm'
import {
getNodePathFromSourceRange,
getNodeFromPath,
@ -21,8 +17,6 @@ import { GetInfoModal } from '../SetHorVertDistanceModal'
import { createLiteral, createVariableDeclaration } from '../../lang/modifyAst'
import { removeDoubleNegatives } from '../AvailableVarsHelpers'
import { updateCursors } from '../../lang/util'
import { ActionIcon } from 'components/ActionIcon'
import { sketchButtonClassnames } from 'Toolbar'
const getModalInfo = create(GetInfoModal as any)

View File

@ -1,7 +1,7 @@
import { useState, useEffect } from 'react'
import { create } from 'react-modal-promise'
import { toolTips, useStore } from '../../useStore'
import { Value } from '../../lang/abstractSyntaxTreeTypes'
import { Value } from '../../lang/wasm'
import {
getNodePathFromSourceRange,
getNodeFromPath,
@ -147,7 +147,7 @@ export const SetAngleLength = ({
callBack: updateCursors(setCursor, selectionRanges, pathToNodeMap),
})
} catch (e) {
console.log('e', e)
console.log('erorr', e)
}
}}
disabled={!enableAngLen}

View File

@ -1,6 +1,11 @@
import { fireEvent, render, screen } from '@testing-library/react'
import UserSidebarMenu from './UserSidebarMenu'
import { BrowserRouter } from 'react-router-dom'
import {
Route,
RouterProvider,
createMemoryRouter,
createRoutesFromElements,
} from 'react-router-dom'
import { Models } from '@kittycad/lib'
import { GlobalStateProvider } from './GlobalStateProvider'
import CommandBarProvider from './CommandBar'
@ -93,11 +98,24 @@ describe('UserSidebarMenu tests', () => {
function TestWrap({ children }: { children: React.ReactNode }) {
// wrap in router and xState context
return (
<BrowserRouter>
// We have to use a memory router in the testing environment,
// and we have to use the createMemoryRouter function instead of <MemoryRouter /> as of react-router v6.4:
// https://reactrouter.com/en/6.16.0/routers/picking-a-router#using-v64-data-apis
const router = createMemoryRouter(
createRoutesFromElements(
<Route
path="/file/:id"
element={
<CommandBarProvider>
<GlobalStateProvider>{children}</GlobalStateProvider>
</CommandBarProvider>
</BrowserRouter>
)
}
/>
),
{
initialEntries: ['/file/new'],
initialIndex: 0,
}
)
return <RouterProvider router={router} />
}

View File

@ -1,18 +1,24 @@
import { Popover, Transition } from '@headlessui/react'
import { ActionButton } from './ActionButton'
import { faBars, faGear, faSignOutAlt } from '@fortawesome/free-solid-svg-icons'
import {
faBars,
faBug,
faGear,
faSignOutAlt,
} from '@fortawesome/free-solid-svg-icons'
import { faGithub } from '@fortawesome/free-brands-svg-icons'
import { useLocation, useNavigate } from 'react-router-dom'
import { Fragment, useState } from 'react'
import { paths } from '../Router'
import makeUrlPathRelative from '../lib/makeUrlPathRelative'
import { Models } from '@kittycad/lib'
import { useGlobalStateContext } from 'hooks/useGlobalStateContext'
import { useAbsoluteFilePath } from 'hooks/useAbsoluteFilePath'
type User = Models['User_type']
const UserSidebarMenu = ({ user }: { user?: User }) => {
const location = useLocation()
const filePath = useAbsoluteFilePath()
const displayedName = getDisplayName(user)
const [imageLoadFailed, setImageLoadFailed] = useState(false)
const navigate = useNavigate()
@ -127,23 +133,30 @@ const UserSidebarMenu = ({ user }: { user?: User }) => {
// since /settings is a nested route the sidebar doesn't close
// automatically when navigating to it
close()
navigate(
(location.pathname.endsWith('/')
? location.pathname.slice(0, -1)
: location.pathname) + paths.SETTINGS
)
const targetPath = location.pathname.includes(paths.FILE)
? filePath + paths.SETTINGS
: paths.HOME + paths.SETTINGS
navigate(targetPath)
}}
>
Settings
</ActionButton>
<ActionButton
Element="link"
Element="externalLink"
to="https://github.com/KittyCAD/modeling-app/discussions"
icon={{ icon: faGithub }}
className="border-transparent dark:border-transparent dark:hover:border-liquid-60"
>
Request a feature
</ActionButton>
<ActionButton
Element="externalLink"
to="https://github.com/KittyCAD/modeling-app/issues/new"
icon={{ icon: faBug }}
className="border-transparent dark:border-transparent dark:hover:border-liquid-60"
>
Report a bug
</ActionButton>
<ActionButton
Element="button"
onClick={() => send('Log out')}

View File

@ -109,7 +109,6 @@ export default class Client extends jsrpc.JSONRPCServerAndClient {
}
}
messageString += message
// console.log(messageString)
return
})

View File

@ -96,8 +96,6 @@ export class LanguageServerPlugin implements PluginValue {
async sendChange({ documentText }: { documentText: string }) {
if (!this.client.ready) return
console.log(documentText.length)
if (documentText.length > 5000) {
// Clear out the text it thinks we have, large documents will throw a stack error.
// This is obviously not a good fix but it works for now til we figure

View File

@ -0,0 +1,12 @@
import { BROWSER_FILE_NAME, IndexLoaderData, paths } from 'Router'
import { useRouteLoaderData } from 'react-router-dom'
export function useAbsoluteFilePath() {
const routeData = useRouteLoaderData(paths.FILE) as IndexLoaderData
return (
paths.FILE +
'/' +
encodeURIComponent(routeData?.project?.path || BROWSER_FILE_NAME)
)
}

View File

@ -2,35 +2,37 @@
// Once we have xState this should be removed
import { useStore, Selections } from 'useStore'
import { useEffect, useState } from 'react'
import { useEffect } from 'react'
import { v4 as uuidv4 } from 'uuid'
import { ArtifactMap, EngineCommandManager } from 'lang/std/engineConnection'
import { Models } from '@kittycad/lib/dist/types/src'
import { isReducedMotion } from 'lang/util'
import { isOverlap } from 'lib/utils'
interface DefaultPlanes {
xy: string
// TODO re-enable
// yz: string
// xz: string
}
import { engineCommandManager } from '../lang/std/engineConnection'
import { DefaultPlanes } from '../wasm-lib/kcl/bindings/DefaultPlanes'
import { getNodeFromPath } from '../lang/queryAst'
import { CallExpression, PipeExpression } from '../lang/wasm'
export function useAppMode() {
const {
guiMode,
setGuiMode,
selectionRanges,
engineCommandManager,
selectionRangeTypeMap,
defaultPlanes,
setDefaultPlanes,
setCurrentPlane,
ast,
} = useStore((s) => ({
guiMode: s.guiMode,
setGuiMode: s.setGuiMode,
selectionRanges: s.selectionRanges,
engineCommandManager: s.engineCommandManager,
selectionRangeTypeMap: s.selectionRangeTypeMap,
defaultPlanes: s.defaultPlanes,
setDefaultPlanes: s.setDefaultPlanes,
setCurrentPlane: s.setCurrentPlane,
ast: s.ast,
}))
const [defaultPlanes, setDefaultPlanes] = useState<DefaultPlanes | null>(null)
useEffect(() => {
if (
guiMode.mode === 'sketch' &&
@ -40,8 +42,10 @@ export function useAppMode() {
const createAndShowPlanes = async () => {
let localDefaultPlanes: DefaultPlanes
if (!defaultPlanes) {
localDefaultPlanes = await initDefaultPlanes(engineCommandManager)
setDefaultPlanes(localDefaultPlanes)
const newDefaultPlanes = await initDefaultPlanes(engineCommandManager)
if (!newDefaultPlanes) return
setDefaultPlanes(newDefaultPlanes)
localDefaultPlanes = newDefaultPlanes
} else {
localDefaultPlanes = defaultPlanes
}
@ -57,20 +61,52 @@ export function useAppMode() {
const enableSketchMode = async () => {
let localDefaultPlanes: DefaultPlanes
if (!defaultPlanes) {
localDefaultPlanes = await initDefaultPlanes(engineCommandManager)
setDefaultPlanes(localDefaultPlanes)
const newDefaultPlanes = await initDefaultPlanes(engineCommandManager)
if (!newDefaultPlanes) return
setDefaultPlanes(newDefaultPlanes)
localDefaultPlanes = newDefaultPlanes
} else {
localDefaultPlanes = defaultPlanes
}
setDefaultPlanesHidden(engineCommandManager, localDefaultPlanes, true)
// TODO figure out the plane to use based on the sketch
// maybe it's easier to make a new plane than rely on the defaults
await engineCommandManager?.sendSceneCommand({
const pipeExpression = getNodeFromPath<PipeExpression>(
ast,
guiMode.pathToNode,
'PipeExpression'
).node
if (pipeExpression.type !== 'PipeExpression') return /// bad bad bad
const sketchCallExpression = pipeExpression.body.find(
(e) =>
e.type === 'CallExpression' && e.callee.name === 'startSketchOn'
) as CallExpression
if (!sketchCallExpression) return // also bad bad bad
const firstArg = sketchCallExpression.arguments[0]
let planeId = ''
if (firstArg.type === 'Literal' && firstArg.value) {
const planeStrCleaned = firstArg.value
.toString()
.toLowerCase()
.replace('-', '')
if (
planeStrCleaned === 'xy' ||
planeStrCleaned === 'xz' ||
planeStrCleaned === 'yz'
) {
planeId = localDefaultPlanes[planeStrCleaned]
}
}
if (!planeId) return // they are on some non default plane, which we don't support yet
setCurrentPlane(planeId)
await engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: {
type: 'sketch_mode_enable',
plane_id: localDefaultPlanes.xy,
plane_id: planeId,
ortho: true,
animated: !isReducedMotion(),
},
@ -135,7 +171,7 @@ export function useAppMode() {
])
useEffect(() => {
const unSub = engineCommandManager?.subscribeTo({
const unSub = engineCommandManager.subscribeTo({
event: 'select_with_point',
callback: async ({ data }) => {
if (!data.entity_id) return
@ -144,8 +180,8 @@ export function useAppMode() {
// user clicked something else in the scene
return
}
const sketchModeResponse = await engineCommandManager?.sendSceneCommand(
{
setCurrentPlane(data.entity_id)
const sketchModeResponse = await engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: {
@ -154,8 +190,7 @@ export function useAppMode() {
ortho: true,
animated: !isReducedMotion(),
},
}
)
})
setDefaultPlanesHidden(engineCommandManager, defaultPlanes, true)
const sketchUuid = uuidv4()
const proms: any[] = []
@ -178,8 +213,7 @@ export function useAppMode() {
},
})
)
const res = await Promise.all(proms)
console.log('res', res)
await Promise.all(proms)
setGuiMode({
mode: 'sketch',
sketchMode: 'sketchEdit',
@ -202,14 +236,16 @@ async function createPlane(
x_axis,
y_axis,
color,
hidden,
}: {
x_axis: Models['Point3d_type']
y_axis: Models['Point3d_type']
color: Models['Color_type']
hidden: boolean
}
) {
const planeId = uuidv4()
await engineCommandManager?.sendSceneCommand({
await engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req',
cmd: {
type: 'make_plane',
@ -218,10 +254,11 @@ async function createPlane(
x_axis,
y_axis,
clobber: false,
hide: hidden,
},
cmd_id: planeId,
})
await engineCommandManager?.sendSceneCommand({
await engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req',
cmd: {
type: 'plane_set_color',
@ -233,13 +270,22 @@ async function createPlane(
return planeId
}
function setDefaultPlanesHidden(
engineCommandManager: EngineCommandManager | undefined,
export function setDefaultPlanesHidden(
engineCommandManager: EngineCommandManager,
defaultPlanes: DefaultPlanes,
hidden: boolean
) {
Object.values(defaultPlanes).forEach((planeId) => {
engineCommandManager?.sendSceneCommand({
hidePlane(engineCommandManager, planeId, hidden)
})
}
function hidePlane(
engineCommandManager: EngineCommandManager,
planeId: string,
hidden: boolean
) {
engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: {
@ -248,37 +294,49 @@ function setDefaultPlanesHidden(
hidden: hidden,
},
})
})
}
async function initDefaultPlanes(
engineCommandManager: EngineCommandManager
): Promise<DefaultPlanes> {
export async function initDefaultPlanes(
engineCommandManager: EngineCommandManager,
hidePlanes?: boolean
): Promise<DefaultPlanes | null> {
if (!engineCommandManager.engineConnection?.isReady()) {
return null
}
const xy = await createPlane(engineCommandManager, {
x_axis: { x: 1, y: 0, z: 0 },
y_axis: { x: 0, y: 1, z: 0 },
color: { r: 0.7, g: 0.28, b: 0.28, a: 0.4 },
hidden: hidePlanes ? true : false,
})
// TODO re-enable
// const yz = createPlane(engineCommandManager, {
// x_axis: { x: 0, y: 1, z: 0 },
// y_axis: { x: 0, y: 0, z: 1 },
// color: { r: 0.28, g: 0.7, b: 0.28, a: 0.4 },
// })
// const xz = createPlane(engineCommandManager, {
// x_axis: { x: 1, y: 0, z: 0 },
// y_axis: { x: 0, y: 0, z: 1 },
// color: { r: 0.28, g: 0.28, b: 0.7, a: 0.4 },
// })
return { xy }
if (hidePlanes) {
hidePlane(engineCommandManager, xy, true)
}
const yz = await createPlane(engineCommandManager, {
x_axis: { x: 0, y: 1, z: 0 },
y_axis: { x: 0, y: 0, z: 1 },
color: { r: 0.28, g: 0.7, b: 0.28, a: 0.4 },
hidden: hidePlanes ? true : false,
})
if (hidePlanes) {
hidePlane(engineCommandManager, yz, true)
}
const xz = await createPlane(engineCommandManager, {
x_axis: { x: 1, y: 0, z: 0 },
y_axis: { x: 0, y: 0, z: 1 },
color: { r: 0.28, g: 0.28, b: 0.7, a: 0.4 },
hidden: hidePlanes ? true : false,
})
return { xy, yz, xz }
}
function isCursorInSketchCommandRange(
artifactMap: ArtifactMap,
selectionRanges: Selections
): string | false {
const overlapingEntries = Object.entries(artifactMap || {}).filter(
([id, artifact]) =>
const overlapingEntries: [string, ArtifactMap[string]][] = Object.entries(
artifactMap
).filter(([id, artifact]: [string, ArtifactMap[string]]) =>
selectionRanges.codeBasedSelections.some(
(selection) =>
Array.isArray(selection?.range) &&

View File

@ -1,14 +1,9 @@
import { useEffect } from 'react'
import { useStore } from 'useStore'
import { engineCommandManager } from '../lang/std/engineConnection'
export function useEngineConnectionSubscriptions() {
const {
engineCommandManager,
setCursor2,
setHighlightRange,
highlightRange,
} = useStore((s) => ({
engineCommandManager: s.engineCommandManager,
const { setCursor2, setHighlightRange, highlightRange } = useStore((s) => ({
setCursor2: s.setCursor2,
setHighlightRange: s.setHighlightRange,
highlightRange: s.highlightRange,

View File

@ -1,53 +1,92 @@
import { useLayoutEffect } from 'react'
import { _executor } from '../lang/executor'
import { useLayoutEffect, useEffect, useRef } from 'react'
import { _executor } from '../lang/wasm'
import { useStore } from '../useStore'
import { EngineCommandManager } from '../lang/std/engineConnection'
import { engineCommandManager } from '../lang/std/engineConnection'
import { deferExecution } from 'lib/utils'
export function useSetupEngineManager(
streamRef: React.RefObject<HTMLDivElement>,
token?: string
) {
const {
setEngineCommandManager,
setMediaStream,
setIsStreamReady,
setStreamDimensions,
streamDimensions,
executeCode,
} = useStore((s) => ({
setEngineCommandManager: s.setEngineCommandManager,
setMediaStream: s.setMediaStream,
setIsStreamReady: s.setIsStreamReady,
setStreamDimensions: s.setStreamDimensions,
streamDimensions: s.streamDimensions,
executeCode: s.executeCode,
}))
const streamWidth = streamRef?.current?.offsetWidth
const streamHeight = streamRef?.current?.offsetHeight
const width = streamWidth ? streamWidth : 0
const quadWidth = Math.round(width / 4) * 4
const height = streamHeight ? streamHeight : 0
const quadHeight = Math.round(height / 4) * 4
const hasSetNonZeroDimensions = useRef<boolean>(false)
useEffect(() => {
executeCode()
}, [])
useLayoutEffect(() => {
setStreamDimensions({
streamWidth: quadWidth,
streamHeight: quadHeight,
})
if (!width || !height) return
const eng = new EngineCommandManager({
// Load the engine command manager once with the initial width and height,
// then we do not want to reload it.
const { width: quadWidth, height: quadHeight } = getDimensions(
streamWidth,
streamHeight
)
if (!hasSetNonZeroDimensions.current && quadHeight && quadWidth) {
engineCommandManager.start({
setMediaStream,
setIsStreamReady,
width: quadWidth,
height: quadHeight,
executeCode,
token,
})
setEngineCommandManager(eng)
eng.waitForReady.then(() => {
executeCode()
setStreamDimensions({
streamWidth: quadWidth,
streamHeight: quadHeight,
})
hasSetNonZeroDimensions.current = true
}
}, [streamRef?.current?.offsetWidth, streamRef?.current?.offsetHeight])
useEffect(() => {
const handleResize = deferExecution(() => {
const { width, height } = getDimensions(
streamRef?.current?.offsetWidth,
streamRef?.current?.offsetHeight
)
if (
streamDimensions.streamWidth !== width ||
streamDimensions.streamHeight !== height
) {
engineCommandManager.handleResize({
streamWidth: width,
streamHeight: height,
})
setStreamDimensions({
streamWidth: width,
streamHeight: height,
})
}
}, 500)
window.addEventListener('resize', handleResize)
return () => {
eng?.tearDown()
window.removeEventListener('resize', handleResize)
}
}, [quadWidth, quadHeight])
}, [])
}
function getDimensions(streamWidth?: number, streamHeight?: number) {
const width = streamWidth ? streamWidth : 0
const quadWidth = Math.round(width / 4) * 4
const height = streamHeight ? streamHeight : 0
const quadHeight = Math.round(height / 4) * 4
return { width: quadWidth, height: quadHeight }
}

View File

@ -48,7 +48,7 @@ export function useConvertToVariable() {
updateAst(_modifiedAst, true)
} catch (e) {
console.log('e', e)
console.log('error', e)
}
}

View File

@ -1,12 +1,11 @@
import { parser_wasm } from './abstractSyntaxTree'
import { KCLError } from './errors'
import { initPromise } from './rust'
import { initPromise, parse } from './wasm'
beforeAll(() => initPromise)
describe('testing AST', () => {
test('5 + 6', () => {
const result = parser_wasm('5 +6')
const result = parse('5 +6')
delete (result as any).nonCodeMeta
expect(result.body).toEqual([
{
@ -37,7 +36,7 @@ describe('testing AST', () => {
])
})
test('const myVar = 5', () => {
const { body } = parser_wasm('const myVar = 5')
const { body } = parse('const myVar = 5')
expect(body).toEqual([
{
type: 'VariableDeclaration',
@ -71,7 +70,7 @@ describe('testing AST', () => {
const code = `const myVar = 5
const newVar = myVar + 1
`
const { body } = parser_wasm(code)
const { body } = parse(code)
expect(body).toEqual([
{
type: 'VariableDeclaration',
@ -143,7 +142,7 @@ const newVar = myVar + 1
describe('testing function declaration', () => {
test('fn funcN = () => {}', () => {
const { body } = parser_wasm('fn funcN = () => {}')
const { body } = parse('fn funcN = () => {}')
delete (body[0] as any).declarations[0].init.body.nonCodeMeta
expect(body).toEqual([
{
@ -179,7 +178,7 @@ describe('testing function declaration', () => {
])
})
test('fn funcN = (a, b) => {return a + b}', () => {
const { body } = parser_wasm(
const { body } = parse(
['fn funcN = (a, b) => {', ' return a + b', '}'].join('\n')
)
delete (body[0] as any).declarations[0].init.body.nonCodeMeta
@ -256,7 +255,7 @@ describe('testing function declaration', () => {
test('call expression assignment', () => {
const code = `fn funcN = (a, b) => { return a + b }
const myVar = funcN(1, 2)`
const { body } = parser_wasm(code)
const { body } = parse(code)
delete (body[0] as any).declarations[0].init.body.nonCodeMeta
expect(body).toEqual([
{
@ -388,7 +387,7 @@ describe('testing pipe operator special', () => {
|> lineTo([1, 1], %)
|> rx(45, %)
`
const { body } = parser_wasm(code)
const { body } = parse(code)
delete (body[0] as any).declarations[0].init.nonCodeMeta
expect(body).toEqual([
{
@ -404,7 +403,7 @@ describe('testing pipe operator special', () => {
id: { type: 'Identifier', start: 6, end: 14, name: 'mySketch' },
init: {
type: 'PipeExpression',
start: 15,
start: 17,
end: 145,
body: [
{
@ -624,7 +623,7 @@ describe('testing pipe operator special', () => {
})
test('pipe operator with binary expression', () => {
let code = `const myVar = 5 + 6 |> myFunc(45, %)`
const { body } = parser_wasm(code)
const { body } = parse(code)
delete (body as any)[0].declarations[0].init.nonCodeMeta
expect(body).toEqual([
{
@ -645,7 +644,7 @@ describe('testing pipe operator special', () => {
},
init: {
type: 'PipeExpression',
start: 12,
start: 14,
end: 36,
body: [
{
@ -706,7 +705,7 @@ describe('testing pipe operator special', () => {
})
test('array expression', () => {
let code = `const yo = [1, '2', three, 4 + 5]`
const { body } = parser_wasm(code)
const { body } = parse(code)
expect(body).toEqual([
{
type: 'VariableDeclaration',
@ -781,7 +780,7 @@ describe('testing pipe operator special', () => {
'const three = 3',
"const yo = {aStr: 'str', anum: 2, identifier: three, binExp: 4 + 5}",
].join('\n')
const { body } = parser_wasm(code)
const { body } = parse(code)
expect(body).toEqual([
{
type: 'VariableDeclaration',
@ -925,7 +924,7 @@ describe('testing pipe operator special', () => {
const code = `const yo = {key: {
key2: 'value'
}}`
const { body } = parser_wasm(code)
const { body } = parse(code)
expect(body).toEqual([
{
type: 'VariableDeclaration',
@ -993,7 +992,7 @@ describe('testing pipe operator special', () => {
})
test('object expression with array ast', () => {
const code = `const yo = {key: [1, '2']}`
const { body } = parser_wasm(code)
const { body } = parse(code)
expect(body).toEqual([
{
type: 'VariableDeclaration',
@ -1057,7 +1056,7 @@ describe('testing pipe operator special', () => {
})
test('object memberExpression simple', () => {
const code = `const prop = yo.one.two`
const { body } = parser_wasm(code)
const { body } = parse(code)
expect(body).toEqual([
{
type: 'VariableDeclaration',
@ -1112,7 +1111,7 @@ describe('testing pipe operator special', () => {
})
test('object memberExpression with square braces', () => {
const code = `const prop = yo.one["two"]`
const { body } = parser_wasm(code)
const { body } = parse(code)
expect(body).toEqual([
{
type: 'VariableDeclaration',
@ -1168,7 +1167,7 @@ describe('testing pipe operator special', () => {
})
test('object memberExpression with two square braces literal and identifier', () => {
const code = `const prop = yo["one"][two]`
const { body } = parser_wasm(code)
const { body } = parse(code)
expect(body).toEqual([
{
type: 'VariableDeclaration',
@ -1227,7 +1226,7 @@ describe('testing pipe operator special', () => {
describe('nests binary expressions correctly', () => {
it('works with the simple case', () => {
const code = `const yo = 1 + 2`
const { body } = parser_wasm(code)
const { body } = parse(code)
expect(body[0]).toEqual({
type: 'VariableDeclaration',
start: 0,
@ -1271,7 +1270,7 @@ describe('nests binary expressions correctly', () => {
it('should nest according to precedence with multiply first', () => {
// should be binExp { binExp { lit-1 * lit-2 } + lit}
const code = `const yo = 1 * 2 + 3`
const { body } = parser_wasm(code)
const { body } = parse(code)
expect(body[0]).toEqual({
type: 'VariableDeclaration',
start: 0,
@ -1328,7 +1327,7 @@ describe('nests binary expressions correctly', () => {
it('should nest according to precedence with sum first', () => {
// should be binExp { lit-1 + binExp { lit-2 * lit-3 } }
const code = `const yo = 1 + 2 * 3`
const { body } = parser_wasm(code)
const { body } = parse(code)
expect(body[0]).toEqual({
type: 'VariableDeclaration',
start: 0,
@ -1384,7 +1383,7 @@ describe('nests binary expressions correctly', () => {
})
it('should nest properly with two opperators of equal precedence', () => {
const code = `const yo = 1 + 2 - 3`
const { body } = parser_wasm(code)
const { body } = parse(code)
expect((body[0] as any).declarations[0].init).toEqual({
type: 'BinaryExpression',
start: 11,
@ -1421,7 +1420,7 @@ describe('nests binary expressions correctly', () => {
})
it('should nest properly with two opperators of equal (but higher) precedence', () => {
const code = `const yo = 1 * 2 / 3`
const { body } = parser_wasm(code)
const { body } = parse(code)
expect((body[0] as any).declarations[0].init).toEqual({
type: 'BinaryExpression',
start: 11,
@ -1458,7 +1457,7 @@ describe('nests binary expressions correctly', () => {
})
it('should nest properly with longer example', () => {
const code = `const yo = 1 + 2 * (3 - 4) / 5 + 6`
const { body } = parser_wasm(code)
const { body } = parse(code)
const init = (body[0] as any).declarations[0].init
expect(init).toEqual({
type: 'BinaryExpression',
@ -1520,14 +1519,12 @@ const key = 'c'`
value: 'this is a comment',
},
}
const { nonCodeMeta } = parser_wasm(code)
const { nonCodeMeta } = parse(code)
expect(nonCodeMeta.nonCodeNodes[0]).toEqual(nonCodeMetaInstance)
// extra whitespace won't change it's position (0) or value (NB the start end would have changed though)
const codeWithExtraStartWhitespace = '\n\n\n' + code
const { nonCodeMeta: nonCodeMeta2 } = parser_wasm(
codeWithExtraStartWhitespace
)
const { nonCodeMeta: nonCodeMeta2 } = parse(codeWithExtraStartWhitespace)
expect(nonCodeMeta2.nonCodeNodes[0].value).toStrictEqual(
nonCodeMetaInstance.value
)
@ -1545,7 +1542,7 @@ const key = 'c'`
|> close(%)
`
const { body } = parser_wasm(code)
const { body } = parse(code)
const indexOfSecondLineToExpression = 2
const sketchNonCodeMeta = (body as any)[0].declarations[0].init.nonCodeMeta
.nonCodeNodes
@ -1569,7 +1566,7 @@ const key = 'c'`
' |> rx(90, %)',
].join('\n')
const { body } = parser_wasm(code)
const { body } = parse(code)
const sketchNonCodeMeta = (body[0] as any).declarations[0].init.nonCodeMeta
.nonCodeNodes
expect(sketchNonCodeMeta[3]).toEqual({
@ -1587,7 +1584,7 @@ const key = 'c'`
describe('test UnaryExpression', () => {
it('should parse a unary expression in simple var dec situation', () => {
const code = `const myVar = -min(4, 100)`
const { body } = parser_wasm(code)
const { body } = parse(code)
const myVarInit = (body?.[0] as any).declarations[0]?.init
expect(myVarInit).toEqual({
type: 'UnaryExpression',
@ -1613,7 +1610,7 @@ describe('test UnaryExpression', () => {
describe('testing nested call expressions', () => {
it('callExp in a binExp in a callExp', () => {
const code = 'const myVar = min(100, 1 + legLen(5, 3))'
const { body } = parser_wasm(code)
const { body } = parse(code)
const myVarInit = (body?.[0] as any).declarations[0]?.init
expect(myVarInit).toEqual({
type: 'CallExpression',
@ -1651,7 +1648,7 @@ describe('testing nested call expressions', () => {
describe('should recognise callExpresions in binaryExpressions', () => {
const code = "xLineTo(segEndX('seg02', %) + 1, %)"
it('should recognise the callExp', () => {
const { body } = parser_wasm(code)
const { body } = parse(code)
const callExpArgs = (body?.[0] as any).expression?.arguments
expect(callExpArgs).toEqual([
{
@ -1690,8 +1687,7 @@ describe('parsing errors', () => {
let _theError
try {
const result = expect(parser_wasm(code))
console.log('result', result)
const result = expect(parse(code))
} catch (e) {
_theError = e
}

View File

@ -1,48 +0,0 @@
import { Program } from './abstractSyntaxTreeTypes'
import { parse_js } from '../wasm-lib/pkg/wasm_lib'
import { initPromise } from './rust'
import { Token } from './tokeniser'
import { KCLError } from './errors'
import { KclError as RustKclError } from '../wasm-lib/kcl/bindings/KclError'
export const rangeTypeFix = (ranges: number[][]): [number, number][] =>
ranges.map(([start, end]) => [start, end])
export const parser_wasm = (code: string): Program => {
try {
const program: Program = parse_js(code)
return program
} catch (e: any) {
const parsed: RustKclError = JSON.parse(e.toString())
const kclError = new KCLError(
parsed.kind,
parsed.msg,
rangeTypeFix(parsed.sourceRanges)
)
console.log(kclError)
throw kclError
}
}
export async function asyncParser(code: string): Promise<Program> {
await initPromise
try {
const program: Program = parse_js(code)
return program
} catch (e: any) {
const parsed: RustKclError = JSON.parse(e.toString())
const kclError = new KCLError(
parsed.kind,
parsed.msg,
rangeTypeFix(parsed.sourceRanges)
)
console.log(kclError)
throw kclError
}
}
export function rangeOfToken(token: Token | undefined): [number, number][] {
return token === undefined ? [] : [[token.start, token.end]]
}

View File

@ -1,37 +0,0 @@
export type { Program } from '../wasm-lib/kcl/bindings/Program'
export type { Value } from '../wasm-lib/kcl/bindings/Value'
export type { ObjectExpression } from '../wasm-lib/kcl/bindings/ObjectExpression'
export type { MemberExpression } from '../wasm-lib/kcl/bindings/MemberExpression'
export type { PipeExpression } from '../wasm-lib/kcl/bindings/PipeExpression'
export type { VariableDeclaration } from '../wasm-lib/kcl/bindings/VariableDeclaration'
export type { PipeSubstitution } from '../wasm-lib/kcl/bindings/PipeSubstitution'
export type { Identifier } from '../wasm-lib/kcl/bindings/Identifier'
export type { UnaryExpression } from '../wasm-lib/kcl/bindings/UnaryExpression'
export type { BinaryExpression } from '../wasm-lib/kcl/bindings/BinaryExpression'
export type { ReturnStatement } from '../wasm-lib/kcl/bindings/ReturnStatement'
export type { ExpressionStatement } from '../wasm-lib/kcl/bindings/ExpressionStatement'
export type { CallExpression } from '../wasm-lib/kcl/bindings/CallExpression'
export type { VariableDeclarator } from '../wasm-lib/kcl/bindings/VariableDeclarator'
export type { BinaryPart } from '../wasm-lib/kcl/bindings/BinaryPart'
export type { Literal } from '../wasm-lib/kcl/bindings/Literal'
export type { ArrayExpression } from '../wasm-lib/kcl/bindings/ArrayExpression'
export type SyntaxType =
| 'Program'
| 'ExpressionStatement'
| 'BinaryExpression'
| 'CallExpression'
| 'Identifier'
| 'ReturnStatement'
| 'VariableDeclaration'
| 'VariableDeclarator'
| 'MemberExpression'
| 'ArrayExpression'
| 'ObjectExpression'
| 'ObjectProperty'
| 'FunctionExpression'
| 'PipeExpression'
| 'PipeSubstitution'
| 'Literal'
| 'NonCodeNode'
| 'UnaryExpression'

View File

@ -1,5 +1,4 @@
import { parser_wasm } from './abstractSyntaxTree'
import { initPromise } from './rust'
import { parse, initPromise } from './wasm'
import { enginelessExecutor } from '../lib/testHelpers'
beforeAll(() => initPromise)
@ -8,12 +7,13 @@ describe('testing artifacts', () => {
// Enable rotations #152
test('sketch artifacts', async () => {
const code = `
const mySketch001 = startSketchAt([0, 0])
const mySketch001 = startSketchOn('XY')
|> startProfileAt([0, 0], %)
|> lineTo([-1.59, -1.54], %)
|> lineTo([0.46, -5.82], %)
// |> rx(45, %)
show(mySketch001)`
const programMemory = await enginelessExecutor(parser_wasm(code))
const programMemory = await enginelessExecutor(parse(code))
// @ts-ignore
const shown = programMemory?.return?.map(
// @ts-ignore
@ -28,7 +28,7 @@ show(mySketch001)`
name: '',
__geoMeta: {
id: expect.any(String),
sourceRange: [21, 42],
sourceRange: [46, 71],
},
},
value: [
@ -38,7 +38,7 @@ show(mySketch001)`
to: [-1.59, -1.54],
from: [0, 0],
__geoMeta: {
sourceRange: [48, 73],
sourceRange: [77, 102],
id: expect.any(String),
},
},
@ -48,7 +48,7 @@ show(mySketch001)`
from: [-1.59, -1.54],
name: '',
__geoMeta: {
sourceRange: [79, 103],
sourceRange: [108, 132],
id: expect.any(String),
},
},
@ -56,20 +56,22 @@ show(mySketch001)`
position: [0, 0, 0],
rotation: [0, 0, 0, 1],
id: expect.any(String),
__meta: [{ sourceRange: [21, 42] }],
planeId: expect.any(String),
__meta: [{ sourceRange: [46, 71] }],
},
])
})
test('extrude artifacts', async () => {
// Enable rotations #152
const code = `
const mySketch001 = startSketchAt([0, 0])
const mySketch001 = startSketchOn('XY')
|> startProfileAt([0, 0], %)
|> lineTo([-1.59, -1.54], %)
|> lineTo([0.46, -5.82], %)
// |> rx(45, %)
|> extrude(2, %)
show(mySketch001)`
const programMemory = await enginelessExecutor(parser_wasm(code))
const programMemory = await enginelessExecutor(parse(code))
// @ts-ignore
const shown = programMemory?.return?.map(
// @ts-ignore
@ -83,7 +85,7 @@ show(mySketch001)`
height: 2,
position: [0, 0, 0],
rotation: [0, 0, 0, 1],
__meta: [{ sourceRange: [21, 42] }],
__meta: [{ sourceRange: [46, 71] }],
},
])
})
@ -91,7 +93,8 @@ show(mySketch001)`
// Enable rotations #152
// TODO #153 in order for getExtrudeWallTransform to work we need to query the engine for the location of a face.
const code = `
const sk1 = startSketchAt([0, 0])
const sk1 = startSketchOn('XY')
|> startProfileAt([0, 0], %)
|> lineTo([-2.5, 0], %)
|> lineTo({ to: [0, 10], tag: "p" }, %)
|> lineTo([2.5, 0], %)
@ -100,7 +103,8 @@ const sk1 = startSketchAt([0, 0])
// |> ry(5, %)
const theExtrude = extrude(2, sk1)
// const theTransf = getExtrudeWallTransform('p', theExtrude)
const sk2 = startSketchAt([0, 0])
const sk2 = startSketchOn('XY')
|> startProfileAt([0, 0], %)
|> lineTo([-2.5, 0], %)
|> lineTo({ to: [0, 3], tag: "p" }, %)
|> lineTo([2.5, 0], %)
@ -109,7 +113,7 @@ const sk2 = startSketchAt([0, 0])
show(theExtrude, sk2)`
const programMemory = await enginelessExecutor(parser_wasm(code))
const programMemory = await enginelessExecutor(parse(code))
// @ts-ignore
const geos = programMemory?.return?.map(
// @ts-ignore
@ -123,7 +127,7 @@ show(theExtrude, sk2)`
height: 2,
position: [0, 0, 0],
rotation: [0, 0, 0, 1],
__meta: [{ sourceRange: [13, 34] }],
__meta: [{ sourceRange: [38, 63] }],
},
{
type: 'ExtrudeGroup',
@ -132,7 +136,7 @@ show(theExtrude, sk2)`
height: 2,
position: [0, 0, 0],
rotation: [0, 0, 0, 1],
__meta: [{ sourceRange: [302, 323] }],
__meta: [{ sourceRange: [356, 381] }],
},
])
})

View File

@ -1,10 +1,7 @@
import fs from 'node:fs'
import { parser_wasm } from './abstractSyntaxTree'
import { ProgramMemory, SketchGroup } from './executor'
import { initPromise } from './rust'
import { parse, ProgramMemory, SketchGroup, initPromise } from './wasm'
import { enginelessExecutor } from '../lib/testHelpers'
import { vi } from 'vitest'
import { KCLError } from './errors'
beforeAll(() => initPromise)
@ -44,7 +41,8 @@ const newVar = myVar + 1`
expect(root.magicNum.value).toBe(69)
})
it('sketch declaration', async () => {
let code = `const mySketch = startSketchAt([0,0])
let code = `const mySketch = startSketchOn('XY')
|> startProfileAt([0,0], %)
|> lineTo({to: [0,2], tag: "myPath"}, %)
|> lineTo([2,3], %)
|> lineTo({ to: [5,-1], tag: "rightPath" }, %)
@ -60,7 +58,7 @@ show(mySketch)
to: [0, 2],
from: [0, 0],
__geoMeta: {
sourceRange: [43, 80],
sourceRange: [72, 109],
id: expect.any(String),
},
name: 'myPath',
@ -71,7 +69,7 @@ show(mySketch)
from: [0, 2],
name: '',
__geoMeta: {
sourceRange: [86, 102],
sourceRange: [115, 131],
id: expect.any(String),
},
},
@ -80,7 +78,7 @@ show(mySketch)
to: [5, -1],
from: [2, 3],
__geoMeta: {
sourceRange: [108, 151],
sourceRange: [137, 180],
id: expect.any(String),
},
name: 'rightPath',
@ -90,8 +88,8 @@ show(mySketch)
expect(_return).toEqual([
{
type: 'Identifier',
start: 174,
end: 182,
start: 203,
end: 211,
name: 'mySketch',
},
])
@ -135,7 +133,8 @@ show(mySketch)
it('execute pipe sketch into call expression', async () => {
// Enable rotations #152
const code = [
'const mySk1 = startSketchAt([0,0])',
"const mySk1 = startSketchOn('XY')",
' |> startProfileAt([0,0], %)',
' |> lineTo([1,1], %)',
' |> lineTo({to: [0, 1], tag: "myPath"}, %)',
' |> lineTo([1,1], %)',
@ -150,7 +149,7 @@ show(mySketch)
name: '',
__geoMeta: {
id: expect.any(String),
sourceRange: [14, 34],
sourceRange: [39, 63],
},
},
value: [
@ -160,7 +159,7 @@ show(mySketch)
from: [0, 0],
name: '',
__geoMeta: {
sourceRange: [40, 56],
sourceRange: [69, 85],
id: expect.any(String),
},
},
@ -169,7 +168,7 @@ show(mySketch)
to: [0, 1],
from: [1, 1],
__geoMeta: {
sourceRange: [62, 100],
sourceRange: [91, 129],
id: expect.any(String),
},
name: 'myPath',
@ -180,7 +179,7 @@ show(mySketch)
from: [0, 1],
name: '',
__geoMeta: {
sourceRange: [106, 122],
sourceRange: [135, 151],
id: expect.any(String),
},
},
@ -188,7 +187,8 @@ show(mySketch)
position: [0, 0, 0],
rotation: [0, 0, 0, 1],
id: expect.any(String),
__meta: [{ sourceRange: [14, 34] }],
planeId: expect.any(String),
__meta: [{ sourceRange: [39, 63] }],
})
})
it('execute array expression', async () => {
@ -332,7 +332,8 @@ describe('testing math operators', () => {
})
it('with unaryExpression in ArrayExpression in CallExpression, checking nothing funny happens when used in a sketch', async () => {
const code = [
'const part001 = startSketchAt([0, 0])',
"const part001 = startSketchOn('XY')",
' |> startProfileAt([0, 0], %)',
'|> line([-2.21, -legLen(5, min(3, 999))], %)',
].join('\n')
const { root } = await exe(code)
@ -344,7 +345,8 @@ describe('testing math operators', () => {
it('test that % substitution feeds down CallExp->ArrExp->UnaryExp->CallExp', async () => {
const code = [
`const myVar = 3`,
`const part001 = startSketchAt([0, 0])`,
`const part001 = startSketchOn('XY')`,
` |> startProfileAt([0, 0], %)`,
` |> line({ to: [3, 4], tag: 'seg01' }, %)`,
` |> line([`,
` min(segLen('seg01', %), myVar),`,
@ -380,7 +382,8 @@ describe('testing math operators', () => {
describe('Testing Errors', () => {
it('should throw an error when a variable is not defined', async () => {
const code = `const myVar = 5
const theExtrude = startSketchAt([0, 0])
const theExtrude = startSketchOn('XY')
|> startProfileAt([0, 0], %)
|> line([-2.4, 5], %)
|> line([-0.76], myVarZ, %)
|> line([5,5], %)
@ -391,7 +394,7 @@ show(theExtrude)`
new KCLError(
'undefined_value',
'memory item key `myVarZ` is not defined',
[[100, 106]]
[[129, 135]]
)
)
})
@ -403,7 +406,7 @@ async function exe(
code: string,
programMemory: ProgramMemory = { root: {}, return: null }
) {
const ast = parser_wasm(code)
const ast = parse(code)
const result = await enginelessExecutor(ast, programMemory)
return result

View File

@ -1,81 +0,0 @@
import { Program } from './abstractSyntaxTreeTypes'
import {
EngineCommandManager,
ArtifactMap,
SourceRangeMap,
} from './std/engineConnection'
import { ProgramReturn } from '../wasm-lib/kcl/bindings/ProgramReturn'
import { MemoryItem } from '../wasm-lib/kcl/bindings/MemoryItem'
import { execute_wasm } from '../wasm-lib/pkg/wasm_lib'
import { KCLError } from './errors'
import { KclError as RustKclError } from '../wasm-lib/kcl/bindings/KclError'
import { rangeTypeFix } from './abstractSyntaxTree'
export type { SourceRange } from '../wasm-lib/kcl/bindings/SourceRange'
export type { Position } from '../wasm-lib/kcl/bindings/Position'
export type { Rotation } from '../wasm-lib/kcl/bindings/Rotation'
export type { Path } from '../wasm-lib/kcl/bindings/Path'
export type { SketchGroup } from '../wasm-lib/kcl/bindings/SketchGroup'
export type { MemoryItem } from '../wasm-lib/kcl/bindings/MemoryItem'
export type { ExtrudeSurface } from '../wasm-lib/kcl/bindings/ExtrudeSurface'
export type PathToNode = [string | number, string][]
interface Memory {
[key: string]: MemoryItem
}
export interface ProgramMemory {
root: Memory
return: ProgramReturn | null
}
export const executor = async (
node: Program,
programMemory: ProgramMemory = { root: {}, return: null },
engineCommandManager: EngineCommandManager,
// work around while the gemotry is still be stored on the frontend
// will be removed when the stream UI is added.
tempMapCallback: (a: {
artifactMap: ArtifactMap
sourceRangeMap: SourceRangeMap
}) => void = () => {}
): Promise<ProgramMemory> => {
engineCommandManager.startNewSession()
const _programMemory = await _executor(
node,
programMemory,
engineCommandManager
)
const { artifactMap, sourceRangeMap } =
await engineCommandManager.waitForAllCommands(node, _programMemory)
tempMapCallback({ artifactMap, sourceRangeMap })
engineCommandManager.endSession()
return _programMemory
}
export const _executor = async (
node: Program,
programMemory: ProgramMemory = { root: {}, return: null },
engineCommandManager: EngineCommandManager
): Promise<ProgramMemory> => {
try {
const memory: ProgramMemory = await execute_wasm(
JSON.stringify(node),
JSON.stringify(programMemory),
engineCommandManager
)
return memory
} catch (e: any) {
const parsed: RustKclError = JSON.parse(e.toString())
const kclError = new KCLError(
parsed.kind,
parsed.msg,
rangeTypeFix(parsed.sourceRanges)
)
console.log(kclError)
throw kclError
}
}

View File

@ -1,7 +1,5 @@
import { getNodePathFromSourceRange, getNodeFromPath } from './queryAst'
import { parser_wasm } from './abstractSyntaxTree'
import { initPromise } from './rust'
import { Identifier } from './abstractSyntaxTreeTypes'
import { Identifier, parse, initPromise } from './wasm'
beforeAll(() => initPromise)
@ -21,7 +19,7 @@ const sk3 = startSketchAt([0, 0])
lineToSubstringIndex + subStr.length,
]
const ast = parser_wasm(code)
const ast = parse(code)
const nodePath = getNodePathFromSourceRange(ast, sourceRange)
const { node } = getNodeFromPath<any>(ast, nodePath)
@ -46,7 +44,7 @@ const b1 = cube([0,0], 10)`
subStrIndex + 'pos'.length,
]
const ast = parser_wasm(code)
const ast = parse(code)
const nodePath = getNodePathFromSourceRange(ast, sourceRange)
const node = getNodeFromPath<Identifier>(ast, nodePath).node
@ -80,7 +78,7 @@ const b1 = cube([0,0], 10)`
subStrIndex + 'scale'.length,
]
const ast = parser_wasm(code)
const ast = parse(code)
const nodePath = getNodePathFromSourceRange(ast, sourceRange)
const node = getNodeFromPath<Identifier>(ast, nodePath).node
expect(nodePath).toEqual([

View File

@ -1,4 +1,4 @@
import { parser_wasm } from './abstractSyntaxTree'
import { parse, recast, initPromise } from './wasm'
import {
createLiteral,
createIdentifier,
@ -13,8 +13,6 @@ import {
giveSketchFnCallTag,
moveValueIntoNewVariable,
} from './modifyAst'
import { recast } from './recast'
import { initPromise } from './rust'
import { enginelessExecutor } from '../lib/testHelpers'
beforeAll(() => initPromise)
@ -111,8 +109,8 @@ describe('Testing addSketchTo', () => {
'yz'
)
const str = recast(result.modifiedAst)
expect(str).toBe(`const part001 = startSketchAt('default')
|> ry(90, %)
expect(str).toBe(`const part001 = startSketchOn('YZ')
|> startProfileAt('default', %)
|> line('default', %)
show(part001)
`)
@ -126,7 +124,7 @@ function giveSketchFnCallTagTestHelper(
// giveSketchFnCallTag inputs and outputs an ast, which is very verbose for testing
// this wrapper changes the input and output to code
// making it more of an integration test, but easier to read the test intention is the goal
const ast = parser_wasm(code)
const ast = parse(code)
const start = code.indexOf(searchStr)
const range: [number, number] = [start, start + searchStr.length]
const { modifiedAst, tag, isTagExisting } = giveSketchFnCallTag(ast, range)
@ -135,7 +133,8 @@ function giveSketchFnCallTagTestHelper(
}
describe('Testing giveSketchFnCallTag', () => {
const code = `const part001 = startSketchAt([0, 0])
const code = `const part001 = startSketchOn('XY')
|> startProfileAt([0, 0], %)
|> line([-2.57, -0.13], %)
|> line([0, 0.83], %)
|> line([0.82, 0.34], %)
@ -187,7 +186,8 @@ fn ghi = (x) => {
const abc = 3
const identifierGuy = 5
const yo = 5 + 6
const part001 = startSketchAt([-1.2, 4.83])
const part001 = startSketchOn('XY')
|> startProfileAt([-1.2, 4.83], %)
|> line([2.8, 0], %)
|> angledLine([100 + 100, 3.09], %)
|> angledLine([abc, 3.09], %)
@ -197,7 +197,7 @@ const part001 = startSketchAt([-1.2, 4.83])
const yo2 = hmm([identifierGuy + 5])
show(part001)`
it('should move a binary expression into a new variable', async () => {
const ast = parser_wasm(code)
const ast = parse(code)
const programMemory = await enginelessExecutor(ast)
const startIndex = code.indexOf('100 + 100') + 1
const { modifiedAst } = moveValueIntoNewVariable(
@ -211,7 +211,7 @@ show(part001)`
expect(newCode).toContain(`angledLine([newVar, 3.09], %)`)
})
it('should move a value into a new variable', async () => {
const ast = parser_wasm(code)
const ast = parse(code)
const programMemory = await enginelessExecutor(ast)
const startIndex = code.indexOf('2.8') + 1
const { modifiedAst } = moveValueIntoNewVariable(
@ -225,7 +225,7 @@ show(part001)`
expect(newCode).toContain(`line([newVar, 0], %)`)
})
it('should move a callExpression into a new variable', async () => {
const ast = parser_wasm(code)
const ast = parse(code)
const programMemory = await enginelessExecutor(ast)
const startIndex = code.indexOf('def(')
const { modifiedAst } = moveValueIntoNewVariable(
@ -239,7 +239,7 @@ show(part001)`
expect(newCode).toContain(`angledLine([newVar, 3.09], %)`)
})
it('should move a binary expression with call expression into a new variable', async () => {
const ast = parser_wasm(code)
const ast = parse(code)
const programMemory = await enginelessExecutor(ast)
const startIndex = code.indexOf('jkl(') + 1
const { modifiedAst } = moveValueIntoNewVariable(
@ -253,7 +253,7 @@ show(part001)`
expect(newCode).toContain(`angledLine([newVar, 3.09], %)`)
})
it('should move a identifier into a new variable', async () => {
const ast = parser_wasm(code)
const ast = parse(code)
const programMemory = await enginelessExecutor(ast)
const startIndex = code.indexOf('identifierGuy +') + 1
const { modifiedAst } = moveValueIntoNewVariable(

View File

@ -14,14 +14,15 @@ import {
ObjectExpression,
UnaryExpression,
BinaryExpression,
} from './abstractSyntaxTreeTypes'
PathToNode,
ProgramMemory,
} from './wasm'
import {
findAllPreviousVariables,
getNodeFromPath,
getNodePathFromSourceRange,
isNodeSafeToReplace,
} from './queryAst'
import { PathToNode, ProgramMemory } from './executor'
import {
addTagForSketchOnFace,
getFirstArg,
@ -31,21 +32,26 @@ import { isLiteralArrayOrStatic } from './std/sketchcombos'
export function addStartSketch(
node: Program,
axis: 'xy' | 'xz' | 'yz' | '-xy' | '-xz' | '-yz',
start: [number, number],
end: [number, number]
): { modifiedAst: Program; id: string; pathToNode: PathToNode } {
const _node = { ...node }
const _name = findUniqueName(node, 'part')
const startSketchAt = createCallExpression('startSketchAt', [
const startSketchOn = createCallExpressionStdLib('startSketchOn', [
createLiteral(axis.toUpperCase()),
])
const startProfileAt = createCallExpressionStdLib('startProfileAt', [
createArrayExpression([createLiteral(start[0]), createLiteral(start[1])]),
createPipeSubstitution(),
])
const initialLineTo = createCallExpression('line', [
createArrayExpression([createLiteral(end[0]), createLiteral(end[1])]),
createPipeSubstitution(),
])
const pipeBody = [startSketchAt, initialLineTo]
const pipeBody = [startSketchOn, startProfileAt, initialLineTo]
const variableDeclaration = createVariableDeclaration(
_name,
@ -78,11 +84,11 @@ export function addSketchTo(
const _node = { ...node }
const _name = name || findUniqueName(node, 'part')
const startSketchAt = createCallExpressionStdLib('startSketchAt', [
createLiteral('default'),
const startSketchOn = createCallExpressionStdLib('startSketchOn', [
createLiteral(axis.toUpperCase()),
])
const rotate = createCallExpression(axis === 'xz' ? 'rx' : 'ry', [
createLiteral(90),
const startProfileAt = createCallExpressionStdLib('startProfileAt', [
createLiteral('default'),
createPipeSubstitution(),
])
const initialLineTo = createCallExpressionStdLib('line', [
@ -90,10 +96,7 @@ export function addSketchTo(
createPipeSubstitution(),
])
const pipeBody =
axis !== 'xy'
? [startSketchAt, rotate, initialLineTo]
: [startSketchAt, initialLineTo]
const pipeBody = [startSketchOn, startProfileAt, initialLineTo]
const variableDeclaration = createVariableDeclaration(
_name,

View File

@ -1,11 +1,10 @@
import { parser_wasm } from './abstractSyntaxTree'
import { parse, recast, initPromise } from './wasm'
import {
findAllPreviousVariables,
isNodeSafeToReplace,
isTypeInValue,
getNodePathFromSourceRange,
} from './queryAst'
import { initPromise } from './rust'
import { enginelessExecutor } from '../lib/testHelpers'
import {
createArrayExpression,
@ -13,7 +12,6 @@ import {
createLiteral,
createPipeSubstitution,
} from './modifyAst'
import { recast } from './recast'
beforeAll(() => initPromise)
@ -28,7 +26,8 @@ const halfArmAngle = armAngle / 2
const arrExpShouldNotBeIncluded = [1, 2, 3]
const objExpShouldNotBeIncluded = { a: 1, b: 2, c: 3 }
const part001 = startSketchAt([0, 0])
const part001 = startSketchOn('XY')
|> startProfileAt([0, 0], %)
|> yLineTo(1, %)
|> xLine(3.84, %) // selection-range-7ish-before-this
@ -36,7 +35,7 @@ const variableBelowShouldNotBeIncluded = 3
show(part001)`
const rangeStart = code.indexOf('// selection-range-7ish-before-this') - 7
const ast = parser_wasm(code)
const ast = parse(code)
const programMemory = await enginelessExecutor(ast)
const { variables, bodyPath, insertIndex } = findAllPreviousVariables(
@ -59,7 +58,8 @@ show(part001)`
})
describe('testing argIsNotIdentifier', () => {
const code = `const part001 = startSketchAt([-1.2, 4.83])
const code = `const part001 = startSketchOn('XY')
|> startProfileAt([-1.2, 4.83], %)
|> line([2.8, 0], %)
|> angledLine([100 + 100, 3.09], %)
|> angledLine([abc, 3.09], %)
@ -70,7 +70,7 @@ const yo = 5 + 6
const yo2 = hmm([identifierGuy + 5])
show(part001)`
it('find a safe binaryExpression', () => {
const ast = parser_wasm(code)
const ast = parse(code)
const rangeStart = code.indexOf('100 + 100') + 2
const result = isNodeSafeToReplace(ast, [rangeStart, rangeStart])
expect(result.isSafe).toBe(true)
@ -84,7 +84,7 @@ show(part001)`
expect(outCode).toContain(`angledLine([replaceName, 3.09], %)`)
})
it('find a safe Identifier', () => {
const ast = parser_wasm(code)
const ast = parse(code)
const rangeStart = code.indexOf('abc')
const result = isNodeSafeToReplace(ast, [rangeStart, rangeStart])
expect(result.isSafe).toBe(true)
@ -92,7 +92,7 @@ show(part001)`
expect(code.slice(result.value.start, result.value.end)).toBe('abc')
})
it('find a safe CallExpression', () => {
const ast = parser_wasm(code)
const ast = parse(code)
const rangeStart = code.indexOf('def')
const result = isNodeSafeToReplace(ast, [rangeStart, rangeStart])
expect(result.isSafe).toBe(true)
@ -106,7 +106,7 @@ show(part001)`
expect(outCode).toContain(`angledLine([replaceName, 3.09], %)`)
})
it('find an UNsafe CallExpression, as it has a PipeSubstitution', () => {
const ast = parser_wasm(code)
const ast = parse(code)
const rangeStart = code.indexOf('ghi')
const range: [number, number] = [rangeStart, rangeStart]
const result = isNodeSafeToReplace(ast, range)
@ -115,7 +115,7 @@ show(part001)`
expect(code.slice(result.value.start, result.value.end)).toBe('ghi(%)')
})
it('find an UNsafe Identifier, as it is a callee', () => {
const ast = parser_wasm(code)
const ast = parse(code)
const rangeStart = code.indexOf('ine([2.8,')
const result = isNodeSafeToReplace(ast, [rangeStart, rangeStart])
expect(result.isSafe).toBe(false)
@ -125,7 +125,7 @@ show(part001)`
)
})
it("find a safe BinaryExpression that's assigned to a variable", () => {
const ast = parser_wasm(code)
const ast = parse(code)
const rangeStart = code.indexOf('5 + 6') + 1
const result = isNodeSafeToReplace(ast, [rangeStart, rangeStart])
expect(result.isSafe).toBe(true)
@ -139,7 +139,7 @@ show(part001)`
expect(outCode).toContain(`const yo = replaceName`)
})
it('find a safe BinaryExpression that has a CallExpression within', () => {
const ast = parser_wasm(code)
const ast = parse(code)
const rangeStart = code.indexOf('jkl') + 1
const result = isNodeSafeToReplace(ast, [rangeStart, rangeStart])
expect(result.isSafe).toBe(true)
@ -155,7 +155,7 @@ show(part001)`
expect(outCode).toContain(`angledLine([replaceName, 3.09], %)`)
})
it('find a safe BinaryExpression within a CallExpression', () => {
const ast = parser_wasm(code)
const ast = parse(code)
const rangeStart = code.indexOf('identifierGuy') + 1
const result = isNodeSafeToReplace(ast, [rangeStart, rangeStart])
expect(result.isSafe).toBe(true)
@ -196,14 +196,15 @@ show(part001)`
})
describe('testing getNodePathFromSourceRange', () => {
const code = `const part001 = startSketchAt([0.39, -0.05])
const code = `const part001 = startSketchOn('XY')
|> startProfileAt([0.39, -0.05], %)
|> line([0.94, 2.61], %)
|> line([-0.21, -1.4], %)
show(part001)`
it('finds the second line when cursor is put at the end', () => {
const searchLn = `line([0.94, 2.61], %)`
const sourceIndex = code.indexOf(searchLn) + searchLn.length
const ast = parser_wasm(code)
const ast = parse(code)
const result = getNodePathFromSourceRange(ast, [sourceIndex, sourceIndex])
expect(result).toEqual([
['body', ''],
@ -212,13 +213,13 @@ show(part001)`
[0, 'index'],
['init', ''],
['body', 'PipeExpression'],
[1, 'index'],
[2, 'index'],
])
})
it('finds the last line when cursor is put at the end', () => {
const searchLn = `line([-0.21, -1.4], %)`
const sourceIndex = code.indexOf(searchLn) + searchLn.length
const ast = parser_wasm(code)
const ast = parse(code)
const result = getNodePathFromSourceRange(ast, [sourceIndex, sourceIndex])
const expected = [
['body', ''],
@ -227,7 +228,7 @@ show(part001)`
[0, 'index'],
['init', ''],
['body', 'PipeExpression'],
[2, 'index'],
[3, 'index'],
]
expect(result).toEqual(expected)
// expect similar result for start of line

View File

@ -1,4 +1,3 @@
import { PathToNode, ProgramMemory, SketchGroup, SourceRange } from './executor'
import { Selection, ToolTip } from '../useStore'
import {
BinaryExpression,
@ -10,7 +9,11 @@ import {
VariableDeclaration,
ReturnStatement,
ArrayExpression,
} from './abstractSyntaxTreeTypes'
PathToNode,
ProgramMemory,
SketchGroup,
SourceRange,
} from './wasm'
import { createIdentifier, splitPathAtLastIndex } from './modifyAst'
import { getSketchSegmentFromSourceRange } from './std/sketchConstraints'
import { getAngle } from '../lib/utils'

View File

@ -1,8 +1,5 @@
import { recast } from './recast'
import { parser_wasm } from './abstractSyntaxTree'
import { Program } from './abstractSyntaxTreeTypes'
import { parse, Program, recast, initPromise } from './wasm'
import fs from 'node:fs'
import { initPromise } from './rust'
beforeAll(() => initPromise)
@ -366,6 +363,6 @@ describe('it recasts binary expression using brackets where needed', () => {
// helpers
function code2ast(code: string): { ast: Program } {
const ast = parser_wasm(code)
const ast = parse(code)
return { ast }
}

View File

@ -1,13 +0,0 @@
import { Program } from './abstractSyntaxTreeTypes'
import { recast_wasm } from '../wasm-lib/pkg/wasm_lib'
export const recast = (ast: Program): string => {
try {
const s: string = recast_wasm(JSON.stringify(ast))
return s
} catch (e) {
// TODO: do something real with the error.
console.log('recast', e)
throw e
}
}

View File

@ -1,20 +0,0 @@
import init from '../wasm-lib/pkg/wasm_lib'
const initialise = async () => {
const baseUrl =
typeof window === 'undefined'
? 'http://127.0.0.1:3000'
: window.location.origin.includes('tauri://localhost')
? 'tauri://localhost'
: window.location.origin.includes('localhost')
? 'http://localhost:3000'
: window.location.origin && window.location.origin !== 'null'
? window.location.origin
: 'http://localhost:3000'
const fullUrl = baseUrl + '/wasm_lib_bg.wasm'
const input = await fetch(fullUrl)
const buffer = await input.arrayBuffer()
return init(buffer)
}
export const initPromise = initialise()

View File

@ -1,4 +1,9 @@
import { ProgramMemory, SourceRange } from 'lang/executor'
import {
ProgramMemory,
SourceRange,
Program,
VariableDeclarator,
} from 'lang/wasm'
import { Selections } from 'useStore'
import { VITE_KC_API_WS_MODELING_URL, VITE_KC_CONNECTION_TIMEOUT_MS } from 'env'
import { Models } from '@kittycad/lib'
@ -6,7 +11,6 @@ import { exportSave } from 'lib/exportSave'
import { v4 as uuidv4 } from 'uuid'
import * as Sentry from '@sentry/react'
import { getNodeFromPath, getNodePathFromSourceRange } from 'lang/queryAst'
import { Program, VariableDeclarator } from 'lang/abstractSyntaxTreeTypes'
let lastMessage = ''
@ -356,6 +360,11 @@ export class EngineConnection {
if (this.shouldTrace()) {
iceSpan.resolve?.()
}
} else if (this.pc?.iceConnectionState === 'failed') {
// failed is a terminal state; let's explicitly kill the
// connection to the server at this point.
console.log('failed to negotiate ice connection; restarting')
this.close()
}
})
@ -487,9 +496,11 @@ export class EngineConnection {
this.onDataChannelOpen(this)
this.onEngineConnectionOpen(this)
this.ready = true
this.connecting = false
// Do this after we set the connection is ready to avoid errors when
// we try to send messages before the connection is ready.
this.onEngineConnectionOpen(this)
})
this.unreliableDataChannel.addEventListener('close', (event) => {
@ -582,6 +593,9 @@ export class EngineCommandManager {
outSequence = 1
inSequence = 1
engineConnection?: EngineConnection
// Folks should realize that wait for ready does not get called _everytime_
// the connection resets and restarts, it only gets called the first time.
// Be careful what you put here.
waitForReady: Promise<void> = new Promise(() => {})
private resolveReady = () => {}
@ -595,19 +609,36 @@ export class EngineCommandManager {
[localUnsubscribeId: string]: (a: any) => void
}
} = {} as any
constructor({
constructor() {
this.engineConnection = undefined
}
start({
setMediaStream,
setIsStreamReady,
width,
height,
executeCode,
token,
}: {
setMediaStream: (stream: MediaStream) => void
setIsStreamReady: (isStreamReady: boolean) => void
width: number
height: number
executeCode: (code?: string, force?: boolean) => void
token?: string
}) {
if (width === 0 || height === 0) {
return
}
// If we already have an engine connection, just need to resize the stream.
if (this.engineConnection) {
this.handleResize({ streamWidth: width, streamHeight: height })
return
}
this.waitForReady = new Promise((resolve) => {
this.resolveReady = resolve
})
@ -618,6 +649,32 @@ export class EngineCommandManager {
onEngineConnectionOpen: () => {
this.resolveReady()
setIsStreamReady(true)
// Make the axis gizmo.
// We do this after the connection opened to avoid a race condition.
// Connected opened is the last thing that happens when the stream
// is ready.
// We also do this here because we want to ensure we create the gizmo
// and execute the code everytime the stream is restarted.
const gizmoId = uuidv4()
this.sendSceneCommand({
type: 'modeling_cmd_req',
cmd_id: gizmoId,
cmd: {
type: 'make_axes_gizmo',
clobber: false,
// If true, axes gizmo will be placed in the corner of the screen.
// If false, it will be placed at the origin of the scene.
gizmo_mode: true,
},
})
// We execute the code here to make sure if the stream was to
// restart in a session, we want to make sure to execute the code.
// We force it to re-execute the code because we want to make sure
// the code is executed everytime the stream is restarted.
// We pass undefined for the code so it reads from the current state.
executeCode(undefined, true)
},
onClose: () => {
setIsStreamReady(false)
@ -668,7 +725,11 @@ export class EngineCommandManager {
message.request_id
) {
this.handleModelingCommand(message.resp, message.request_id)
} else if (!message.success && message.request_id) {
} else if (
!message.success &&
message.request_id &&
this.artifactMap[message.request_id]
) {
this.handleFailedModelingCommand(message)
}
}
@ -689,6 +750,30 @@ export class EngineCommandManager {
this.engineConnection?.connect()
}
handleResize({
streamWidth,
streamHeight,
}: {
streamWidth: number
streamHeight: number
}) {
console.log('handleResize', streamWidth, streamHeight)
if (!this.engineConnection?.isReady()) {
return
}
const resizeCmd: EngineCommand = {
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: {
type: 'reconfigure_stream',
width: streamWidth,
height: streamHeight,
fps: 60,
},
}
this.engineConnection?.send(resizeCmd)
}
handleModelingCommand(message: WebSocketResponse, id: string) {
if (message.type !== 'modeling') {
return
@ -854,6 +939,14 @@ export class EngineCommandManager {
})
}
sendSceneCommand(command: EngineCommand): Promise<any> {
if (this.engineConnection === undefined) {
return Promise.resolve()
}
if (!this.engineConnection?.isReady()) {
return Promise.resolve()
}
if (
command.type === 'modeling_cmd_req' &&
command.cmd.type !== lastMessage
@ -861,9 +954,6 @@ export class EngineCommandManager {
console.log('sending command', command.cmd.type)
lastMessage = command.cmd.type
}
if (!this.engineConnection?.isReady()) {
return Promise.resolve()
}
if (command.type !== 'modeling_cmd_req') return Promise.resolve()
const cmd = command.cmd
if (
@ -905,6 +995,9 @@ export class EngineCommandManager {
range: SourceRange
command: EngineCommand | string
}): Promise<any> {
if (this.engineConnection === undefined) {
return Promise.resolve()
}
this.sourceRangeMap[id] = range
if (!this.engineConnection?.isReady()) {
@ -950,6 +1043,9 @@ export class EngineCommandManager {
rangeStr: string,
commandStr: string
): Promise<any> {
if (this.engineConnection === undefined) {
return Promise.resolve()
}
if (id === undefined) {
throw new Error('id is undefined')
}
@ -1000,6 +1096,9 @@ export class EngineCommandManager {
}
}
private async fixIdMappings(ast: Program, programMemory: ProgramMemory) {
if (this.engineConnection === undefined) {
return
}
/* This is a temporary solution since the cmd_ids that are sent through when
sending 'extend_path' ids are not used as the segment ids.
@ -1079,3 +1178,5 @@ export class EngineCommandManager {
})
}
}
export const engineCommandManager = new EngineCommandManager()

View File

@ -6,13 +6,9 @@ import {
getXComponent,
addCloseToPipe,
} from './sketch'
import { parser_wasm } from '../abstractSyntaxTree'
import { parse, recast, initPromise } from '../wasm'
import { getNodePathFromSourceRange } from '../queryAst'
import { recast } from '../recast'
import { enginelessExecutor } from '../../lib/testHelpers'
import { initPromise } from '../rust'
beforeAll(() => initPromise)
const eachQuad: [number, [number, number]][] = [
[-315, [1, 1]],
@ -29,6 +25,8 @@ const eachQuad: [number, [number, number]][] = [
[675, [1, -1]],
]
beforeAll(() => initPromise)
describe('testing getYComponent', () => {
it('should return the vertical component of a vector correctly when given angles in each quadrant (and with angles < 0, or > 360)', () => {
const expected: [number, number][] = []
@ -98,7 +96,8 @@ describe('testing changeSketchArguments', () => {
const lineAfterChange = 'lineTo([2, 3], %)'
test('changeSketchArguments', async () => {
// Enable rotations #152
const genCode = (line: string) => `const mySketch001 = startSketchAt([0, 0])
const genCode = (line: string) => `const mySketch001 = startSketchOn('XY')
|> startProfileAt([0, 0], %)
|> ${line}
|> lineTo([0.46, -5.82], %)
// |> rx(45, %)
@ -106,7 +105,7 @@ show(mySketch001)
`
const code = genCode(lineToChange)
const expectedCode = genCode(lineAfterChange)
const ast = parser_wasm(code)
const ast = parse(code)
const programMemory = await enginelessExecutor(ast)
const sourceStart = code.indexOf(lineToChange)
const { modifiedAst } = changeSketchArguments(
@ -139,15 +138,16 @@ describe('testing addNewSketchLn', () => {
test('addNewSketchLn', async () => {
// Enable rotations #152
const code = `
const mySketch001 = startSketchAt([0, 0])
const mySketch001 = startSketchOn('XY')
|> startProfileAt([0, 0], %)
// |> rx(45, %)
|> lineTo([-1.59, -1.54], %)
|> lineTo([0.46, -5.82], %)
show(mySketch001)`
const ast = parser_wasm(code)
const ast = parse(code)
const programMemory = await enginelessExecutor(ast)
const sourceStart = code.indexOf(lineToChange)
expect(sourceStart).toBe(66)
expect(sourceStart).toBe(95)
let { modifiedAst } = addNewSketchLn({
node: ast,
programMemory,
@ -162,7 +162,8 @@ show(mySketch001)`
],
})
// Enable rotations #152
let expectedCode = `const mySketch001 = startSketchAt([0, 0])
let expectedCode = `const mySketch001 = startSketchOn('XY')
|> startProfileAt([0, 0], %)
// |> rx(45, %)
|> lineTo([-1.59, -1.54], %)
|> lineTo([0.46, -5.82], %)
@ -183,7 +184,8 @@ show(mySketch001)
],
})
expectedCode = `const mySketch001 = startSketchAt([0, 0])
expectedCode = `const mySketch001 = startSketchOn('XY')
|> startProfileAt([0, 0], %)
// |> rx(45, %)
|> lineTo([-1.59, -1.54], %)
|> lineTo([0.46, -5.82], %)
@ -198,14 +200,15 @@ describe('testing addTagForSketchOnFace', () => {
it('needs to be in it', async () => {
const originalLine = 'lineTo([-1.59, -1.54], %)'
// Enable rotations #152
const genCode = (line: string) => `const mySketch001 = startSketchAt([0, 0])
const genCode = (line: string) => `const mySketch001 = startSketchOn('XY')
|> startProfileAt([0, 0], %)
// |> rx(45, %)
|> ${line}
|> lineTo([0.46, -5.82], %)
show(mySketch001)
`
const code = genCode(originalLine)
const ast = parser_wasm(code)
const ast = parse(code)
const programMemory = await enginelessExecutor(ast)
const sourceStart = code.indexOf(originalLine)
const sourceRange: [number, number] = [

View File

@ -4,9 +4,6 @@ import {
SketchGroup,
SourceRange,
PathToNode,
MemoryItem,
} from '../executor'
import {
Program,
PipeExpression,
CallExpression,
@ -14,7 +11,7 @@ import {
Value,
Literal,
VariableDeclaration,
} from '../abstractSyntaxTreeTypes'
} from '../wasm'
import {
getNodeFromPath,
getNodeFromPathCurry,
@ -38,7 +35,6 @@ import {
findUniqueName,
} from '../modifyAst'
import { roundOff, getLength, getAngle } from '../../lib/utils'
import { getSketchSegmentFromSourceRange } from './sketchConstraints'
import { perpendicularDistance } from 'sketch-helpers'
export type Coords2d = [number, number]

View File

@ -1,12 +1,9 @@
import { parser_wasm } from '../abstractSyntaxTree'
import { SketchGroup } from '../executor'
import { parse, SketchGroup, recast, initPromise } from '../wasm'
import {
ConstraintType,
getTransformInfos,
transformAstSketchLines,
} from './sketchcombos'
import { recast } from '../recast'
import { initPromise } from '../rust'
import { getSketchSegmentFromSourceRange } from './sketchConstraints'
import { Selection } from '../../useStore'
import { enginelessExecutor } from '../../lib/testHelpers'
@ -31,7 +28,7 @@ async function testingSwapSketchFnCall({
type: 'default',
range: [startIndex, startIndex + callToSwap.length],
}
const ast = parser_wasm(inputCode)
const ast = parse(inputCode)
const programMemory = await enginelessExecutor(ast)
const selections = {
codeBasedSelections: [range],
@ -55,7 +52,8 @@ async function testingSwapSketchFnCall({
describe('testing swaping out sketch calls with xLine/xLineTo', () => {
const bigExampleArr = [
`const part001 = startSketchAt([0, 0])`,
`const part001 = startSketchOn('XY')`,
` |> startProfileAt([0, 0], %)`,
` |> lineTo({ to: [1, 1], tag: 'abc1' }, %)`,
` |> line({ to: [-2.04, -0.7], tag: 'abc2' }, %)`,
` |> angledLine({`,
@ -280,7 +278,8 @@ describe('testing swaping out sketch calls with xLine/xLineTo while keeping vari
`const angledLineOfYLengthY = 0.89`,
`const angledLineToXx = -1.86`,
`const angledLineToYy = -0.76`,
`const part001 = startSketchAt([0, 0])`,
`const part001 = startSketchOn('XY')`,
` |> startProfileAt([0, 0], %)`,
// ` |> rx(90, %)`,
` |> lineTo([1, 1], %)`,
` |> line([lineX, 2.13], %)`,
@ -374,14 +373,15 @@ describe('testing swaping out sketch calls with xLine/xLineTo while keeping vari
describe('testing getSketchSegmentIndexFromSourceRange', () => {
const code = `
const part001 = startSketchAt([0, 0.04]) // segment-in-start
const part001 = startSketchOn('XY')
|> startProfileAt([0, 0.04], %) // segment-in-start
|> line([0, 0.4], %)
|> xLine(3.48, %)
|> line([2.14, 1.35], %) // normal-segment
|> xLine(3.54, %)
show(part001)`
it('normal case works', async () => {
const programMemory = await enginelessExecutor(parser_wasm(code))
const programMemory = await enginelessExecutor(parse(code))
const index = code.indexOf('// normal-segment') - 7
const { __geoMeta, ...segment } = getSketchSegmentFromSourceRange(
programMemory.root['part001'] as SketchGroup,
@ -395,7 +395,7 @@ show(part001)`
})
})
it('verify it works when the segment is in the `start` property', async () => {
const programMemory = await enginelessExecutor(parser_wasm(code))
const programMemory = await enginelessExecutor(parse(code))
const index = code.indexOf('// segment-in-start') - 7
const { __geoMeta, ...segment } = getSketchSegmentFromSourceRange(
programMemory.root['part001'] as SketchGroup,

View File

@ -3,8 +3,10 @@ import {
Program,
VariableDeclarator,
CallExpression,
} from '../abstractSyntaxTreeTypes'
import { SketchGroup, SourceRange, Path } from '../executor'
SketchGroup,
SourceRange,
Path,
} from '../wasm'
export function getSketchSegmentFromSourceRange(
sketchGroup: SketchGroup,

View File

@ -1,5 +1,4 @@
import { parser_wasm } from '../abstractSyntaxTree'
import { Value } from '../abstractSyntaxTreeTypes'
import { parse, Value, recast, initPromise } from '../wasm'
import {
getConstraintType,
getTransformInfos,
@ -8,10 +7,8 @@ import {
ConstraintType,
getConstraintLevelFromSourceRange,
} from './sketchcombos'
import { initPromise } from '../rust'
import { Selections, ToolTip } from '../../useStore'
import { enginelessExecutor } from '../../lib/testHelpers'
import { recast } from '../../lang/recast'
beforeAll(() => initPromise)
@ -63,7 +60,7 @@ describe('testing getConstraintType', () => {
function getConstraintTypeFromSourceHelper(
code: string
): ReturnType<typeof getConstraintType> {
const ast = parser_wasm(code)
const ast = parse(code)
const args = (ast.body[0] as any).expression.arguments[0].elements as [
Value,
Value
@ -74,7 +71,7 @@ function getConstraintTypeFromSourceHelper(
function getConstraintTypeFromSourceHelper2(
code: string
): ReturnType<typeof getConstraintType> {
const ast = parser_wasm(code)
const ast = parse(code)
const arg = (ast.body[0] as any).expression.arguments[0] as Value
const fnName = (ast.body[0] as any).expression.callee.name as ToolTip
return getConstraintType(arg, fnName)
@ -95,7 +92,8 @@ const myVar2 = 5
const myVar3 = 6
const myAng = 40
const myAng2 = 134
const part001 = startSketchAt([0, 0])
const part001 = startSketchOn('XY')
|> startProfileAt([0, 0], %)
|> line([1, 3.82], %) // ln-should-get-tag
|> lineTo([myVar, 1], %) // ln-lineTo-xAbsolute should use angleToMatchLengthX helper
|> lineTo([1, myVar], %) // ln-lineTo-yAbsolute should use angleToMatchLengthY helper
@ -131,7 +129,8 @@ const myVar2 = 5
const myVar3 = 6
const myAng = 40
const myAng2 = 134
const part001 = startSketchAt([0, 0])
const part001 = startSketchOn('XY')
|> startProfileAt([0, 0], %)
|> line({ to: [1, 3.82], tag: 'seg01' }, %) // ln-should-get-tag
|> angledLineToX([
-angleToMatchLengthX('seg01', myVar, %),
@ -199,7 +198,7 @@ const part001 = startSketchAt([0, 0])
show(part001)
`
it('should transform the ast', async () => {
const ast = parser_wasm(inputScript)
const ast = parse(inputScript)
const selectionRanges: Selections['codeBasedSelections'] = inputScript
.split('\n')
.filter((ln) => ln.includes('//'))
@ -234,7 +233,8 @@ describe('testing transformAstForSketchLines for vertical and horizontal constra
const inputScript = `const myVar = 2
const myVar2 = 12
const myVar3 = -10
const part001 = startSketchAt([0, 0])
const part001 = startSketchOn('XY')
|> startProfileAt([0, 0], %)
|> lineTo([1, 1], %)
|> line([-6.28, 1.4], %) // select for horizontal constraint 1
|> line([-1.07, myVar], %) // select for vertical constraint 1
@ -262,7 +262,8 @@ show(part001)
const expectModifiedScript = `const myVar = 2
const myVar2 = 12
const myVar3 = -10
const part001 = startSketchAt([0, 0])
const part001 = startSketchOn('XY')
|> startProfileAt([0, 0], %)
|> lineTo([1, 1], %)
|> xLine(-6.28, %) // select for horizontal constraint 1
|> line([-1.07, myVar], %) // select for vertical constraint 1
@ -286,7 +287,7 @@ const part001 = startSketchAt([0, 0])
|> angledLineToY([301, myVar], %) // select for vertical constraint 10
show(part001)
`
const ast = parser_wasm(inputScript)
const ast = parse(inputScript)
const selectionRanges: Selections['codeBasedSelections'] = inputScript
.split('\n')
.filter((ln) => ln.includes('// select for horizontal constraint'))
@ -320,7 +321,8 @@ show(part001)
const expectModifiedScript = `const myVar = 2
const myVar2 = 12
const myVar3 = -10
const part001 = startSketchAt([0, 0])
const part001 = startSketchOn('XY')
|> startProfileAt([0, 0], %)
|> lineTo([1, 1], %)
|> line([-6.28, 1.4], %) // select for horizontal constraint 1
|> yLine(myVar, %) // select for vertical constraint 1
@ -344,7 +346,7 @@ const part001 = startSketchAt([0, 0])
|> yLineTo(myVar, %) // select for vertical constraint 10
show(part001)
`
const ast = parser_wasm(inputScript)
const ast = parse(inputScript)
const selectionRanges: Selections['codeBasedSelections'] = inputScript
.split('\n')
.filter((ln) => ln.includes('// select for vertical constraint'))
@ -379,7 +381,8 @@ show(part001)
describe('testing transformAstForSketchLines for vertical and horizontal distance constraints', () => {
describe('testing setHorzDistance for line', () => {
const inputScript = `const myVar = 1
const part001 = startSketchAt([0, 0])
const part001 = startSketchOn('XY')
|> startProfileAt([0, 0], %)
|> line([0.31, 1.67], %) // base selection
|> line([0.45, 1.46], %)
|> line([0.45, 1.46], %) // free
@ -435,7 +438,7 @@ async function helperThing(
linesOfInterest: string[],
constraint: ConstraintType
): Promise<string> {
const ast = parser_wasm(inputScript)
const ast = parse(inputScript)
const selectionRanges: Selections['codeBasedSelections'] = inputScript
.split('\n')
.filter((ln) =>
@ -480,7 +483,8 @@ const baseThickHalf = baseThick / 2
const halfHeight = totalHeight / 2
const halfArmAngle = armAngle / 2
const part001 = startSketchAt([-0.01, -0.05])
const part001 = startSketchOn('XY')
|> startProfileAt([-0.01, -0.05], %)
|> line([0.01, 0.94 + 0], %) // partial
|> xLine(3.03, %) // partial
|> angledLine({
@ -498,7 +502,7 @@ const part001 = startSketchAt([-0.01, -0.05])
|> xLine(-3.43 + 0, %) // full
|> angledLineOfXLength([243 + 0, 1.2 + 0], %) // full
show(part001)`
const ast = parser_wasm(code)
const ast = parse(code)
const constraintLevels: ReturnType<
typeof getConstraintLevelFromSourceRange
>[] = ['full', 'partial', 'free']

View File

@ -6,7 +6,9 @@ import {
Value,
BinaryPart,
VariableDeclarator,
} from '../abstractSyntaxTreeTypes'
PathToNode,
ProgramMemory,
} from '../wasm'
import {
getNodeFromPath,
getNodeFromPathCurry,
@ -25,10 +27,8 @@ import {
giveSketchFnCallTag,
} from '../modifyAst'
import { createFirstArg, getFirstArg, replaceSketchLine } from './sketch'
import { PathToNode, ProgramMemory } from '../executor'
import { getSketchSegmentFromSourceRange } from './sketchConstraints'
import { getAngle, roundOff, normaliseAngle } from '../../lib/utils'
import { MemoryItem } from 'wasm-lib/kcl/bindings/MemoryItem'
type LineInputsType =
| 'xAbsolute'
@ -1279,7 +1279,7 @@ export function getTransformInfos(
}) as TransformInfo[]
return theTransforms
} catch (error) {
console.log(error)
console.log('error', error)
return []
}
}

View File

@ -1,12 +1,12 @@
import { parser_wasm } from '../abstractSyntaxTree'
import { parse, initPromise } from '../wasm'
import { enginelessExecutor } from '../../lib/testHelpers'
import { initPromise } from '../rust'
beforeAll(() => initPromise)
describe('testing angledLineThatIntersects', () => {
it('angledLineThatIntersects should intersect with another line', async () => {
const code = (offset: string) => `const part001 = startSketchAt([0, 0])
const code = (offset: string) => `const part001 = startSketchOn('XY')
|> startProfileAt([0, 0], %)
|> lineTo({to:[2, 2], tag: "yo"}, %)
|> lineTo([3, 1], %)
|> angledLineThatIntersects({
@ -17,9 +17,9 @@ describe('testing angledLineThatIntersects', () => {
}, %)
const intersect = segEndX('yo2', part001)
show(part001)`
const { root } = await enginelessExecutor(parser_wasm(code('-1')))
const { root } = await enginelessExecutor(parse(code('-1')))
expect(root.intersect.value).toBe(1 + Math.sqrt(2))
const { root: noOffset } = await enginelessExecutor(parser_wasm(code('0')))
const { root: noOffset } = await enginelessExecutor(parse(code('0')))
expect(noOffset.intersect.value).toBeCloseTo(1)
})
})

View File

@ -1,7 +1,12 @@
import { ProgramMemory, Path, SourceRange } from '../executor'
import { Program, Value } from '../abstractSyntaxTreeTypes'
import {
ProgramMemory,
Path,
SourceRange,
Program,
Value,
PathToNode,
} from '../wasm'
import { ToolTip } from '../../useStore'
import { PathToNode } from '../executor'
import { EngineCommandManager } from './engineConnection'
export interface InternalFirstArg {

View File

@ -1,5 +1,4 @@
import { lexer, asyncLexer } from './tokeniser'
import { initPromise } from './rust'
import { lexer, initPromise } from './wasm'
beforeAll(() => initPromise)
@ -10,9 +9,9 @@ describe('testing lexer', () => {
const code3 = `const yo = 45 /* this is a comment
const ya = 6 */
const yi=45`
expect(await asyncLexer(code)).toEqual(lexer(code))
expect(await asyncLexer(code2)).toEqual(lexer(code2))
expect(await asyncLexer(code3)).toEqual(lexer(code3))
expect(lexer(code)).toEqual(lexer(code))
expect(lexer(code2)).toEqual(lexer(code2))
expect(lexer(code3)).toEqual(lexer(code3))
})
it('test lexer', () => {
expect(stringSummaryLexer('1 + 2')).toEqual([

View File

@ -1,28 +0,0 @@
import { lexer_js } from '../wasm-lib/pkg/wasm_lib'
import { initPromise } from './rust'
import { Token } from '../wasm-lib/kcl/bindings/Token'
export type { Token } from '../wasm-lib/kcl/bindings/Token'
export async function asyncLexer(str: string): Promise<Token[]> {
await initPromise
try {
const tokens: Token[] = lexer_js(str)
return tokens
} catch (e) {
// TODO: do something real with the error.
console.log('lexer', e)
throw e
}
}
export function lexer(str: string): Token[] {
try {
const tokens: Token[] = lexer_js(str)
return tokens
} catch (e) {
// TODO: do something real with the error.
console.log('lexer', e)
throw e
}
}

View File

@ -1,6 +1,5 @@
import { Selections, StoreState } from '../useStore'
import { Program } from './abstractSyntaxTreeTypes'
import { PathToNode } from './executor'
import { Program, PathToNode } from './wasm'
import { getNodeFromPath } from './queryAst'
export function updateCursors(

222
src/lang/wasm.ts Normal file
View File

@ -0,0 +1,222 @@
import init, {
parse_wasm,
recast_wasm,
execute_wasm,
lexer_wasm,
modify_ast_for_sketch_wasm,
} from '../wasm-lib/pkg/wasm_lib'
import { KCLError } from './errors'
import { KclError as RustKclError } from '../wasm-lib/kcl/bindings/KclError'
import {
EngineCommandManager,
ArtifactMap,
SourceRangeMap,
} from './std/engineConnection'
import { ProgramReturn } from '../wasm-lib/kcl/bindings/ProgramReturn'
import { MemoryItem } from '../wasm-lib/kcl/bindings/MemoryItem'
import type { Program } from '../wasm-lib/kcl/bindings/Program'
import type { Token } from '../wasm-lib/kcl/bindings/Token'
import { DefaultPlanes } from '../wasm-lib/kcl/bindings/DefaultPlanes'
export type { Program } from '../wasm-lib/kcl/bindings/Program'
export type { Value } from '../wasm-lib/kcl/bindings/Value'
export type { ObjectExpression } from '../wasm-lib/kcl/bindings/ObjectExpression'
export type { MemberExpression } from '../wasm-lib/kcl/bindings/MemberExpression'
export type { PipeExpression } from '../wasm-lib/kcl/bindings/PipeExpression'
export type { VariableDeclaration } from '../wasm-lib/kcl/bindings/VariableDeclaration'
export type { PipeSubstitution } from '../wasm-lib/kcl/bindings/PipeSubstitution'
export type { Identifier } from '../wasm-lib/kcl/bindings/Identifier'
export type { UnaryExpression } from '../wasm-lib/kcl/bindings/UnaryExpression'
export type { BinaryExpression } from '../wasm-lib/kcl/bindings/BinaryExpression'
export type { ReturnStatement } from '../wasm-lib/kcl/bindings/ReturnStatement'
export type { ExpressionStatement } from '../wasm-lib/kcl/bindings/ExpressionStatement'
export type { CallExpression } from '../wasm-lib/kcl/bindings/CallExpression'
export type { VariableDeclarator } from '../wasm-lib/kcl/bindings/VariableDeclarator'
export type { BinaryPart } from '../wasm-lib/kcl/bindings/BinaryPart'
export type { Literal } from '../wasm-lib/kcl/bindings/Literal'
export type { ArrayExpression } from '../wasm-lib/kcl/bindings/ArrayExpression'
export type SyntaxType =
| 'Program'
| 'ExpressionStatement'
| 'BinaryExpression'
| 'CallExpression'
| 'Identifier'
| 'ReturnStatement'
| 'VariableDeclaration'
| 'VariableDeclarator'
| 'MemberExpression'
| 'ArrayExpression'
| 'ObjectExpression'
| 'ObjectProperty'
| 'FunctionExpression'
| 'PipeExpression'
| 'PipeSubstitution'
| 'Literal'
| 'NonCodeNode'
| 'UnaryExpression'
export type { SourceRange } from '../wasm-lib/kcl/bindings/SourceRange'
export type { Position } from '../wasm-lib/kcl/bindings/Position'
export type { Rotation } from '../wasm-lib/kcl/bindings/Rotation'
export type { Path } from '../wasm-lib/kcl/bindings/Path'
export type { SketchGroup } from '../wasm-lib/kcl/bindings/SketchGroup'
export type { MemoryItem } from '../wasm-lib/kcl/bindings/MemoryItem'
export type { ExtrudeSurface } from '../wasm-lib/kcl/bindings/ExtrudeSurface'
// Initialise the wasm module.
const initialise = async () => {
const baseUrl =
typeof window === 'undefined'
? 'http://127.0.0.1:3000'
: window.location.origin.includes('tauri://localhost')
? 'tauri://localhost'
: window.location.origin.includes('localhost')
? 'http://localhost:3000'
: window.location.origin && window.location.origin !== 'null'
? window.location.origin
: 'http://localhost:3000'
const fullUrl = baseUrl + '/wasm_lib_bg.wasm'
const input = await fetch(fullUrl)
const buffer = await input.arrayBuffer()
return init(buffer)
}
export const initPromise = initialise()
export const rangeTypeFix = (ranges: number[][]): [number, number][] =>
ranges.map(([start, end]) => [start, end])
export const parse = (code: string): Program => {
try {
const program: Program = parse_wasm(code)
return program
} catch (e: any) {
const parsed: RustKclError = JSON.parse(e.toString())
const kclError = new KCLError(
parsed.kind,
parsed.msg,
rangeTypeFix(parsed.sourceRanges)
)
console.log(kclError)
throw kclError
}
}
export type PathToNode = [string | number, string][]
interface Memory {
[key: string]: MemoryItem
}
export interface ProgramMemory {
root: Memory
return: ProgramReturn | null
}
export const executor = async (
node: Program,
programMemory: ProgramMemory = { root: {}, return: null },
engineCommandManager: EngineCommandManager,
planes: DefaultPlanes,
// work around while the gemotry is still be stored on the frontend
// will be removed when the stream UI is added.
tempMapCallback: (a: {
artifactMap: ArtifactMap
sourceRangeMap: SourceRangeMap
}) => void = () => {}
): Promise<ProgramMemory> => {
engineCommandManager.startNewSession()
const _programMemory = await _executor(
node,
programMemory,
engineCommandManager,
planes
)
const { artifactMap, sourceRangeMap } =
await engineCommandManager.waitForAllCommands(node, _programMemory)
tempMapCallback({ artifactMap, sourceRangeMap })
engineCommandManager.endSession()
return _programMemory
}
export const _executor = async (
node: Program,
programMemory: ProgramMemory = { root: {}, return: null },
engineCommandManager: EngineCommandManager,
planes: DefaultPlanes
): Promise<ProgramMemory> => {
try {
const memory: ProgramMemory = await execute_wasm(
JSON.stringify(node),
JSON.stringify(programMemory),
engineCommandManager,
JSON.stringify(planes)
)
return memory
} catch (e: any) {
const parsed: RustKclError = JSON.parse(e.toString())
const kclError = new KCLError(
parsed.kind,
parsed.msg,
rangeTypeFix(parsed.sourceRanges)
)
console.log(kclError)
throw kclError
}
}
export const recast = (ast: Program): string => {
try {
const s: string = recast_wasm(JSON.stringify(ast))
return s
} catch (e) {
// TODO: do something real with the error.
console.log('recast error', e)
throw e
}
}
export function lexer(str: string): Token[] {
try {
const tokens: Token[] = lexer_wasm(str)
return tokens
} catch (e) {
// TODO: do something real with the error.
console.log('lexer error', e)
throw e
}
}
export const modifyAstForSketch = async (
engineCommandManager: EngineCommandManager,
ast: Program,
variableName: string,
currentPlane: string,
engineId: string
): Promise<Program> => {
try {
const updatedAst: Program = await modify_ast_for_sketch_wasm(
engineCommandManager,
JSON.stringify(ast),
variableName,
JSON.stringify(currentPlane),
engineId
)
return updatedAst
} catch (e: any) {
const parsed: RustKclError = JSON.parse(e.toString())
const kclError = new KCLError(
parsed.kind,
parsed.msg,
rangeTypeFix(parsed.sourceRanges)
)
console.log(kclError)
throw kclError
}
}

View File

@ -1,19 +1,28 @@
export const bracket = `// Material: 6061-T6 Aluminum
const sigmaAllow = 35000 // psi
const width = 9 // inch
export const bracket = `const sigmaAllow = 15000 // psi
const width = 11 // inch
const p = 150 // Force on shelf - lbs
const distance = 6 // inches
const distance = 12 // inches
const FOS = 2
const thickness = sqrt(distance * p * FOS * 6 / ( sigmaAllow * width ))
const filletR = thickness * 2
const shelfMountL = 9
const wallMountL = 8
const leg1 = 5 // inches
const leg2 = 8 // inches
const thickness = sqrt(distance * p * FOS * 6 / sigmaAllow / width) // inches
const bracket = startSketchAt([0, 0])
|> line([0, leg1], %)
|> line([leg2, 0], %)
const bracket = startSketchOn('XY')
|> startProfileAt([0, 0], %)
|> line([0, wallMountL], %)
|> tangentalArc({
radius: filletR,
offset: 90
}, %)
|> line([-shelfMountL, 0], %)
|> line([0, -thickness], %)
|> line([-leg2 + thickness, 0], %)
|> line([0, -leg1 + thickness], %)
|> line([shelfMountL, 0], %)
|> tangentalArc({
radius: filletR - thickness,
offset: -90
}, %)
|> line([0, -wallMountL], %)
|> close(%)
|> extrude(width, %)

View File

@ -25,7 +25,7 @@ export async function exportSave(data: ArrayBuffer) {
}
// Write the file.
await writeBinaryFile(filePath, uintArray)
await writeBinaryFile(filePath, file.contents)
} else {
// Download the file to the user's computer.
// Now we need to download the files to the user's downloads folder.
@ -39,6 +39,6 @@ export async function exportSave(data: ArrayBuffer) {
}
} catch (e) {
// TODO: do something real with the error.
console.log('export', e)
console.log('export error', e)
}
}

View File

@ -36,7 +36,7 @@ export async function initializeProjectDirectory(directory: string) {
try {
docDirectory = await documentDir()
} catch (e) {
console.log(e)
console.log('error', e)
docDirectory = await homeDir() // seems to work better on Linux
}

View File

@ -1,11 +1,10 @@
import { Program } from '../lang/abstractSyntaxTreeTypes'
import { ProgramMemory, _executor } from '../lang/executor'
import { Program, ProgramMemory, _executor, SourceRange } from '../lang/wasm'
import {
EngineCommandManager,
EngineCommand,
} from '../lang/std/engineConnection'
import { SourceRange } from 'lang/executor'
import { Models } from '@kittycad/lib'
import { v4 as uuidv4 } from 'uuid'
type WebSocketResponse = Models['OkWebSocketResponseData_type']
@ -66,7 +65,11 @@ export async function enginelessExecutor(
}) as any as EngineCommandManager
await mockEngineCommandManager.waitForReady
mockEngineCommandManager.startNewSession()
const programMemory = await _executor(ast, pm, mockEngineCommandManager)
const programMemory = await _executor(ast, pm, mockEngineCommandManager, {
xy: uuidv4(),
yz: uuidv4(),
xz: uuidv4(),
})
await mockEngineCommandManager.waitForAllCommands()
return programMemory
}
@ -75,15 +78,21 @@ export async function executor(
ast: Program,
pm: ProgramMemory = { root: {}, return: null }
): Promise<ProgramMemory> {
const engineCommandManager = new EngineCommandManager({
const engineCommandManager = new EngineCommandManager()
engineCommandManager.start({
setIsStreamReady: () => {},
setMediaStream: () => {},
width: 100,
height: 100,
width: 0,
height: 0,
executeCode: () => {},
})
await engineCommandManager.waitForReady
engineCommandManager.startNewSession()
const programMemory = await _executor(ast, pm, engineCommandManager)
const programMemory = await _executor(ast, pm, engineCommandManager, {
xy: uuidv4(),
yz: uuidv4(),
xz: uuidv4(),
})
await engineCommandManager.waitForAllCommands(ast, programMemory)
return programMemory
}

View File

@ -1,5 +1,5 @@
import { isOverlap, roundOff } from './utils'
import { SourceRange } from '../lang/executor'
import { SourceRange } from '../lang/wasm'
describe('testing isOverlapping', () => {
testBothOrders([0, 3], [3, 10])

View File

@ -1,4 +1,4 @@
import { SourceRange } from '../lang/executor'
import { SourceRange } from '../lang/wasm'
export function isOverlap(a: SourceRange, b: SourceRange) {
const [startingRange, secondRange] = a[0] < b[0] ? [a, b] : [b, a]

View File

@ -2,6 +2,7 @@ import { assign, createMachine } from 'xstate'
import { CommandBarMeta } from '../lib/commands'
import { Themes, getSystemTheme, setThemeClass } from '../lib/theme'
import { CameraSystem, cameraSystems } from 'lib/cameraControls'
import { Models } from '@kittycad/lib'
export const DEFAULT_PROJECT_NAME = 'project-$nnn'
@ -11,11 +12,11 @@ export enum UnitSystem {
}
export const baseUnits = {
imperial: ['in', 'ft'],
imperial: ['in', 'ft', 'yd'],
metric: ['mm', 'cm', 'm'],
} as const
export type BaseUnit = 'in' | 'ft' | 'mm' | 'cm' | 'm'
export type BaseUnit = Models['UnitLength_type']
export const baseUnitsUnion = Object.values(baseUnits).flatMap((v) => v)

View File

@ -1,6 +1,11 @@
import { faArrowRight, faXmark } from '@fortawesome/free-solid-svg-icons'
import { ActionButton } from '../../components/ActionButton'
import { onboardingPaths, useDismiss, useNextClick } from '.'
import {
ONBOARDING_PROJECT_NAME,
onboardingPaths,
useDismiss,
useNextClick,
} from '.'
import { useGlobalStateContext } from 'hooks/useGlobalStateContext'
import { Themes, getSystemTheme } from 'lib/theme'
import { bracket } from 'lib/exampleKcl'
@ -25,14 +30,20 @@ function OnboardingWithNewFile() {
}))
const {
settings: {
context: { defaultDirectory, defaultProjectName },
context: { defaultDirectory },
},
} = useGlobalStateContext()
async function createAndOpenNewProject() {
const projects = await getProjectsInDir(defaultDirectory)
const nextIndex = await getNextProjectIndex(defaultProjectName, projects)
const name = interpolateProjectNameWithIndex(defaultProjectName, nextIndex)
const nextIndex = await getNextProjectIndex(
ONBOARDING_PROJECT_NAME,
projects
)
const name = interpolateProjectNameWithIndex(
ONBOARDING_PROJECT_NAME,
nextIndex
)
const newFile = await createNewProject(defaultDirectory + '/' + name)
navigate(`${paths.FILE}/${encodeURIComponent(newFile.path)}`)
}

View File

@ -1,5 +1,5 @@
import { useHotkeys } from 'react-hotkeys-hook'
import { Outlet, useRouteLoaderData, useNavigate } from 'react-router-dom'
import { Outlet, useNavigate } from 'react-router-dom'
import Introduction from './Introduction'
import Camera from './Camera'
import Sketching from './Sketching'
@ -15,7 +15,10 @@ import UserMenu from './UserMenu'
import ProjectMenu from './ProjectMenu'
import Export from './Export'
import FutureWork from './FutureWork'
import { IndexLoaderData, paths } from 'Router'
import { paths } from 'Router'
import { useAbsoluteFilePath } from 'hooks/useAbsoluteFilePath'
export const ONBOARDING_PROJECT_NAME = 'Tutorial Project $nn'
export const onboardingPaths = {
INDEX: '/',
@ -86,29 +89,23 @@ export const onboardingRoutes = [
]
export function useNextClick(newStatus: string) {
const filePath = useAbsoluteFilePath()
const {
settings: { send },
} = useGlobalStateContext()
const navigate = useNavigate()
const { project } = useRouteLoaderData(paths.FILE) as IndexLoaderData
return useCallback(() => {
send({
type: 'Set Onboarding Status',
data: { onboardingStatus: newStatus },
})
navigate(
paths.FILE +
'/' +
encodeURIComponent(project?.path || 'new') +
paths.ONBOARDING.INDEX.slice(0, -1) +
newStatus
)
}, [project, newStatus, send, navigate])
navigate(filePath + paths.ONBOARDING.INDEX.slice(0, -1) + newStatus)
}, [filePath, newStatus, send, navigate])
}
export function useDismiss() {
const routeData = useRouteLoaderData(paths.FILE) as IndexLoaderData
const filePath = useAbsoluteFilePath()
const {
settings: { send },
} = useGlobalStateContext()
@ -119,10 +116,8 @@ export function useDismiss() {
type: 'Set Onboarding Status',
data: { onboardingStatus: 'dismissed' },
})
navigate(
paths.FILE + '/' + encodeURIComponent(routeData?.project?.path || 'new')
)
}, [send, navigate, routeData])
navigate(filePath)
}, [send, navigate, filePath])
}
const Onboarding = () => {

View File

@ -24,11 +24,19 @@ import {
} from 'lib/cameraControls'
import { UnitSystem } from 'machines/settingsMachine'
import { useDotDotSlash } from 'hooks/useDotDotSlash'
import {
createNewProject,
getNextProjectIndex,
getProjectsInDir,
interpolateProjectNameWithIndex,
} from 'lib/tauriFS'
import { ONBOARDING_PROJECT_NAME } from './Onboarding'
export const Settings = () => {
const loaderData = useRouteLoaderData(paths.FILE) as IndexLoaderData
const navigate = useNavigate()
const location = useLocation()
const isFileSettings = location.pathname.includes(paths.FILE)
const dotDotSlash = useDotDotSlash()
useHotkeys('esc', () => navigate(dotDotSlash()))
const {
@ -63,6 +71,33 @@ export const Settings = () => {
}
}
function restartOnboarding() {
send({
type: 'Set Onboarding Status',
data: { onboardingStatus: '' },
})
if (isFileSettings) {
navigate(dotDotSlash(1) + paths.ONBOARDING.INDEX)
} else {
createAndOpenNewProject()
}
}
async function createAndOpenNewProject() {
const projects = await getProjectsInDir(defaultDirectory)
const nextIndex = await getNextProjectIndex(
ONBOARDING_PROJECT_NAME,
projects
)
const name = interpolateProjectNameWithIndex(
ONBOARDING_PROJECT_NAME,
nextIndex
)
const newFile = await createNewProject(defaultDirectory + '/' + name)
navigate(`${paths.FILE}/${encodeURIComponent(newFile.path)}`)
}
return (
<div className="fixed inset-0 z-40 overflow-auto body-bg">
<AppHeader showToolbar={false} project={loaderData?.project}>
@ -257,26 +292,18 @@ export const Settings = () => {
))}
</select>
</SettingsSection>
{location.pathname.includes(paths.FILE) && (
<SettingsSection
title="Onboarding"
description="Replay the onboarding process"
>
<ActionButton
Element="button"
onClick={() => {
send({
type: 'Set Onboarding Status',
data: { onboardingStatus: '' },
})
navigate(dotDotSlash(1) + paths.ONBOARDING.INDEX)
}}
onClick={restartOnboarding}
icon={{ icon: faArrowRotateBack }}
>
Replay Onboarding
</ActionButton>
</SettingsSection>
)}
</div>
</div>
)

View File

@ -1,24 +1,27 @@
import { create } from 'zustand'
import { persist } from 'zustand/middleware'
import { addLineHighlight, EditorView } from './editor/highlightextension'
import { parser_wasm } from './lang/abstractSyntaxTree'
import { Program } from './lang/abstractSyntaxTreeTypes'
import { getNodeFromPath } from './lang/queryAst'
import { enginelessExecutor } from './lib/testHelpers'
import {
parse,
Program,
_executor,
recast,
ProgramMemory,
Position,
PathToNode,
Rotation,
SourceRange,
} from './lang/executor'
import { recast } from './lang/recast'
} from './lang/wasm'
import { getNodeFromPath } from './lang/queryAst'
import { enginelessExecutor } from './lib/testHelpers'
import { EditorSelection } from '@codemirror/state'
import { EngineCommandManager } from './lang/std/engineConnection'
import { KCLError } from './lang/errors'
import { deferExecution } from 'lib/utils'
import { _executor } from './lang/executor'
import { bracket } from 'lib/exampleKcl'
import { engineCommandManager } from './lang/std/engineConnection'
import { DefaultPlanes } from './wasm-lib/kcl/bindings/DefaultPlanes'
import { initDefaultPlanes } from './hooks/useAppMode'
export type Selection = {
type: 'default' | 'line-end' | 'line-mid'
@ -156,14 +159,12 @@ export interface StoreState {
code: string
setCode: (code: string) => void
deferredSetCode: (code: string) => void
executeCode: (code?: string) => void
executeCode: (code?: string, force?: boolean) => void
formatCode: () => void
programMemory: ProgramMemory
setProgramMemory: (programMemory: ProgramMemory) => void
isShiftDown: boolean
setIsShiftDown: (isShiftDown: boolean) => void
engineCommandManager?: EngineCommandManager
setEngineCommandManager: (engineCommandManager: EngineCommandManager) => void
mediaStream?: MediaStream
setMediaStream: (mediaStream: MediaStream) => void
isStreamReady: boolean
@ -183,6 +184,10 @@ export interface StoreState {
}) => void
isExecuting: boolean
setIsExecuting: (isExecuting: boolean) => void
defaultPlanes: DefaultPlanes | null
setDefaultPlanes: (defaultPlanes: DefaultPlanes) => void
currentPlane: string | null
setCurrentPlane: (currentPlane: string) => void
showHomeMenu: boolean
setHomeShowMenu: (showMenu: boolean) => void
@ -222,11 +227,22 @@ export const useStore = create<StoreState>()(
editorView.dispatch({ effects: addLineHighlight.of(selection) })
}
},
executeCode: async (code) => {
executeCode: async (code, force) => {
if (!get().defaultPlanes) {
let defaultPlanes = await initDefaultPlanes(
engineCommandManager,
true
)
if (!defaultPlanes) return
get().setDefaultPlanes(defaultPlanes)
}
const result = await executeCode({
code: code || get().code,
lastAst: get().ast,
engineCommandManager: get().engineCommandManager,
engineCommandManager: engineCommandManager,
defaultPlanes: get().defaultPlanes!,
force,
})
if (!result.isChange) {
return
@ -332,13 +348,20 @@ export const useStore = create<StoreState>()(
executeAst: async (ast) => {
const _ast = ast || get().ast
if (!get().isStreamReady) return
const engineCommandManager = get().engineCommandManager!
if (!engineCommandManager) return
if (!get().defaultPlanes) {
let defaultPlanes = await initDefaultPlanes(
engineCommandManager,
true
)
if (!defaultPlanes) return
get().setDefaultPlanes(defaultPlanes)
}
set({ isExecuting: true })
const { logs, errors, programMemory } = await executeAst({
ast: _ast,
engineCommandManager,
defaultPlanes: get().defaultPlanes!,
})
set({
programMemory,
@ -350,13 +373,21 @@ export const useStore = create<StoreState>()(
executeAstMock: async (ast) => {
const _ast = ast || get().ast
if (!get().isStreamReady) return
const engineCommandManager = get().engineCommandManager!
if (!engineCommandManager) return
if (!get().defaultPlanes) {
let defaultPlanes = await initDefaultPlanes(
engineCommandManager,
true
)
if (!defaultPlanes) return
get().setDefaultPlanes(defaultPlanes)
}
const { logs, errors, programMemory } = await executeAst({
ast: _ast,
engineCommandManager,
useFakeExecutor: true,
defaultPlanes: get().defaultPlanes!,
})
set({
programMemory,
@ -371,7 +402,7 @@ export const useStore = create<StoreState>()(
{ focusPath, callBack = () => {} } = {}
) => {
const newCode = recast(ast)
const astWithUpdatedSource = parser_wasm(newCode)
const astWithUpdatedSource = parse(newCode)
callBack(astWithUpdatedSource)
set({
@ -427,7 +458,7 @@ export const useStore = create<StoreState>()(
},
formatCode: async () => {
const code = get().code
const ast = parser_wasm(code)
const ast = parse(code)
const newCode = recast(ast)
set({ code: newCode, ast })
},
@ -435,8 +466,6 @@ export const useStore = create<StoreState>()(
setProgramMemory: (programMemory) => set({ programMemory }),
isShiftDown: false,
setIsShiftDown: (isShiftDown) => set({ isShiftDown }),
setEngineCommandManager: (engineCommandManager) =>
set({ engineCommandManager }),
setMediaStream: (mediaStream) => set({ mediaStream }),
isStreamReady: false,
setIsStreamReady: (isStreamReady) => set({ isStreamReady }),
@ -454,9 +483,15 @@ export const useStore = create<StoreState>()(
fileId: '',
setFileId: (fileId) => set({ fileId }),
streamDimensions: { streamWidth: 1280, streamHeight: 720 },
setStreamDimensions: (streamDimensions) => set({ streamDimensions }),
setStreamDimensions: (streamDimensions) => {
set({ streamDimensions })
},
isExecuting: false,
setIsExecuting: (isExecuting) => set({ isExecuting }),
defaultPlanes: null,
setDefaultPlanes: (defaultPlanes) => set({ defaultPlanes }),
currentPlane: null,
setCurrentPlane: (currentPlane) => set({ currentPlane }),
// tauri specific app settings
defaultDir: {
@ -516,10 +551,14 @@ async function executeCode({
engineCommandManager,
code,
lastAst,
defaultPlanes,
force,
}: {
code: string
lastAst: Program
engineCommandManager?: EngineCommandManager
engineCommandManager: EngineCommandManager
defaultPlanes: DefaultPlanes
force?: boolean
}): Promise<
| {
logs: string[]
@ -532,14 +571,14 @@ async function executeCode({
> {
let ast: Program
try {
ast = parser_wasm(code)
ast = parse(code)
} catch (e) {
let errors: KCLError[] = []
let logs: string[] = [JSON.stringify(e)]
if (e instanceof KCLError) {
errors = [e]
logs = []
if (e.msg === 'file is empty') engineCommandManager?.endSession()
if (e.msg === 'file is empty') engineCommandManager.endSession()
}
return {
isChange: true,
@ -562,12 +601,13 @@ async function executeCode({
}
// Check if the ast we have is equal to the ast in the storage.
// If it is, we don't need to update the ast.
if (!engineCommandManager || JSON.stringify(ast) === JSON.stringify(lastAst))
if (JSON.stringify(ast) === JSON.stringify(lastAst) && !force)
return { isChange: false }
const { logs, errors, programMemory } = await executeAst({
ast,
engineCommandManager,
defaultPlanes,
})
return {
ast,
@ -581,10 +621,12 @@ async function executeCode({
async function executeAst({
ast,
engineCommandManager,
defaultPlanes,
useFakeExecutor = false,
}: {
ast: Program
engineCommandManager: EngineCommandManager
defaultPlanes: DefaultPlanes
useFakeExecutor?: boolean
}): Promise<{
logs: string[]
@ -607,7 +649,8 @@ async function executeAst({
root: defaultProgramMemory,
return: null,
},
engineCommandManager
engineCommandManager,
defaultPlanes
))
await engineCommandManager.waitForAllCommands(ast, programMemory)

View File

@ -6,10 +6,10 @@
serial-integration = { max-threads = 4 }
[profile.default]
slow-timeout = { period = "10s", terminate-after = 1 }
slow-timeout = { period = "60s", terminate-after = 1 }
[profile.ci]
slow-timeout = { period = "60s", terminate-after = 10 }
slow-timeout = { period = "120s", terminate-after = 10 }
[[profile.default.overrides]]
filter = "test(serial_test_)"

View File

@ -71,9 +71,9 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299"
[[package]]
name = "anstream"
version = "0.5.0"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1f58811cfac344940f1a400b6e6231ce35171f614f26439e80f8c1465c5cc0c"
checksum = "f6cd65a4b849ace0b7f6daeebcc1a1d111282227ca745458c61dbf670e52a597"
dependencies = [
"anstyle",
"anstyle-parse",
@ -109,9 +109,9 @@ dependencies = [
[[package]]
name = "anstyle-wincon"
version = "2.1.0"
version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58f54d10c6dfa51283a066ceab3ec1ab78d13fae00aa49243a45e4571fb79dfd"
checksum = "0238ca56c96dfa37bdf7c373c8886dd591322500aceeeccdb2216fe06dc2f796"
dependencies = [
"anstyle",
"windows-sys 0.48.0",
@ -440,9 +440,9 @@ dependencies = [
[[package]]
name = "clap"
version = "4.4.4"
version = "4.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1d7b8d5ec32af0fadc644bf1fd509a688c2103b185644bb1e29d164e0703136"
checksum = "d04704f56c2cde07f43e8e2c154b43f216dc5c92fc98ada720177362f953b956"
dependencies = [
"clap_builder",
"clap_derive",
@ -450,9 +450,9 @@ dependencies = [
[[package]]
name = "clap_builder"
version = "4.4.4"
version = "4.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5179bb514e4d7c2051749d8fcefa2ed6d06a9f4e6d69faf3805f5d80b8cf8d56"
checksum = "0e231faeaca65ebd1ea3c737966bf858971cd38c3849107aa3ea7de90a804e45"
dependencies = [
"anstream",
"anstyle",
@ -695,6 +695,20 @@ dependencies = [
"syn 2.0.37",
]
[[package]]
name = "derive-docs"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c357dec14992ba88803535217ed83d6f6cd80efcb8fa8e3f8a30a9b84fadc1c7"
dependencies = [
"convert_case",
"proc-macro2",
"quote",
"serde",
"serde_tokenstream",
"syn 2.0.37",
]
[[package]]
name = "diff"
version = "0.1.13"
@ -788,9 +802,9 @@ dependencies = [
[[package]]
name = "expectorate"
version = "1.0.7"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "710ab6a2d57038a835d66f78d5af3fa5d27c1ec4682f823b9203c48826cb0591"
checksum = "de6f19b25bdfa2747ae775f37cd109c31f1272d4e4c83095be0727840aa1d75f"
dependencies = [
"console",
"newline-converter",
@ -1376,7 +1390,7 @@ dependencies = [
[[package]]
name = "kcl-lib"
version = "0.1.31"
version = "0.1.33"
dependencies = [
"anyhow",
"async-recursion",
@ -1385,7 +1399,7 @@ dependencies = [
"clap",
"criterion",
"dashmap",
"derive-docs",
"derive-docs 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"expectorate",
"futures",
"itertools 0.11.0",
@ -1394,7 +1408,6 @@ dependencies = [
"lazy_static",
"parse-display",
"pretty_assertions",
"regex",
"reqwest",
"schemars",
"serde",
@ -1408,13 +1421,14 @@ dependencies = [
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
"winnow",
]
[[package]]
name = "kittycad"
version = "0.2.26"
version = "0.2.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2623ee601ce203476229df3f9d3a14664cb43e3f7455e9ac8ed91aacaa6163d"
checksum = "539b323537b877fc8dd130362b8f1af9af8051c19208bb8bfd816ab7c330f2bb"
dependencies = [
"anyhow",
"async-trait",
@ -1719,7 +1733,7 @@ checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
[[package]]
name = "openapitor"
version = "0.0.9"
source = "git+https://github.com/KittyCAD/kittycad.rs?branch=main#61a16059b3eaf8793a2a2e1edbc0d770f284fea3"
source = "git+https://github.com/KittyCAD/kittycad.rs?branch=main#fa0345c514fcc9ae6cd74ae35c8e5c2800fec34f"
dependencies = [
"Inflector",
"anyhow",
@ -2203,9 +2217,9 @@ checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da"
[[package]]
name = "reqwest"
version = "0.11.20"
version = "0.11.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e9ad3fe7488d7e34558a2033d45a0c90b72d97b4f80705666fea71472e2e6a1"
checksum = "046cd98826c46c2ac8ddecae268eb5c2e58628688a5fc7a2643704a73faba95b"
dependencies = [
"base64 0.21.4",
"bytes",
@ -2230,6 +2244,7 @@ dependencies = [
"serde",
"serde_json",
"serde_urlencoded",
"system-configuration",
"tokio",
"tokio-rustls",
"tower-service",
@ -2879,6 +2894,27 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "system-configuration"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7"
dependencies = [
"bitflags 1.3.2",
"core-foundation",
"system-configuration-sys",
]
[[package]]
name = "system-configuration-sys"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9"
dependencies = [
"core-foundation-sys",
"libc",
]
[[package]]
name = "take_mut"
version = "0.2.2"
@ -2945,18 +2981,18 @@ dependencies = [
[[package]]
name = "thiserror"
version = "1.0.48"
version = "1.0.49"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d6d7a740b8a666a7e828dd00da9c0dc290dff53154ea77ac109281de90589b7"
checksum = "1177e8c6d7ede7afde3585fd2513e611227efd6481bd78d2e82ba1ce16557ed4"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.48"
version = "1.0.49"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49922ecae66cc8a249b77e68d1d0623c1b2c514f0060c27cdc68bd62a1219d35"
checksum = "10712f02019e9288794769fba95cd6847df9874d49d871d062172f9dd41bc4cc"
dependencies = [
"proc-macro2",
"quote",
@ -3081,9 +3117,9 @@ dependencies = [
[[package]]
name = "tokio-tungstenite"
version = "0.20.0"
version = "0.20.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b2dbec703c26b00d74844519606ef15d09a7d6857860f84ad223dec002ddea2"
checksum = "212d5dcb2a1ce06d81107c3d0ffa3121fe974b73f068c8282cb1c32328113b6c"
dependencies = [
"futures-util",
"log",
@ -3303,9 +3339,9 @@ dependencies = [
[[package]]
name = "tungstenite"
version = "0.20.0"
version = "0.20.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e862a1c4128df0112ab625f55cd5c934bcb4312ba80b39ae4b4835a3fd58e649"
checksum = "9e3dac10fd62eaf6617d3a904ae222845979aec67c615d1c842b4002c7666fb9"
dependencies = [
"byteorder",
"bytes",
@ -3792,6 +3828,15 @@ version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
[[package]]
name = "winnow"
version = "0.5.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c2e3184b9c4e92ad5167ca73039d0c42476302ab603e2fec4487511f38ccefc"
dependencies = [
"memchr",
]
[[package]]
name = "winreg"
version = "0.50.0"

View File

@ -11,7 +11,7 @@ crate-type = ["cdylib"]
bson = { version = "2.7.0", features = ["uuid-1", "chrono"] }
gloo-utils = "0.2.0"
kcl-lib = { path = "kcl" }
kittycad = { version = "0.2.25", default-features = false, features = ["js"] }
kittycad = { version = "0.2.31", default-features = false, features = ["js"] }
serde_json = "1.0.107"
uuid = { version = "1.4.1", features = ["v4", "js", "serde"] }
wasm-bindgen = "0.2.87"
@ -20,9 +20,9 @@ wasm-bindgen-futures = "0.4.37"
[dev-dependencies]
anyhow = "1"
image = "0.24.7"
kittycad = "0.2.25"
kittycad = "0.2.31"
pretty_assertions = "1.4.0"
reqwest = { version = "0.11.20", default-features = false }
reqwest = { version = "0.11.22", default-features = false }
tokio = { version = "1.32.0", features = ["rt-multi-thread", "macros", "time"] }
twenty-twenty = "0.6.1"
uuid = { version = "1.4.1", features = ["v4", "js", "serde"] }

View File

@ -19,6 +19,6 @@ serde_tokenstream = "0.2"
syn = { version = "2.0.37", features = ["full"] }
[dev-dependencies]
expectorate = "1.0.7"
expectorate = "1.1.0"
openapitor = { git = "https://github.com/KittyCAD/kittycad.rs", branch = "main" }
pretty_assertions = "1.4.0"

View File

@ -1,7 +1,7 @@
[package]
name = "kcl-lib"
description = "KittyCAD Language"
version = "0.1.31"
version = "0.1.33"
edition = "2021"
license = "MIT"
@ -11,20 +11,20 @@ license = "MIT"
anyhow = { version = "1.0.75", features = ["backtrace"] }
async-recursion = "1.0.5"
async-trait = "0.1.73"
clap = { version = "4.4.3", features = ["cargo", "derive", "env", "unicode"], optional = true }
clap = { version = "4.4.6", features = ["cargo", "derive", "env", "unicode"], optional = true }
dashmap = "5.5.3"
#derive-docs = { version = "0.1.4" }
derive-docs = { path = "../derive-docs" }
kittycad = { version = "0.2.25", default-features = false, features = ["js"] }
derive-docs = { version = "0.1.4" }
#derive-docs = { path = "../derive-docs" }
kittycad = { version = "0.2.31", default-features = false, features = ["js"] }
lazy_static = "1.4.0"
parse-display = "0.8.2"
regex = "1.7.1"
schemars = { version = "0.8", features = ["impl_json_schema", "url", "uuid1"] }
serde = { version = "1.0.188", features = ["derive"] }
serde_json = "1.0.107"
thiserror = "1.0.48"
thiserror = "1.0.49"
ts-rs = { version = "7", package = "ts-rs-json-value", features = ["serde-json-impl", "schemars-impl", "uuid-impl"] }
uuid = { version = "1.4.1", features = ["v4", "js", "serde"] }
winnow = "0.5.15"
[target.'cfg(target_arch = "wasm32")'.dependencies]
js-sys = { version = "0.3.64" }
@ -36,7 +36,7 @@ web-sys = { version = "0.3.64", features = ["console"] }
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
bson = { version = "2.7.0", features = ["uuid-1", "chrono"] }
futures = { version = "0.3.28" }
reqwest = { version = "0.11.20", default-features = false }
reqwest = { version = "0.11.22", default-features = false }
tokio = { version = "1.32.0", features = ["full"] }
tokio-tungstenite = { version = "0.20.0", features = ["rustls-tls-native-roots"] }
tower-lsp = { version = "0.20.0", features = ["proposed"] }
@ -52,7 +52,7 @@ debug = true
[dev-dependencies]
criterion = "0.5.1"
expectorate = "1.0.7"
expectorate = "1.1.0"
itertools = "0.11.0"
pretty_assertions = "1.4.0"
tokio = { version = "1.32.0", features = ["rt-multi-thread", "macros", "time"] }

View File

@ -1,24 +1,32 @@
use criterion::{criterion_group, criterion_main, Criterion};
use criterion::{black_box, criterion_group, criterion_main, Criterion};
pub fn criterion_benchmark(c: &mut Criterion) {
c.bench_function("parse + lex cube", |b| b.iter(|| lex_and_parse(CUBE_PROGRAM)));
c.bench_function("parse + lex big kitt", |b| {
b.iter(|| lex_and_parse(include_str!("../../tests/executor/inputs/kittycad_svg.kcl")))
});
c.bench_function("parse + lex pipes_on_pipes", |b| {
b.iter(|| lex_and_parse(include_str!("../../tests/executor/inputs/pipes_on_pipes.kcl")))
});
pub fn bench_lex(c: &mut Criterion) {
c.bench_function("lex_cube", |b| b.iter(|| lex(CUBE_PROGRAM)));
c.bench_function("lex_big_kitt", |b| b.iter(|| lex(KITT_PROGRAM)));
c.bench_function("lex_pipes_on_pipes", |b| b.iter(|| lex(PIPES_PROGRAM)));
}
pub fn bench_lex_parse(c: &mut Criterion) {
c.bench_function("parse_lex_cube", |b| b.iter(|| lex_and_parse(CUBE_PROGRAM)));
c.bench_function("parse_lex_big_kitt", |b| b.iter(|| lex_and_parse(KITT_PROGRAM)));
c.bench_function("parse_lex_pipes_on_pipes", |b| b.iter(|| lex_and_parse(PIPES_PROGRAM)));
}
fn lex(program: &str) {
black_box(kcl_lib::token::lexer(program));
}
fn lex_and_parse(program: &str) {
let tokens = kcl_lib::tokeniser::lexer(program);
let tokens = kcl_lib::token::lexer(program);
let parser = kcl_lib::parser::Parser::new(tokens);
parser.ast().unwrap();
black_box(parser.ast().unwrap());
}
criterion_group!(benches, criterion_benchmark);
criterion_group!(benches, bench_lex, bench_lex_parse);
criterion_main!(benches);
const KITT_PROGRAM: &str = include_str!("../../tests/executor/inputs/kittycad_svg.kcl");
const PIPES_PROGRAM: &str = include_str!("../../tests/executor/inputs/pipes_on_pipes.kcl");
const CUBE_PROGRAM: &str = r#"fn cube = (pos, scale) => {
const sg = startSketchAt(pos)
|> line([0, scale], %)

View File

@ -1 +1 @@
enum-variant-size-threshold = 24
enum-variant-size-threshold = 48

View File

@ -709,7 +709,6 @@ dependencies = [
"kittycad",
"lazy_static",
"parse-display",
"regex",
"reqwest",
"schemars",
"serde",
@ -723,6 +722,7 @@ dependencies = [
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
"winnow",
]
[[package]]
@ -1878,9 +1878,9 @@ dependencies = [
[[package]]
name = "tungstenite"
version = "0.20.0"
version = "0.20.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e862a1c4128df0112ab625f55cd5c934bcb4312ba80b39ae4b4835a3fd58e649"
checksum = "9e3dac10fd62eaf6617d3a904ae222845979aec67c615d1c842b4002c7666fb9"
dependencies = [
"byteorder",
"bytes",
@ -2158,6 +2158,15 @@ version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
[[package]]
name = "winnow"
version = "0.5.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c2e3184b9c4e92ad5167ca73039d0c42476302ab603e2fec4487511f38ccefc"
dependencies = [
"memchr",
]
[[package]]
name = "winreg"
version = "0.50.0"

View File

@ -31,6 +31,8 @@ pub async fn modify_ast_for_sketch(
program: &mut Program,
// The name of the sketch.
sketch_name: &str,
// The type of plane the sketch is on. `XY` or `XZ`, etc
plane: crate::executor::PlaneType,
// The ID of the parent sketch.
sketch_id: uuid::Uuid,
) -> Result<String, KclError> {
@ -153,10 +155,11 @@ pub async fn modify_ast_for_sketch(
y: (first_control_points.points[1].y - first_control_points.points[0].y),
z: (first_control_points.points[1].z - first_control_points.points[0].z),
};
let sketch = create_start_sketch_at(
let sketch = create_start_sketch_on(
sketch_name,
[first_control_points.points[0].x, first_control_points.points[0].y],
[start_sketch_at_end.x, start_sketch_at_end.y],
plane,
additional_lines,
)?;
@ -166,7 +169,7 @@ pub async fn modify_ast_for_sketch(
let recasted = program.recast(&FormatOptions::default(), 0);
// Re-parse the ast so we get the correct source ranges.
let tokens = crate::tokeniser::lexer(&recasted);
let tokens = crate::token::lexer(&recasted);
let parser = crate::parser::Parser::new(tokens);
*program = parser.ast()?;
@ -174,19 +177,24 @@ pub async fn modify_ast_for_sketch(
}
/// Create a pipe expression that starts a sketch at the given point and draws a line to the given point.
fn create_start_sketch_at(
fn create_start_sketch_on(
name: &str,
start: [f64; 2],
end: [f64; 2],
plane: crate::executor::PlaneType,
additional_lines: Vec<[f64; 2]>,
) -> Result<VariableDeclarator, KclError> {
let start_sketch_at = CallExpression::new(
"startSketchAt",
vec![ArrayExpression::new(vec![
let start_sketch_on = CallExpression::new("startSketchOn", vec![Literal::new(plane.to_string().into()).into()])?;
let start_profile_at = CallExpression::new(
"startProfileAt",
vec![
ArrayExpression::new(vec![
Literal::new(round_before_recast(start[0]).into()).into(),
Literal::new(round_before_recast(start[1]).into()).into(),
])
.into()],
.into(),
PipeSubstitution::new().into(),
],
)?;
// Keep track of where we are so we can close the sketch if we need to.
@ -209,7 +217,7 @@ fn create_start_sketch_at(
],
)?;
let mut pipe_body = vec![start_sketch_at.into(), initial_line.into()];
let mut pipe_body = vec![start_sketch_on.into(), start_profile_at.into(), initial_line.into()];
for (index, line) in additional_lines.iter().enumerate() {
current_position.x += line[0];

View File

@ -1,6 +1,6 @@
//! Data types for the AST.
use std::collections::HashMap;
use std::{collections::HashMap, fmt::Write};
use anyhow::Result;
use parse_display::{Display, FromStr};
@ -10,9 +10,8 @@ use serde_json::Map;
use tower_lsp::lsp_types::{CompletionItem, CompletionItemKind, DocumentSymbol, Range as LspRange, SymbolKind};
use crate::{
engine::EngineConnection,
errors::{KclError, KclErrorDetails},
executor::{MemoryItem, Metadata, PipeInfo, ProgramMemory, SourceRange, UserVal},
executor::{ExecutorContext, MemoryItem, Metadata, PipeInfo, ProgramMemory, SourceRange, UserVal},
parser::PIPE_OPERATOR,
};
@ -29,8 +28,8 @@ pub struct Program {
impl Program {
pub fn recast(&self, options: &FormatOptions, indentation_level: usize) -> String {
let indentation = options.get_indentation(indentation_level);
let result = self
.body
let result =
self.body
.iter()
.map(|statement| match statement.clone() {
BodyItem::ExpressionStatement(expression_statement) => {
@ -41,16 +40,17 @@ impl Program {
BodyItem::VariableDeclaration(variable_declaration) => variable_declaration
.declarations
.iter()
.map(|declaration| {
format!(
.fold(String::new(), |mut output, declaration| {
let _ = write!(
output,
"{}{} {} = {}",
indentation,
variable_declaration.kind,
declaration.id.name,
declaration.init.recast(options, 0, false)
)
})
.collect::<String>(),
);
output
}),
BodyItem::ReturnStatement(return_statement) => {
format!(
"{}return {}",
@ -60,7 +60,7 @@ impl Program {
}
})
.enumerate()
.map(|(index, recast_str)| {
.fold(String::new(), |mut output, (index, recast_str)| {
let start_string = if index == 0 {
// We need to indent.
if let Some(start) = self.non_code_meta.start.clone() {
@ -91,9 +91,9 @@ impl Program {
custom_white_space_or_comment
};
format!("{}{}{}", start_string, recast_str, end_string)
let _ = write!(output, "{}{}{}", start_string, recast_str, end_string);
output
})
.collect::<String>()
.trim()
.to_string();
@ -258,6 +258,23 @@ impl Program {
}
}
/// Replace a value with the new value, use the source range for matching the exact value.
pub fn replace_value(&mut self, source_range: SourceRange, new_value: Value) {
for item in &mut self.body {
match item {
BodyItem::ExpressionStatement(ref mut expression_statement) => expression_statement
.expression
.replace_value(source_range, new_value.clone()),
BodyItem::VariableDeclaration(ref mut variable_declaration) => {
variable_declaration.replace_value(source_range, new_value.clone())
}
BodyItem::ReturnStatement(ref mut return_statement) => {
return_statement.argument.replace_value(source_range, new_value.clone())
}
}
}
}
/// Get the variable declaration with the given name.
pub fn get_variable(&self, name: &str) -> Option<&VariableDeclarator> {
for item in &self.body {
@ -393,6 +410,27 @@ impl Value {
}
}
pub fn replace_value(&mut self, source_range: SourceRange, new_value: Value) {
if source_range == self.clone().into() {
*self = new_value;
return;
}
match self {
Value::BinaryExpression(ref mut bin_exp) => bin_exp.replace_value(source_range, new_value),
Value::ArrayExpression(ref mut array_exp) => array_exp.replace_value(source_range, new_value),
Value::ObjectExpression(ref mut obj_exp) => obj_exp.replace_value(source_range, new_value),
Value::MemberExpression(_) => {}
Value::Literal(_) => {}
Value::FunctionExpression(ref mut func_exp) => func_exp.replace_value(source_range, new_value),
Value::CallExpression(ref mut call_exp) => call_exp.replace_value(source_range, new_value),
Value::Identifier(_) => {}
Value::PipeExpression(ref mut pipe_exp) => pipe_exp.replace_value(source_range, new_value),
Value::UnaryExpression(ref mut unary_exp) => unary_exp.replace_value(source_range, new_value),
Value::PipeSubstitution(_) => {}
}
}
pub fn start(&self) -> usize {
match self {
Value::Literal(literal) => literal.start(),
@ -538,6 +576,23 @@ impl BinaryPart {
}
}
pub fn replace_value(&mut self, source_range: SourceRange, new_value: Value) {
match self {
BinaryPart::Literal(_) => {}
BinaryPart::Identifier(_) => {}
BinaryPart::BinaryExpression(ref mut binary_expression) => {
binary_expression.replace_value(source_range, new_value)
}
BinaryPart::CallExpression(ref mut call_expression) => {
call_expression.replace_value(source_range, new_value)
}
BinaryPart::UnaryExpression(ref mut unary_expression) => {
unary_expression.replace_value(source_range, new_value)
}
BinaryPart::MemberExpression(_) => {}
}
}
fn recast(&self, options: &FormatOptions, indentation_level: usize) -> String {
match &self {
BinaryPart::Literal(literal) => literal.recast(),
@ -576,7 +631,7 @@ impl BinaryPart {
&self,
memory: &mut ProgramMemory,
pipe_info: &mut PipeInfo,
engine: &EngineConnection,
ctx: &ExecutorContext,
) -> Result<MemoryItem, KclError> {
// We DO NOT set this gloablly because if we did and this was called inside a pipe it would
// stop the execution of the pipe.
@ -591,13 +646,13 @@ impl BinaryPart {
Ok(value.clone())
}
BinaryPart::BinaryExpression(binary_expression) => {
binary_expression.get_result(memory, &mut new_pipe_info, engine).await
binary_expression.get_result(memory, &mut new_pipe_info, ctx).await
}
BinaryPart::CallExpression(call_expression) => {
call_expression.execute(memory, &mut new_pipe_info, engine).await
call_expression.execute(memory, &mut new_pipe_info, ctx).await
}
BinaryPart::UnaryExpression(unary_expression) => {
unary_expression.get_result(memory, &mut new_pipe_info, engine).await
unary_expression.get_result(memory, &mut new_pipe_info, ctx).await
}
BinaryPart::MemberExpression(member_expression) => member_expression.get_result(memory),
}
@ -801,6 +856,12 @@ impl CallExpression {
})
}
pub fn replace_value(&mut self, source_range: SourceRange, new_value: Value) {
for arg in &mut self.arguments {
arg.replace_value(source_range, new_value.clone());
}
}
fn recast(&self, options: &FormatOptions, indentation_level: usize, is_in_pipe: bool) -> String {
format!(
"{}({})",
@ -818,7 +879,7 @@ impl CallExpression {
&self,
memory: &mut ProgramMemory,
pipe_info: &mut PipeInfo,
engine: &EngineConnection,
ctx: &ExecutorContext,
) -> Result<MemoryItem, KclError> {
let fn_name = self.callee.name.clone();
@ -832,7 +893,7 @@ impl CallExpression {
value.clone()
}
Value::BinaryExpression(binary_expression) => {
binary_expression.get_result(memory, pipe_info, engine).await?
binary_expression.get_result(memory, pipe_info, ctx).await?
}
Value::CallExpression(call_expression) => {
// We DO NOT set this gloablly because if we did and this was called inside a pipe it would
@ -840,15 +901,11 @@ impl CallExpression {
// THIS IS IMPORTANT.
let mut new_pipe_info = pipe_info.clone();
new_pipe_info.is_in_pipe = false;
call_expression.execute(memory, &mut new_pipe_info, engine).await?
call_expression.execute(memory, &mut new_pipe_info, ctx).await?
}
Value::UnaryExpression(unary_expression) => {
unary_expression.get_result(memory, pipe_info, engine).await?
}
Value::ObjectExpression(object_expression) => {
object_expression.execute(memory, pipe_info, engine).await?
}
Value::ArrayExpression(array_expression) => array_expression.execute(memory, pipe_info, engine).await?,
Value::UnaryExpression(unary_expression) => unary_expression.get_result(memory, pipe_info, ctx).await?,
Value::ObjectExpression(object_expression) => object_expression.execute(memory, pipe_info, ctx).await?,
Value::ArrayExpression(array_expression) => array_expression.execute(memory, pipe_info, ctx).await?,
Value::PipeExpression(pipe_expression) => {
return Err(KclError::Semantic(KclErrorDetails {
message: format!("PipeExpression not implemented here: {:?}", pipe_expression),
@ -880,12 +937,12 @@ impl CallExpression {
match &self.function {
Function::StdLib { func } => {
// Attempt to call the function.
let args = crate::std::Args::new(fn_args, self.into(), engine.clone());
let args = crate::std::Args::new(fn_args, self.into(), ctx.clone());
let result = func.std_lib_fn()(args).await?;
if pipe_info.is_in_pipe {
pipe_info.index += 1;
pipe_info.previous_results.push(result);
execute_pipe_body(memory, &pipe_info.body.clone(), pipe_info, self.into(), engine).await
execute_pipe_body(memory, &pipe_info.body.clone(), pipe_info, self.into(), ctx).await
} else {
Ok(result)
}
@ -893,7 +950,7 @@ impl CallExpression {
Function::InMemory => {
let func = memory.get(&fn_name, self.into())?;
let result = func
.call_fn(fn_args, memory.clone(), engine.clone())
.call_fn(fn_args, memory.clone(), ctx.clone())
.await?
.ok_or_else(|| {
KclError::UndefinedValue(KclErrorDetails {
@ -908,7 +965,7 @@ impl CallExpression {
pipe_info.index += 1;
pipe_info.previous_results.push(result);
execute_pipe_body(memory, &pipe_info.body.clone(), pipe_info, self.into(), engine).await
execute_pipe_body(memory, &pipe_info.body.clone(), pipe_info, self.into(), ctx).await
} else {
Ok(result)
}
@ -1014,6 +1071,12 @@ impl VariableDeclaration {
}
}
pub fn replace_value(&mut self, source_range: SourceRange, new_value: Value) {
for declaration in &mut self.declarations {
declaration.init.replace_value(source_range, new_value.clone());
}
}
/// Returns a value that includes the given character position.
pub fn get_value_for_position(&self, pos: usize) -> Option<&Value> {
for declaration in &self.declarations {
@ -1367,6 +1430,12 @@ impl ArrayExpression {
}
}
pub fn replace_value(&mut self, source_range: SourceRange, new_value: Value) {
for element in &mut self.elements {
element.replace_value(source_range, new_value.clone());
}
}
pub fn get_constraint_level(&self) -> ConstraintLevel {
if self.elements.is_empty() {
return ConstraintLevel::Ignore {
@ -1434,7 +1503,7 @@ impl ArrayExpression {
&self,
memory: &mut ProgramMemory,
pipe_info: &mut PipeInfo,
engine: &EngineConnection,
ctx: &ExecutorContext,
) -> Result<MemoryItem, KclError> {
let mut results = Vec::with_capacity(self.elements.len());
@ -1446,7 +1515,7 @@ impl ArrayExpression {
value.clone()
}
Value::BinaryExpression(binary_expression) => {
binary_expression.get_result(memory, pipe_info, engine).await?
binary_expression.get_result(memory, pipe_info, ctx).await?
}
Value::CallExpression(call_expression) => {
// We DO NOT set this gloablly because if we did and this was called inside a pipe it would
@ -1454,16 +1523,12 @@ impl ArrayExpression {
// THIS IS IMPORTANT.
let mut new_pipe_info = pipe_info.clone();
new_pipe_info.is_in_pipe = false;
call_expression.execute(memory, &mut new_pipe_info, engine).await?
call_expression.execute(memory, &mut new_pipe_info, ctx).await?
}
Value::UnaryExpression(unary_expression) => {
unary_expression.get_result(memory, pipe_info, engine).await?
}
Value::ObjectExpression(object_expression) => {
object_expression.execute(memory, pipe_info, engine).await?
}
Value::ArrayExpression(array_expression) => array_expression.execute(memory, pipe_info, engine).await?,
Value::PipeExpression(pipe_expression) => pipe_expression.get_result(memory, pipe_info, engine).await?,
Value::UnaryExpression(unary_expression) => unary_expression.get_result(memory, pipe_info, ctx).await?,
Value::ObjectExpression(object_expression) => object_expression.execute(memory, pipe_info, ctx).await?,
Value::ArrayExpression(array_expression) => array_expression.execute(memory, pipe_info, ctx).await?,
Value::PipeExpression(pipe_expression) => pipe_expression.get_result(memory, pipe_info, ctx).await?,
Value::PipeSubstitution(pipe_substitution) => {
return Err(KclError::Semantic(KclErrorDetails {
message: format!("PipeSubstitution not implemented here: {:?}", pipe_substitution),
@ -1517,6 +1582,12 @@ impl ObjectExpression {
}
}
pub fn replace_value(&mut self, source_range: SourceRange, new_value: Value) {
for property in &mut self.properties {
property.value.replace_value(source_range, new_value.clone());
}
}
pub fn get_constraint_level(&self) -> ConstraintLevel {
if self.properties.is_empty() {
return ConstraintLevel::Ignore {
@ -1584,7 +1655,7 @@ impl ObjectExpression {
&self,
memory: &mut ProgramMemory,
pipe_info: &mut PipeInfo,
engine: &EngineConnection,
ctx: &ExecutorContext,
) -> Result<MemoryItem, KclError> {
let mut object = Map::new();
for property in &self.properties {
@ -1595,7 +1666,7 @@ impl ObjectExpression {
value.clone()
}
Value::BinaryExpression(binary_expression) => {
binary_expression.get_result(memory, pipe_info, engine).await?
binary_expression.get_result(memory, pipe_info, ctx).await?
}
Value::CallExpression(call_expression) => {
// We DO NOT set this gloablly because if we did and this was called inside a pipe it would
@ -1603,16 +1674,12 @@ impl ObjectExpression {
// THIS IS IMPORTANT.
let mut new_pipe_info = pipe_info.clone();
new_pipe_info.is_in_pipe = false;
call_expression.execute(memory, &mut new_pipe_info, engine).await?
call_expression.execute(memory, &mut new_pipe_info, ctx).await?
}
Value::UnaryExpression(unary_expression) => {
unary_expression.get_result(memory, pipe_info, engine).await?
}
Value::ObjectExpression(object_expression) => {
object_expression.execute(memory, pipe_info, engine).await?
}
Value::ArrayExpression(array_expression) => array_expression.execute(memory, pipe_info, engine).await?,
Value::PipeExpression(pipe_expression) => pipe_expression.get_result(memory, pipe_info, engine).await?,
Value::UnaryExpression(unary_expression) => unary_expression.get_result(memory, pipe_info, ctx).await?,
Value::ObjectExpression(object_expression) => object_expression.execute(memory, pipe_info, ctx).await?,
Value::ArrayExpression(array_expression) => array_expression.execute(memory, pipe_info, ctx).await?,
Value::PipeExpression(pipe_expression) => pipe_expression.get_result(memory, pipe_info, ctx).await?,
Value::PipeSubstitution(pipe_substitution) => {
return Err(KclError::Semantic(KclErrorDetails {
message: format!("PipeSubstitution not implemented here: {:?}", pipe_substitution),
@ -1961,6 +2028,11 @@ impl BinaryExpression {
}
}
pub fn replace_value(&mut self, source_range: SourceRange, new_value: Value) {
self.left.replace_value(source_range, new_value.clone());
self.right.replace_value(source_range, new_value);
}
pub fn get_constraint_level(&self) -> ConstraintLevel {
let left_constraint_level = self.left.get_constraint_level();
let right_constraint_level = self.right.get_constraint_level();
@ -2025,7 +2097,7 @@ impl BinaryExpression {
&self,
memory: &mut ProgramMemory,
pipe_info: &mut PipeInfo,
engine: &EngineConnection,
ctx: &ExecutorContext,
) -> Result<MemoryItem, KclError> {
// We DO NOT set this gloablly because if we did and this was called inside a pipe it would
// stop the execution of the pipe.
@ -2035,12 +2107,12 @@ impl BinaryExpression {
let left_json_value = self
.left
.get_result(memory, &mut new_pipe_info, engine)
.get_result(memory, &mut new_pipe_info, ctx)
.await?
.get_json_value()?;
let right_json_value = self
.right
.get_result(memory, &mut new_pipe_info, engine)
.get_result(memory, &mut new_pipe_info, ctx)
.await?
.get_json_value()?;
@ -2183,6 +2255,10 @@ impl UnaryExpression {
}
}
pub fn replace_value(&mut self, source_range: SourceRange, new_value: Value) {
self.argument.replace_value(source_range, new_value);
}
pub fn get_constraint_level(&self) -> ConstraintLevel {
self.argument.get_constraint_level()
}
@ -2195,7 +2271,7 @@ impl UnaryExpression {
&self,
memory: &mut ProgramMemory,
pipe_info: &mut PipeInfo,
engine: &EngineConnection,
ctx: &ExecutorContext,
) -> Result<MemoryItem, KclError> {
// We DO NOT set this gloablly because if we did and this was called inside a pipe it would
// stop the execution of the pipe.
@ -2206,7 +2282,7 @@ impl UnaryExpression {
let num = parse_json_number_as_f64(
&self
.argument
.get_result(memory, &mut new_pipe_info, engine)
.get_result(memory, &mut new_pipe_info, ctx)
.await?
.get_json_value()?,
self.into(),
@ -2278,6 +2354,12 @@ impl PipeExpression {
}
}
pub fn replace_value(&mut self, source_range: SourceRange, new_value: Value) {
for value in &mut self.body {
value.replace_value(source_range, new_value.clone());
}
}
pub fn get_constraint_level(&self) -> ConstraintLevel {
if self.body.is_empty() {
return ConstraintLevel::Ignore {
@ -2333,12 +2415,12 @@ impl PipeExpression {
&self,
memory: &mut ProgramMemory,
pipe_info: &mut PipeInfo,
engine: &EngineConnection,
ctx: &ExecutorContext,
) -> Result<MemoryItem, KclError> {
// Reset the previous results.
pipe_info.previous_results = vec![];
pipe_info.index = 0;
execute_pipe_body(memory, &self.body, pipe_info, self.into(), engine).await
execute_pipe_body(memory, &self.body, pipe_info, self.into(), ctx).await
}
/// Rename all identifiers that have the old name to the new given name.
@ -2355,7 +2437,7 @@ async fn execute_pipe_body(
body: &[Value],
pipe_info: &mut PipeInfo,
source_range: SourceRange,
engine: &EngineConnection,
ctx: &ExecutorContext,
) -> Result<MemoryItem, KclError> {
if pipe_info.index == body.len() {
pipe_info.is_in_pipe = false;
@ -2380,15 +2462,15 @@ async fn execute_pipe_body(
match expression {
Value::BinaryExpression(binary_expression) => {
let result = binary_expression.get_result(memory, pipe_info, engine).await?;
let result = binary_expression.get_result(memory, pipe_info, ctx).await?;
pipe_info.previous_results.push(result);
pipe_info.index += 1;
execute_pipe_body(memory, body, pipe_info, source_range, engine).await
execute_pipe_body(memory, body, pipe_info, source_range, ctx).await
}
Value::CallExpression(call_expression) => {
pipe_info.is_in_pipe = true;
pipe_info.body = body.to_vec();
call_expression.execute(memory, pipe_info, engine).await
call_expression.execute(memory, pipe_info, ctx).await
}
_ => {
// Return an error this should not happen.
@ -2420,6 +2502,10 @@ impl FunctionExpression {
}
}
pub fn replace_value(&mut self, source_range: SourceRange, new_value: Value) {
self.body.replace_value(source_range, new_value);
}
pub fn recast(&self, options: &FormatOptions, indentation_level: usize) -> String {
// We don't want to end with a new line inside nested functions.
let mut new_options = options.clone();
@ -2677,7 +2763,8 @@ mod tests {
#[test]
fn test_get_lsp_symbols() {
let code = r#"const part001 = startSketchAt([0.0000000000, 5.0000000000])
let code = r#"const part001 = startSketchOn('XY')
|> startProfileAt([0.0000000000, 5.0000000000], %)
|> line([0.4900857016, -0.0240763666], %)
const part002 = "part002"
@ -2691,42 +2778,28 @@ fn ghi = (x) => {
}
show(part001)"#;
let tokens = crate::tokeniser::lexer(code);
let tokens = crate::token::lexer(code);
let parser = crate::parser::Parser::new(tokens);
let program = parser.ast().unwrap();
let symbols = program.get_lsp_symbols(code);
assert_eq!(symbols.len(), 7);
}
#[test]
fn test_recast_with_std_and_non_stdlib() {
let some_program_string = r#"{"body":[{"type":"VariableDeclaration","start":0,"end":0,"declarations":[{"type":"VariableDeclarator","start":0,"end":0,"id":{"type":"Identifier","start":0,"end":0,"name":"part001"},"init":{"type":"PipeExpression","start":0,"end":0,"body":[{"type":"CallExpression","start":0,"end":0,"callee":{"type":"Identifier","start":0,"end":0,"name":"startSketchAt"},"function":{"type":"StdLib","func":{"name":"startSketchAt","summary":"","description":"","tags":[],"returnValue":{"type":"","required":false,"name":"","schema":{}},"args":[],"unpublished":false,"deprecated":false}},"optional":false,"arguments":[{"type":"Literal","start":0,"end":0,"value":"default","raw":"default"}]},{"type":"CallExpression","start":0,"end":0,"callee":{"type":"Identifier","start":0,"end":0,"name":"ry"},"function":{"type":"InMemory"},"optional":false,"arguments":[{"type":"Literal","start":0,"end":0,"value":90,"raw":"90"},{"type":"PipeSubstitution","start":0,"end":0}]},{"type":"CallExpression","start":0,"end":0,"callee":{"type":"Identifier","start":0,"end":0,"name":"line"},"function":{"type":"StdLib","func":{"name":"line","summary":"","description":"","tags":[],"returnValue":{"type":"","required":false,"name":"","schema":{}},"args":[],"unpublished":false,"deprecated":false}},"optional":false,"arguments":[{"type":"Literal","start":0,"end":0,"value":"default","raw":"default"},{"type":"PipeSubstitution","start":0,"end":0}]}],"nonCodeMeta":{"nonCodeNodes":{},"start":null}}}],"kind":"const"},{"type":"ExpressionStatement","start":0,"end":0,"expression":{"type":"CallExpression","start":0,"end":0,"callee":{"type":"Identifier","start":0,"end":0,"name":"show"},"function":{"type":"StdLib","func":{"name":"show","summary":"","description":"","tags":[],"returnValue":{"type":"","required":false,"name":"","schema":{}},"args":[],"unpublished":false,"deprecated":false}},"optional":false,"arguments":[{"type":"Identifier","start":0,"end":0,"name":"part001"}]}}],"start":0,"end":0,"nonCodeMeta":{"nonCodeNodes":{},"start":null}}"#;
let some_program: crate::ast::types::Program = serde_json::from_str(some_program_string).unwrap();
let recasted = some_program.recast(&Default::default(), 0);
assert_eq!(
recasted,
r#"const part001 = startSketchAt('default')
|> ry(90, %)
|> line('default', %)
show(part001)
"#
);
}
#[test]
fn test_recast_with_bad_indentation() {
let some_program_string = r#"const part001 = startSketchAt([0.0, 5.0])
let some_program_string = r#"const part001 = startSketchOn('XY')
|> startProfileAt([0.0, 5.0], %)
|> line([0.4900857016, -0.0240763666], %)
|> line([0.6804562304, 0.9087880491], %)"#;
let tokens = crate::tokeniser::lexer(some_program_string);
let tokens = crate::token::lexer(some_program_string);
let parser = crate::parser::Parser::new(tokens);
let program = parser.ast().unwrap();
let recasted = program.recast(&Default::default(), 0);
assert_eq!(
recasted,
r#"const part001 = startSketchAt([0.0, 5.0])
r#"const part001 = startSketchOn('XY')
|> startProfileAt([0.0, 5.0], %)
|> line([0.4900857016, -0.0240763666], %)
|> line([0.6804562304, 0.9087880491], %)
"#
@ -2735,17 +2808,19 @@ show(part001)
#[test]
fn test_recast_with_bad_indentation_and_inline_comment() {
let some_program_string = r#"const part001 = startSketchAt([0.0, 5.0])
let some_program_string = r#"const part001 = startSketchOn('XY')
|> startProfileAt([0.0, 5.0], %)
|> line([0.4900857016, -0.0240763666], %) // hello world
|> line([0.6804562304, 0.9087880491], %)"#;
let tokens = crate::tokeniser::lexer(some_program_string);
let tokens = crate::token::lexer(some_program_string);
let parser = crate::parser::Parser::new(tokens);
let program = parser.ast().unwrap();
let recasted = program.recast(&Default::default(), 0);
assert_eq!(
recasted,
r#"const part001 = startSketchAt([0.0, 5.0])
r#"const part001 = startSketchOn('XY')
|> startProfileAt([0.0, 5.0], %)
|> line([0.4900857016, -0.0240763666], %) // hello world
|> line([0.6804562304, 0.9087880491], %)
"#
@ -2753,18 +2828,20 @@ show(part001)
}
#[test]
fn test_recast_with_bad_indentation_and_line_comment() {
let some_program_string = r#"const part001 = startSketchAt([0.0, 5.0])
let some_program_string = r#"const part001 = startSketchOn('XY')
|> startProfileAt([0.0, 5.0], %)
|> line([0.4900857016, -0.0240763666], %)
// hello world
|> line([0.6804562304, 0.9087880491], %)"#;
let tokens = crate::tokeniser::lexer(some_program_string);
let tokens = crate::token::lexer(some_program_string);
let parser = crate::parser::Parser::new(tokens);
let program = parser.ast().unwrap();
let recasted = program.recast(&Default::default(), 0);
assert_eq!(
recasted,
r#"const part001 = startSketchAt([0.0, 5.0])
r#"const part001 = startSketchOn('XY')
|> startProfileAt([0.0, 5.0], %)
|> line([0.4900857016, -0.0240763666], %)
// hello world
|> line([0.6804562304, 0.9087880491], %)
@ -2783,7 +2860,7 @@ show(part001)
// this is also a comment
return things
}"#;
let tokens = crate::tokeniser::lexer(some_program_string);
let tokens = crate::token::lexer(some_program_string);
let parser = crate::parser::Parser::new(tokens);
let program = parser.ast().unwrap();
@ -2806,7 +2883,8 @@ show(part001)
#[test]
fn test_recast_lots_of_comments() {
let some_program_string = r#"// comment at start
const mySk1 = startSketchAt([0, 0])
const mySk1 = startSketchOn('XY')
|> startProfileAt([0, 0], %)
|> lineTo([1, 1], %)
// comment here
|> lineTo({ to: [0, 1], tag: 'myTag' }, %)
@ -2820,7 +2898,7 @@ const mySk1 = startSketchAt([0, 0])
|> ry(45, %)
|> rx(45, %)
// one more for good measure"#;
let tokens = crate::tokeniser::lexer(some_program_string);
let tokens = crate::token::lexer(some_program_string);
let parser = crate::parser::Parser::new(tokens);
let program = parser.ast().unwrap();
@ -2828,7 +2906,8 @@ const mySk1 = startSketchAt([0, 0])
assert_eq!(
recasted,
r#"// comment at start
const mySk1 = startSketchAt([0, 0])
const mySk1 = startSketchOn('XY')
|> startProfileAt([0, 0], %)
|> lineTo([1, 1], %)
// comment here
|> lineTo({ to: [0, 1], tag: 'myTag' }, %)
@ -2848,7 +2927,8 @@ a comment between pipe expression statements */
#[test]
fn test_recast_multiline_object() {
let some_program_string = r#"const part001 = startSketchAt([-0.01, -0.08])
let some_program_string = r#"const part001 = startSketchOn('XY')
|> startProfileAt([-0.01, -0.08], %)
|> line({ to: [0.62, 4.15], tag: 'seg01' }, %)
|> line([2.77, -1.24], %)
|> angledLineThatIntersects({
@ -2859,7 +2939,7 @@ a comment between pipe expression statements */
|> line([-0.42, -1.72], %)
show(part001)"#;
let tokens = crate::tokeniser::lexer(some_program_string);
let tokens = crate::token::lexer(some_program_string);
let parser = crate::parser::Parser::new(tokens);
let program = parser.ast().unwrap();
@ -2885,7 +2965,7 @@ const yo = [
" hey oooooo really long long long"
]
"#;
let tokens = crate::tokeniser::lexer(some_program_string);
let tokens = crate::token::lexer(some_program_string);
let parser = crate::parser::Parser::new(tokens);
let program = parser.ast().unwrap();
@ -2903,7 +2983,7 @@ const key = 'c'
const things = "things"
// this is also a comment"#;
let tokens = crate::tokeniser::lexer(some_program_string);
let tokens = crate::token::lexer(some_program_string);
let parser = crate::parser::Parser::new(tokens);
let program = parser.ast().unwrap();
@ -2921,7 +3001,7 @@ const things = "things"
// a comment
"
}"#;
let tokens = crate::tokeniser::lexer(some_program_string);
let tokens = crate::token::lexer(some_program_string);
let parser = crate::parser::Parser::new(tokens);
let program = parser.ast().unwrap();
@ -2936,7 +3016,8 @@ const myVar2 = 5
const myVar3 = 6
const myAng = 40
const myAng2 = 134
const part001 = startSketchAt([0, 0])
const part001 = startSketchOn('XY')
|> startProfileAt([0, 0], %)
|> line({ to: [1, 3.82], tag: 'seg01' }, %) // ln-should-get-tag
|> angledLineToX([
-angleToMatchLengthX('seg01', myVar, %),
@ -2946,7 +3027,7 @@ const part001 = startSketchAt([0, 0])
-angleToMatchLengthY('seg01', myVar, %),
myVar
], %) // ln-lineTo-yAbsolute should use angleToMatchLengthY helper"#;
let tokens = crate::tokeniser::lexer(some_program_string);
let tokens = crate::token::lexer(some_program_string);
let parser = crate::parser::Parser::new(tokens);
let program = parser.ast().unwrap();
@ -2961,7 +3042,8 @@ const myVar2 = 5
const myVar3 = 6
const myAng = 40
const myAng2 = 134
const part001 = startSketchAt([0, 0])
const part001 = startSketchOn('XY')
|> startProfileAt([0, 0], %)
|> line({ to: [1, 3.82], tag: 'seg01' }, %) // ln-should-get-tag
|> angledLineToX([
-angleToMatchLengthX('seg01', myVar, %),
@ -2972,7 +3054,7 @@ const part001 = startSketchAt([0, 0])
myVar
], %) // ln-lineTo-yAbsolute should use angleToMatchLengthY helper
"#;
let tokens = crate::tokeniser::lexer(some_program_string);
let tokens = crate::token::lexer(some_program_string);
let parser = crate::parser::Parser::new(tokens);
let program = parser.ast().unwrap();
@ -2989,7 +3071,8 @@ const part001 = startSketchAt([0, 0])
#[test]
fn test_recast_after_rename_std() {
let some_program_string = r#"const part001 = startSketchAt([0.0000000000, 5.0000000000])
let some_program_string = r#"const part001 = startSketchOn('XY')
|> startProfileAt([0.0000000000, 5.0000000000], %)
|> line([0.4900857016, -0.0240763666], %)
const part002 = "part002"
@ -3003,7 +3086,7 @@ fn ghi = (part001) => {
}
show(part001)"#;
let tokens = crate::tokeniser::lexer(some_program_string);
let tokens = crate::token::lexer(some_program_string);
let parser = crate::parser::Parser::new(tokens);
let mut program = parser.ast().unwrap();
program.rename_symbol("mySuperCoolPart", 6);
@ -3011,7 +3094,8 @@ show(part001)"#;
let recasted = program.recast(&Default::default(), 0);
assert_eq!(
recasted,
r#"const mySuperCoolPart = startSketchAt([0.0, 5.0])
r#"const mySuperCoolPart = startSketchOn('XY')
|> startProfileAt([0.0, 5.0], %)
|> line([0.4900857016, -0.0240763666], %)
const part002 = "part002"
@ -3034,7 +3118,7 @@ show(mySuperCoolPart)
let some_program_string = r#"fn ghi = (x, y, z) => {
return x
}"#;
let tokens = crate::tokeniser::lexer(some_program_string);
let tokens = crate::token::lexer(some_program_string);
let parser = crate::parser::Parser::new(tokens);
let mut program = parser.ast().unwrap();
program.rename_symbol("newName", 10);
@ -3055,7 +3139,8 @@ show(mySuperCoolPart)
const l = 8
const h = 10
const firstExtrude = startSketchAt([0,0])
const firstExtrude = startSketchOn('XY')
|> startProfileAt([0,0], %)
|> line([0, l], %)
|> line([w, 0], %)
|> line([0, -l], %)
@ -3063,7 +3148,7 @@ const firstExtrude = startSketchAt([0,0])
|> extrude(h, %)
show(firstExtrude)"#;
let tokens = crate::tokeniser::lexer(some_program_string);
let tokens = crate::token::lexer(some_program_string);
let parser = crate::parser::Parser::new(tokens);
let program = parser.ast().unwrap();
@ -3074,7 +3159,8 @@ show(firstExtrude)"#;
const l = 8
const h = 10
const firstExtrude = startSketchAt([0, 0])
const firstExtrude = startSketchOn('XY')
|> startProfileAt([0, 0], %)
|> line([0, l], %)
|> line([w, 0], %)
|> line([0, -l], %)
@ -3089,7 +3175,7 @@ show(firstExtrude)
#[tokio::test(flavor = "multi_thread")]
async fn test_recast_math_start_negative() {
let some_program_string = r#"const myVar = -5 + 6"#;
let tokens = crate::tokeniser::lexer(some_program_string);
let tokens = crate::token::lexer(some_program_string);
let parser = crate::parser::Parser::new(tokens);
let program = parser.ast().unwrap();
@ -3105,7 +3191,7 @@ const FOS = 2
const sigmaAllow = 8
const width = 20
const thickness = sqrt(distance * p * FOS * 6 / (sigmaAllow * width))"#;
let tokens = crate::tokeniser::lexer(some_program_string);
let tokens = crate::token::lexer(some_program_string);
let parser = crate::parser::Parser::new(tokens);
let program = parser.ast().unwrap();

View File

@ -3,13 +3,15 @@
use std::collections::HashMap;
use anyhow::Result;
use kittycad::types::{Color, ModelingCmd, Point3D};
use parse_display::{Display, FromStr};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use tower_lsp::lsp_types::{Position as LspPosition, Range as LspRange};
use crate::{
ast::types::{BodyItem, Function, FunctionExpression, Value},
engine::EngineConnection,
engine::{EngineConnection, EngineManager},
errors::{KclError, KclErrorDetails},
};
@ -101,6 +103,7 @@ impl ProgramReturn {
#[serde(tag = "type")]
pub enum MemoryItem {
UserVal(UserVal),
Plane(Box<Plane>),
SketchGroup(Box<SketchGroup>),
ExtrudeGroup(Box<ExtrudeGroup>),
#[ts(skip)]
@ -115,6 +118,166 @@ pub enum MemoryItem {
},
}
/// A plane.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
#[ts(export)]
#[serde(rename_all = "camelCase")]
pub struct Plane {
/// The id of the plane.
pub id: uuid::Uuid,
// The code for the plane either a string or custom.
pub value: PlaneType,
/// Origin of the plane.
pub origin: Point3d,
/// What should the planes X axis be?
pub x_axis: Point3d,
/// What should the planes Y axis be?
pub y_axis: Point3d,
/// The z-axis (normal).
pub z_axis: Point3d,
#[serde(rename = "__meta")]
pub meta: Vec<Metadata>,
}
/// Type for a plane.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, FromStr, Display)]
#[ts(export)]
#[serde(rename_all = "camelCase")]
#[display(style = "camelCase")]
pub enum PlaneType {
#[serde(rename = "XY", alias = "xy")]
#[display("XY")]
XY,
#[serde(rename = "XZ", alias = "xz")]
#[display("XZ")]
XZ,
#[serde(rename = "YZ", alias = "yz")]
#[display("YZ")]
YZ,
/// A custom plane.
#[serde(rename = "Custom")]
#[display("Custom")]
Custom,
}
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
#[ts(export)]
#[serde(rename_all = "camelCase")]
pub struct DefaultPlanes {
pub xy: uuid::Uuid,
pub xz: uuid::Uuid,
pub yz: uuid::Uuid,
}
impl DefaultPlanes {
pub async fn new(engine: &EngineConnection) -> Result<Self, KclError> {
// Create new default planes.
let default_size = 60.0;
let default_origin = Point3D { x: 0.0, y: 0.0, z: 0.0 };
// Create xy plane.
let xy = uuid::Uuid::new_v4();
engine
.send_modeling_cmd(
xy,
SourceRange::default(),
ModelingCmd::MakePlane {
clobber: false,
origin: default_origin.clone(),
size: default_size,
x_axis: Point3D { x: 1.0, y: 0.0, z: 0.0 },
y_axis: Point3D { x: 0.0, y: 1.0, z: 0.0 },
hide: Some(true),
},
)
.await?;
// Set the color.
engine
.send_modeling_cmd(
uuid::Uuid::new_v4(),
SourceRange::default(),
ModelingCmd::PlaneSetColor {
color: Color {
r: 0.7,
g: 0.28,
b: 0.28,
a: 0.4,
},
plane_id: xy,
},
)
.await?;
// Create yz plane.
let yz = uuid::Uuid::new_v4();
engine
.send_modeling_cmd(
yz,
SourceRange::default(),
ModelingCmd::MakePlane {
clobber: false,
origin: default_origin.clone(),
size: default_size,
x_axis: Point3D { x: 0.0, y: 1.0, z: 0.0 },
y_axis: Point3D { x: 0.0, y: 0.0, z: 1.0 },
hide: Some(true),
},
)
.await?;
// Set the color.
engine
.send_modeling_cmd(
uuid::Uuid::new_v4(),
SourceRange::default(),
ModelingCmd::PlaneSetColor {
color: Color {
r: 0.28,
g: 0.7,
b: 0.28,
a: 0.4,
},
plane_id: yz,
},
)
.await?;
// Create xz plane.
let xz = uuid::Uuid::new_v4();
engine
.send_modeling_cmd(
xz,
SourceRange::default(),
ModelingCmd::MakePlane {
clobber: false,
origin: default_origin,
size: default_size,
x_axis: Point3D { x: 1.0, y: 0.0, z: 0.0 },
y_axis: Point3D { x: 0.0, y: 0.0, z: 1.0 },
hide: Some(true),
},
)
.await?;
// Set the color.
engine
.send_modeling_cmd(
uuid::Uuid::new_v4(),
SourceRange::default(),
ModelingCmd::PlaneSetColor {
color: Color {
r: 0.28,
g: 0.28,
b: 0.7,
a: 0.4,
},
plane_id: xz,
},
)
.await?;
Ok(Self { xy, xz, yz })
}
}
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
#[ts(export)]
#[serde(tag = "type", rename_all = "camelCase")]
@ -140,7 +303,7 @@ pub type MemoryFunction =
memory: ProgramMemory,
expression: Box<FunctionExpression>,
metadata: Vec<Metadata>,
engine: EngineConnection,
ctx: ExecutorContext,
) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<Option<ProgramReturn>, KclError>>>>;
fn force_memory_function<
@ -149,7 +312,7 @@ fn force_memory_function<
ProgramMemory,
Box<FunctionExpression>,
Vec<Metadata>,
EngineConnection,
ExecutorContext,
) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<Option<ProgramReturn>, KclError>>>>,
>(
f: F,
@ -165,6 +328,7 @@ impl From<MemoryItem> for Vec<SourceRange> {
MemoryItem::ExtrudeGroup(e) => e.meta.iter().map(|m| m.source_range).collect(),
MemoryItem::ExtrudeTransform(e) => e.meta.iter().map(|m| m.source_range).collect(),
MemoryItem::Function { meta, .. } => meta.iter().map(|m| m.source_range).collect(),
MemoryItem::Plane(p) => p.meta.iter().map(|m| m.source_range).collect(),
}
}
}
@ -187,11 +351,11 @@ impl MemoryItem {
&self,
args: Vec<MemoryItem>,
memory: ProgramMemory,
engine: EngineConnection,
ctx: ExecutorContext,
) -> Result<Option<ProgramReturn>, KclError> {
if let MemoryItem::Function { func, expression, meta } = &self {
if let Some(func) = func {
func(args, memory, expression.clone(), meta.clone(), engine).await
func(args, memory, expression.clone(), meta.clone(), ctx).await
} else {
Err(KclError::Semantic(KclErrorDetails {
message: format!("Not a function: {:?}", expression),
@ -222,6 +386,8 @@ pub struct SketchGroup {
pub position: Position,
/// The rotation of the sketch group.
pub rotation: Rotation,
/// The plane id of the sketch group.
pub plane_id: Option<uuid::Uuid>,
/// Metadata.
#[serde(rename = "__meta")]
pub meta: Vec<Metadata>,
@ -414,6 +580,18 @@ pub struct Point3d {
pub z: f64,
}
impl Point3d {
pub fn new(x: f64, y: f64, z: f64) -> Self {
Self { x, y, z }
}
}
impl From<Point3d> for kittycad::types::Point3D {
fn from(p: Point3d) -> Self {
Self { x: p.x, y: p.y, z: p.z }
}
}
/// Metadata.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
#[ts(export)]
@ -593,12 +771,19 @@ impl Default for PipeInfo {
}
}
/// The executor context.
#[derive(Debug, Clone)]
pub struct ExecutorContext {
pub engine: EngineConnection,
pub planes: DefaultPlanes,
}
/// Execute a AST's program.
pub async fn execute(
program: crate::ast::types::Program,
memory: &mut ProgramMemory,
options: BodyType,
engine: &EngineConnection,
ctx: &ExecutorContext,
) -> Result<ProgramMemory, KclError> {
let mut pipe_info = PipeInfo::default();
@ -617,7 +802,23 @@ pub async fn execute(
args.push(memory_item.clone());
}
Value::CallExpression(call_expr) => {
let result = call_expr.execute(memory, &mut pipe_info, engine).await?;
let result = call_expr.execute(memory, &mut pipe_info, ctx).await?;
args.push(result);
}
Value::BinaryExpression(binary_expression) => {
let result = binary_expression.get_result(memory, &mut pipe_info, ctx).await?;
args.push(result);
}
Value::UnaryExpression(unary_expression) => {
let result = unary_expression.get_result(memory, &mut pipe_info, ctx).await?;
args.push(result);
}
Value::ObjectExpression(object_expression) => {
let result = object_expression.execute(memory, &mut pipe_info, ctx).await?;
args.push(result);
}
Value::ArrayExpression(array_expression) => {
let result = array_expression.execute(memory, &mut pipe_info, ctx).await?;
args.push(result);
}
// We do nothing for the rest.
@ -635,7 +836,7 @@ pub async fn execute(
memory.return_ = Some(ProgramReturn::Arguments(call_expr.arguments.clone()));
} else if let Some(func) = memory.clone().root.get(&fn_name) {
let result = func.call_fn(args.clone(), memory.clone(), engine.clone()).await?;
let result = func.call_fn(args.clone(), memory.clone(), ctx.clone()).await?;
memory.return_ = result;
} else {
@ -661,7 +862,7 @@ pub async fn execute(
memory.add(&var_name, value.clone(), source_range)?;
}
Value::BinaryExpression(binary_expression) => {
let result = binary_expression.get_result(memory, &mut pipe_info, engine).await?;
let result = binary_expression.get_result(memory, &mut pipe_info, ctx).await?;
memory.add(&var_name, result, source_range)?;
}
Value::FunctionExpression(function_expression) => {
@ -670,7 +871,7 @@ pub async fn execute(
memory: ProgramMemory,
function_expression: Box<FunctionExpression>,
_metadata: Vec<Metadata>,
engine: EngineConnection| {
ctx: ExecutorContext| {
Box::pin(async move {
let mut fn_memory = memory.clone();
@ -679,7 +880,7 @@ pub async fn execute(
message: format!(
"Expected {} arguments, got {}",
function_expression.params.len(),
args.len()
args.len(),
),
source_ranges: vec![(&function_expression).into()],
}));
@ -698,7 +899,7 @@ pub async fn execute(
function_expression.body.clone(),
&mut fn_memory,
BodyType::Block,
&engine,
&ctx,
)
.await?;
@ -717,11 +918,11 @@ pub async fn execute(
)?;
}
Value::CallExpression(call_expression) => {
let result = call_expression.execute(memory, &mut pipe_info, engine).await?;
let result = call_expression.execute(memory, &mut pipe_info, ctx).await?;
memory.add(&var_name, result, source_range)?;
}
Value::PipeExpression(pipe_expression) => {
let result = pipe_expression.get_result(memory, &mut pipe_info, engine).await?;
let result = pipe_expression.get_result(memory, &mut pipe_info, ctx).await?;
memory.add(&var_name, result, source_range)?;
}
Value::PipeSubstitution(pipe_substitution) => {
@ -734,11 +935,11 @@ pub async fn execute(
}));
}
Value::ArrayExpression(array_expression) => {
let result = array_expression.execute(memory, &mut pipe_info, engine).await?;
let result = array_expression.execute(memory, &mut pipe_info, ctx).await?;
memory.add(&var_name, result, source_range)?;
}
Value::ObjectExpression(object_expression) => {
let result = object_expression.execute(memory, &mut pipe_info, engine).await?;
let result = object_expression.execute(memory, &mut pipe_info, ctx).await?;
memory.add(&var_name, result, source_range)?;
}
Value::MemberExpression(member_expression) => {
@ -746,7 +947,7 @@ pub async fn execute(
memory.add(&var_name, result, source_range)?;
}
Value::UnaryExpression(unary_expression) => {
let result = unary_expression.get_result(memory, &mut pipe_info, engine).await?;
let result = unary_expression.get_result(memory, &mut pipe_info, ctx).await?;
memory.add(&var_name, result, source_range)?;
}
}
@ -754,11 +955,11 @@ pub async fn execute(
}
BodyItem::ReturnStatement(return_statement) => match &return_statement.argument {
Value::BinaryExpression(bin_expr) => {
let result = bin_expr.get_result(memory, &mut pipe_info, engine).await?;
let result = bin_expr.get_result(memory, &mut pipe_info, ctx).await?;
memory.return_ = Some(ProgramReturn::Value(result));
}
Value::UnaryExpression(unary_expr) => {
let result = unary_expr.get_result(memory, &mut pipe_info, engine).await?;
let result = unary_expr.get_result(memory, &mut pipe_info, ctx).await?;
memory.return_ = Some(ProgramReturn::Value(result));
}
Value::Identifier(identifier) => {
@ -769,15 +970,15 @@ pub async fn execute(
memory.return_ = Some(ProgramReturn::Value(literal.into()));
}
Value::ArrayExpression(array_expr) => {
let result = array_expr.execute(memory, &mut pipe_info, engine).await?;
let result = array_expr.execute(memory, &mut pipe_info, ctx).await?;
memory.return_ = Some(ProgramReturn::Value(result));
}
Value::ObjectExpression(obj_expr) => {
let result = obj_expr.execute(memory, &mut pipe_info, engine).await?;
let result = obj_expr.execute(memory, &mut pipe_info, ctx).await?;
memory.return_ = Some(ProgramReturn::Value(result));
}
Value::CallExpression(call_expr) => {
let result = call_expr.execute(memory, &mut pipe_info, engine).await?;
let result = call_expr.execute(memory, &mut pipe_info, ctx).await?;
memory.return_ = Some(ProgramReturn::Value(result));
}
Value::MemberExpression(member_expr) => {
@ -785,7 +986,7 @@ pub async fn execute(
memory.return_ = Some(ProgramReturn::Value(result));
}
Value::PipeExpression(pipe_expr) => {
let result = pipe_expr.get_result(memory, &mut pipe_info, engine).await?;
let result = pipe_expr.get_result(memory, &mut pipe_info, ctx).await?;
memory.return_ = Some(ProgramReturn::Value(result));
}
Value::PipeSubstitution(_) => {}
@ -804,12 +1005,14 @@ mod tests {
use super::*;
pub async fn parse_execute(code: &str) -> Result<ProgramMemory> {
let tokens = crate::tokeniser::lexer(code);
let tokens = crate::token::lexer(code);
let parser = crate::parser::Parser::new(tokens);
let program = parser.ast()?;
let mut mem: ProgramMemory = Default::default();
let engine = EngineConnection::new().await?;
let memory = execute(program, &mut mem, BodyType::Root, &engine).await?;
let planes = DefaultPlanes::new(&engine).await?;
let ctx = ExecutorContext { engine, planes };
let memory = execute(program, &mut mem, BodyType::Root, &ctx).await?;
Ok(memory)
}
@ -833,7 +1036,8 @@ const newVar = myVar + 1"#;
async fn test_execute_angled_line_that_intersects() {
let ast_fn = |offset: &str| -> String {
format!(
r#"const part001 = startSketchAt([0, 0])
r#"const part001 = startSketchOn('XY')
|> startProfileAt([0, 0], %)
|> lineTo({{to:[2, 2], tag: "yo"}}, %)
|> lineTo([3, 1], %)
|> angledLineThatIntersects({{
@ -880,7 +1084,8 @@ const yo = 5 + 6
const abc = 3
const identifierGuy = 5
const part001 = startSketchAt([-1.2, 4.83])
const part001 = startSketchOn('XY')
|> startProfileAt([-1.2, 4.83], %)
|> line([2.8, 0], %)
|> angledLine([100 + 100, 3.01], %)
|> angledLine([abc, 3.02], %)
@ -897,7 +1102,8 @@ show(part001)"#;
#[tokio::test(flavor = "multi_thread")]
async fn test_execute_with_pipe_substitutions_unary() {
let ast = r#"const myVar = 3
const part001 = startSketchAt([0, 0])
const part001 = startSketchOn('XY')
|> startProfileAt([0, 0], %)
|> line({ to: [3, 4], tag: 'seg01' }, %)
|> line([
min(segLen('seg01', %), myVar),
@ -912,7 +1118,8 @@ show(part001)"#;
#[tokio::test(flavor = "multi_thread")]
async fn test_execute_with_pipe_substitutions() {
let ast = r#"const myVar = 3
const part001 = startSketchAt([0, 0])
const part001 = startSketchOn('XY')
|> startProfileAt([0, 0], %)
|> line({ to: [3, 4], tag: 'seg01' }, %)
|> line([
min(segLen('seg01', %), myVar),
@ -935,7 +1142,8 @@ const halfArmAngle = armAngle / 2
const arrExpShouldNotBeIncluded = [1, 2, 3]
const objExpShouldNotBeIncluded = { a: 1, b: 2, c: 3 }
const part001 = startSketchAt([0, 0])
const part001 = startSketchOn('XY')
|> startProfileAt([0, 0], %)
|> yLineTo(1, %)
|> xLine(3.84, %) // selection-range-7ish-before-this
@ -956,7 +1164,8 @@ fn thing = () => {
return -8
}
const firstExtrude = startSketchAt([0,0])
const firstExtrude = startSketchOn('XY')
|> startProfileAt([0,0], %)
|> line([0, l], %)
|> line([w, 0], %)
|> line([0, thing()], %)
@ -978,7 +1187,8 @@ fn thing = (x) => {
return -x
}
const firstExtrude = startSketchAt([0,0])
const firstExtrude = startSketchOn('XY')
|> startProfileAt([0,0], %)
|> line([0, l], %)
|> line([w, 0], %)
|> line([0, thing(8)], %)
@ -1000,7 +1210,8 @@ fn thing = (x) => {
return [0, -x]
}
const firstExtrude = startSketchAt([0,0])
const firstExtrude = startSketchOn('XY')
|> startProfileAt([0,0], %)
|> line([0, l], %)
|> line([w, 0], %)
|> line(thing(8), %)
@ -1026,7 +1237,8 @@ fn thing = (x) => {
return other_thing(x)
}
const firstExtrude = startSketchAt([0,0])
const firstExtrude = startSketchOn('XY')
|> startProfileAt([0,0], %)
|> line([0, l], %)
|> line([w, 0], %)
|> line([0, thing(8)], %)
@ -1041,7 +1253,8 @@ show(firstExtrude)"#;
#[tokio::test(flavor = "multi_thread")]
async fn test_execute_with_function_sketch() {
let ast = r#"fn box = (h, l, w) => {
const myBox = startSketchAt([0,0])
const myBox = startSketchOn('XY')
|> startProfileAt([0,0], %)
|> line([0, l], %)
|> line([w, 0], %)
|> line([0, -l], %)
@ -1061,7 +1274,8 @@ show(fnBox)"#;
#[tokio::test(flavor = "multi_thread")]
async fn test_get_member_of_object_with_function_period() {
let ast = r#"fn box = (obj) => {
let myBox = startSketchAt(obj.start)
let myBox = startSketchOn('XY')
|> startProfileAt(obj.start, %)
|> line([0, obj.l], %)
|> line([obj.w, 0], %)
|> line([0, -obj.l], %)
@ -1081,7 +1295,8 @@ show(thisBox)
#[tokio::test(flavor = "multi_thread")]
async fn test_get_member_of_object_with_function_brace() {
let ast = r#"fn box = (obj) => {
let myBox = startSketchAt(obj["start"])
let myBox = startSketchOn('XY')
|> startProfileAt(obj["start"], %)
|> line([0, obj["l"]], %)
|> line([obj["w"], 0], %)
|> line([0, -obj["l"]], %)
@ -1101,7 +1316,8 @@ show(thisBox)
#[tokio::test(flavor = "multi_thread")]
async fn test_get_member_of_object_with_function_mix_period_brace() {
let ast = r#"fn box = (obj) => {
let myBox = startSketchAt(obj["start"])
let myBox = startSketchOn('XY')
|> startProfileAt(obj["start"], %)
|> line([0, obj["l"]], %)
|> line([obj["w"], 0], %)
|> line([10 - obj["w"], -obj.l], %)
@ -1122,7 +1338,8 @@ show(thisBox)
#[ignore] // ignore til we get loops
async fn test_execute_with_function_sketch_loop_objects() {
let ast = r#"fn box = (obj) => {
let myBox = startSketchAt(obj.start)
let myBox = startSketchOn('XY')
|> startProfileAt(obj.start, %)
|> line([0, obj.l], %)
|> line([obj.w, 0], %)
|> line([0, -obj.l], %)
@ -1144,7 +1361,8 @@ for var in [{start: [0,0], l: 6, w: 10, h: 3}, {start: [-10,-10], l: 3, w: 5, h:
#[ignore] // ignore til we get loops
async fn test_execute_with_function_sketch_loop_array() {
let ast = r#"fn box = (h, l, w, start) => {
const myBox = startSketchAt([0,0])
const myBox = startSketchOn('XY')
|> startProfileAt([0,0], %)
|> line([0, l], %)
|> line([w, 0], %)
|> line([0, -l], %)
@ -1166,7 +1384,8 @@ for var in [[3, 6, 10, [0,0]], [1.5, 3, 5, [-10,-10]]] {
#[tokio::test(flavor = "multi_thread")]
async fn test_get_member_of_array_with_function() {
let ast = r#"fn box = (array) => {
let myBox = startSketchAt(array[0])
let myBox =startSketchOn('XY')
|> startProfileAt(array[0], %)
|> line([0, array[1]], %)
|> line([array[2], 0], %)
|> line([0, -array[1]], %)
@ -1240,7 +1459,8 @@ const leg1 = 5 // inches
const leg2 = 8 // inches
fn thickness = () => { return 0.56 }
const bracket = startSketchAt([0,0])
const bracket = startSketchOn('XY')
|> startProfileAt([0,0], %)
|> line([0, leg1], %)
|> line([leg2, 0], %)
|> line([0, -thickness()], %)
@ -1264,7 +1484,8 @@ const leg2 = 8 // inches
const thickness_squared = distance * p * FOS * 6 / sigmaAllow
const thickness = 0.56 // inches. App does not support square root function yet
const bracket = startSketchAt([0,0])
const bracket = startSketchOn('XY')
|> startProfileAt([0,0], %)
|> line([0, leg1], %)
|> line([leg2, 0], %)
|> line([0, -thickness], %)
@ -1284,7 +1505,8 @@ const leg1 = 5 // inches
const leg2 = 8 // inches
const thickness_squared = (distance * p * FOS * 6 / (sigmaAllow - width))
const thickness = 0.32 // inches. App does not support square root function yet
const bracket = startSketchAt([0,0])
const bracket = startSketchOn('XY')
|> startProfileAt([0,0], %)
|> line([0, leg1], %)
|> line([leg2, 0], %)
|> line([0, -thickness], %)
@ -1308,7 +1530,8 @@ const leg1 = 5 // inches
const leg2 = 8 // inches
const thickness_squared = distance * p * FOS * 6 / (sigmaAllow - width)
const thickness = 0.32 // inches. App does not support square root function yet
const bracket = startSketchAt([0,0])
const bracket = startSketchOn('XY')
|> startProfileAt([0,0], %)
|> line([0, leg1], %)
|> line([leg2, 0], %)
|> line([0, -thickness], %)

View File

@ -9,4 +9,4 @@ pub mod math_parser;
pub mod parser;
pub mod server;
pub mod std;
pub mod tokeniser;
pub mod token;

View File

@ -10,8 +10,8 @@ use crate::{
},
errors::{KclError, KclErrorDetails},
executor::SourceRange,
parser::{is_not_code_token, Parser},
tokeniser::{Token, TokenType},
parser::Parser,
token::{Token, TokenType},
};
#[derive(Debug, PartialEq, Eq, Deserialize, Serialize, Clone, ts_rs::TS)]
@ -334,7 +334,7 @@ impl ReversePolishNotation {
return rpn.parse();
}
if is_not_code_token(current_token) {
if !current_token.is_code_token() {
let rpn = ReversePolishNotation::new(&self.parser.tokens[1..], &self.previous_postfix, &self.operators);
return rpn.parse();
}
@ -704,7 +704,7 @@ mod test {
#[test]
fn test_parse_expression() {
let tokens = crate::tokeniser::lexer("1 + 2");
let tokens = crate::token::lexer("1 + 2");
let mut parser = MathParser::new(&tokens);
let result = parser.parse().unwrap();
assert_eq!(
@ -731,7 +731,7 @@ mod test {
#[test]
fn test_parse_expression_add_no_spaces() {
let tokens = crate::tokeniser::lexer("1+2");
let tokens = crate::token::lexer("1+2");
let mut parser = MathParser::new(&tokens);
let result = parser.parse().unwrap();
assert_eq!(
@ -758,7 +758,7 @@ mod test {
#[test]
fn test_parse_expression_sub_no_spaces() {
let tokens = crate::tokeniser::lexer("1 -2");
let tokens = crate::token::lexer("1 -2");
let mut parser = MathParser::new(&tokens);
let result = parser.parse().unwrap();
assert_eq!(
@ -785,7 +785,7 @@ mod test {
#[test]
fn test_parse_expression_plus_followed_by_star() {
let tokens = crate::tokeniser::lexer("1 + 2 * 3");
let tokens = crate::token::lexer("1 + 2 * 3");
let mut parser = MathParser::new(&tokens);
let result = parser.parse().unwrap();
assert_eq!(
@ -823,7 +823,7 @@ mod test {
#[test]
fn test_parse_expression_with_parentheses() {
let tokens = crate::tokeniser::lexer("1 * ( 2 + 3 )");
let tokens = crate::token::lexer("1 * ( 2 + 3 )");
let mut parser = MathParser::new(&tokens);
let result = parser.parse().unwrap();
assert_eq!(
@ -861,7 +861,7 @@ mod test {
#[test]
fn test_parse_expression_parens_in_middle() {
let tokens = crate::tokeniser::lexer("1 * ( 2 + 3 ) / 4");
let tokens = crate::token::lexer("1 * ( 2 + 3 ) / 4");
let mut parser = MathParser::new(&tokens);
let result = parser.parse().unwrap();
assert_eq!(
@ -910,7 +910,7 @@ mod test {
#[test]
fn test_parse_expression_parans_and_predence() {
let tokens = crate::tokeniser::lexer("1 + ( 2 + 3 ) / 4");
let tokens = crate::token::lexer("1 + ( 2 + 3 ) / 4");
let mut parser = MathParser::new(&tokens);
let result = parser.parse().unwrap();
assert_eq!(
@ -958,7 +958,7 @@ mod test {
}
#[test]
fn test_parse_expression_nested() {
let tokens = crate::tokeniser::lexer("1 * (( 2 + 3 ) / 4 + 5 )");
let tokens = crate::token::lexer("1 * (( 2 + 3 ) / 4 + 5 )");
let mut parser = MathParser::new(&tokens);
let result = parser.parse().unwrap();
assert_eq!(
@ -1017,7 +1017,7 @@ mod test {
}
#[test]
fn test_parse_expression_redundant_braces() {
let tokens = crate::tokeniser::lexer("1 * ((( 2 + 3 )))");
let tokens = crate::token::lexer("1 * ((( 2 + 3 )))");
let mut parser = MathParser::new(&tokens);
let result = parser.parse().unwrap();
assert_eq!(
@ -1055,7 +1055,7 @@ mod test {
#[test]
fn test_reverse_polish_notation_simple() {
let parser = ReversePolishNotation::new(&crate::tokeniser::lexer("1 + 2"), &[], &[]);
let parser = ReversePolishNotation::new(&crate::token::lexer("1 + 2"), &[], &[]);
let result = parser.parse().unwrap();
assert_eq!(
result,
@ -1084,7 +1084,7 @@ mod test {
#[test]
fn test_reverse_polish_notation_complex() {
let parser = ReversePolishNotation::new(&crate::tokeniser::lexer("1 + 2 * 3"), &[], &[]);
let parser = ReversePolishNotation::new(&crate::token::lexer("1 + 2 * 3"), &[], &[]);
let result = parser.parse().unwrap();
assert_eq!(
result,
@ -1125,7 +1125,7 @@ mod test {
#[test]
fn test_reverse_polish_notation_complex_with_parentheses() {
let parser = ReversePolishNotation::new(&crate::tokeniser::lexer("1 * ( 2 + 3 )"), &[], &[]);
let parser = ReversePolishNotation::new(&crate::token::lexer("1 * ( 2 + 3 )"), &[], &[]);
let result = parser.parse().unwrap();
assert_eq!(
result,
@ -1179,7 +1179,7 @@ mod test {
#[test]
fn test_parse_expression_redundant_braces_around_literal() {
let code = "2 + (((3)))";
let tokens = crate::tokeniser::lexer(code);
let tokens = crate::token::lexer(code);
let mut parser = MathParser::new(&tokens);
let result = parser.parse().unwrap();
assert_eq!(
@ -1274,7 +1274,7 @@ mod test {
#[test]
fn test_parse_expression_braces_around_lots_of_math() {
let code = "(distance * p * FOS * 6 / (sigmaAllow * width))";
let tokens = crate::tokeniser::lexer(code);
let tokens = crate::token::lexer(code);
let mut parser = MathParser::new(&tokens);
let result = parser.parse();
assert!(result.is_ok());
@ -1283,7 +1283,7 @@ mod test {
#[test]
fn test_parse_expression_braces_around_internals_lots_of_math() {
let code = "distance * p * FOS * 6 / (sigmaAllow * width)";
let tokens = crate::tokeniser::lexer(code);
let tokens = crate::token::lexer(code);
let mut parser = MathParser::new(&tokens);
let result = parser.parse();
assert!(result.is_ok());

File diff suppressed because it is too large Load Diff

View File

@ -34,7 +34,7 @@ pub struct Backend {
/// The types of tokens the server supports.
pub token_types: Vec<SemanticTokenType>,
/// Token maps.
pub token_map: DashMap<String, Vec<crate::tokeniser::Token>>,
pub token_map: DashMap<String, Vec<crate::token::Token>>,
/// AST maps.
pub ast_map: DashMap<String, crate::ast::types::Program>,
/// Current code.
@ -56,7 +56,7 @@ impl Backend {
// Lets update the tokens.
self.current_code_map
.insert(params.uri.to_string(), params.text.clone());
let tokens = crate::tokeniser::lexer(&params.text);
let tokens = crate::token::lexer(&params.text);
self.token_map.insert(params.uri.to_string(), tokens.clone());
// Update the semantic tokens map.
@ -69,9 +69,7 @@ impl Backend {
continue;
};
if token.token_type == crate::tokeniser::TokenType::Word
&& self.stdlib_completions.contains_key(&token.value)
{
if token.token_type == crate::token::TokenType::Word && self.stdlib_completions.contains_key(&token.value) {
// This is a stdlib function.
token_type = SemanticTokenType::FUNCTION;
}
@ -549,7 +547,7 @@ impl LanguageServer for Backend {
// Parse the ast.
// I don't know if we need to do this again since it should be updated in the context.
// But I figure better safe than sorry since this will write back out to the file.
let tokens = crate::tokeniser::lexer(&current_code);
let tokens = crate::token::lexer(&current_code);
let parser = crate::parser::Parser::new(tokens);
let Ok(ast) = parser.ast() else {
return Ok(None);
@ -581,7 +579,7 @@ impl LanguageServer for Backend {
// Parse the ast.
// I don't know if we need to do this again since it should be updated in the context.
// But I figure better safe than sorry since this will write back out to the file.
let tokens = crate::tokeniser::lexer(&current_code);
let tokens = crate::token::lexer(&current_code);
let parser = crate::parser::Parser::new(tokens);
let Ok(mut ast) = parser.ast() else {
return Ok(None);

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