Compare commits
1 Commits
do-not-flu
...
franknoiro
Author | SHA1 | Date | |
---|---|---|---|
cac7aa2bee |
10
.github/workflows/cargo-test.yml
vendored
10
.github/workflows/cargo-test.yml
vendored
@ -1,22 +1,16 @@
|
|||||||
name: cargo test
|
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
pull_request:
|
pull_request:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
schedule:
|
|
||||||
- cron: 0 * * * * # hourly
|
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
pull-requests: write
|
pull-requests: write
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
name: cargo test
|
||||||
jobs:
|
jobs:
|
||||||
build-test-artifacts:
|
build-test-artifacts:
|
||||||
name: Build test artifacts
|
name: Build test artifacts
|
||||||
@ -199,7 +193,7 @@ jobs:
|
|||||||
TAB_API_KEY: ${{ secrets.TAB_API_KEY }}
|
TAB_API_KEY: ${{ secrets.TAB_API_KEY }}
|
||||||
CI_COMMIT_SHA: ${{ github.event.pull_request.head.sha }}
|
CI_COMMIT_SHA: ${{ github.event.pull_request.head.sha }}
|
||||||
CI_PR_NUMBER: ${{ github.event.pull_request.number }}
|
CI_PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||||
CI_SUITE: e2e:kcl
|
CI_SUITE: unit:kcl
|
||||||
run-internal-kcl-samples:
|
run-internal-kcl-samples:
|
||||||
name: cargo test (internal-kcl-samples)
|
name: cargo test (internal-kcl-samples)
|
||||||
runs-on:
|
runs-on:
|
||||||
|
6
.github/workflows/e2e-tests.yml
vendored
6
.github/workflows/e2e-tests.yml
vendored
@ -1,5 +1,4 @@
|
|||||||
name: E2E Tests
|
name: E2E Tests
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
@ -157,7 +156,7 @@ jobs:
|
|||||||
TAB_API_KEY: ${{ secrets.TAB_API_KEY }}
|
TAB_API_KEY: ${{ secrets.TAB_API_KEY }}
|
||||||
CI_COMMIT_SHA: ${{ github.event.pull_request.head.sha }}
|
CI_COMMIT_SHA: ${{ github.event.pull_request.head.sha }}
|
||||||
CI_PR_NUMBER: ${{ github.event.pull_request.number }}
|
CI_PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||||
CI_SUITE: e2e:snapshots
|
CI_SUITE: snapshots
|
||||||
TARGET: web
|
TARGET: web
|
||||||
|
|
||||||
- name: Update snapshots
|
- name: Update snapshots
|
||||||
@ -169,7 +168,7 @@ jobs:
|
|||||||
TAB_API_KEY: ${{ secrets.TAB_API_KEY }}
|
TAB_API_KEY: ${{ secrets.TAB_API_KEY }}
|
||||||
CI_COMMIT_SHA: ${{ github.event.pull_request.head.sha }}
|
CI_COMMIT_SHA: ${{ github.event.pull_request.head.sha }}
|
||||||
CI_PR_NUMBER: ${{ github.event.pull_request.number }}
|
CI_PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||||
CI_SUITE: e2e:snapshots
|
CI_SUITE: snapshots
|
||||||
TARGET: web
|
TARGET: web
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v4
|
- uses: actions/upload-artifact@v4
|
||||||
@ -320,7 +319,6 @@ jobs:
|
|||||||
TAB_API_KEY: ${{ secrets.TAB_API_KEY }}
|
TAB_API_KEY: ${{ secrets.TAB_API_KEY }}
|
||||||
CI_COMMIT_SHA: ${{ github.event.pull_request.head.sha }}
|
CI_COMMIT_SHA: ${{ github.event.pull_request.head.sha }}
|
||||||
CI_PR_NUMBER: ${{ github.event.pull_request.number }}
|
CI_PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||||
CI_SUITE: e2e:desktop
|
|
||||||
TARGET: desktop
|
TARGET: desktop
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v4
|
- uses: actions/upload-artifact@v4
|
||||||
|
@ -122,11 +122,12 @@ https://github.com/KittyCAD/modeling-app/issues/new
|
|||||||
|
|
||||||
#### 2. Push a new tag
|
#### 2. Push a new tag
|
||||||
|
|
||||||
Decide on a `v`-prefixed semver `VERSION` (eg. `v1.2.3`) with the team and tag the repo, eg. on latest main:
|
Create a new tag and push it to the repo. The `semantic-release.sh` script will automatically bump the minor part, which we use the most. For instance going from `v0.27.0` to `v0.28.0`.
|
||||||
|
|
||||||
```
|
```
|
||||||
|
VERSION=$(./scripts/semantic-release.sh)
|
||||||
git tag $VERSION
|
git tag $VERSION
|
||||||
git push origin $VERSION
|
git push origin --tags
|
||||||
```
|
```
|
||||||
|
|
||||||
This will trigger the `build-apps` workflow, set the version, build & sign the apps, and generate release files.
|
This will trigger the `build-apps` workflow, set the version, build & sign the apps, and generate release files.
|
||||||
|
@ -4,7 +4,7 @@ Compared to other CAD software, getting Zoo Design Studio up and running is quic
|
|||||||
|
|
||||||
## Windows
|
## Windows
|
||||||
|
|
||||||
1. Download the [Zoo Design Studio installer](https://zoo.dev/design-studio/download) for Windows and for your processor type.
|
1. Download the [Zoo Design Studio installer](https://zoo.dev/modeling-app/download) for Windows and for your processor type.
|
||||||
|
|
||||||
2. Once downloaded, run the installer `Zoo Design Studio-{version}-{arch}-win.exe` which should take a few seconds.
|
2. Once downloaded, run the installer `Zoo Design Studio-{version}-{arch}-win.exe` which should take a few seconds.
|
||||||
|
|
||||||
@ -12,7 +12,7 @@ Compared to other CAD software, getting Zoo Design Studio up and running is quic
|
|||||||
|
|
||||||
## macOS
|
## macOS
|
||||||
|
|
||||||
1. Download the [Zoo Design Studio installer](https://zoo.dev/design-studio/download) for macOS and for your processor type.
|
1. Download the [Zoo Design Studio installer](https://zoo.dev/modeling-app/download) for macOS and for your processor type.
|
||||||
|
|
||||||
2. Once downloaded, open the disk image `Zoo Design Studio-{version}-{arch}-mac.dmg` and drag the applications to your `Applications` directory.
|
2. Once downloaded, open the disk image `Zoo Design Studio-{version}-{arch}-mac.dmg` and drag the applications to your `Applications` directory.
|
||||||
|
|
||||||
@ -21,7 +21,7 @@ Compared to other CAD software, getting Zoo Design Studio up and running is quic
|
|||||||
|
|
||||||
## Linux
|
## Linux
|
||||||
|
|
||||||
1. Download the [Zoo Design Studio installer](https://zoo.dev/design-studio/download) for Linux and for your processor type.
|
1. Download the [Zoo Design Studio installer](https://zoo.dev/modeling-app/download) for Linux and for your processor type.
|
||||||
|
|
||||||
2. Install the dependencies needed to run the [AppImage format](https://appimage.org/).
|
2. Install the dependencies needed to run the [AppImage format](https://appimage.org/).
|
||||||
- On Ubuntu, install the FUSE library with these commands in a terminal.
|
- On Ubuntu, install the FUSE library with these commands in a terminal.
|
||||||
|
10
README.md
10
README.md
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
# Zoo Design Studio
|
# Zoo Design Studio
|
||||||
|
|
||||||
[zoo.dev/design-studio](https://zoo.dev/design-studio)
|
[zoo.dev/modeling-app](https://zoo.dev/modeling-app)
|
||||||
|
|
||||||
A CAD application from the future, brought to you by the [Zoo team](https://zoo.dev).
|
A CAD application from the future, brought to you by the [Zoo team](https://zoo.dev).
|
||||||
|
|
||||||
@ -40,8 +40,12 @@ The 3D view in Design Studio is just a video stream from our hosted geometry eng
|
|||||||
|
|
||||||
## Get Started
|
## Get Started
|
||||||
|
|
||||||
We recommend downloading the latest application binary from our [website](https://zoo.dev/design-studio/download). If you don't see your platform or architecture supported there, please file an issue. See the [installation guide](INSTALL.md) for additional instructions.
|
We recommend downloading the latest application binary from our [releases](https://github.com/KittyCAD/modeling-app/releases) page. If you don't see your platform or architecture supported there, please file an issue.
|
||||||
|
|
||||||
## Developing
|
## Developing
|
||||||
|
|
||||||
Finally, if you'd like to run a development build or contribute to the project, please visit our [contributor guide](CONTRIBUTING.md) to get started. To contribute to the KittyCAD Language, see the dedicated [readme](rust/kcl-lib/README.md) for KCL.
|
Finally, if you'd like to run a development build or contribute to the project, please visit our [contributor guide](CONTRIBUTING.md) to get started.
|
||||||
|
|
||||||
|
## KCL
|
||||||
|
|
||||||
|
To contribute to the KittyCAD Language, see the [README](https://github.com/KittyCAD/modeling-app/tree/main/rust/kcl-lib) for KCL.
|
||||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -16,8 +16,6 @@ layout: manual
|
|||||||
* [`helix`](/docs/kcl-std/functions/std-helix)
|
* [`helix`](/docs/kcl-std/functions/std-helix)
|
||||||
* [`offsetPlane`](/docs/kcl-std/functions/std-offsetPlane)
|
* [`offsetPlane`](/docs/kcl-std/functions/std-offsetPlane)
|
||||||
* [`patternLinear2d`](/docs/kcl-std/patternLinear2d)
|
* [`patternLinear2d`](/docs/kcl-std/patternLinear2d)
|
||||||
* [**std::appearance**](/docs/kcl-std/modules/std-appearance)
|
|
||||||
* [`appearance::hexString`](/docs/kcl-std/functions/std-appearance-hexString)
|
|
||||||
* [**std::array**](/docs/kcl-std/modules/std-array)
|
* [**std::array**](/docs/kcl-std/modules/std-array)
|
||||||
* [`map`](/docs/kcl-std/functions/std-array-map)
|
* [`map`](/docs/kcl-std/functions/std-array-map)
|
||||||
* [`pop`](/docs/kcl-std/functions/std-array-pop)
|
* [`pop`](/docs/kcl-std/functions/std-array-pop)
|
||||||
|
@ -1,16 +0,0 @@
|
|||||||
---
|
|
||||||
title: "appearance"
|
|
||||||
subtitle: "Module in std"
|
|
||||||
excerpt: ""
|
|
||||||
layout: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Functions and constants
|
|
||||||
|
|
||||||
* [`appearance::hexString`](/docs/kcl-std/functions/std-appearance-hexString)
|
|
||||||
|
|
@ -15,7 +15,6 @@ You might also want the [KCL language reference](/docs/kcl-lang) or the [KCL gui
|
|||||||
|
|
||||||
## Modules
|
## Modules
|
||||||
|
|
||||||
* [`appearance::appearance`](/docs/kcl-std/modules/std-appearance)
|
|
||||||
* [`array`](/docs/kcl-std/modules/std-array)
|
* [`array`](/docs/kcl-std/modules/std-array)
|
||||||
* [`math`](/docs/kcl-std/modules/std-math)
|
* [`math`](/docs/kcl-std/modules/std-math)
|
||||||
* [`sketch`](/docs/kcl-std/modules/std-sketch)
|
* [`sketch`](/docs/kcl-std/modules/std-sketch)
|
||||||
|
@ -26,7 +26,7 @@ patternLinear3d(
|
|||||||
| `solids` | [`[Solid]`](/docs/kcl-std/types/std-types-Solid) | The solid(s) to duplicate | Yes |
|
| `solids` | [`[Solid]`](/docs/kcl-std/types/std-types-Solid) | The solid(s) to duplicate | Yes |
|
||||||
| `instances` | [`number`](/docs/kcl-std/types/std-types-number) | The number of total instances. Must be greater than or equal to 1. This includes the original entity. For example, if instances is 2, there will be two copies -- the original, and one new copy. If instances is 1, this has no effect. | Yes |
|
| `instances` | [`number`](/docs/kcl-std/types/std-types-number) | The number of total instances. Must be greater than or equal to 1. This includes the original entity. For example, if instances is 2, there will be two copies -- the original, and one new copy. If instances is 1, this has no effect. | Yes |
|
||||||
| `distance` | [`number`](/docs/kcl-std/types/std-types-number) | Distance between each repetition. Also known as 'spacing'. | Yes |
|
| `distance` | [`number`](/docs/kcl-std/types/std-types-number) | Distance between each repetition. Also known as 'spacing'. | Yes |
|
||||||
| `axis` | [`Point3d`](/docs/kcl-std/types/std-types-Point3d) | The axis of the pattern. A 3D vector. | Yes |
|
| `axis` | [`Point3d`](/docs/kcl-std/types/std-types-Point3d) | The axis of the pattern. A 2D vector. | Yes |
|
||||||
| `useOriginal` | [`bool`](/docs/kcl-std/types/std-types-bool) | If the target was sketched on an extrusion, setting this will use the original sketch as the target, not the entire joined solid. Defaults to false. | No |
|
| `useOriginal` | [`bool`](/docs/kcl-std/types/std-types-bool) | If the target was sketched on an extrusion, setting this will use the original sketch as the target, not the entire joined solid. Defaults to false. | No |
|
||||||
|
|
||||||
### Returns
|
### Returns
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -78,10 +78,11 @@ extrude001 = extrude(sketch001, length = 5)`
|
|||||||
|
|
||||||
// Delete a character to break the KCL
|
// Delete a character to break the KCL
|
||||||
await editor.openPane()
|
await editor.openPane()
|
||||||
await editor.scrollToText('extrude(%, length = width)')
|
await editor.scrollToText('bracketLeg1Sketch, length = thickness)')
|
||||||
await page.getByText('extrude(%, length = width)').click()
|
await page
|
||||||
|
.getByText('extrude(bracketLeg1Sketch, length = thickness)')
|
||||||
await page.keyboard.press(')')
|
.click()
|
||||||
|
await page.keyboard.press('Backspace')
|
||||||
|
|
||||||
// Ensure that a badge appears on the button
|
// Ensure that a badge appears on the button
|
||||||
await expect(codePaneButtonHolder).toContainText('notification')
|
await expect(codePaneButtonHolder).toContainText('notification')
|
||||||
@ -98,11 +99,16 @@ extrude001 = extrude(sketch001, length = 5)`
|
|||||||
|
|
||||||
await page.waitForTimeout(500)
|
await page.waitForTimeout(500)
|
||||||
|
|
||||||
|
// Ensure that a badge appears on the button
|
||||||
|
await expect(codePaneButtonHolder).toContainText('notification')
|
||||||
|
// Ensure we have no errors in the gutter.
|
||||||
|
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||||
|
|
||||||
// Open the code pane
|
// Open the code pane
|
||||||
await editor.openPane()
|
await editor.openPane()
|
||||||
|
|
||||||
// Go to our problematic code again
|
// Go to our problematic code again (missing closing paren!)
|
||||||
await editor.scrollToText('extrude(%, length = w')
|
await editor.scrollToText('extrude(bracketLeg1Sketch, length = thickness')
|
||||||
|
|
||||||
// Ensure that a badge appears on the button
|
// Ensure that a badge appears on the button
|
||||||
await expect(codePaneButtonHolder).toContainText('notification')
|
await expect(codePaneButtonHolder).toContainText('notification')
|
||||||
|
@ -1001,7 +1001,7 @@ a1 = startSketchOn(offsetPlane(XY, offset = 10))
|
|||||||
await expect(page.locator('.cm-content')).toHaveText(
|
await expect(page.locator('.cm-content')).toHaveText(
|
||||||
`@settings(defaultLengthUnit = in)
|
`@settings(defaultLengthUnit = in)
|
||||||
sketch001 = startSketchOn(XZ)
|
sketch001 = startSketchOn(XZ)
|
||||||
|> startProfile(%, at = [0, 12])
|
|> startProfile(%, at = [3.14, 12])
|
||||||
|> xLine(%, length = 5) // lin`.replaceAll('\n', '')
|
|> xLine(%, length = 5) // lin`.replaceAll('\n', '')
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -1076,7 +1076,7 @@ sketch001 = startSketchOn(XZ)
|
|||||||
await expect(page.locator('.cm-content')).toHaveText(
|
await expect(page.locator('.cm-content')).toHaveText(
|
||||||
`@settings(defaultLengthUnit = in)
|
`@settings(defaultLengthUnit = in)
|
||||||
sketch001 = startSketchOn(XZ)
|
sketch001 = startSketchOn(XZ)
|
||||||
|> startProfile(%, at = [0, 12])
|
|> startProfile(%, at = [3.14, 12])
|
||||||
|> xLine(%, length = 5) // lin`.replaceAll('\n', '')
|
|> xLine(%, length = 5) // lin`.replaceAll('\n', '')
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@ -1590,38 +1590,4 @@ sketch001 = startSketchOn(XZ)
|
|||||||
await expect(page.getByTestId('center-rectangle')).toBeVisible()
|
await expect(page.getByTestId('center-rectangle')).toBeVisible()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test('syntax errors still show when reopening KCL pane', async ({
|
|
||||||
page,
|
|
||||||
homePage,
|
|
||||||
scene,
|
|
||||||
cmdBar,
|
|
||||||
}) => {
|
|
||||||
const u = await getUtils(page)
|
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
|
||||||
|
|
||||||
// Wait for connection, this is especially important for this test, because safeParse is invoked when
|
|
||||||
// connection is established which would interfere with the test if it happened during later steps.
|
|
||||||
await scene.connectionEstablished()
|
|
||||||
await scene.settled(cmdBar)
|
|
||||||
|
|
||||||
// Code with no error
|
|
||||||
await u.codeLocator.fill(`x = 7`)
|
|
||||||
await page.waitForTimeout(200) // allow some time for the error to show potentially
|
|
||||||
await expect(page.locator('.cm-lint-marker-error')).toHaveCount(0)
|
|
||||||
|
|
||||||
// Code with error
|
|
||||||
await u.codeLocator.fill(`x 7`)
|
|
||||||
await expect(page.locator('.cm-lint-marker-error')).toHaveCount(1)
|
|
||||||
|
|
||||||
// Close and reopen KCL code panel
|
|
||||||
await u.closeKclCodePanel()
|
|
||||||
await expect(page.locator('.cm-lint-marker-error')).toHaveCount(0) // error disappears on close
|
|
||||||
await u.openKclCodePanel()
|
|
||||||
|
|
||||||
// Verify error is still visible
|
|
||||||
await expect(page.locator('.cm-lint-marker-error')).toHaveCount(1)
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
@ -70,28 +70,22 @@ test.describe('Point-and-click assemblies tests', () => {
|
|||||||
await test.step('Setup parts and expect empty assembly scene', async () => {
|
await test.step('Setup parts and expect empty assembly scene', async () => {
|
||||||
const projectName = 'assembly'
|
const projectName = 'assembly'
|
||||||
await context.folderSetupFn(async (dir) => {
|
await context.folderSetupFn(async (dir) => {
|
||||||
const projDir = path.join(dir, projectName)
|
const bracketDir = path.join(dir, projectName)
|
||||||
const nestedProjDir = path.join(dir, projectName, 'nested', 'twice')
|
await fsp.mkdir(bracketDir, { recursive: true })
|
||||||
await fsp.mkdir(projDir, { recursive: true })
|
|
||||||
await fsp.mkdir(nestedProjDir, { recursive: true })
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
fsp.copyFile(
|
fsp.copyFile(
|
||||||
executorInputPath('cylinder.kcl'),
|
executorInputPath('cylinder.kcl'),
|
||||||
path.join(projDir, 'cylinder.kcl')
|
path.join(bracketDir, 'cylinder.kcl')
|
||||||
),
|
|
||||||
fsp.copyFile(
|
|
||||||
executorInputPath('cylinder.kcl'),
|
|
||||||
path.join(nestedProjDir, 'main.kcl')
|
|
||||||
),
|
),
|
||||||
fsp.copyFile(
|
fsp.copyFile(
|
||||||
executorInputPath('e2e-can-sketch-on-chamfer.kcl'),
|
executorInputPath('e2e-can-sketch-on-chamfer.kcl'),
|
||||||
path.join(projDir, 'bracket.kcl')
|
path.join(bracketDir, 'bracket.kcl')
|
||||||
),
|
),
|
||||||
fsp.copyFile(
|
fsp.copyFile(
|
||||||
testsInputPath('cube.step'),
|
testsInputPath('cube.step'),
|
||||||
path.join(projDir, 'cube.step')
|
path.join(bracketDir, 'cube.step')
|
||||||
),
|
),
|
||||||
fsp.writeFile(path.join(projDir, 'main.kcl'), ''),
|
fsp.writeFile(path.join(bracketDir, 'main.kcl'), ''),
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||||
@ -173,25 +167,6 @@ test.describe('Point-and-click assemblies tests', () => {
|
|||||||
await expect(
|
await expect(
|
||||||
page.getByText('This file is already imported')
|
page.getByText('This file is already imported')
|
||||||
).toBeVisible()
|
).toBeVisible()
|
||||||
await cmdBar.closeCmdBar()
|
|
||||||
})
|
|
||||||
|
|
||||||
await test.step('Insert a nested kcl part', async () => {
|
|
||||||
await insertPartIntoAssembly(
|
|
||||||
'nested/twice/main.kcl',
|
|
||||||
'main',
|
|
||||||
toolbar,
|
|
||||||
cmdBar,
|
|
||||||
page
|
|
||||||
)
|
|
||||||
await toolbar.openPane('code')
|
|
||||||
await page.waitForTimeout(10000)
|
|
||||||
await editor.expectEditor.toContain(
|
|
||||||
`
|
|
||||||
import "nested/twice/main.kcl" as main
|
|
||||||
`,
|
|
||||||
{ shouldNormalise: true }
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import fs from 'fs'
|
||||||
|
import { join } from 'path'
|
||||||
import type { Page } from '@playwright/test'
|
import type { Page } from '@playwright/test'
|
||||||
|
|
||||||
import { createProject, getUtils } from '@e2e/playwright/test-utils'
|
import { createProject, getUtils } from '@e2e/playwright/test-utils'
|
||||||
@ -401,6 +403,106 @@ test.describe('Text-to-CAD tests', () => {
|
|||||||
await expect(page.getByText(promptWithNewline)).toBeVisible()
|
await expect(page.getByText(promptWithNewline)).toBeVisible()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// This will be fine once greg makes prompt at top of file deterministic
|
||||||
|
test('can do many at once and get many prompts back, and interact with many', async ({
|
||||||
|
page,
|
||||||
|
homePage,
|
||||||
|
cmdBar,
|
||||||
|
}) => {
|
||||||
|
// Let this test run longer since we've seen it timeout.
|
||||||
|
test.setTimeout(180_000)
|
||||||
|
|
||||||
|
const u = await getUtils(page)
|
||||||
|
|
||||||
|
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||||
|
|
||||||
|
await homePage.goToModelingScene()
|
||||||
|
await u.waitForPageLoad()
|
||||||
|
|
||||||
|
await sendPromptFromCommandBarAndSetExistingProject(
|
||||||
|
page,
|
||||||
|
'a 2x4 lego',
|
||||||
|
cmdBar
|
||||||
|
)
|
||||||
|
|
||||||
|
await sendPromptFromCommandBarAndSetExistingProject(
|
||||||
|
page,
|
||||||
|
'a 2x8 lego',
|
||||||
|
cmdBar
|
||||||
|
)
|
||||||
|
|
||||||
|
await sendPromptFromCommandBarAndSetExistingProject(
|
||||||
|
page,
|
||||||
|
'a 2x10 lego',
|
||||||
|
cmdBar
|
||||||
|
)
|
||||||
|
|
||||||
|
// Find the toast.
|
||||||
|
// Look out for the toast message
|
||||||
|
const submittingToastMessage = page.getByText(
|
||||||
|
`Submitting to Text-to-CAD API...`
|
||||||
|
)
|
||||||
|
await expect(submittingToastMessage.first()).toBeVisible()
|
||||||
|
|
||||||
|
const generatingToastMessage = page.getByText(
|
||||||
|
`Generating parametric model...`
|
||||||
|
)
|
||||||
|
await expect(generatingToastMessage.first()).toBeVisible({
|
||||||
|
timeout: 10_000,
|
||||||
|
})
|
||||||
|
|
||||||
|
const successToastMessage = page.getByText(`Text-to-CAD successful`)
|
||||||
|
// We should have three success toasts.
|
||||||
|
await expect(successToastMessage).toHaveCount(3, { timeout: 25_000 })
|
||||||
|
|
||||||
|
await expect(page.getByText(`a 2x4 lego`)).toBeVisible()
|
||||||
|
await expect(page.getByText(`a 2x8 lego`)).toBeVisible()
|
||||||
|
await expect(page.getByText(`a 2x10 lego`)).toBeVisible()
|
||||||
|
|
||||||
|
// Ensure if you reject one, the others stay.
|
||||||
|
const rejectButton = page.getByRole('button', { name: 'Reject' })
|
||||||
|
await expect(rejectButton.first()).toBeVisible()
|
||||||
|
// Click the reject button on the first toast.
|
||||||
|
await rejectButton.first().click()
|
||||||
|
|
||||||
|
// The first toast should disappear, but not the others.
|
||||||
|
await expect(page.getByText(`a 2x10 lego`)).not.toBeVisible()
|
||||||
|
await expect(page.getByText(`a 2x8 lego`)).toBeVisible()
|
||||||
|
await expect(page.getByText(`a 2x4 lego`)).toBeVisible()
|
||||||
|
|
||||||
|
// Ensure you can copy the code for one of the models remaining.
|
||||||
|
const copyToClipboardButton = page.getByRole('button', {
|
||||||
|
name: 'Accept',
|
||||||
|
})
|
||||||
|
await expect(copyToClipboardButton.first()).toBeVisible()
|
||||||
|
// Click the button.
|
||||||
|
await copyToClipboardButton.first().click()
|
||||||
|
|
||||||
|
// Do NOT do AI tests like this: "Expect the code to be pasted."
|
||||||
|
// Reason: AI tests are NONDETERMINISTIC. Thus we need to be as most
|
||||||
|
// general as we can for the assertion.
|
||||||
|
// We can use Kolmogorov complexity as a measurement of the
|
||||||
|
// "probably most minimal version of this program" to have a lower
|
||||||
|
// bound to work with. It is completely by feel because there are
|
||||||
|
// no proofs that any program is its smallest self.
|
||||||
|
const code2x8 = await page.locator('.cm-content').innerText()
|
||||||
|
await expect(code2x8.length).toBeGreaterThan(249)
|
||||||
|
|
||||||
|
// Ensure the final toast remains.
|
||||||
|
await expect(page.getByText(`a 2x10 lego`)).not.toBeVisible()
|
||||||
|
await expect(page.getByText(`Prompt: "a 2x8 lego`)).not.toBeVisible()
|
||||||
|
await expect(page.getByText(`a 2x4 lego`)).toBeVisible()
|
||||||
|
|
||||||
|
// Ensure you can copy the code for the final model.
|
||||||
|
await expect(copyToClipboardButton).toBeVisible()
|
||||||
|
// Click the button.
|
||||||
|
await copyToClipboardButton.click()
|
||||||
|
|
||||||
|
// Expect the code to be pasted.
|
||||||
|
const code2x4 = await page.locator('.cm-content').innerText()
|
||||||
|
await expect(code2x4.length).toBeGreaterThan(249)
|
||||||
|
})
|
||||||
|
|
||||||
test('can do many at once with errors, clicking dismiss error does not dismiss all', async ({
|
test('can do many at once with errors, clicking dismiss error does not dismiss all', async ({
|
||||||
page,
|
page,
|
||||||
homePage,
|
homePage,
|
||||||
@ -573,6 +675,82 @@ async function sendPromptFromCommandBarAndSetExistingProject(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test(
|
||||||
|
'Text-to-CAD functionality',
|
||||||
|
{ tag: '@electron' },
|
||||||
|
async ({ context, page, cmdBar }, testInfo) => {
|
||||||
|
const projectName = 'project-000'
|
||||||
|
const prompt = 'lego 2x4'
|
||||||
|
const textToCadFileName = 'lego-2x4.kcl'
|
||||||
|
|
||||||
|
const { dir } = await context.folderSetupFn(async () => {})
|
||||||
|
|
||||||
|
const fileExists = () =>
|
||||||
|
fs.existsSync(join(dir, projectName, textToCadFileName))
|
||||||
|
|
||||||
|
const { openFilePanel, openKclCodePanel, waitForPageLoad } = await getUtils(
|
||||||
|
page,
|
||||||
|
test
|
||||||
|
)
|
||||||
|
|
||||||
|
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||||
|
|
||||||
|
// Locators
|
||||||
|
const projectMenuButton = page
|
||||||
|
.getByTestId('project-sidebar-toggle')
|
||||||
|
.filter({ hasText: projectName })
|
||||||
|
const textToCadFileButton = page.getByRole('listitem').filter({
|
||||||
|
has: page.getByRole('button', { name: textToCadFileName }),
|
||||||
|
})
|
||||||
|
const textToCadComment = page.getByText(
|
||||||
|
`// Generated by Text-to-CAD: ${prompt}`
|
||||||
|
)
|
||||||
|
|
||||||
|
// Create and navigate to the project
|
||||||
|
await createProject({ name: 'project-000', page })
|
||||||
|
|
||||||
|
// Wait for Start Sketch otherwise you will not have access Text-to-CAD command
|
||||||
|
await waitForPageLoad()
|
||||||
|
await openFilePanel()
|
||||||
|
await openKclCodePanel()
|
||||||
|
|
||||||
|
await test.step(`Test file creation`, async () => {
|
||||||
|
await sendPromptFromCommandBarAndSetExistingProject(
|
||||||
|
page,
|
||||||
|
prompt,
|
||||||
|
cmdBar,
|
||||||
|
projectName
|
||||||
|
)
|
||||||
|
// File is considered created if it shows up in the Project Files pane
|
||||||
|
await expect(textToCadFileButton).toBeVisible({ timeout: 20_000 })
|
||||||
|
expect(fileExists()).toBeTruthy()
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step(`Test file navigation`, async () => {
|
||||||
|
await expect(projectMenuButton).toContainText('main.kcl')
|
||||||
|
await textToCadFileButton.click()
|
||||||
|
// File can be navigated and loaded assuming a specific KCL comment is loaded into the KCL code pane
|
||||||
|
await expect(textToCadComment).toBeVisible({ timeout: 20_000 })
|
||||||
|
await expect(projectMenuButton).toContainText(textToCadFileName)
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step(`Test file deletion on rejection`, async () => {
|
||||||
|
const rejectButton = page.getByRole('button', { name: 'Reject' })
|
||||||
|
// A file is created and can be navigated to while this prompt is still opened
|
||||||
|
// Click the "Reject" button within the prompt and it will delete the file.
|
||||||
|
await rejectButton.click()
|
||||||
|
|
||||||
|
const submittingToastMessage = page.getByText(
|
||||||
|
`Successfully deleted file "lego-2x4.kcl"`
|
||||||
|
)
|
||||||
|
await expect(submittingToastMessage).toBeVisible()
|
||||||
|
expect(fileExists()).toBeFalsy()
|
||||||
|
// Confirm we've navigated back to the main.kcl file after deletion
|
||||||
|
await expect(projectMenuButton).toContainText('main.kcl')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Below there are twelve (12) tests for testing the navigation and file creation
|
* Below there are twelve (12) tests for testing the navigation and file creation
|
||||||
* logic around text to cad. The Text to CAD command is now globally available
|
* logic around text to cad. The Text to CAD command is now globally available
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
<link rel="apple-touch-icon" href="/logo192.png" />
|
<link rel="apple-touch-icon" href="/logo192.png" />
|
||||||
<link rel="manifest" href="/manifest.json" />
|
<link rel="manifest" href="/manifest.json" />
|
||||||
<link rel="stylesheet" href="./inter/inter.css" />
|
<link rel="stylesheet" href="./inter/inter.css" />
|
||||||
|
<link rel="stylesheet" href="https://use.typekit.net/zzv8rvm.css" />
|
||||||
<script
|
<script
|
||||||
defer
|
defer
|
||||||
data-domain="app.zoo.dev"
|
data-domain="app.zoo.dev"
|
||||||
|
1
package-lock.json
generated
1
package-lock.json
generated
@ -2492,7 +2492,6 @@
|
|||||||
},
|
},
|
||||||
"node_modules/@clack/prompts/node_modules/is-unicode-supported": {
|
"node_modules/@clack/prompts/node_modules/is-unicode-supported": {
|
||||||
"version": "1.3.0",
|
"version": "1.3.0",
|
||||||
"extraneous": true,
|
|
||||||
"inBundle": true,
|
"inBundle": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
|
@ -27,7 +27,7 @@ if len(modified_release_body) > max_length:
|
|||||||
# Message to send to Discord
|
# Message to send to Discord
|
||||||
data = {
|
data = {
|
||||||
"content": textwrap.dedent(f'''
|
"content": textwrap.dedent(f'''
|
||||||
**{release_version}** is now available! Check out the latest features and improvements here: <https://zoo.dev/design-studio>
|
**{release_version}** is now available! Check out the latest features and improvements here: <https://zoo.dev/modeling-app/download>
|
||||||
|
|
||||||
{modified_release_body}
|
{modified_release_body}
|
||||||
'''),
|
'''),
|
||||||
|
142
public/kcl-samples/80-20-rail/main.kcl
Normal file
142
public/kcl-samples/80-20-rail/main.kcl
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
// 80/20 Rail
|
||||||
|
// An 80/20 extruded aluminum linear rail. T-slot profile adjustable by profile height, rail length, and origin position
|
||||||
|
|
||||||
|
// Set units
|
||||||
|
@settings(defaultLengthUnit = in, kclVersion = 1.0)
|
||||||
|
|
||||||
|
// Create a function to make the 80-20 rail
|
||||||
|
fn rail8020(originStart, railHeight, railLength) {
|
||||||
|
// Sketch side 1 of profile
|
||||||
|
sketch001 = startSketchOn(-XZ)
|
||||||
|
|> startProfile(at = [
|
||||||
|
originStart[0],
|
||||||
|
0.1 * railHeight + originStart[1]
|
||||||
|
])
|
||||||
|
|> arc(angleStart = 180, angleEnd = 270, radius = 0.1 * railHeight)
|
||||||
|
|> arc(angleStart = 180, angleEnd = 0, radius = 0.072 / 4 * railHeight)
|
||||||
|
|> xLine(length = 0.1 * railHeight)
|
||||||
|
|> arc(angleStart = 180, angleEnd = 0, radius = 0.072 / 4 * railHeight)
|
||||||
|
|> xLine(length = 0.06 * railHeight, tag = $edge1)
|
||||||
|
|> yLine(length = 0.087 * railHeight, tag = $edge2)
|
||||||
|
|> xLine(length = -0.183 * railHeight, tag = $edge3)
|
||||||
|
|> angledLine(angle = 45, endAbsoluteY = (1 - 0.356) / 2 * railHeight + originStart[1], tag = $edge4)
|
||||||
|
|> xLine(length = 0.232 * railHeight, tag = $edge5)
|
||||||
|
|> angledLine(angle = -45, endAbsoluteY = 0.087 * railHeight + originStart[1], tag = $edge6)
|
||||||
|
|> xLine(length = -0.183 * railHeight, tag = $edge7)
|
||||||
|
|> yLine(length = -0.087 * railHeight, tag = $edge8)
|
||||||
|
|> xLine(length = 0.06 * railHeight)
|
||||||
|
|> arc(angleStart = 180, angleEnd = 0, radius = 0.072 / 4 * railHeight)
|
||||||
|
|> xLine(length = 0.1 * railHeight)
|
||||||
|
|> arc(angleStart = 180, angleEnd = 0, radius = 0.072 / 4 * railHeight)
|
||||||
|
|> arc(angleStart = -90, angleEnd = 0, radius = 0.1 * railHeight)
|
||||||
|
|
||||||
|
// Sketch side 2 of profile
|
||||||
|
|> arc(angleStart = 270, angleEnd = 90, radius = 0.072 / 4 * railHeight)
|
||||||
|
|> yLine(length = 0.1 * railHeight)
|
||||||
|
|> arc(angleStart = 270, angleEnd = 90, radius = 0.072 / 4 * railHeight)
|
||||||
|
|> yLine(length = 0.06 * railHeight, tag = $edge9)
|
||||||
|
|> xLine(length = -0.087 * railHeight, tag = $edge10)
|
||||||
|
|> yLine(length = -0.183 * railHeight, tag = $edge11) // edge11
|
||||||
|
|> angledLine(angle = 135, endAbsoluteX = ((1 - 0.356) / 2 + 0.356) * railHeight + originStart[0], tag = $edge12) // edge12
|
||||||
|
|> yLine(length = 0.232 * railHeight, tag = $edge13) // 13
|
||||||
|
|> angledLine(angle = 45, endAbsoluteX = (1 - 0.087) * railHeight + originStart[0], tag = $edge14) // 14
|
||||||
|
|> yLine(length = -0.183 * railHeight, tag = $edge15) // 15
|
||||||
|
|> xLine(length = 0.087 * railHeight, tag = $edge16)
|
||||||
|
|> yLine(length = 0.06 * railHeight)
|
||||||
|
|> arc(angleStart = 270, angleEnd = 90, radius = 0.072 / 4 * railHeight)
|
||||||
|
|> yLine(length = 0.1 * railHeight)
|
||||||
|
|> arc(angleStart = 270, angleEnd = 90, radius = 0.072 / 4 * railHeight)
|
||||||
|
|
||||||
|
// Sketch side 3 of profile
|
||||||
|
|> arc(angleStart = 0, angleEnd = 90, radius = 0.1 * railHeight)
|
||||||
|
|> arc(angleStart = 0, angleEnd = -180, radius = 0.072 / 4 * railHeight)
|
||||||
|
|> xLine(length = -0.1 * railHeight)
|
||||||
|
|> arc(angleStart = 0, angleEnd = -180, radius = 0.072 / 4 * railHeight)
|
||||||
|
|> xLine(length = -0.06 * railHeight, tag = $edge17)
|
||||||
|
|> yLine(length = -0.087 * railHeight, tag = $edge18)
|
||||||
|
|> xLine(length = 0.183 * railHeight, tag = $edge19)
|
||||||
|
|> angledLine(angle = 45, endAbsoluteY = ((1 - 0.356) / 2 + 0.356) * railHeight + originStart[1], tag = $edge20)
|
||||||
|
|> xLine(length = -0.232 * railHeight, tag = $edge21)
|
||||||
|
|> angledLine(angle = 135, endAbsoluteY = (1 - 0.087) * railHeight + originStart[1], tag = $edge22)
|
||||||
|
|> xLine(length = 0.183 * railHeight, tag = $edge23)
|
||||||
|
|> yLine(length = 0.087 * railHeight, tag = $edge24)
|
||||||
|
|> xLine(length = -0.06 * railHeight)
|
||||||
|
|> arc(angleStart = 0, angleEnd = -180, radius = 0.072 / 4 * railHeight)
|
||||||
|
|> xLine(length = -0.1 * railHeight)
|
||||||
|
|> arc(angleStart = 0, angleEnd = -180, radius = 0.072 / 4 * railHeight)
|
||||||
|
|> arc(angleStart = 90, angleEnd = 180, radius = 0.1 * railHeight)
|
||||||
|
|
||||||
|
// Sketch side 4 of profile
|
||||||
|
|> arc(angleStart = 90, angleEnd = -90, radius = 0.072 / 4 * railHeight)
|
||||||
|
|> yLine(length = -0.1 * railHeight)
|
||||||
|
|> arc(angleStart = 90, angleEnd = -90, radius = 0.072 / 4 * railHeight)
|
||||||
|
|> yLine(length = -0.06 * railHeight, tag = $edge25)
|
||||||
|
|> xLine(length = 0.087 * railHeight, tag = $edge26)
|
||||||
|
|> yLine(length = 0.183 * railHeight, tag = $edge27)
|
||||||
|
|> angledLine(angle = 135, endAbsoluteX = (1 - 0.356) / 2 * railHeight + originStart[0], tag = $edge28)
|
||||||
|
|> yLine(length = -0.232 * railHeight, tag = $edge29)
|
||||||
|
|> angledLine(angle = 45, endAbsoluteX = 0.087 * railHeight + originStart[0], tag = $edge30)
|
||||||
|
|> yLine(length = 0.183 * railHeight, tag = $edge31)
|
||||||
|
|> xLine(length = -0.087 * railHeight, tag = $edge32)
|
||||||
|
|> yLine(length = -0.06 * railHeight)
|
||||||
|
|> arc(angleStart = 90, angleEnd = -90, radius = 0.072 / 4 * railHeight)
|
||||||
|
|> yLine(length = -0.1 * railHeight)
|
||||||
|
|> arc(angleStart = 90, angleEnd = -90, radius = 0.072 / 4 * railHeight)
|
||||||
|
|> close()
|
||||||
|
|
||||||
|
// Sketch center hole of profile
|
||||||
|
|> subtract2d(tool = circle(
|
||||||
|
center = [
|
||||||
|
.5 * railHeight + originStart[0],
|
||||||
|
.5 * railHeight + originStart[1]
|
||||||
|
],
|
||||||
|
radius = .205 * railHeight / 2,
|
||||||
|
))
|
||||||
|
|> extrude(length = railLength)
|
||||||
|
|> fillet(
|
||||||
|
radius = 0.06,
|
||||||
|
tags = [
|
||||||
|
getNextAdjacentEdge(edge3),
|
||||||
|
getNextAdjacentEdge(edge4),
|
||||||
|
getNextAdjacentEdge(edge5),
|
||||||
|
getNextAdjacentEdge(edge6),
|
||||||
|
getNextAdjacentEdge(edge11),
|
||||||
|
getNextAdjacentEdge(edge12),
|
||||||
|
getNextAdjacentEdge(edge13),
|
||||||
|
getNextAdjacentEdge(edge14),
|
||||||
|
getNextAdjacentEdge(edge19),
|
||||||
|
getNextAdjacentEdge(edge20),
|
||||||
|
getNextAdjacentEdge(edge21),
|
||||||
|
getNextAdjacentEdge(edge22),
|
||||||
|
getNextAdjacentEdge(edge27),
|
||||||
|
getNextAdjacentEdge(edge28),
|
||||||
|
getNextAdjacentEdge(edge29),
|
||||||
|
getNextAdjacentEdge(edge30)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|> fillet(
|
||||||
|
radius = 0.03,
|
||||||
|
tags = [
|
||||||
|
getNextAdjacentEdge(edge1),
|
||||||
|
getNextAdjacentEdge(edge2),
|
||||||
|
getNextAdjacentEdge(edge7),
|
||||||
|
getNextAdjacentEdge(edge8),
|
||||||
|
getNextAdjacentEdge(edge9),
|
||||||
|
getNextAdjacentEdge(edge10),
|
||||||
|
getNextAdjacentEdge(edge15),
|
||||||
|
getNextAdjacentEdge(edge16),
|
||||||
|
getNextAdjacentEdge(edge17),
|
||||||
|
getNextAdjacentEdge(edge18),
|
||||||
|
getNextAdjacentEdge(edge23),
|
||||||
|
getNextAdjacentEdge(edge24),
|
||||||
|
getNextAdjacentEdge(edge25),
|
||||||
|
getNextAdjacentEdge(edge26),
|
||||||
|
getNextAdjacentEdge(edge31),
|
||||||
|
getNextAdjacentEdge(edge32)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
return sketch001
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate one adjustable rail of 80/20
|
||||||
|
rail8020(originStart = [0, 0], railHeight = 1.5, railLength = 48)
|
@ -23,6 +23,8 @@ KCL samples conform to a set of style guidelines to ensure consistency and reada
|
|||||||
When you submit a PR to add or modify KCL samples, images will be generated and added to the repository automatically.
|
When you submit a PR to add or modify KCL samples, images will be generated and added to the repository automatically.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
#### [80-20-rail](80-20-rail/main.kcl) ([screenshot](screenshots/80-20-rail.png))
|
||||||
|
[](80-20-rail/main.kcl)
|
||||||
#### [axial-fan](axial-fan/main.kcl) ([screenshot](screenshots/axial-fan.png))
|
#### [axial-fan](axial-fan/main.kcl) ([screenshot](screenshots/axial-fan.png))
|
||||||
[](axial-fan/main.kcl)
|
[](axial-fan/main.kcl)
|
||||||
#### [ball-bearing](ball-bearing/main.kcl) ([screenshot](screenshots/ball-bearing.png))
|
#### [ball-bearing](ball-bearing/main.kcl) ([screenshot](screenshots/ball-bearing.png))
|
||||||
@ -141,16 +143,10 @@ When you submit a PR to add or modify KCL samples, images will be generated and
|
|||||||
[](spur-reduction-gearset/main.kcl)
|
[](spur-reduction-gearset/main.kcl)
|
||||||
#### [surgical-drill-guide](surgical-drill-guide/main.kcl) ([screenshot](screenshots/surgical-drill-guide.png))
|
#### [surgical-drill-guide](surgical-drill-guide/main.kcl) ([screenshot](screenshots/surgical-drill-guide.png))
|
||||||
[](surgical-drill-guide/main.kcl)
|
[](surgical-drill-guide/main.kcl)
|
||||||
#### [t-slot-rail](t-slot-rail/main.kcl) ([screenshot](screenshots/t-slot-rail.png))
|
|
||||||
[](t-slot-rail/main.kcl)
|
|
||||||
#### [telemetry-antenna](telemetry-antenna/main.kcl) ([screenshot](screenshots/telemetry-antenna.png))
|
|
||||||
[](telemetry-antenna/main.kcl)
|
|
||||||
#### [thermal-block-insert](thermal-block-insert/main.kcl) ([screenshot](screenshots/thermal-block-insert.png))
|
#### [thermal-block-insert](thermal-block-insert/main.kcl) ([screenshot](screenshots/thermal-block-insert.png))
|
||||||
[](thermal-block-insert/main.kcl)
|
[](thermal-block-insert/main.kcl)
|
||||||
#### [tooling-nest-block](tooling-nest-block/main.kcl) ([screenshot](screenshots/tooling-nest-block.png))
|
#### [tooling-nest-block](tooling-nest-block/main.kcl) ([screenshot](screenshots/tooling-nest-block.png))
|
||||||
[](tooling-nest-block/main.kcl)
|
[](tooling-nest-block/main.kcl)
|
||||||
#### [truss-structure](truss-structure/main.kcl) ([screenshot](screenshots/truss-structure.png))
|
|
||||||
[](truss-structure/main.kcl)
|
|
||||||
#### [utility-sink](utility-sink/main.kcl) ([screenshot](screenshots/utility-sink.png))
|
#### [utility-sink](utility-sink/main.kcl) ([screenshot](screenshots/utility-sink.png))
|
||||||
[](utility-sink/main.kcl)
|
[](utility-sink/main.kcl)
|
||||||
#### [walkie-talkie](walkie-talkie/main.kcl) ([screenshot](screenshots/walkie-talkie.png))
|
#### [walkie-talkie](walkie-talkie/main.kcl) ([screenshot](screenshots/walkie-talkie.png))
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
// Brake Rotor
|
|
||||||
// A 320mm vented brake disc (rotor), with straight vanes, 30mm thick. The disc bell should accommodate 5 M12 wheel studs on a 114.3mm pitch circle diameter.
|
// A 320mm vented brake disc (rotor), with straight vanes, 30mm thick. The disc bell should accommodate 5 M12 wheel studs on a 114.3mm pitch circle diameter.
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,4 +1,14 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"file": "main.kcl",
|
||||||
|
"pathFromProjectDirectoryToFirstFile": "80-20-rail/main.kcl",
|
||||||
|
"multipleFiles": false,
|
||||||
|
"title": "80/20 Rail",
|
||||||
|
"description": "An 80/20 extruded aluminum linear rail. T-slot profile adjustable by profile height, rail length, and origin position",
|
||||||
|
"files": [
|
||||||
|
"main.kcl"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"file": "main.kcl",
|
"file": "main.kcl",
|
||||||
"pathFromProjectDirectoryToFirstFile": "axial-fan/main.kcl",
|
"pathFromProjectDirectoryToFirstFile": "axial-fan/main.kcl",
|
||||||
@ -68,8 +78,8 @@
|
|||||||
"file": "main.kcl",
|
"file": "main.kcl",
|
||||||
"pathFromProjectDirectoryToFirstFile": "brake-rotor/main.kcl",
|
"pathFromProjectDirectoryToFirstFile": "brake-rotor/main.kcl",
|
||||||
"multipleFiles": false,
|
"multipleFiles": false,
|
||||||
"title": "Brake Rotor",
|
"title": "A 320mm vented brake disc (rotor), with straight vanes, 30mm thick. The disc bell should accommodate 5 M12 wheel studs on a 114.3mm pitch circle diameter.",
|
||||||
"description": "A 320mm vented brake disc (rotor), with straight vanes, 30mm thick. The disc bell should accommodate 5 M12 wheel studs on a 114.3mm pitch circle diameter.",
|
"description": "",
|
||||||
"files": [
|
"files": [
|
||||||
"main.kcl"
|
"main.kcl"
|
||||||
]
|
]
|
||||||
@ -622,26 +632,6 @@
|
|||||||
"main.kcl"
|
"main.kcl"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"file": "main.kcl",
|
|
||||||
"pathFromProjectDirectoryToFirstFile": "t-slot-rail/main.kcl",
|
|
||||||
"multipleFiles": false,
|
|
||||||
"title": "T-Slotted Framing Rail",
|
|
||||||
"description": "A T-slotted framing rail, or T-slot extrusion, is a rectangular or square aluminum profile with a \"T\" shaped slot along one or more sides. These slots allow for easy attachment of various hardware components like brackets, connectors, and fasteners, making it a versatile and customizable framing system.",
|
|
||||||
"files": [
|
|
||||||
"main.kcl"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"file": "main.kcl",
|
|
||||||
"pathFromProjectDirectoryToFirstFile": "telemetry-antenna/main.kcl",
|
|
||||||
"multipleFiles": false,
|
|
||||||
"title": "Aircraft telemetry antenna plate",
|
|
||||||
"description": "Consists of a circular base plate 3 inches in diameter and 0.08 inches thick, with a tapered monopole antenna mounted at the top with a base diameter of 0.65 inches and height of 1.36 inches. Also consists of a mounting base and connector at the bottom of the plate. The plate also has 6 countersunk holes at a defined pitch circle diameter.",
|
|
||||||
"files": [
|
|
||||||
"main.kcl"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"file": "main.kcl",
|
"file": "main.kcl",
|
||||||
"pathFromProjectDirectoryToFirstFile": "thermal-block-insert/main.kcl",
|
"pathFromProjectDirectoryToFirstFile": "thermal-block-insert/main.kcl",
|
||||||
@ -662,16 +652,6 @@
|
|||||||
"main.kcl"
|
"main.kcl"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"file": "main.kcl",
|
|
||||||
"pathFromProjectDirectoryToFirstFile": "truss-structure/main.kcl",
|
|
||||||
"multipleFiles": false,
|
|
||||||
"title": "Truss Structure",
|
|
||||||
"description": "A truss structure is a framework composed of triangular units made from straight members connected at joints, often called nodes. Trusses are widely used in architecture, civil engineering, and construction for their ability to support large loads with minimal material.",
|
|
||||||
"files": [
|
|
||||||
"main.kcl"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"file": "main.kcl",
|
"file": "main.kcl",
|
||||||
"pathFromProjectDirectoryToFirstFile": "utility-sink/main.kcl",
|
"pathFromProjectDirectoryToFirstFile": "utility-sink/main.kcl",
|
||||||
|
BIN
public/kcl-samples/screenshots/80-20-rail.png
Normal file
BIN
public/kcl-samples/screenshots/80-20-rail.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 25 KiB |
Binary file not shown.
Before Width: | Height: | Size: 81 KiB After Width: | Height: | Size: 82 KiB |
Binary file not shown.
Before Width: | Height: | Size: 33 KiB |
Binary file not shown.
Before Width: | Height: | Size: 68 KiB |
Binary file not shown.
Before Width: | Height: | Size: 68 KiB |
@ -1,58 +0,0 @@
|
|||||||
// T-Slotted Framing Rail
|
|
||||||
// A T-slotted framing rail, or T-slot extrusion, is a rectangular or square aluminum profile with a "T" shaped slot along one or more sides. These slots allow for easy attachment of various hardware components like brackets, connectors, and fasteners, making it a versatile and customizable framing system.
|
|
||||||
|
|
||||||
// Set units
|
|
||||||
@settings(defaultLengthUnit = in, kclVersion = 1.0)
|
|
||||||
|
|
||||||
// Define parameters
|
|
||||||
interiorRadius = 0.01
|
|
||||||
scoreDepth = 0.018
|
|
||||||
arcEnd = 0.0275
|
|
||||||
holeDiameter = 0.262
|
|
||||||
|
|
||||||
fn railTslot(railHeight, railLength) {
|
|
||||||
// Sketch one inner leg of the extruded rail
|
|
||||||
railProfile = startSketchOn(XZ)
|
|
||||||
|> startProfile(at = [0.5, (1 - 0.356) / 2])
|
|
||||||
|> xLine(length = -0.08)
|
|
||||||
|> tangentialArc(angle = 45, radius = .09)
|
|
||||||
|> angledLine(angle = 45, endAbsoluteY = 0.113)
|
|
||||||
|> tangentialArc(angle = 135, radius = interiorRadius)
|
|
||||||
|> xLine(endAbsolute = .5 - (.320 / 2) - interiorRadius)
|
|
||||||
|> tangentialArc(angle = -90, radius = interiorRadius)
|
|
||||||
|> yLine(endAbsolute = interiorRadius)
|
|
||||||
|> tangentialArc(angle = -90, radius = interiorRadius)
|
|
||||||
|> xLine(length = -0.03)
|
|
||||||
|> arc(angleStart = 0, angleEnd = 180, radius = scoreDepth)
|
|
||||||
|> xLine(length = -0.1)
|
|
||||||
|> arc(angleStart = 0, angleEnd = 180, radius = scoreDepth)
|
|
||||||
|> xLine(length = -0.03)
|
|
||||||
|> tangentialArc(endAbsolute = [arcEnd, arcEnd])
|
|
||||||
|
|
||||||
// Mirror the sketch about the diagonal to complete the leg. Then mirror across the center of the profile in the horizontal and vertical directions. Then close the sketch
|
|
||||||
|> mirror2d(axis = {
|
|
||||||
direction = [1.0, 1.0],
|
|
||||||
origin = [0.0, 0.0]
|
|
||||||
})
|
|
||||||
|> mirror2d(axis = {
|
|
||||||
direction = [1.0, 0.0],
|
|
||||||
origin = [0.0, 0.5]
|
|
||||||
})
|
|
||||||
|> mirror2d(axis = {
|
|
||||||
direction = [0.0, 1.0],
|
|
||||||
origin = [0.5, 0.0]
|
|
||||||
})
|
|
||||||
|> close()
|
|
||||||
|
|
||||||
// Sketch a dimensioned hole in the center of the profile
|
|
||||||
|> subtract2d(tool = circle(center = [railHeight / 2, railHeight / 2], radius = holeDiameter / 2))
|
|
||||||
|
|
||||||
// Scale the entire sketch by a factor of the rail height, then extrude
|
|
||||||
|> scale(x = railHeight, z = railHeight)
|
|
||||||
|> extrude(length = -railLength)
|
|
||||||
|
|
||||||
return railProfile
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate one rail using the rail function
|
|
||||||
railTslot(railHeight = 1.5, railLength = 2ft)
|
|
@ -1,63 +0,0 @@
|
|||||||
// Aircraft telemetry antenna plate
|
|
||||||
// Consists of a circular base plate 3 inches in diameter and 0.08 inches thick, with a tapered monopole antenna mounted at the top with a base diameter of 0.65 inches and height of 1.36 inches. Also consists of a mounting base and connector at the bottom of the plate. The plate also has 6 countersunk holes at a defined pitch circle diameter.
|
|
||||||
|
|
||||||
// Set units
|
|
||||||
@settings(defaultLengthUnit = in)
|
|
||||||
|
|
||||||
// Define parameters
|
|
||||||
plateThickness = 0.08
|
|
||||||
plateDia = 3
|
|
||||||
antennaBaseDia = 0.65
|
|
||||||
antennaAngle = 95
|
|
||||||
antennaHeight = 1.36
|
|
||||||
seatingDia = 0.625
|
|
||||||
totalHeight = 2.14
|
|
||||||
|
|
||||||
boltDiameter = .196
|
|
||||||
boltPitchCircleDiameter = 2.5
|
|
||||||
|
|
||||||
// 2D cross-sectional profile of the part that will later be revolved
|
|
||||||
antennaCrossSectionSketch = startSketchOn(YZ)
|
|
||||||
antennaCrossSectionProfile = startProfile(antennaCrossSectionSketch, at = [plateDia / 2, 0])
|
|
||||||
|> yLine(length = plateThickness)
|
|
||||||
|> xLine(length = -(plateDia - antennaBaseDia) / 2, tag = $seg03)
|
|
||||||
|> angledLine(angle = antennaAngle, length = 1.1, tag = $seg01)
|
|
||||||
|> tangentialArc(endAbsolute = [0.025, antennaHeight])
|
|
||||||
|> xLine(endAbsolute = 0, tag = $seg02)
|
|
||||||
|> yLine(length = -totalHeight)
|
|
||||||
|> xLine(length = .25)
|
|
||||||
|> yLine(length = .05)
|
|
||||||
|> angledLine(angle = 45, length = 0.025)
|
|
||||||
|> yLine(length = .125)
|
|
||||||
|> angledLine(angle = 135, length = 0.025)
|
|
||||||
|> yLine(length = .125)
|
|
||||||
|> xLine(length = .025)
|
|
||||||
|> yLine(length = .025)
|
|
||||||
|> xLine(endAbsolute = seatingDia / 2)
|
|
||||||
|> yLine(endAbsolute = -0.25)
|
|
||||||
|> xLine(endAbsolute = 0.6)
|
|
||||||
|> yLine(endAbsolute = 0)
|
|
||||||
|> close()
|
|
||||||
|
|
||||||
// Revolution about y-axis of earlier profile
|
|
||||||
antennaCrossSectionRevolve = revolve(antennaCrossSectionProfile, angle = 360, axis = Y)
|
|
||||||
|
|
||||||
// Function to create a countersunk hole
|
|
||||||
fn countersink(@holePosition) {
|
|
||||||
startSketchOn(antennaCrossSectionRevolve, face = seg03)
|
|
||||||
|> circle(center = holePosition, radius = boltDiameter / 2, tag = $hole01)
|
|
||||||
|> extrude(length = -plateThickness)
|
|
||||||
|> chamfer(length = 0.04, tags = [hole01])
|
|
||||||
return { }
|
|
||||||
}
|
|
||||||
|
|
||||||
// PCD converted to radius for positioning the holes
|
|
||||||
r = boltPitchCircleDiameter / 2
|
|
||||||
|
|
||||||
// 6 countersunk holes using the countersink function
|
|
||||||
countersink([r, 0]) // 0 °
|
|
||||||
countersink([r * 0.5, r * 0.8660254]) // 60 °
|
|
||||||
countersink([-r * 0.5, r * 0.8660254]) // 120 °
|
|
||||||
countersink([-r, 0]) // 180 °
|
|
||||||
countersink([-r * 0.5, -r * 0.8660254]) // 240 °
|
|
||||||
countersink([r * 0.5, -r * 0.8660254]) // 300 °
|
|
@ -1,142 +0,0 @@
|
|||||||
// Truss Structure
|
|
||||||
// A truss structure is a framework composed of triangular units made from straight members connected at joints, often called nodes. Trusses are widely used in architecture, civil engineering, and construction for their ability to support large loads with minimal material.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@settings(defaultLengthUnit = in)
|
|
||||||
|
|
||||||
// Define parameters
|
|
||||||
thickness = 4
|
|
||||||
totalLength = 180
|
|
||||||
totalWidth = 120
|
|
||||||
totalHeight = 120
|
|
||||||
legHeight = 48
|
|
||||||
topTrussAngle = 25
|
|
||||||
beamWidth = 4
|
|
||||||
beamLength = 2
|
|
||||||
sparAngle = 30
|
|
||||||
nFrames = 3
|
|
||||||
crossBeamLength = 82
|
|
||||||
|
|
||||||
// Sketch the top frame
|
|
||||||
topFrameSketch = startSketchOn(YZ)
|
|
||||||
profile001 = startProfile(topFrameSketch, at = [totalWidth / 2, 0])
|
|
||||||
|> xLine(length = -totalWidth, tag = $bottomFace)
|
|
||||||
|> yLine(length = 12)
|
|
||||||
|> angledLine(angle = topTrussAngle, endAbsoluteX = 0, tag = $tag001)
|
|
||||||
|> angledLine(angle = -topTrussAngle, endAbsoluteX = totalWidth / 2, tag = $tag002)
|
|
||||||
|> close()
|
|
||||||
|
|
||||||
// Create two holes in the top frame sketch to create the center beam
|
|
||||||
profile002 = startProfile(topFrameSketch, at = [totalWidth / 2 - thickness, thickness])
|
|
||||||
|> xLine(endAbsolute = thickness / 2)
|
|
||||||
|> yLine(endAbsolute = segEndY(tag001) - thickness)
|
|
||||||
|> angledLine(endAbsoluteX = profileStartX(%), angle = -topTrussAngle)
|
|
||||||
|> close(%)
|
|
||||||
|
|
||||||
profile003 = startProfile(topFrameSketch, at = [-totalWidth / 2 + thickness, thickness])
|
|
||||||
|> xLine(endAbsolute = -thickness / 2)
|
|
||||||
|> yLine(endAbsolute = segEndY(tag001) - thickness)
|
|
||||||
|> angledLine(endAbsoluteX = profileStartX(%), angle = 180 + topTrussAngle)
|
|
||||||
|> close(%)
|
|
||||||
profile004 = subtract2d(profile001, tool = profile002)
|
|
||||||
subtract2d(profile001, tool = profile003)
|
|
||||||
|
|
||||||
// Extrude the sketch to make the top frame
|
|
||||||
topFrame = extrude(profile001, length = beamLength)
|
|
||||||
|
|
||||||
// Spar 1
|
|
||||||
sketch002 = startSketchOn(offsetPlane(YZ, offset = .1))
|
|
||||||
profile006 = startProfile(sketch002, at = [thickness / 2 - 1, 14])
|
|
||||||
|> angledLine(angle = sparAngle, length = 25)
|
|
||||||
|> angledLine(angle = -topTrussAngle, length = 5)
|
|
||||||
|> angledLine(angle = 180 + sparAngle, endAbsoluteX = profileStartX(%))
|
|
||||||
|> close(%)
|
|
||||||
|
|
||||||
spar001 = extrude(profile006, length = 1.8)
|
|
||||||
|
|
||||||
// Spar2
|
|
||||||
profile007 = startProfile(sketch002, at = [-thickness / 2 + 1, 14])
|
|
||||||
|> angledLine(angle = 180 - sparAngle, length = 25)
|
|
||||||
|> angledLine(angle = 180 + topTrussAngle, length = 5)
|
|
||||||
|> angledLine(angle = -sparAngle, endAbsoluteX = profileStartX(%))
|
|
||||||
|> close(%)
|
|
||||||
|
|
||||||
spar002 = extrude(profile007, length = 1.8)
|
|
||||||
|
|
||||||
// Combine the top frame with the intermediate support beams
|
|
||||||
newFrame = topFrame + spar001 + spar002
|
|
||||||
|
|
||||||
// Create the two legs on the frame
|
|
||||||
leg001Sketch = startSketchOn(offsetPlane(XY, offset = .1))
|
|
||||||
legProfile001 = startProfile(leg001Sketch, at = [0, -totalWidth / 2])
|
|
||||||
|> xLine(%, length = beamLength - .1)
|
|
||||||
|> yLine(%, length = beamWidth - 1)
|
|
||||||
|> xLine(%, endAbsolute = profileStartX(%))
|
|
||||||
|> close(%)
|
|
||||||
|
|
||||||
legProfile002 = startProfile(leg001Sketch, at = [0, totalWidth / 2])
|
|
||||||
|> xLine(%, length = beamLength - .1)
|
|
||||||
|> yLine(%, length = -(beamWidth - 1))
|
|
||||||
|> xLine(%, endAbsolute = profileStartX(%))
|
|
||||||
|> close(%)
|
|
||||||
leg001 = extrude(legProfile001, length = -legHeight - .1)
|
|
||||||
leg002 = extrude(legProfile002, length = -legHeight - .1)
|
|
||||||
|
|
||||||
// Combine the top frame with the legs and pattern
|
|
||||||
fullFrame = newFrame + leg001 + leg002
|
|
||||||
|> patternLinear3d(
|
|
||||||
%,
|
|
||||||
instances = nFrames,
|
|
||||||
distance = crossBeamLength + beamLength,
|
|
||||||
axis = [-1, 0, 0],
|
|
||||||
)
|
|
||||||
|
|
||||||
// Create the center cross beam
|
|
||||||
centerCrossBeamSketch = startSketchOn(YZ)
|
|
||||||
profile005 = startProfile(centerCrossBeamSketch, at = [0, segEndY(tag001) - 1])
|
|
||||||
|> angledLine(%, angle = -topTrussAngle, length = beamWidth * 3 / 8)
|
|
||||||
|> yLine(length = -beamWidth * 3 / 8)
|
|
||||||
|> angledLine(%, angle = 180 - topTrussAngle, length = beamWidth * 3 / 8)
|
|
||||||
|> angledLine(%, angle = 180 + topTrussAngle, length = beamWidth * 3 / 8)
|
|
||||||
|> yLine(length = beamWidth * 3 / 8)
|
|
||||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|
||||||
|> close()
|
|
||||||
|
|
||||||
// Extrude the center cross beam and pattern to every frame
|
|
||||||
centerCrossBeam = extrude(profile005, length = -crossBeamLength)
|
|
||||||
|> patternLinear3d(
|
|
||||||
%,
|
|
||||||
instances = nFrames - 1,
|
|
||||||
distance = crossBeamLength + beamLength,
|
|
||||||
axis = [-1, 0, 0],
|
|
||||||
)
|
|
||||||
|
|
||||||
// Create the two side cross beams
|
|
||||||
sideCrossBeamSketch = startSketchOn(-YZ)
|
|
||||||
profile008 = startProfile(
|
|
||||||
sideCrossBeamSketch,
|
|
||||||
at = [
|
|
||||||
-totalWidth / 2 + 0.5,
|
|
||||||
segEndY(tag002) - .5
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|> yLine(length = -beamLength)
|
|
||||||
|> xLine(length = 3 / 4 * beamWidth)
|
|
||||||
|> yLine(length = beamLength)
|
|
||||||
|> close()
|
|
||||||
profile009 = startProfile(sideCrossBeamSketch, at = [totalWidth / 2, segEndY(tag002) - .5])
|
|
||||||
|> yLine(length = -beamLength)
|
|
||||||
|> xLine(%, length = -3 / 4 * beamWidth)
|
|
||||||
|> yLine(%, length = beamLength)
|
|
||||||
|> close(%)
|
|
||||||
|
|
||||||
// Extrude the side cross beams and pattern to every frame.
|
|
||||||
extrude([profile008, profile009], length = crossBeamLength)
|
|
||||||
|> patternLinear3d(
|
|
||||||
%,
|
|
||||||
instances = nFrames - 1,
|
|
||||||
distance = crossBeamLength + beamLength,
|
|
||||||
axis = [-1, 0, 0],
|
|
||||||
)
|
|
20
rust/Cargo.lock
generated
20
rust/Cargo.lock
generated
@ -1815,7 +1815,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kcl-bumper"
|
name = "kcl-bumper"
|
||||||
version = "0.1.77"
|
version = "0.1.75"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"clap",
|
"clap",
|
||||||
@ -1826,7 +1826,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kcl-derive-docs"
|
name = "kcl-derive-docs"
|
||||||
version = "0.1.77"
|
version = "0.1.75"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"Inflector",
|
"Inflector",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
@ -1845,7 +1845,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kcl-directory-test-macro"
|
name = "kcl-directory-test-macro"
|
||||||
version = "0.1.77"
|
version = "0.1.75"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"convert_case",
|
"convert_case",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
@ -1855,7 +1855,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kcl-language-server"
|
name = "kcl-language-server"
|
||||||
version = "0.2.77"
|
version = "0.2.75"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"clap",
|
"clap",
|
||||||
@ -1876,7 +1876,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kcl-language-server-release"
|
name = "kcl-language-server-release"
|
||||||
version = "0.1.77"
|
version = "0.1.75"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"clap",
|
"clap",
|
||||||
@ -1896,7 +1896,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kcl-lib"
|
name = "kcl-lib"
|
||||||
version = "0.2.77"
|
version = "0.2.75"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"approx 0.5.1",
|
"approx 0.5.1",
|
||||||
@ -1973,7 +1973,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kcl-python-bindings"
|
name = "kcl-python-bindings"
|
||||||
version = "0.3.77"
|
version = "0.3.75"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"kcl-lib",
|
"kcl-lib",
|
||||||
@ -1988,7 +1988,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kcl-test-server"
|
name = "kcl-test-server"
|
||||||
version = "0.1.77"
|
version = "0.1.75"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"hyper 0.14.32",
|
"hyper 0.14.32",
|
||||||
@ -2001,7 +2001,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kcl-to-core"
|
name = "kcl-to-core"
|
||||||
version = "0.1.77"
|
version = "0.1.75"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@ -2015,7 +2015,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kcl-wasm-lib"
|
name = "kcl-wasm-lib"
|
||||||
version = "0.1.77"
|
version = "0.1.75"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bson",
|
"bson",
|
||||||
|
@ -36,20 +36,20 @@ new-sim-test test_name render_to_png="true":
|
|||||||
|
|
||||||
# Run a KCL deterministic simulation test case and accept output.
|
# Run a KCL deterministic simulation test case and accept output.
|
||||||
overwrite-sim-test-sample test_name:
|
overwrite-sim-test-sample test_name:
|
||||||
ZOO_SIM_UPDATE=always EXPECTORATE=overwrite TWENTY_TWENTY=overwrite {{cita}} {{kcl_lib_flags}} --no-quiet -- simulation_tests::kcl_samples::parse_{{test_name}}
|
EXPECTORATE=overwrite TWENTY_TWENTY=overwrite {{cita}} {{kcl_lib_flags}} --no-quiet -- simulation_tests::kcl_samples::parse_{{test_name}}
|
||||||
ZOO_SIM_UPDATE=always EXPECTORATE=overwrite TWENTY_TWENTY=overwrite {{cita}} {{kcl_lib_flags}} --no-quiet -- simulation_tests::kcl_samples::unparse_{{test_name}}
|
EXPECTORATE=overwrite TWENTY_TWENTY=overwrite {{cita}} {{kcl_lib_flags}} --no-quiet -- simulation_tests::kcl_samples::unparse_{{test_name}}
|
||||||
ZOO_SIM_UPDATE=always EXPECTORATE=overwrite TWENTY_TWENTY=overwrite {{cita}} {{kcl_lib_flags}} --no-quiet -- simulation_tests::kcl_samples::kcl_test_execute_{{test_name}}
|
EXPECTORATE=overwrite TWENTY_TWENTY=overwrite {{cita}} {{kcl_lib_flags}} --no-quiet -- simulation_tests::kcl_samples::kcl_test_execute_{{test_name}}
|
||||||
ZOO_SIM_UPDATE=always EXPECTORATE=overwrite TWENTY_TWENTY=overwrite {{cita}} {{kcl_lib_flags}} --no-quiet -- simulation_tests::kcl_samples::test_after_engine
|
EXPECTORATE=overwrite TWENTY_TWENTY=overwrite {{cita}} {{kcl_lib_flags}} --no-quiet -- simulation_tests::kcl_samples::test_after_engine
|
||||||
|
|
||||||
overwrite-sim-test test_name:
|
overwrite-sim-test test_name:
|
||||||
ZOO_SIM_UPDATE=always EXPECTORATE=overwrite TWENTY_TWENTY=overwrite {{cita}} {{kcl_lib_flags}} --no-quiet -- simulation_tests::{{test_name}}::parse
|
EXPECTORATE=overwrite TWENTY_TWENTY=overwrite {{cita}} {{kcl_lib_flags}} --no-quiet -- simulation_tests::{{test_name}}::parse
|
||||||
ZOO_SIM_UPDATE=always EXPECTORATE=overwrite TWENTY_TWENTY=overwrite {{cita}} {{kcl_lib_flags}} --no-quiet -- simulation_tests::{{test_name}}::unparse
|
EXPECTORATE=overwrite TWENTY_TWENTY=overwrite {{cita}} {{kcl_lib_flags}} --no-quiet -- simulation_tests::{{test_name}}::unparse
|
||||||
ZOO_SIM_UPDATE=always EXPECTORATE=overwrite TWENTY_TWENTY=overwrite {{cita}} {{kcl_lib_flags}} --no-quiet -- simulation_tests::{{test_name}}::kcl_test_execute
|
EXPECTORATE=overwrite TWENTY_TWENTY=overwrite {{cita}} {{kcl_lib_flags}} --no-quiet -- simulation_tests::{{test_name}}::kcl_test_execute
|
||||||
[ {{test_name}} != "kcl_samples" ] || ZOO_SIM_UPDATE=always EXPECTORATE=overwrite TWENTY_TWENTY=overwrite {{cita}} {{kcl_lib_flags}} --no-quiet -- simulation_tests::{{test_name}}::test_after_engine
|
[ {{test_name}} != "kcl_samples" ] || EXPECTORATE=overwrite TWENTY_TWENTY=overwrite {{cita}} {{kcl_lib_flags}} --no-quiet -- simulation_tests::{{test_name}}::test_after_engine
|
||||||
|
|
||||||
# Regenerate all the simulation test output.
|
# Regenerate all the simulation test output.
|
||||||
redo-sim-tests:
|
redo-sim-tests:
|
||||||
ZOO_SIM_UPDATE=always EXPECTORATE=overwrite TWENTY_TWENTY=overwrite {{cita}} {{kcl_lib_flags}} --no-quiet -- simulation_tests
|
EXPECTORATE=overwrite TWENTY_TWENTY=overwrite {{cita}} {{kcl_lib_flags}} --no-quiet -- simulation_tests
|
||||||
|
|
||||||
test:
|
test:
|
||||||
cargo install cargo-nextest
|
cargo install cargo-nextest
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "kcl-bumper"
|
name = "kcl-bumper"
|
||||||
version = "0.1.77"
|
version = "0.1.75"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
repository = "https://github.com/KittyCAD/modeling-api"
|
repository = "https://github.com/KittyCAD/modeling-api"
|
||||||
rust-version = "1.76"
|
rust-version = "1.76"
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "kcl-derive-docs"
|
name = "kcl-derive-docs"
|
||||||
description = "A tool for generating documentation from Rust derive macros"
|
description = "A tool for generating documentation from Rust derive macros"
|
||||||
version = "0.1.77"
|
version = "0.1.75"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
repository = "https://github.com/KittyCAD/modeling-app"
|
repository = "https://github.com/KittyCAD/modeling-app"
|
||||||
|
@ -1,142 +0,0 @@
|
|||||||
use proc_macro2::Span;
|
|
||||||
use quote::{quote, ToTokens};
|
|
||||||
|
|
||||||
pub fn do_for_each_example_test(item: proc_macro2::TokenStream) -> proc_macro2::TokenStream {
|
|
||||||
let item: syn::ItemFn = syn::parse2(item.clone()).unwrap();
|
|
||||||
let mut result = proc_macro2::TokenStream::new();
|
|
||||||
for name in TEST_NAMES {
|
|
||||||
let mut item = item.clone();
|
|
||||||
item.sig.ident = syn::Ident::new(
|
|
||||||
&format!("{}_{}", item.sig.ident, name.replace('-', "_")),
|
|
||||||
Span::call_site(),
|
|
||||||
);
|
|
||||||
let name = name.to_owned();
|
|
||||||
let stmts = &item.block.stmts;
|
|
||||||
let block = quote! {
|
|
||||||
{
|
|
||||||
const NAME: &str = #name;
|
|
||||||
#(#stmts)*
|
|
||||||
}
|
|
||||||
};
|
|
||||||
item.block = Box::new(syn::parse2(block).unwrap());
|
|
||||||
result.extend(Some(item.into_token_stream()));
|
|
||||||
}
|
|
||||||
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn do_for_all_example_test(item: proc_macro2::TokenStream) -> proc_macro2::TokenStream {
|
|
||||||
let mut item: syn::ItemFn = syn::parse2(item).unwrap();
|
|
||||||
let len = TEST_NAMES.len();
|
|
||||||
|
|
||||||
let stmts = &item.block.stmts;
|
|
||||||
let test_names = TEST_NAMES.iter().map(|n| n.to_owned());
|
|
||||||
let block = quote! {
|
|
||||||
{
|
|
||||||
const TEST_NAMES: [&str; #len] = [#(#test_names,)*];
|
|
||||||
#(#stmts)*
|
|
||||||
}
|
|
||||||
};
|
|
||||||
item.block = Box::new(syn::parse2(block).unwrap());
|
|
||||||
|
|
||||||
item.into_token_stream()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const TEST_NAMES: &[&str] = &[
|
|
||||||
"std-appearance-hexString-0",
|
|
||||||
"std-appearance-hexString-1",
|
|
||||||
"std-appearance-hexString-2",
|
|
||||||
"std-array-map-0",
|
|
||||||
"std-array-map-1",
|
|
||||||
"std-array-pop-0",
|
|
||||||
"std-array-push-0",
|
|
||||||
"std-array-reduce-0",
|
|
||||||
"std-array-reduce-1",
|
|
||||||
"std-array-reduce-2",
|
|
||||||
"std-clone-0",
|
|
||||||
"std-clone-1",
|
|
||||||
"std-clone-2",
|
|
||||||
"std-clone-3",
|
|
||||||
"std-clone-4",
|
|
||||||
"std-clone-5",
|
|
||||||
"std-clone-6",
|
|
||||||
"std-clone-7",
|
|
||||||
"std-clone-8",
|
|
||||||
"std-clone-9",
|
|
||||||
"std-helix-0",
|
|
||||||
"std-helix-1",
|
|
||||||
"std-helix-2",
|
|
||||||
"std-helix-3",
|
|
||||||
"std-math-abs-0",
|
|
||||||
"std-math-acos-0",
|
|
||||||
"std-math-asin-0",
|
|
||||||
"std-math-atan-0",
|
|
||||||
"std-math-atan2-0",
|
|
||||||
"std-math-ceil-0",
|
|
||||||
"std-math-cos-0",
|
|
||||||
"std-math-floor-0",
|
|
||||||
"std-math-ln-0",
|
|
||||||
"std-math-legLen-0",
|
|
||||||
"std-math-legAngX-0",
|
|
||||||
"std-math-legAngY-0",
|
|
||||||
"std-math-log-0",
|
|
||||||
"std-math-log10-0",
|
|
||||||
"std-math-log2-0",
|
|
||||||
"std-math-max-0",
|
|
||||||
"std-math-min-0",
|
|
||||||
"std-math-polar-0",
|
|
||||||
"std-math-pow-0",
|
|
||||||
"std-math-rem-0",
|
|
||||||
"std-math-round-0",
|
|
||||||
"std-math-sin-0",
|
|
||||||
"std-math-sqrt-0",
|
|
||||||
"std-math-tan-0",
|
|
||||||
"std-offsetPlane-0",
|
|
||||||
"std-offsetPlane-1",
|
|
||||||
"std-offsetPlane-2",
|
|
||||||
"std-offsetPlane-3",
|
|
||||||
"std-offsetPlane-4",
|
|
||||||
"std-sketch-circle-0",
|
|
||||||
"std-sketch-circle-1",
|
|
||||||
"std-sketch-patternTransform2d-0",
|
|
||||||
"std-sketch-revolve-0",
|
|
||||||
"std-sketch-revolve-1",
|
|
||||||
"std-sketch-revolve-10",
|
|
||||||
"std-sketch-revolve-11",
|
|
||||||
"std-sketch-revolve-12",
|
|
||||||
"std-sketch-revolve-2",
|
|
||||||
"std-sketch-revolve-3",
|
|
||||||
"std-sketch-revolve-4",
|
|
||||||
"std-sketch-revolve-5",
|
|
||||||
"std-sketch-revolve-6",
|
|
||||||
"std-sketch-revolve-7",
|
|
||||||
"std-sketch-revolve-8",
|
|
||||||
"std-sketch-revolve-9",
|
|
||||||
"std-solid-chamfer-0",
|
|
||||||
"std-solid-chamfer-1",
|
|
||||||
"std-solid-fillet-0",
|
|
||||||
"std-solid-fillet-1",
|
|
||||||
"std-solid-hollow-0",
|
|
||||||
"std-solid-hollow-1",
|
|
||||||
"std-solid-hollow-2",
|
|
||||||
"std-solid-patternTransform-0",
|
|
||||||
"std-solid-patternTransform-1",
|
|
||||||
"std-solid-patternTransform-2",
|
|
||||||
"std-solid-patternTransform-3",
|
|
||||||
"std-solid-patternTransform-4",
|
|
||||||
"std-solid-patternTransform-5",
|
|
||||||
"std-solid-shell-0",
|
|
||||||
"std-solid-shell-1",
|
|
||||||
"std-solid-shell-2",
|
|
||||||
"std-solid-shell-3",
|
|
||||||
"std-solid-shell-4",
|
|
||||||
"std-solid-shell-5",
|
|
||||||
"std-solid-shell-6",
|
|
||||||
"std-transform-mirror2d-0",
|
|
||||||
"std-transform-mirror2d-1",
|
|
||||||
"std-transform-mirror2d-2",
|
|
||||||
"std-transform-mirror2d-3",
|
|
||||||
"std-transform-mirror2d-4",
|
|
||||||
"std-units-toDegrees-0",
|
|
||||||
"std-units-toRadians-0",
|
|
||||||
];
|
|
@ -2,16 +2,16 @@
|
|||||||
// automated enforcement.
|
// automated enforcement.
|
||||||
#![allow(clippy::style)]
|
#![allow(clippy::style)]
|
||||||
|
|
||||||
mod example_tests;
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
mod unbox;
|
mod unbox;
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::{collections::HashMap, fs};
|
||||||
|
|
||||||
use convert_case::Casing;
|
use convert_case::Casing;
|
||||||
use inflector::{cases::camelcase::to_camel_case, Inflector};
|
use inflector::{cases::camelcase::to_camel_case, Inflector};
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
|
use proc_macro2::Span;
|
||||||
use quote::{format_ident, quote, quote_spanned, ToTokens};
|
use quote::{format_ident, quote, quote_spanned, ToTokens};
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
@ -28,13 +28,8 @@ pub fn stdlib(attr: proc_macro::TokenStream, item: proc_macro::TokenStream) -> p
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[proc_macro_attribute]
|
#[proc_macro_attribute]
|
||||||
pub fn for_each_example_test(_attr: proc_macro::TokenStream, item: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
pub fn for_each_std_mod(_attr: proc_macro::TokenStream, item: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||||
example_tests::do_for_each_example_test(item.into()).into()
|
do_for_each_std_mod(item.into()).into()
|
||||||
}
|
|
||||||
|
|
||||||
#[proc_macro_attribute]
|
|
||||||
pub fn for_all_example_test(_attr: proc_macro::TokenStream, item: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
|
||||||
example_tests::do_for_all_example_test(item.into()).into()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Describes an argument of a stdlib function.
|
/// Describes an argument of a stdlib function.
|
||||||
@ -47,14 +42,6 @@ struct ArgMetadata {
|
|||||||
/// Does not do anything if the argument is already required.
|
/// Does not do anything if the argument is already required.
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
include_in_snippet: bool,
|
include_in_snippet: bool,
|
||||||
|
|
||||||
/// The snippet should suggest this value for the arg.
|
|
||||||
#[serde(default)]
|
|
||||||
snippet_value: Option<String>,
|
|
||||||
|
|
||||||
/// The snippet should suggest this value for the arg.
|
|
||||||
#[serde(default)]
|
|
||||||
snippet_value_array: Option<Vec<String>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Deserialize, Debug)]
|
||||||
@ -82,6 +69,11 @@ struct StdlibMetadata {
|
|||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
feature_tree_operation: bool,
|
feature_tree_operation: bool,
|
||||||
|
|
||||||
|
/// If true, expects keyword arguments.
|
||||||
|
/// If false, expects positional arguments.
|
||||||
|
#[serde(default)]
|
||||||
|
keywords: bool,
|
||||||
|
|
||||||
/// If true, the first argument is unlabeled.
|
/// If true, the first argument is unlabeled.
|
||||||
/// If false, all arguments require labels.
|
/// If false, all arguments require labels.
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
@ -100,6 +92,34 @@ fn do_stdlib(
|
|||||||
do_stdlib_inner(metadata, attr, item)
|
do_stdlib_inner(metadata, attr, item)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn do_for_each_std_mod(item: proc_macro2::TokenStream) -> proc_macro2::TokenStream {
|
||||||
|
let item: syn::ItemFn = syn::parse2(item.clone()).unwrap();
|
||||||
|
let mut result = proc_macro2::TokenStream::new();
|
||||||
|
for name in fs::read_dir("kcl-lib/std").unwrap().filter_map(|e| {
|
||||||
|
let e = e.unwrap();
|
||||||
|
let filename = e.file_name();
|
||||||
|
filename.to_str().unwrap().strip_suffix(".kcl").map(str::to_owned)
|
||||||
|
}) {
|
||||||
|
for i in 0..10_usize {
|
||||||
|
let mut item = item.clone();
|
||||||
|
item.sig.ident = syn::Ident::new(&format!("{}_{}_shard_{i}", item.sig.ident, name), Span::call_site());
|
||||||
|
let stmts = &item.block.stmts;
|
||||||
|
let block = quote! {
|
||||||
|
{
|
||||||
|
const STD_MOD_NAME: &str = #name;
|
||||||
|
const SHARD: usize = #i;
|
||||||
|
const SHARD_COUNT: usize = 10;
|
||||||
|
#(#stmts)*
|
||||||
|
}
|
||||||
|
};
|
||||||
|
item.block = Box::new(syn::parse2(block).unwrap());
|
||||||
|
result.extend(Some(item.into_token_stream()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
fn do_output(res: Result<(proc_macro2::TokenStream, Vec<Error>), Error>) -> proc_macro::TokenStream {
|
fn do_output(res: Result<(proc_macro2::TokenStream, Vec<Error>), Error>) -> proc_macro::TokenStream {
|
||||||
match res {
|
match res {
|
||||||
Err(err) => err.to_compile_error().into(),
|
Err(err) => err.to_compile_error().into(),
|
||||||
@ -287,6 +307,12 @@ fn do_stdlib_inner(
|
|||||||
quote! { false }
|
quote! { false }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let uses_keyword_arguments = if metadata.keywords {
|
||||||
|
quote! { true }
|
||||||
|
} else {
|
||||||
|
quote! { false }
|
||||||
|
};
|
||||||
|
|
||||||
let docs_crate = get_crate(None);
|
let docs_crate = get_crate(None);
|
||||||
|
|
||||||
// When the user attaches this proc macro to a function with the wrong type
|
// When the user attaches this proc macro to a function with the wrong type
|
||||||
@ -315,10 +341,6 @@ fn do_stdlib_inner(
|
|||||||
}
|
}
|
||||||
.trim_start_matches('_')
|
.trim_start_matches('_')
|
||||||
.to_string();
|
.to_string();
|
||||||
// These aren't really KCL args, they're just state that each stdlib function's impl needs.
|
|
||||||
if arg_name == "exec_state" || arg_name == "args" {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let ty = match arg {
|
let ty = match arg {
|
||||||
syn::FnArg::Receiver(pat) => pat.ty.as_ref().into_token_stream(),
|
syn::FnArg::Receiver(pat) => pat.ty.as_ref().into_token_stream(),
|
||||||
@ -329,24 +351,27 @@ fn do_stdlib_inner(
|
|||||||
|
|
||||||
let ty_string = rust_type_to_openapi_type(&ty_string);
|
let ty_string = rust_type_to_openapi_type(&ty_string);
|
||||||
let required = !ty_ident.to_string().starts_with("Option <");
|
let required = !ty_ident.to_string().starts_with("Option <");
|
||||||
let Some(arg_meta) = metadata.args.get(&arg_name) else {
|
let arg_meta = metadata.args.get(&arg_name);
|
||||||
errors.push(Error::new_spanned(arg, format!("arg {arg_name} not found")));
|
let description = if let Some(s) = arg_meta.map(|arg| &arg.docs) {
|
||||||
|
quote! { #s }
|
||||||
|
} else if metadata.keywords && ty_string != "Args" && ty_string != "ExecState" {
|
||||||
|
errors.push(Error::new_spanned(
|
||||||
|
&arg,
|
||||||
|
"Argument was not documented in the args block",
|
||||||
|
));
|
||||||
continue;
|
continue;
|
||||||
|
} else {
|
||||||
|
quote! { String::new() }
|
||||||
};
|
};
|
||||||
let description = arg_meta.docs.clone();
|
let include_in_snippet = required || arg_meta.map(|arg| arg.include_in_snippet).unwrap_or_default();
|
||||||
let include_in_snippet = required || arg_meta.include_in_snippet;
|
|
||||||
let snippet_value = arg_meta.snippet_value.clone();
|
|
||||||
let snippet_value_array = arg_meta.snippet_value_array.clone();
|
|
||||||
if snippet_value.is_some() && snippet_value_array.is_some() {
|
|
||||||
errors.push(Error::new_spanned(arg, format!("arg {arg_name} has set both snippet_value and snippet_value array, but at most one of these may be set. Please delete one of them.")));
|
|
||||||
}
|
|
||||||
let label_required = !(i == 0 && metadata.unlabeled_first);
|
let label_required = !(i == 0 && metadata.unlabeled_first);
|
||||||
let camel_case_arg_name = to_camel_case(&arg_name);
|
let camel_case_arg_name = to_camel_case(&arg_name);
|
||||||
if ty_string != "ExecState" && ty_string != "Args" {
|
if ty_string != "ExecState" && ty_string != "Args" {
|
||||||
let schema = quote! {
|
let schema = quote! {
|
||||||
generator.root_schema_for::<#ty_ident>()
|
generator.root_schema_for::<#ty_ident>()
|
||||||
};
|
};
|
||||||
let q0 = quote! {
|
arg_types.push(quote! {
|
||||||
|
#docs_crate::StdLibFnArg {
|
||||||
name: #camel_case_arg_name.to_string(),
|
name: #camel_case_arg_name.to_string(),
|
||||||
type_: #ty_string.to_string(),
|
type_: #ty_string.to_string(),
|
||||||
schema: #schema,
|
schema: #schema,
|
||||||
@ -354,32 +379,6 @@ fn do_stdlib_inner(
|
|||||||
label_required: #label_required,
|
label_required: #label_required,
|
||||||
description: #description.to_string(),
|
description: #description.to_string(),
|
||||||
include_in_snippet: #include_in_snippet,
|
include_in_snippet: #include_in_snippet,
|
||||||
};
|
|
||||||
let q1 = if let Some(snippet_value) = snippet_value {
|
|
||||||
quote! {
|
|
||||||
snippet_value: Some(#snippet_value.to_owned()),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
quote! {
|
|
||||||
snippet_value: None,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let q2 = if let Some(snippet_value_array) = snippet_value_array {
|
|
||||||
quote! {
|
|
||||||
snippet_value_array: Some(vec![
|
|
||||||
#(#snippet_value_array.to_owned()),*
|
|
||||||
]),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
quote! {
|
|
||||||
snippet_value_array: None,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
arg_types.push(quote! {
|
|
||||||
#docs_crate::StdLibFnArg {
|
|
||||||
#q0
|
|
||||||
#q1
|
|
||||||
#q2
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -443,8 +442,6 @@ fn do_stdlib_inner(
|
|||||||
label_required: true,
|
label_required: true,
|
||||||
description: String::new(),
|
description: String::new(),
|
||||||
include_in_snippet: true,
|
include_in_snippet: true,
|
||||||
snippet_value: None,
|
|
||||||
snippet_value_array: None,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -511,6 +508,10 @@ fn do_stdlib_inner(
|
|||||||
vec![#(#tags),*]
|
vec![#(#tags),*]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn keyword_arguments(&self) -> bool {
|
||||||
|
#uses_keyword_arguments
|
||||||
|
}
|
||||||
|
|
||||||
fn args(&self, inline_subschemas: bool) -> Vec<#docs_crate::StdLibFnArg> {
|
fn args(&self, inline_subschemas: bool) -> Vec<#docs_crate::StdLibFnArg> {
|
||||||
let mut settings = schemars::gen::SchemaSettings::openapi3();
|
let mut settings = schemars::gen::SchemaSettings::openapi3();
|
||||||
// We set this to false so we can recurse them later.
|
// We set this to false so we can recurse them later.
|
||||||
|
@ -40,9 +40,6 @@ fn test_args_with_refs() {
|
|||||||
let (item, mut errors) = do_stdlib(
|
let (item, mut errors) = do_stdlib(
|
||||||
quote! {
|
quote! {
|
||||||
name = "someFn",
|
name = "someFn",
|
||||||
args = {
|
|
||||||
data = { docs = "The data for this function"},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
quote! {
|
quote! {
|
||||||
/// Docs
|
/// Docs
|
||||||
@ -68,9 +65,6 @@ fn test_args_with_lifetime() {
|
|||||||
let (item, mut errors) = do_stdlib(
|
let (item, mut errors) = do_stdlib(
|
||||||
quote! {
|
quote! {
|
||||||
name = "someFn",
|
name = "someFn",
|
||||||
args = {
|
|
||||||
data = { docs = "Arg for the function" },
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
quote! {
|
quote! {
|
||||||
/// Docs
|
/// Docs
|
||||||
@ -123,8 +117,7 @@ fn test_stdlib_line_to() {
|
|||||||
quote! {
|
quote! {
|
||||||
name = "lineTo",
|
name = "lineTo",
|
||||||
args = {
|
args = {
|
||||||
data = { docs = "the sketch you're adding the line to" },
|
sketch = { docs = "the sketch you're adding the line to" }
|
||||||
sketch = { docs = "the sketch you're adding the line to" },
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
quote! {
|
quote! {
|
||||||
|
@ -91,6 +91,10 @@ impl crate::docs::StdLibFn for SomeFn {
|
|||||||
vec![]
|
vec![]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn keyword_arguments(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
fn args(&self, inline_subschemas: bool) -> Vec<crate::docs::StdLibFnArg> {
|
fn args(&self, inline_subschemas: bool) -> Vec<crate::docs::StdLibFnArg> {
|
||||||
let mut settings = schemars::gen::SchemaSettings::openapi3();
|
let mut settings = schemars::gen::SchemaSettings::openapi3();
|
||||||
settings.inline_subschemas = inline_subschemas;
|
settings.inline_subschemas = inline_subschemas;
|
||||||
@ -101,10 +105,8 @@ impl crate::docs::StdLibFn for SomeFn {
|
|||||||
schema: generator.root_schema_for::<Foo>(),
|
schema: generator.root_schema_for::<Foo>(),
|
||||||
required: true,
|
required: true,
|
||||||
label_required: true,
|
label_required: true,
|
||||||
description: "Arg for the function".to_string(),
|
description: String::new().to_string(),
|
||||||
include_in_snippet: true,
|
include_in_snippet: true,
|
||||||
snippet_value: None,
|
|
||||||
snippet_value_array: None,
|
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,8 +123,6 @@ impl crate::docs::StdLibFn for SomeFn {
|
|||||||
label_required: true,
|
label_required: true,
|
||||||
description: String::new(),
|
description: String::new(),
|
||||||
include_in_snippet: true,
|
include_in_snippet: true,
|
||||||
snippet_value: None,
|
|
||||||
snippet_value_array: None,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,6 +91,10 @@ impl crate::docs::StdLibFn for SomeFn {
|
|||||||
vec![]
|
vec![]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn keyword_arguments(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
fn args(&self, inline_subschemas: bool) -> Vec<crate::docs::StdLibFnArg> {
|
fn args(&self, inline_subschemas: bool) -> Vec<crate::docs::StdLibFnArg> {
|
||||||
let mut settings = schemars::gen::SchemaSettings::openapi3();
|
let mut settings = schemars::gen::SchemaSettings::openapi3();
|
||||||
settings.inline_subschemas = inline_subschemas;
|
settings.inline_subschemas = inline_subschemas;
|
||||||
@ -101,10 +105,8 @@ impl crate::docs::StdLibFn for SomeFn {
|
|||||||
schema: generator.root_schema_for::<str>(),
|
schema: generator.root_schema_for::<str>(),
|
||||||
required: true,
|
required: true,
|
||||||
label_required: true,
|
label_required: true,
|
||||||
description: "The data for this function".to_string(),
|
description: String::new().to_string(),
|
||||||
include_in_snippet: true,
|
include_in_snippet: true,
|
||||||
snippet_value: None,
|
|
||||||
snippet_value_array: None,
|
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,8 +123,6 @@ impl crate::docs::StdLibFn for SomeFn {
|
|||||||
label_required: true,
|
label_required: true,
|
||||||
description: String::new(),
|
description: String::new(),
|
||||||
include_in_snippet: true,
|
include_in_snippet: true,
|
||||||
snippet_value: None,
|
|
||||||
snippet_value_array: None,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,11 +92,23 @@ impl crate::docs::StdLibFn for Show {
|
|||||||
vec![]
|
vec![]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn keyword_arguments(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
fn args(&self, inline_subschemas: bool) -> Vec<crate::docs::StdLibFnArg> {
|
fn args(&self, inline_subschemas: bool) -> Vec<crate::docs::StdLibFnArg> {
|
||||||
let mut settings = schemars::gen::SchemaSettings::openapi3();
|
let mut settings = schemars::gen::SchemaSettings::openapi3();
|
||||||
settings.inline_subschemas = inline_subschemas;
|
settings.inline_subschemas = inline_subschemas;
|
||||||
let mut generator = schemars::gen::SchemaGenerator::new(settings);
|
let mut generator = schemars::gen::SchemaGenerator::new(settings);
|
||||||
vec![]
|
vec![crate::docs::StdLibFnArg {
|
||||||
|
name: "args".to_string(),
|
||||||
|
type_: "[number]".to_string(),
|
||||||
|
schema: generator.root_schema_for::<[f64; 2usize]>(),
|
||||||
|
required: true,
|
||||||
|
label_required: true,
|
||||||
|
description: String::new().to_string(),
|
||||||
|
include_in_snippet: true,
|
||||||
|
}]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn return_value(&self, inline_subschemas: bool) -> Option<crate::docs::StdLibFnArg> {
|
fn return_value(&self, inline_subschemas: bool) -> Option<crate::docs::StdLibFnArg> {
|
||||||
@ -112,8 +124,6 @@ impl crate::docs::StdLibFn for Show {
|
|||||||
label_required: true,
|
label_required: true,
|
||||||
description: String::new(),
|
description: String::new(),
|
||||||
include_in_snippet: true,
|
include_in_snippet: true,
|
||||||
snippet_value: None,
|
|
||||||
snippet_value_array: None,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,11 +92,23 @@ impl crate::docs::StdLibFn for Show {
|
|||||||
vec![]
|
vec![]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn keyword_arguments(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
fn args(&self, inline_subschemas: bool) -> Vec<crate::docs::StdLibFnArg> {
|
fn args(&self, inline_subschemas: bool) -> Vec<crate::docs::StdLibFnArg> {
|
||||||
let mut settings = schemars::gen::SchemaSettings::openapi3();
|
let mut settings = schemars::gen::SchemaSettings::openapi3();
|
||||||
settings.inline_subschemas = inline_subschemas;
|
settings.inline_subschemas = inline_subschemas;
|
||||||
let mut generator = schemars::gen::SchemaGenerator::new(settings);
|
let mut generator = schemars::gen::SchemaGenerator::new(settings);
|
||||||
vec![]
|
vec![crate::docs::StdLibFnArg {
|
||||||
|
name: "args".to_string(),
|
||||||
|
type_: "number".to_string(),
|
||||||
|
schema: generator.root_schema_for::<f64>(),
|
||||||
|
required: true,
|
||||||
|
label_required: true,
|
||||||
|
description: String::new().to_string(),
|
||||||
|
include_in_snippet: true,
|
||||||
|
}]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn return_value(&self, inline_subschemas: bool) -> Option<crate::docs::StdLibFnArg> {
|
fn return_value(&self, inline_subschemas: bool) -> Option<crate::docs::StdLibFnArg> {
|
||||||
@ -112,8 +124,6 @@ impl crate::docs::StdLibFn for Show {
|
|||||||
label_required: true,
|
label_required: true,
|
||||||
description: String::new(),
|
description: String::new(),
|
||||||
include_in_snippet: true,
|
include_in_snippet: true,
|
||||||
snippet_value: None,
|
|
||||||
snippet_value_array: None,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,11 +93,23 @@ impl crate::docs::StdLibFn for MyFunc {
|
|||||||
vec![]
|
vec![]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn keyword_arguments(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
fn args(&self, inline_subschemas: bool) -> Vec<crate::docs::StdLibFnArg> {
|
fn args(&self, inline_subschemas: bool) -> Vec<crate::docs::StdLibFnArg> {
|
||||||
let mut settings = schemars::gen::SchemaSettings::openapi3();
|
let mut settings = schemars::gen::SchemaSettings::openapi3();
|
||||||
settings.inline_subschemas = inline_subschemas;
|
settings.inline_subschemas = inline_subschemas;
|
||||||
let mut generator = schemars::gen::SchemaGenerator::new(settings);
|
let mut generator = schemars::gen::SchemaGenerator::new(settings);
|
||||||
vec![]
|
vec![crate::docs::StdLibFnArg {
|
||||||
|
name: "args".to_string(),
|
||||||
|
type_: "kittycad::types::InputFormat".to_string(),
|
||||||
|
schema: generator.root_schema_for::<Option<kittycad::types::InputFormat>>(),
|
||||||
|
required: false,
|
||||||
|
label_required: true,
|
||||||
|
description: String::new().to_string(),
|
||||||
|
include_in_snippet: false,
|
||||||
|
}]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn return_value(&self, inline_subschemas: bool) -> Option<crate::docs::StdLibFnArg> {
|
fn return_value(&self, inline_subschemas: bool) -> Option<crate::docs::StdLibFnArg> {
|
||||||
@ -113,8 +125,6 @@ impl crate::docs::StdLibFn for MyFunc {
|
|||||||
label_required: true,
|
label_required: true,
|
||||||
description: String::new(),
|
description: String::new(),
|
||||||
include_in_snippet: true,
|
include_in_snippet: true,
|
||||||
snippet_value: None,
|
|
||||||
snippet_value_array: None,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,6 +93,10 @@ impl crate::docs::StdLibFn for LineTo {
|
|||||||
vec![]
|
vec![]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn keyword_arguments(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
fn args(&self, inline_subschemas: bool) -> Vec<crate::docs::StdLibFnArg> {
|
fn args(&self, inline_subschemas: bool) -> Vec<crate::docs::StdLibFnArg> {
|
||||||
let mut settings = schemars::gen::SchemaSettings::openapi3();
|
let mut settings = schemars::gen::SchemaSettings::openapi3();
|
||||||
settings.inline_subschemas = inline_subschemas;
|
settings.inline_subschemas = inline_subschemas;
|
||||||
@ -104,10 +108,8 @@ impl crate::docs::StdLibFn for LineTo {
|
|||||||
schema: generator.root_schema_for::<LineToData>(),
|
schema: generator.root_schema_for::<LineToData>(),
|
||||||
required: true,
|
required: true,
|
||||||
label_required: true,
|
label_required: true,
|
||||||
description: "the sketch you're adding the line to".to_string(),
|
description: String::new().to_string(),
|
||||||
include_in_snippet: true,
|
include_in_snippet: true,
|
||||||
snippet_value: None,
|
|
||||||
snippet_value_array: None,
|
|
||||||
},
|
},
|
||||||
crate::docs::StdLibFnArg {
|
crate::docs::StdLibFnArg {
|
||||||
name: "sketch".to_string(),
|
name: "sketch".to_string(),
|
||||||
@ -117,8 +119,6 @@ impl crate::docs::StdLibFn for LineTo {
|
|||||||
label_required: true,
|
label_required: true,
|
||||||
description: "the sketch you're adding the line to".to_string(),
|
description: "the sketch you're adding the line to".to_string(),
|
||||||
include_in_snippet: true,
|
include_in_snippet: true,
|
||||||
snippet_value: None,
|
|
||||||
snippet_value_array: None,
|
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -136,8 +136,6 @@ impl crate::docs::StdLibFn for LineTo {
|
|||||||
label_required: true,
|
label_required: true,
|
||||||
description: String::new(),
|
description: String::new(),
|
||||||
include_in_snippet: true,
|
include_in_snippet: true,
|
||||||
snippet_value: None,
|
|
||||||
snippet_value_array: None,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,11 +92,23 @@ impl crate::docs::StdLibFn for Min {
|
|||||||
vec![]
|
vec![]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn keyword_arguments(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
fn args(&self, inline_subschemas: bool) -> Vec<crate::docs::StdLibFnArg> {
|
fn args(&self, inline_subschemas: bool) -> Vec<crate::docs::StdLibFnArg> {
|
||||||
let mut settings = schemars::gen::SchemaSettings::openapi3();
|
let mut settings = schemars::gen::SchemaSettings::openapi3();
|
||||||
settings.inline_subschemas = inline_subschemas;
|
settings.inline_subschemas = inline_subschemas;
|
||||||
let mut generator = schemars::gen::SchemaGenerator::new(settings);
|
let mut generator = schemars::gen::SchemaGenerator::new(settings);
|
||||||
vec![]
|
vec![crate::docs::StdLibFnArg {
|
||||||
|
name: "args".to_string(),
|
||||||
|
type_: "[number]".to_string(),
|
||||||
|
schema: generator.root_schema_for::<Vec<f64>>(),
|
||||||
|
required: true,
|
||||||
|
label_required: true,
|
||||||
|
description: String::new().to_string(),
|
||||||
|
include_in_snippet: true,
|
||||||
|
}]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn return_value(&self, inline_subschemas: bool) -> Option<crate::docs::StdLibFnArg> {
|
fn return_value(&self, inline_subschemas: bool) -> Option<crate::docs::StdLibFnArg> {
|
||||||
@ -112,8 +124,6 @@ impl crate::docs::StdLibFn for Min {
|
|||||||
label_required: true,
|
label_required: true,
|
||||||
description: String::new(),
|
description: String::new(),
|
||||||
include_in_snippet: true,
|
include_in_snippet: true,
|
||||||
snippet_value: None,
|
|
||||||
snippet_value_array: None,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,11 +92,23 @@ impl crate::docs::StdLibFn for Show {
|
|||||||
vec![]
|
vec![]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn keyword_arguments(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
fn args(&self, inline_subschemas: bool) -> Vec<crate::docs::StdLibFnArg> {
|
fn args(&self, inline_subschemas: bool) -> Vec<crate::docs::StdLibFnArg> {
|
||||||
let mut settings = schemars::gen::SchemaSettings::openapi3();
|
let mut settings = schemars::gen::SchemaSettings::openapi3();
|
||||||
settings.inline_subschemas = inline_subschemas;
|
settings.inline_subschemas = inline_subschemas;
|
||||||
let mut generator = schemars::gen::SchemaGenerator::new(settings);
|
let mut generator = schemars::gen::SchemaGenerator::new(settings);
|
||||||
vec![]
|
vec![crate::docs::StdLibFnArg {
|
||||||
|
name: "args".to_string(),
|
||||||
|
type_: "number".to_string(),
|
||||||
|
schema: generator.root_schema_for::<Option<f64>>(),
|
||||||
|
required: false,
|
||||||
|
label_required: true,
|
||||||
|
description: String::new().to_string(),
|
||||||
|
include_in_snippet: false,
|
||||||
|
}]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn return_value(&self, inline_subschemas: bool) -> Option<crate::docs::StdLibFnArg> {
|
fn return_value(&self, inline_subschemas: bool) -> Option<crate::docs::StdLibFnArg> {
|
||||||
@ -112,8 +124,6 @@ impl crate::docs::StdLibFn for Show {
|
|||||||
label_required: true,
|
label_required: true,
|
||||||
description: String::new(),
|
description: String::new(),
|
||||||
include_in_snippet: true,
|
include_in_snippet: true,
|
||||||
snippet_value: None,
|
|
||||||
snippet_value_array: None,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,11 +92,23 @@ impl crate::docs::StdLibFn for Import {
|
|||||||
vec![]
|
vec![]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn keyword_arguments(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
fn args(&self, inline_subschemas: bool) -> Vec<crate::docs::StdLibFnArg> {
|
fn args(&self, inline_subschemas: bool) -> Vec<crate::docs::StdLibFnArg> {
|
||||||
let mut settings = schemars::gen::SchemaSettings::openapi3();
|
let mut settings = schemars::gen::SchemaSettings::openapi3();
|
||||||
settings.inline_subschemas = inline_subschemas;
|
settings.inline_subschemas = inline_subschemas;
|
||||||
let mut generator = schemars::gen::SchemaGenerator::new(settings);
|
let mut generator = schemars::gen::SchemaGenerator::new(settings);
|
||||||
vec![]
|
vec![crate::docs::StdLibFnArg {
|
||||||
|
name: "args".to_string(),
|
||||||
|
type_: "kittycad::types::InputFormat".to_string(),
|
||||||
|
schema: generator.root_schema_for::<Option<kittycad::types::InputFormat>>(),
|
||||||
|
required: false,
|
||||||
|
label_required: true,
|
||||||
|
description: String::new().to_string(),
|
||||||
|
include_in_snippet: false,
|
||||||
|
}]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn return_value(&self, inline_subschemas: bool) -> Option<crate::docs::StdLibFnArg> {
|
fn return_value(&self, inline_subschemas: bool) -> Option<crate::docs::StdLibFnArg> {
|
||||||
@ -112,8 +124,6 @@ impl crate::docs::StdLibFn for Import {
|
|||||||
label_required: true,
|
label_required: true,
|
||||||
description: String::new(),
|
description: String::new(),
|
||||||
include_in_snippet: true,
|
include_in_snippet: true,
|
||||||
snippet_value: None,
|
|
||||||
snippet_value_array: None,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,11 +92,23 @@ impl crate::docs::StdLibFn for Import {
|
|||||||
vec![]
|
vec![]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn keyword_arguments(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
fn args(&self, inline_subschemas: bool) -> Vec<crate::docs::StdLibFnArg> {
|
fn args(&self, inline_subschemas: bool) -> Vec<crate::docs::StdLibFnArg> {
|
||||||
let mut settings = schemars::gen::SchemaSettings::openapi3();
|
let mut settings = schemars::gen::SchemaSettings::openapi3();
|
||||||
settings.inline_subschemas = inline_subschemas;
|
settings.inline_subschemas = inline_subschemas;
|
||||||
let mut generator = schemars::gen::SchemaGenerator::new(settings);
|
let mut generator = schemars::gen::SchemaGenerator::new(settings);
|
||||||
vec![]
|
vec![crate::docs::StdLibFnArg {
|
||||||
|
name: "args".to_string(),
|
||||||
|
type_: "kittycad::types::InputFormat".to_string(),
|
||||||
|
schema: generator.root_schema_for::<Option<kittycad::types::InputFormat>>(),
|
||||||
|
required: false,
|
||||||
|
label_required: true,
|
||||||
|
description: String::new().to_string(),
|
||||||
|
include_in_snippet: false,
|
||||||
|
}]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn return_value(&self, inline_subschemas: bool) -> Option<crate::docs::StdLibFnArg> {
|
fn return_value(&self, inline_subschemas: bool) -> Option<crate::docs::StdLibFnArg> {
|
||||||
@ -112,8 +124,6 @@ impl crate::docs::StdLibFn for Import {
|
|||||||
label_required: true,
|
label_required: true,
|
||||||
description: String::new(),
|
description: String::new(),
|
||||||
include_in_snippet: true,
|
include_in_snippet: true,
|
||||||
snippet_value: None,
|
|
||||||
snippet_value_array: None,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,11 +92,23 @@ impl crate::docs::StdLibFn for Import {
|
|||||||
vec![]
|
vec![]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn keyword_arguments(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
fn args(&self, inline_subschemas: bool) -> Vec<crate::docs::StdLibFnArg> {
|
fn args(&self, inline_subschemas: bool) -> Vec<crate::docs::StdLibFnArg> {
|
||||||
let mut settings = schemars::gen::SchemaSettings::openapi3();
|
let mut settings = schemars::gen::SchemaSettings::openapi3();
|
||||||
settings.inline_subschemas = inline_subschemas;
|
settings.inline_subschemas = inline_subschemas;
|
||||||
let mut generator = schemars::gen::SchemaGenerator::new(settings);
|
let mut generator = schemars::gen::SchemaGenerator::new(settings);
|
||||||
vec![]
|
vec![crate::docs::StdLibFnArg {
|
||||||
|
name: "args".to_string(),
|
||||||
|
type_: "kittycad::types::InputFormat".to_string(),
|
||||||
|
schema: generator.root_schema_for::<Option<kittycad::types::InputFormat>>(),
|
||||||
|
required: false,
|
||||||
|
label_required: true,
|
||||||
|
description: String::new().to_string(),
|
||||||
|
include_in_snippet: false,
|
||||||
|
}]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn return_value(&self, inline_subschemas: bool) -> Option<crate::docs::StdLibFnArg> {
|
fn return_value(&self, inline_subschemas: bool) -> Option<crate::docs::StdLibFnArg> {
|
||||||
@ -112,8 +124,6 @@ impl crate::docs::StdLibFn for Import {
|
|||||||
label_required: true,
|
label_required: true,
|
||||||
description: String::new(),
|
description: String::new(),
|
||||||
include_in_snippet: true,
|
include_in_snippet: true,
|
||||||
snippet_value: None,
|
|
||||||
snippet_value_array: None,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,11 +92,23 @@ impl crate::docs::StdLibFn for Show {
|
|||||||
vec![]
|
vec![]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn keyword_arguments(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
fn args(&self, inline_subschemas: bool) -> Vec<crate::docs::StdLibFnArg> {
|
fn args(&self, inline_subschemas: bool) -> Vec<crate::docs::StdLibFnArg> {
|
||||||
let mut settings = schemars::gen::SchemaSettings::openapi3();
|
let mut settings = schemars::gen::SchemaSettings::openapi3();
|
||||||
settings.inline_subschemas = inline_subschemas;
|
settings.inline_subschemas = inline_subschemas;
|
||||||
let mut generator = schemars::gen::SchemaGenerator::new(settings);
|
let mut generator = schemars::gen::SchemaGenerator::new(settings);
|
||||||
vec![]
|
vec![crate::docs::StdLibFnArg {
|
||||||
|
name: "args".to_string(),
|
||||||
|
type_: "[number]".to_string(),
|
||||||
|
schema: generator.root_schema_for::<Vec<f64>>(),
|
||||||
|
required: true,
|
||||||
|
label_required: true,
|
||||||
|
description: String::new().to_string(),
|
||||||
|
include_in_snippet: true,
|
||||||
|
}]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn return_value(&self, inline_subschemas: bool) -> Option<crate::docs::StdLibFnArg> {
|
fn return_value(&self, inline_subschemas: bool) -> Option<crate::docs::StdLibFnArg> {
|
||||||
@ -112,8 +124,6 @@ impl crate::docs::StdLibFn for Show {
|
|||||||
label_required: true,
|
label_required: true,
|
||||||
description: String::new(),
|
description: String::new(),
|
||||||
include_in_snippet: true,
|
include_in_snippet: true,
|
||||||
snippet_value: None,
|
|
||||||
snippet_value_array: None,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,6 +91,10 @@ impl crate::docs::StdLibFn for SomeFunction {
|
|||||||
vec![]
|
vec![]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn keyword_arguments(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
fn args(&self, inline_subschemas: bool) -> Vec<crate::docs::StdLibFnArg> {
|
fn args(&self, inline_subschemas: bool) -> Vec<crate::docs::StdLibFnArg> {
|
||||||
let mut settings = schemars::gen::SchemaSettings::openapi3();
|
let mut settings = schemars::gen::SchemaSettings::openapi3();
|
||||||
settings.inline_subschemas = inline_subschemas;
|
settings.inline_subschemas = inline_subschemas;
|
||||||
@ -111,8 +115,6 @@ impl crate::docs::StdLibFn for SomeFunction {
|
|||||||
label_required: true,
|
label_required: true,
|
||||||
description: String::new(),
|
description: String::new(),
|
||||||
include_in_snippet: true,
|
include_in_snippet: true,
|
||||||
snippet_value: None,
|
|
||||||
snippet_value_array: None,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "kcl-directory-test-macro"
|
name = "kcl-directory-test-macro"
|
||||||
description = "A tool for generating tests from a directory of kcl files"
|
description = "A tool for generating tests from a directory of kcl files"
|
||||||
version = "0.1.77"
|
version = "0.1.75"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
repository = "https://github.com/KittyCAD/modeling-app"
|
repository = "https://github.com/KittyCAD/modeling-app"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "kcl-language-server-release"
|
name = "kcl-language-server-release"
|
||||||
version = "0.1.77"
|
version = "0.1.75"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
authors = ["KittyCAD Inc <kcl@kittycad.io>"]
|
authors = ["KittyCAD Inc <kcl@kittycad.io>"]
|
||||||
publish = false
|
publish = false
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
name = "kcl-language-server"
|
name = "kcl-language-server"
|
||||||
description = "A language server for KCL."
|
description = "A language server for KCL."
|
||||||
authors = ["KittyCAD Inc <kcl@kittycad.io>"]
|
authors = ["KittyCAD Inc <kcl@kittycad.io>"]
|
||||||
version = "0.2.77"
|
version = "0.2.75"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "kcl-lib"
|
name = "kcl-lib"
|
||||||
description = "KittyCAD Language implementation and tools"
|
description = "KittyCAD Language implementation and tools"
|
||||||
version = "0.2.77"
|
version = "0.2.75"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
repository = "https://github.com/KittyCAD/modeling-app"
|
repository = "https://github.com/KittyCAD/modeling-app"
|
||||||
|
@ -10,9 +10,7 @@ use tower_lsp::lsp_types::{
|
|||||||
use crate::{
|
use crate::{
|
||||||
execution::annotations,
|
execution::annotations,
|
||||||
parsing::{
|
parsing::{
|
||||||
ast::types::{
|
ast::types::{Annotation, ImportSelector, ItemVisibility, Node, NonCodeValue, VariableKind},
|
||||||
Annotation, Expr, ImportSelector, ItemVisibility, LiteralValue, Node, NonCodeValue, VariableKind,
|
|
||||||
},
|
|
||||||
token::NumericSuffix,
|
token::NumericSuffix,
|
||||||
},
|
},
|
||||||
ModuleId,
|
ModuleId,
|
||||||
@ -295,6 +293,17 @@ impl DocData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
fn examples(&self) -> impl Iterator<Item = &String> {
|
||||||
|
match self {
|
||||||
|
DocData::Fn(f) => f.examples.iter(),
|
||||||
|
DocData::Const(c) => c.examples.iter(),
|
||||||
|
DocData::Ty(t) => t.examples.iter(),
|
||||||
|
DocData::Mod(_) => unimplemented!(),
|
||||||
|
}
|
||||||
|
.filter_map(|(s, p)| (!p.norun).then_some(s))
|
||||||
|
}
|
||||||
|
|
||||||
fn expect_mod(&self) -> &ModData {
|
fn expect_mod(&self) -> &ModData {
|
||||||
match self {
|
match self {
|
||||||
DocData::Mod(m) => m,
|
DocData::Mod(m) => m,
|
||||||
@ -622,6 +631,8 @@ impl FnData {
|
|||||||
return "clone(${0:part001})".to_owned();
|
return "clone(${0:part001})".to_owned();
|
||||||
} else if self.name == "hole" {
|
} else if self.name == "hole" {
|
||||||
return "hole(${0:holeSketch}, ${1:%})".to_owned();
|
return "hole(${0:holeSketch}, ${1:%})".to_owned();
|
||||||
|
} else if self.name == "circle" {
|
||||||
|
return "circle(center = [${0:3.14}, ${1:3.14}], diameter = ${2:3.14})".to_owned();
|
||||||
}
|
}
|
||||||
let mut args = Vec::new();
|
let mut args = Vec::new();
|
||||||
let mut index = 0;
|
let mut index = 0;
|
||||||
@ -685,8 +696,6 @@ pub struct ArgData {
|
|||||||
/// This is helpful if the type is really basic, like "number" -- that won't tell the user much about
|
/// This is helpful if the type is really basic, like "number" -- that won't tell the user much about
|
||||||
/// how this argument is meant to be used.
|
/// how this argument is meant to be used.
|
||||||
pub docs: Option<String>,
|
pub docs: Option<String>,
|
||||||
/// If given, LSP should use these as completion items.
|
|
||||||
pub snippet_array: Option<Vec<String>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for ArgData {
|
impl fmt::Display for ArgData {
|
||||||
@ -714,7 +723,6 @@ pub enum ArgKind {
|
|||||||
impl ArgData {
|
impl ArgData {
|
||||||
fn from_ast(arg: &crate::parsing::ast::types::Parameter) -> Self {
|
fn from_ast(arg: &crate::parsing::ast::types::Parameter) -> Self {
|
||||||
let mut result = ArgData {
|
let mut result = ArgData {
|
||||||
snippet_array: Default::default(),
|
|
||||||
name: arg.identifier.name.clone(),
|
name: arg.identifier.name.clone(),
|
||||||
ty: arg.type_.as_ref().map(|t| t.to_string()),
|
ty: arg.type_.as_ref().map(|t| t.to_string()),
|
||||||
docs: None,
|
docs: None,
|
||||||
@ -743,30 +751,6 @@ impl ArgData {
|
|||||||
p.value
|
p.value
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else if p.key.name == "snippetArray" {
|
|
||||||
let Expr::ArrayExpression(arr) = &p.value else {
|
|
||||||
panic!(
|
|
||||||
"Invalid value for `snippetArray`, expected array literal, found {:?}",
|
|
||||||
p.value
|
|
||||||
);
|
|
||||||
};
|
|
||||||
let mut items = Vec::new();
|
|
||||||
for s in &arr.elements {
|
|
||||||
let Expr::Literal(lit) = s else {
|
|
||||||
panic!(
|
|
||||||
"Invalid value in `snippetArray`, all items must be string literals but found {:?}",
|
|
||||||
s
|
|
||||||
);
|
|
||||||
};
|
|
||||||
let LiteralValue::String(litstr) = &lit.inner.value else {
|
|
||||||
panic!(
|
|
||||||
"Invalid value in `snippetArray`, all items must be string literals but found {:?}",
|
|
||||||
s
|
|
||||||
);
|
|
||||||
};
|
|
||||||
items.push(litstr.to_owned());
|
|
||||||
}
|
|
||||||
result.snippet_array = Some(items);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -788,19 +772,6 @@ impl ArgData {
|
|||||||
} else {
|
} else {
|
||||||
format!("{} = ", self.name)
|
format!("{} = ", self.name)
|
||||||
};
|
};
|
||||||
if let Some(vals) = &self.snippet_array {
|
|
||||||
let mut snippet = label.to_owned();
|
|
||||||
snippet.push('[');
|
|
||||||
let n = vals.len();
|
|
||||||
for (i, val) in vals.iter().enumerate() {
|
|
||||||
snippet.push_str(&format!("${{{}:{}}}", index + i, val));
|
|
||||||
if i != n - 1 {
|
|
||||||
snippet.push_str(", ");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
snippet.push(']');
|
|
||||||
return Some((index + n - 1, snippet));
|
|
||||||
}
|
|
||||||
match self.ty.as_deref() {
|
match self.ty.as_deref() {
|
||||||
Some(s) if s.starts_with("number") => Some((index, format!(r#"{label}${{{}:3.14}}"#, index))),
|
Some(s) if s.starts_with("number") => Some((index, format!(r#"{label}${{{}:3.14}}"#, index))),
|
||||||
Some("Point2d") => Some((
|
Some("Point2d") => Some((
|
||||||
@ -1216,7 +1187,7 @@ impl ApplyMeta for ArgData {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use kcl_derive_docs::{for_all_example_test, for_each_example_test};
|
use kcl_derive_docs::for_each_std_mod;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
@ -1254,81 +1225,51 @@ mod test {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[for_all_example_test]
|
#[for_each_std_mod]
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
|
||||||
async fn missing_test_examples() {
|
|
||||||
fn check_mod(m: &ModData) {
|
|
||||||
for d in m.children.values() {
|
|
||||||
let DocData::Fn(f) = d else {
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
|
|
||||||
for i in 0..f.examples.len() {
|
|
||||||
let name = format!("{}-{i}", f.qual_name.replace("::", "-"));
|
|
||||||
assert!(TEST_NAMES.contains(&&*name), "Missing test for example \"{name}\", maybe need to update kcl-derive-docs/src/example_tests.rs?")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let data = walk_prelude();
|
|
||||||
|
|
||||||
check_mod(&data);
|
|
||||||
for m in data.children.values() {
|
|
||||||
if let DocData::Mod(m) = m {
|
|
||||||
check_mod(m);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[for_each_example_test]
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
async fn kcl_test_examples() {
|
async fn kcl_test_examples() {
|
||||||
let std = walk_prelude();
|
let std = walk_prelude();
|
||||||
|
let mut errs = Vec::new();
|
||||||
|
|
||||||
let names = NAME.split('-');
|
let data = if STD_MOD_NAME == "prelude" {
|
||||||
let mut mods: Vec<_> = names.collect();
|
|
||||||
let number = mods.pop().unwrap();
|
|
||||||
let number: usize = number.parse().unwrap();
|
|
||||||
let name = mods.pop().unwrap();
|
|
||||||
let mut qualname = mods.join("::");
|
|
||||||
qualname.push_str("::");
|
|
||||||
qualname.push_str(name);
|
|
||||||
|
|
||||||
let data = if mods.len() == 1 {
|
|
||||||
&std
|
&std
|
||||||
} else {
|
} else {
|
||||||
std.children.get(&format!("M:std::{}", mods[1])).unwrap().expect_mod()
|
std.children
|
||||||
|
.get(&format!("M:std::{STD_MOD_NAME}"))
|
||||||
|
.unwrap()
|
||||||
|
.expect_mod()
|
||||||
};
|
};
|
||||||
|
|
||||||
let Some(DocData::Fn(d)) = data.children.get(&format!("I:{qualname}")) else {
|
let mut count = 0;
|
||||||
panic!("Could not find data for {NAME} (missing a child entry for {qualname}), maybe need to update kcl-derive-docs/src/example_tests.rs?");
|
for d in data.children.values() {
|
||||||
};
|
if let DocData::Mod(_) = d {
|
||||||
|
|
||||||
for (i, eg) in d.examples.iter().enumerate() {
|
|
||||||
if i != number {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let result = match crate::test_server::execute_and_snapshot(&eg.0, None).await {
|
|
||||||
Err(crate::errors::ExecError::Kcl(e)) => {
|
for (i, eg) in d.examples().enumerate() {
|
||||||
panic!("Error testing example {}{i}: {}", d.name, e.error.message());
|
count += 1;
|
||||||
|
if count % SHARD_COUNT != SHARD {
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
Err(other_err) => panic!("{}", other_err),
|
|
||||||
Ok(img) => img,
|
let result = match crate::test_server::execute_and_snapshot(eg, None).await {
|
||||||
};
|
Err(crate::errors::ExecError::Kcl(e)) => {
|
||||||
if eg.1.norun {
|
errs.push(format!("Error testing example {}{i}: {}", d.name(), e.error.message()));
|
||||||
return;
|
continue;
|
||||||
|
}
|
||||||
|
Err(other_err) => panic!("{}", other_err),
|
||||||
|
Ok(img) => img,
|
||||||
|
};
|
||||||
|
twenty_twenty::assert_image(
|
||||||
|
format!("tests/outputs/serial_test_example_{}{i}.png", d.example_name()),
|
||||||
|
&result,
|
||||||
|
0.99,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
twenty_twenty::assert_image(
|
|
||||||
format!(
|
|
||||||
"tests/outputs/serial_test_example_fn_{}{i}.png",
|
|
||||||
qualname.replace("::", "-")
|
|
||||||
),
|
|
||||||
&result,
|
|
||||||
0.99,
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
panic!("Could not find data for {NAME} (no example {number}), maybe need to update kcl-derive-docs/src/example_tests.rs?");
|
if !errs.is_empty() {
|
||||||
|
panic!("{}", errs.join("\n\n"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -78,6 +78,8 @@ pub struct StdLibFnData {
|
|||||||
pub description: String,
|
pub description: String,
|
||||||
/// The tags of the function.
|
/// The tags of the function.
|
||||||
pub tags: Vec<String>,
|
pub tags: Vec<String>,
|
||||||
|
/// If this function uses keyword arguments, or positional arguments.
|
||||||
|
pub keyword_arguments: bool,
|
||||||
/// The args of the function.
|
/// The args of the function.
|
||||||
pub args: Vec<StdLibFnArg>,
|
pub args: Vec<StdLibFnArg>,
|
||||||
/// The return value of the function.
|
/// The return value of the function.
|
||||||
@ -109,13 +111,6 @@ pub struct StdLibFnArg {
|
|||||||
/// Include this in completion snippets?
|
/// Include this in completion snippets?
|
||||||
#[serde(default, skip_serializing_if = "is_false")]
|
#[serde(default, skip_serializing_if = "is_false")]
|
||||||
pub include_in_snippet: bool,
|
pub include_in_snippet: bool,
|
||||||
/// Snippet should suggest this value for the argument.
|
|
||||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
||||||
pub snippet_value: Option<String>,
|
|
||||||
/// Snippet should suggest this value for the argument.
|
|
||||||
/// The suggested value should be an array, with these elements.
|
|
||||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
||||||
pub snippet_value_array: Option<Vec<String>>,
|
|
||||||
/// Additional information that could be used instead of the type's description.
|
/// Additional information that could be used instead of the type's description.
|
||||||
/// This is helpful if the type is really basic, like "u32" -- that won't tell the user much about
|
/// This is helpful if the type is really basic, like "u32" -- that won't tell the user much about
|
||||||
/// how this argument is meant to be used.
|
/// how this argument is meant to be used.
|
||||||
@ -170,21 +165,6 @@ impl StdLibFnArg {
|
|||||||
} else {
|
} else {
|
||||||
""
|
""
|
||||||
};
|
};
|
||||||
if let Some(vals) = &self.snippet_value_array {
|
|
||||||
let mut snippet = label.to_owned();
|
|
||||||
snippet.push('[');
|
|
||||||
for (i, val) in vals.iter().enumerate() {
|
|
||||||
snippet.push_str(&format!("${{{}:{}}}", index + i, val));
|
|
||||||
if i != vals.len() - 1 {
|
|
||||||
snippet.push_str(", ");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
snippet.push(']');
|
|
||||||
return Ok(Some((index + vals.len() - 1, snippet)));
|
|
||||||
}
|
|
||||||
if let Some(val) = &self.snippet_value {
|
|
||||||
return Ok(Some((index, format!("{label}${{{}:{}}}", index, val))));
|
|
||||||
}
|
|
||||||
if (self.type_ == "Sketch"
|
if (self.type_ == "Sketch"
|
||||||
|| self.type_ == "[Sketch]"
|
|| self.type_ == "[Sketch]"
|
||||||
|| self.type_ == "Geometry"
|
|| self.type_ == "Geometry"
|
||||||
@ -470,6 +450,9 @@ pub trait StdLibFn: std::fmt::Debug + Send + Sync {
|
|||||||
/// The description of the function.
|
/// The description of the function.
|
||||||
fn description(&self) -> String;
|
fn description(&self) -> String;
|
||||||
|
|
||||||
|
/// Does this use keyword arguments, or positional?
|
||||||
|
fn keyword_arguments(&self) -> bool;
|
||||||
|
|
||||||
/// The tags of the function.
|
/// The tags of the function.
|
||||||
fn tags(&self) -> Vec<String>;
|
fn tags(&self) -> Vec<String>;
|
||||||
|
|
||||||
@ -504,6 +487,7 @@ pub trait StdLibFn: std::fmt::Debug + Send + Sync {
|
|||||||
summary: self.summary(),
|
summary: self.summary(),
|
||||||
description: self.description(),
|
description: self.description(),
|
||||||
tags: self.tags(),
|
tags: self.tags(),
|
||||||
|
keyword_arguments: self.keyword_arguments(),
|
||||||
args: self.args(false),
|
args: self.args(false),
|
||||||
return_value: self.return_value(false),
|
return_value: self.return_value(false),
|
||||||
unpublished: self.unpublished(),
|
unpublished: self.unpublished(),
|
||||||
@ -587,7 +571,7 @@ pub trait StdLibFn: std::fmt::Debug + Send + Sync {
|
|||||||
} else if self.name() == "subtract2D" {
|
} else if self.name() == "subtract2D" {
|
||||||
return Ok("subtract2d(${0:%}, tool = ${1:%})".to_string());
|
return Ok("subtract2d(${0:%}, tool = ${1:%})".to_string());
|
||||||
}
|
}
|
||||||
let in_keyword_fn = true;
|
let in_keyword_fn = self.keyword_arguments();
|
||||||
let mut args = Vec::new();
|
let mut args = Vec::new();
|
||||||
let mut index = 0;
|
let mut index = 0;
|
||||||
for arg in self.args(true).iter() {
|
for arg in self.args(true).iter() {
|
||||||
@ -1004,13 +988,6 @@ mod tests {
|
|||||||
assert_eq!(snippet, r#"startSketchOn(${0:XY})"#);
|
assert_eq!(snippet, r#"startSketchOn(${0:XY})"#);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn get_autocomplete_snippet_start_profile() {
|
|
||||||
let start_sketch_on_fn: Box<dyn StdLibFn> = Box::new(crate::std::sketch::StartProfile);
|
|
||||||
let snippet = start_sketch_on_fn.to_autocomplete_snippet().unwrap();
|
|
||||||
assert_eq!(snippet, r#"startProfile(${0:%}, at = [${1:0}, ${2:0}])"#);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn get_autocomplete_snippet_pattern_circular_3d() {
|
fn get_autocomplete_snippet_pattern_circular_3d() {
|
||||||
// We test this one specifically because it has ints and floats and strings.
|
// We test this one specifically because it has ints and floats and strings.
|
||||||
@ -1018,7 +995,7 @@ mod tests {
|
|||||||
let snippet = pattern_fn.to_autocomplete_snippet().unwrap();
|
let snippet = pattern_fn.to_autocomplete_snippet().unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
snippet,
|
snippet,
|
||||||
r#"patternCircular3d(${0:%}, instances = ${1:10}, axis = [${2:1}, ${3:0}, ${4:0}], center = [${5:0}, ${6:0}, ${7:0}])"#
|
r#"patternCircular3d(${0:%}, instances = ${1:10}, axis = [${2:3.14}, ${3:3.14}, ${4:3.14}], center = [${5:3.14}, ${6:3.14}, ${7:3.14}])"#
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1040,8 +1017,8 @@ mod tests {
|
|||||||
};
|
};
|
||||||
let snippet = circle_fn.to_autocomplete_snippet();
|
let snippet = circle_fn.to_autocomplete_snippet();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
snippet, r#"circle(center = [${0:0}, ${1:0}], diameter = ${2:3.14})"#,
|
snippet,
|
||||||
"actual = left, expected = right"
|
r#"circle(center = [${0:3.14}, ${1:3.14}], diameter = ${2:3.14})"#
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1071,7 +1048,7 @@ mod tests {
|
|||||||
let snippet = pattern_fn.to_autocomplete_snippet().unwrap();
|
let snippet = pattern_fn.to_autocomplete_snippet().unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
snippet,
|
snippet,
|
||||||
r#"patternLinear2d(${0:%}, instances = ${1:10}, distance = ${2:3.14}, axis = [${3:1}, ${4:0}])"#
|
r#"patternLinear2d(${0:%}, instances = ${1:10}, distance = ${2:3.14}, axis = [${3:3.14}, ${4:3.14}])"#
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,14 +20,6 @@ use crate::{
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod mermaid_tests;
|
mod mermaid_tests;
|
||||||
|
|
||||||
macro_rules! internal_error {
|
|
||||||
($range:expr, $($rest:tt)*) => {{
|
|
||||||
let message = format!($($rest)*);
|
|
||||||
debug_assert!(false, "{}", &message);
|
|
||||||
return Err(KclError::Internal(KclErrorDetails::new(message, vec![$range])));
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A command that may create or update artifacts on the TS side. Because
|
/// A command that may create or update artifacts on the TS side. Because
|
||||||
/// engine commands are batched, we don't have the response yet when these are
|
/// engine commands are batched, we don't have the response yet when these are
|
||||||
/// created.
|
/// created.
|
||||||
@ -1000,10 +992,7 @@ fn artifacts_to_update(
|
|||||||
let path_id = ArtifactId::new(match cmd {
|
let path_id = ArtifactId::new(match cmd {
|
||||||
ModelingCmd::ClosePath(c) => c.path_id,
|
ModelingCmd::ClosePath(c) => c.path_id,
|
||||||
ModelingCmd::ExtendPath(e) => e.path.into(),
|
ModelingCmd::ExtendPath(e) => e.path.into(),
|
||||||
_ => internal_error!(
|
_ => unreachable!(),
|
||||||
range,
|
|
||||||
"Close or extend path command variant not handled: id={id:?}, cmd={cmd:?}"
|
|
||||||
),
|
|
||||||
});
|
});
|
||||||
let mut return_arr = Vec::new();
|
let mut return_arr = Vec::new();
|
||||||
return_arr.push(Artifact::Segment(Segment {
|
return_arr.push(Artifact::Segment(Segment {
|
||||||
@ -1034,69 +1023,6 @@ fn artifacts_to_update(
|
|||||||
}
|
}
|
||||||
return Ok(return_arr);
|
return Ok(return_arr);
|
||||||
}
|
}
|
||||||
ModelingCmd::EntityMirror(kcmc::EntityMirror {
|
|
||||||
ids: original_path_ids, ..
|
|
||||||
})
|
|
||||||
| ModelingCmd::EntityMirrorAcrossEdge(kcmc::EntityMirrorAcrossEdge {
|
|
||||||
ids: original_path_ids, ..
|
|
||||||
}) => {
|
|
||||||
let face_edge_infos = match response {
|
|
||||||
OkModelingCmdResponse::EntityMirror(resp) => &resp.entity_face_edge_ids,
|
|
||||||
OkModelingCmdResponse::EntityMirrorAcrossEdge(resp) => &resp.entity_face_edge_ids,
|
|
||||||
_ => internal_error!(
|
|
||||||
range,
|
|
||||||
"Mirror response variant not handled: id={id:?}, cmd={cmd:?}, response={response:?}"
|
|
||||||
),
|
|
||||||
};
|
|
||||||
if original_path_ids.len() != face_edge_infos.len() {
|
|
||||||
internal_error!(range, "EntityMirror or EntityMirrorAcrossEdge response has different number face edge info than original mirrored paths: id={id:?}, cmd={cmd:?}, response={response:?}");
|
|
||||||
}
|
|
||||||
let mut return_arr = Vec::new();
|
|
||||||
for (face_edge_info, original_path_id) in face_edge_infos.iter().zip(original_path_ids) {
|
|
||||||
let original_path_id = ArtifactId::new(*original_path_id);
|
|
||||||
let path_id = ArtifactId::new(face_edge_info.object_id);
|
|
||||||
// The path may be an existing path that was extended or a new
|
|
||||||
// path.
|
|
||||||
let mut path = if let Some(Artifact::Path(path)) = artifacts.get(&path_id) {
|
|
||||||
// Existing path.
|
|
||||||
path.clone()
|
|
||||||
} else {
|
|
||||||
// It's a new path. We need the original path to get some
|
|
||||||
// of its info.
|
|
||||||
let Some(Artifact::Path(original_path)) = artifacts.get(&original_path_id) else {
|
|
||||||
// We couldn't find the original path. This is a bug.
|
|
||||||
internal_error!(range, "Couldn't find original path for mirror2d: original_path_id={original_path_id:?}, cmd={cmd:?}");
|
|
||||||
};
|
|
||||||
Path {
|
|
||||||
id: path_id,
|
|
||||||
plane_id: original_path.plane_id,
|
|
||||||
seg_ids: Vec::new(),
|
|
||||||
sweep_id: None,
|
|
||||||
solid2d_id: None,
|
|
||||||
code_ref: code_ref.clone(),
|
|
||||||
composite_solid_id: None,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
face_edge_info.edges.iter().for_each(|edge_id| {
|
|
||||||
let edge_id = ArtifactId::new(*edge_id);
|
|
||||||
return_arr.push(Artifact::Segment(Segment {
|
|
||||||
id: edge_id,
|
|
||||||
path_id: path.id,
|
|
||||||
surface_id: None,
|
|
||||||
edge_ids: Vec::new(),
|
|
||||||
edge_cut_id: None,
|
|
||||||
code_ref: code_ref.clone(),
|
|
||||||
common_surface_ids: Vec::new(),
|
|
||||||
}));
|
|
||||||
// Add the edge ID to the path.
|
|
||||||
path.seg_ids.push(edge_id);
|
|
||||||
});
|
|
||||||
|
|
||||||
return_arr.push(Artifact::Path(path));
|
|
||||||
}
|
|
||||||
return Ok(return_arr);
|
|
||||||
}
|
|
||||||
ModelingCmd::Extrude(kcmc::Extrude { target, .. })
|
ModelingCmd::Extrude(kcmc::Extrude { target, .. })
|
||||||
| ModelingCmd::Revolve(kcmc::Revolve { target, .. })
|
| ModelingCmd::Revolve(kcmc::Revolve { target, .. })
|
||||||
| ModelingCmd::RevolveAboutEdge(kcmc::RevolveAboutEdge { target, .. })
|
| ModelingCmd::RevolveAboutEdge(kcmc::RevolveAboutEdge { target, .. })
|
||||||
@ -1106,7 +1032,7 @@ fn artifacts_to_update(
|
|||||||
ModelingCmd::Revolve(_) => SweepSubType::Revolve,
|
ModelingCmd::Revolve(_) => SweepSubType::Revolve,
|
||||||
ModelingCmd::RevolveAboutEdge(_) => SweepSubType::RevolveAboutEdge,
|
ModelingCmd::RevolveAboutEdge(_) => SweepSubType::RevolveAboutEdge,
|
||||||
ModelingCmd::Sweep(_) => SweepSubType::Sweep,
|
ModelingCmd::Sweep(_) => SweepSubType::Sweep,
|
||||||
_ => internal_error!(range, "Sweep-like command variant not handled: id={id:?}, cmd={cmd:?}",),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
let mut return_arr = Vec::new();
|
let mut return_arr = Vec::new();
|
||||||
let target = ArtifactId::from(target);
|
let target = ArtifactId::from(target);
|
||||||
@ -1371,13 +1297,7 @@ fn artifacts_to_update(
|
|||||||
let edge_id = if let Some(edge_id) = cmd.edge_id {
|
let edge_id = if let Some(edge_id) = cmd.edge_id {
|
||||||
ArtifactId::new(edge_id)
|
ArtifactId::new(edge_id)
|
||||||
} else {
|
} else {
|
||||||
let Some(edge_id) = cmd.edge_ids.first() else {
|
cmd.edge_ids.first().unwrap().into()
|
||||||
internal_error!(
|
|
||||||
range,
|
|
||||||
"Solid3dFilletEdge command has no edge ID: id={id:?}, cmd={cmd:?}"
|
|
||||||
);
|
|
||||||
};
|
|
||||||
edge_id.into()
|
|
||||||
};
|
};
|
||||||
return_arr.push(Artifact::EdgeCut(EdgeCut {
|
return_arr.push(Artifact::EdgeCut(EdgeCut {
|
||||||
id,
|
id,
|
||||||
@ -1446,10 +1366,7 @@ fn artifacts_to_update(
|
|||||||
let solid_ids = union.solid_ids.iter().copied().map(ArtifactId::new).collect::<Vec<_>>();
|
let solid_ids = union.solid_ids.iter().copied().map(ArtifactId::new).collect::<Vec<_>>();
|
||||||
(CompositeSolidSubType::Union, solid_ids, Vec::new())
|
(CompositeSolidSubType::Union, solid_ids, Vec::new())
|
||||||
}
|
}
|
||||||
_ => internal_error!(
|
_ => unreachable!(),
|
||||||
range,
|
|
||||||
"Boolean or composite command variant not handled: id={id:?}, cmd={cmd:?}"
|
|
||||||
),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut new_solid_ids = vec![id];
|
let mut new_solid_ids = vec![id];
|
||||||
|
@ -86,7 +86,7 @@ impl ExecutorContext {
|
|||||||
) -> Result<(Option<KclValue>, EnvironmentRef, Vec<String>), KclError> {
|
) -> Result<(Option<KclValue>, EnvironmentRef, Vec<String>), KclError> {
|
||||||
crate::log::log(format!("enter module {path} {}", exec_state.stack()));
|
crate::log::log(format!("enter module {path} {}", exec_state.stack()));
|
||||||
|
|
||||||
let mut local_state = ModuleState::new(path.clone(), exec_state.stack().memory.clone(), Some(module_id));
|
let mut local_state = ModuleState::new(path.std_path(), exec_state.stack().memory.clone(), Some(module_id));
|
||||||
if !preserve_mem {
|
if !preserve_mem {
|
||||||
std::mem::swap(&mut exec_state.mod_local, &mut local_state);
|
std::mem::swap(&mut exec_state.mod_local, &mut local_state);
|
||||||
}
|
}
|
||||||
@ -139,13 +139,8 @@ impl ExecutorContext {
|
|||||||
|
|
||||||
let source_range = SourceRange::from(import_stmt);
|
let source_range = SourceRange::from(import_stmt);
|
||||||
let attrs = &import_stmt.outer_attrs;
|
let attrs = &import_stmt.outer_attrs;
|
||||||
let module_path = ModulePath::from_import_path(
|
|
||||||
&import_stmt.path,
|
|
||||||
&self.settings.project_directory,
|
|
||||||
&exec_state.mod_local.path,
|
|
||||||
)?;
|
|
||||||
let module_id = self
|
let module_id = self
|
||||||
.open_module(&import_stmt.path, attrs, &module_path, exec_state, source_range)
|
.open_module(&import_stmt.path, attrs, exec_state, source_range)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
match &import_stmt.selector {
|
match &import_stmt.selector {
|
||||||
@ -303,9 +298,9 @@ impl ExecutorContext {
|
|||||||
let impl_kind = annotations::get_impl(&ty.outer_attrs, metadata.source_range)?.unwrap_or_default();
|
let impl_kind = annotations::get_impl(&ty.outer_attrs, metadata.source_range)?.unwrap_or_default();
|
||||||
match impl_kind {
|
match impl_kind {
|
||||||
annotations::Impl::Rust => {
|
annotations::Impl::Rust => {
|
||||||
let std_path = match &exec_state.mod_local.path {
|
let std_path = match &exec_state.mod_local.std_path {
|
||||||
ModulePath::Std { value } => value,
|
Some(p) => p,
|
||||||
ModulePath::Local { .. } | ModulePath::Main => {
|
None => {
|
||||||
return Err(KclError::Semantic(KclErrorDetails::new(
|
return Err(KclError::Semantic(KclErrorDetails::new(
|
||||||
"User-defined types are not yet supported.".to_owned(),
|
"User-defined types are not yet supported.".to_owned(),
|
||||||
vec![metadata.source_range],
|
vec![metadata.source_range],
|
||||||
@ -425,15 +420,16 @@ impl ExecutorContext {
|
|||||||
&self,
|
&self,
|
||||||
path: &ImportPath,
|
path: &ImportPath,
|
||||||
attrs: &[Node<Annotation>],
|
attrs: &[Node<Annotation>],
|
||||||
resolved_path: &ModulePath,
|
|
||||||
exec_state: &mut ExecState,
|
exec_state: &mut ExecState,
|
||||||
source_range: SourceRange,
|
source_range: SourceRange,
|
||||||
) -> Result<ModuleId, KclError> {
|
) -> Result<ModuleId, KclError> {
|
||||||
|
let resolved_path = ModulePath::from_import_path(path, &self.settings.project_directory);
|
||||||
|
|
||||||
match path {
|
match path {
|
||||||
ImportPath::Kcl { .. } => {
|
ImportPath::Kcl { .. } => {
|
||||||
exec_state.global.mod_loader.cycle_check(resolved_path, source_range)?;
|
exec_state.global.mod_loader.cycle_check(&resolved_path, source_range)?;
|
||||||
|
|
||||||
if let Some(id) = exec_state.id_for_module(resolved_path) {
|
if let Some(id) = exec_state.id_for_module(&resolved_path) {
|
||||||
return Ok(id);
|
return Ok(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -444,12 +440,12 @@ impl ExecutorContext {
|
|||||||
exec_state.add_id_to_source(id, source.clone());
|
exec_state.add_id_to_source(id, source.clone());
|
||||||
// TODO handle parsing errors properly
|
// TODO handle parsing errors properly
|
||||||
let parsed = crate::parsing::parse_str(&source.source, id).parse_errs_as_err()?;
|
let parsed = crate::parsing::parse_str(&source.source, id).parse_errs_as_err()?;
|
||||||
exec_state.add_module(id, resolved_path.clone(), ModuleRepr::Kcl(parsed, None));
|
exec_state.add_module(id, resolved_path, ModuleRepr::Kcl(parsed, None));
|
||||||
|
|
||||||
Ok(id)
|
Ok(id)
|
||||||
}
|
}
|
||||||
ImportPath::Foreign { .. } => {
|
ImportPath::Foreign { .. } => {
|
||||||
if let Some(id) = exec_state.id_for_module(resolved_path) {
|
if let Some(id) = exec_state.id_for_module(&resolved_path) {
|
||||||
return Ok(id);
|
return Ok(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -459,11 +455,11 @@ impl ExecutorContext {
|
|||||||
exec_state.add_path_to_source_id(resolved_path.clone(), id);
|
exec_state.add_path_to_source_id(resolved_path.clone(), id);
|
||||||
let format = super::import::format_from_annotations(attrs, path, source_range)?;
|
let format = super::import::format_from_annotations(attrs, path, source_range)?;
|
||||||
let geom = super::import::import_foreign(path, format, exec_state, self, source_range).await?;
|
let geom = super::import::import_foreign(path, format, exec_state, self, source_range).await?;
|
||||||
exec_state.add_module(id, resolved_path.clone(), ModuleRepr::Foreign(geom, None));
|
exec_state.add_module(id, resolved_path, ModuleRepr::Foreign(geom, None));
|
||||||
Ok(id)
|
Ok(id)
|
||||||
}
|
}
|
||||||
ImportPath::Std { .. } => {
|
ImportPath::Std { .. } => {
|
||||||
if let Some(id) = exec_state.id_for_module(resolved_path) {
|
if let Some(id) = exec_state.id_for_module(&resolved_path) {
|
||||||
return Ok(id);
|
return Ok(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -475,7 +471,7 @@ impl ExecutorContext {
|
|||||||
let parsed = crate::parsing::parse_str(&source.source, id)
|
let parsed = crate::parsing::parse_str(&source.source, id)
|
||||||
.parse_errs_as_err()
|
.parse_errs_as_err()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
exec_state.add_module(id, resolved_path.clone(), ModuleRepr::Kcl(parsed, None));
|
exec_state.add_module(id, resolved_path, ModuleRepr::Kcl(parsed, None));
|
||||||
Ok(id)
|
Ok(id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -636,7 +632,7 @@ impl ExecutorContext {
|
|||||||
.unwrap_or(false);
|
.unwrap_or(false);
|
||||||
|
|
||||||
if rust_impl {
|
if rust_impl {
|
||||||
if let ModulePath::Std { value: std_path } = &exec_state.mod_local.path {
|
if let Some(std_path) = &exec_state.mod_local.std_path {
|
||||||
let (func, props) = crate::std::std_fn(std_path, statement_kind.expect_name());
|
let (func, props) = crate::std::std_fn(std_path, statement_kind.expect_name());
|
||||||
KclValue::Function {
|
KclValue::Function {
|
||||||
value: FunctionSource::Std {
|
value: FunctionSource::Std {
|
||||||
@ -733,7 +729,23 @@ fn apply_ascription(
|
|||||||
let ty = RuntimeType::from_parsed(ty.inner.clone(), exec_state, value.into())
|
let ty = RuntimeType::from_parsed(ty.inner.clone(), exec_state, value.into())
|
||||||
.map_err(|e| KclError::Semantic(e.into()))?;
|
.map_err(|e| KclError::Semantic(e.into()))?;
|
||||||
|
|
||||||
value.coerce(&ty, false, exec_state).map_err(|_| {
|
let mut value = value.clone();
|
||||||
|
|
||||||
|
// If the number has unknown units but the user is explicitly specifying them, treat the value as having had it's units erased,
|
||||||
|
// rather than forcing the user to explicitly erase them.
|
||||||
|
if let KclValue::Number { value: n, meta, .. } = &value {
|
||||||
|
if let RuntimeType::Primitive(PrimitiveType::Number(num)) = &ty {
|
||||||
|
if num.is_fully_specified() {
|
||||||
|
value = KclValue::Number {
|
||||||
|
ty: NumericType::Any,
|
||||||
|
value: *n,
|
||||||
|
meta: meta.clone(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
value.coerce(&ty, exec_state).map_err(|_| {
|
||||||
let suggestion = if ty == RuntimeType::length() {
|
let suggestion = if ty == RuntimeType::length() {
|
||||||
", you might try coercing to a fully specified numeric type such as `number(mm)`"
|
", you might try coercing to a fully specified numeric type such as `number(mm)`"
|
||||||
} else if ty == RuntimeType::angle() {
|
} else if ty == RuntimeType::angle() {
|
||||||
@ -743,7 +755,7 @@ fn apply_ascription(
|
|||||||
};
|
};
|
||||||
KclError::Semantic(KclErrorDetails::new(
|
KclError::Semantic(KclErrorDetails::new(
|
||||||
format!(
|
format!(
|
||||||
"could not coerce value of type {} to type {ty}{suggestion}",
|
"could not coerce {} value to type {ty}{suggestion}",
|
||||||
value.human_friendly_type()
|
value.human_friendly_type()
|
||||||
),
|
),
|
||||||
vec![source_range],
|
vec![source_range],
|
||||||
@ -1619,8 +1631,8 @@ arr1 = [42]: [number(cm)]
|
|||||||
assert_eq!(*ty, RuntimeType::known_length(UnitLen::Cm));
|
assert_eq!(*ty, RuntimeType::known_length(UnitLen::Cm));
|
||||||
// Compare, ignoring meta.
|
// Compare, ignoring meta.
|
||||||
if let KclValue::Number { value, ty, .. } = &value[0] {
|
if let KclValue::Number { value, ty, .. } = &value[0] {
|
||||||
// It should not convert units.
|
// Converted from mm to cm.
|
||||||
assert_eq!(*value, 42.0);
|
assert_eq!(*value, 4.2);
|
||||||
assert_eq!(*ty, NumericType::Known(UnitType::Length(UnitLen::Cm)));
|
assert_eq!(*ty, NumericType::Known(UnitType::Length(UnitLen::Cm)));
|
||||||
} else {
|
} else {
|
||||||
panic!("Expected a number; found {:?}", value[0]);
|
panic!("Expected a number; found {:?}", value[0]);
|
||||||
@ -1636,7 +1648,7 @@ a = 42: string
|
|||||||
let err = result.unwrap_err();
|
let err = result.unwrap_err();
|
||||||
assert!(
|
assert!(
|
||||||
err.to_string()
|
err.to_string()
|
||||||
.contains("could not coerce value of type number(default units) to type string"),
|
.contains("could not coerce number(default units) value to type string"),
|
||||||
"Expected error but found {err:?}"
|
"Expected error but found {err:?}"
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -1647,7 +1659,7 @@ a = 42: Plane
|
|||||||
let err = result.unwrap_err();
|
let err = result.unwrap_err();
|
||||||
assert!(
|
assert!(
|
||||||
err.to_string()
|
err.to_string()
|
||||||
.contains("could not coerce value of type number(default units) to type Plane"),
|
.contains("could not coerce number(default units) value to type Plane"),
|
||||||
"Expected error but found {err:?}"
|
"Expected error but found {err:?}"
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -1657,9 +1669,8 @@ arr = [0]: [string]
|
|||||||
let result = parse_execute(program).await;
|
let result = parse_execute(program).await;
|
||||||
let err = result.unwrap_err();
|
let err = result.unwrap_err();
|
||||||
assert!(
|
assert!(
|
||||||
err.to_string().contains(
|
err.to_string()
|
||||||
"could not coerce value of type array of number(default units) with 1 value to type [string]"
|
.contains("could not coerce [any; 1] value to type [string]"),
|
||||||
),
|
|
||||||
"Expected error but found {err:?}"
|
"Expected error but found {err:?}"
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -1670,7 +1681,7 @@ mixedArr = [0, "a"]: [number(mm)]
|
|||||||
let err = result.unwrap_err();
|
let err = result.unwrap_err();
|
||||||
assert!(
|
assert!(
|
||||||
err.to_string()
|
err.to_string()
|
||||||
.contains("could not coerce value of type array of number(default units), string with 2 values to type [number(mm)]"),
|
.contains("could not coerce [any; 2] value to type [number(mm)]"),
|
||||||
"Expected error but found {err:?}"
|
"Expected error but found {err:?}"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -606,7 +606,6 @@ fn type_check_params_kw(
|
|||||||
.value
|
.value
|
||||||
.coerce(
|
.coerce(
|
||||||
&RuntimeType::from_parsed(ty.clone(), exec_state, arg.source_range).map_err(|e| KclError::Semantic(e.into()))?,
|
&RuntimeType::from_parsed(ty.clone(), exec_state, arg.source_range).map_err(|e| KclError::Semantic(e.into()))?,
|
||||||
true,
|
|
||||||
exec_state,
|
exec_state,
|
||||||
)
|
)
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
@ -681,7 +680,6 @@ fn type_check_params_kw(
|
|||||||
.coerce(
|
.coerce(
|
||||||
&RuntimeType::from_parsed(ty.clone(), exec_state, arg.1.source_range)
|
&RuntimeType::from_parsed(ty.clone(), exec_state, arg.1.source_range)
|
||||||
.map_err(|e| KclError::Semantic(e.into()))?,
|
.map_err(|e| KclError::Semantic(e.into()))?,
|
||||||
true,
|
|
||||||
exec_state,
|
exec_state,
|
||||||
)
|
)
|
||||||
.map_err(|_| {
|
.map_err(|_| {
|
||||||
@ -799,7 +797,7 @@ fn coerce_result_type(
|
|||||||
if let RuntimeType::Array(inner, ArrayLen::NonEmpty) = &ty {
|
if let RuntimeType::Array(inner, ArrayLen::NonEmpty) = &ty {
|
||||||
ty = RuntimeType::Union(vec![(**inner).clone(), ty]);
|
ty = RuntimeType::Union(vec![(**inner).clone(), ty]);
|
||||||
}
|
}
|
||||||
let val = val.coerce(&ty, true, exec_state).map_err(|_| {
|
let val = val.coerce(&ty, exec_state).map_err(|_| {
|
||||||
KclError::Semantic(KclErrorDetails::new(
|
KclError::Semantic(KclErrorDetails::new(
|
||||||
format!(
|
format!(
|
||||||
"This function requires its result to be of type `{}`, but found {}",
|
"This function requires its result to be of type `{}`, but found {}",
|
||||||
|
@ -281,34 +281,8 @@ impl KclValue {
|
|||||||
/// Human readable type name used in error messages. Should not be relied
|
/// Human readable type name used in error messages. Should not be relied
|
||||||
/// on for program logic.
|
/// on for program logic.
|
||||||
pub(crate) fn human_friendly_type(&self) -> String {
|
pub(crate) fn human_friendly_type(&self) -> String {
|
||||||
self.inner_human_friendly_type(1)
|
if let Some(t) = self.principal_type() {
|
||||||
}
|
return t.to_string();
|
||||||
|
|
||||||
fn inner_human_friendly_type(&self, max_depth: usize) -> String {
|
|
||||||
if let Some(pt) = self.principal_type() {
|
|
||||||
if max_depth > 0 {
|
|
||||||
// The principal type of an array uses the array's element type,
|
|
||||||
// which is oftentimes `any`, and that's not a helpful message. So
|
|
||||||
// we show the actual elements.
|
|
||||||
if let Some(elements) = self.as_array() {
|
|
||||||
// If it's empty, we want to show the type of the array.
|
|
||||||
if !elements.is_empty() {
|
|
||||||
// A max of 3 is good because it's common to use 3D points.
|
|
||||||
let max = 3;
|
|
||||||
let len = elements.len();
|
|
||||||
let ellipsis = if len > max { ", ..." } else { "" };
|
|
||||||
let element_label = if len == 1 { "value" } else { "values" };
|
|
||||||
let element_tys = elements
|
|
||||||
.iter()
|
|
||||||
.take(max)
|
|
||||||
.map(|elem| elem.inner_human_friendly_type(max_depth - 1))
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
.join(", ");
|
|
||||||
return format!("array of {element_tys}{ellipsis} with {len} {element_label}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return pt.to_string();
|
|
||||||
}
|
}
|
||||||
match self {
|
match self {
|
||||||
KclValue::Uuid { .. } => "Unique ID (uuid)",
|
KclValue::Uuid { .. } => "Unique ID (uuid)",
|
||||||
@ -670,88 +644,3 @@ impl From<GeometryWithImportedGeometry> for KclValue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_human_friendly_type() {
|
|
||||||
let len = KclValue::Number {
|
|
||||||
value: 1.0,
|
|
||||||
ty: NumericType::Known(UnitType::Length(UnitLen::Unknown)),
|
|
||||||
meta: vec![],
|
|
||||||
};
|
|
||||||
assert_eq!(len.human_friendly_type(), "number(Length)".to_string());
|
|
||||||
|
|
||||||
let unknown = KclValue::Number {
|
|
||||||
value: 1.0,
|
|
||||||
ty: NumericType::Unknown,
|
|
||||||
meta: vec![],
|
|
||||||
};
|
|
||||||
assert_eq!(unknown.human_friendly_type(), "number(unknown units)".to_string());
|
|
||||||
|
|
||||||
let mm = KclValue::Number {
|
|
||||||
value: 1.0,
|
|
||||||
ty: NumericType::Known(UnitType::Length(UnitLen::Mm)),
|
|
||||||
meta: vec![],
|
|
||||||
};
|
|
||||||
assert_eq!(mm.human_friendly_type(), "number(mm)".to_string());
|
|
||||||
|
|
||||||
let array1_mm = KclValue::HomArray {
|
|
||||||
value: vec![mm.clone()],
|
|
||||||
ty: RuntimeType::any(),
|
|
||||||
};
|
|
||||||
assert_eq!(
|
|
||||||
array1_mm.human_friendly_type(),
|
|
||||||
"array of number(mm) with 1 value".to_string()
|
|
||||||
);
|
|
||||||
|
|
||||||
let array2_mm = KclValue::HomArray {
|
|
||||||
value: vec![mm.clone(), mm.clone()],
|
|
||||||
ty: RuntimeType::any(),
|
|
||||||
};
|
|
||||||
assert_eq!(
|
|
||||||
array2_mm.human_friendly_type(),
|
|
||||||
"array of number(mm), number(mm) with 2 values".to_string()
|
|
||||||
);
|
|
||||||
|
|
||||||
let array3_mm = KclValue::HomArray {
|
|
||||||
value: vec![mm.clone(), mm.clone(), mm.clone()],
|
|
||||||
ty: RuntimeType::any(),
|
|
||||||
};
|
|
||||||
assert_eq!(
|
|
||||||
array3_mm.human_friendly_type(),
|
|
||||||
"array of number(mm), number(mm), number(mm) with 3 values".to_string()
|
|
||||||
);
|
|
||||||
|
|
||||||
let inches = KclValue::Number {
|
|
||||||
value: 1.0,
|
|
||||||
ty: NumericType::Known(UnitType::Length(UnitLen::Inches)),
|
|
||||||
meta: vec![],
|
|
||||||
};
|
|
||||||
let array4 = KclValue::HomArray {
|
|
||||||
value: vec![mm.clone(), mm.clone(), inches.clone(), mm.clone()],
|
|
||||||
ty: RuntimeType::any(),
|
|
||||||
};
|
|
||||||
assert_eq!(
|
|
||||||
array4.human_friendly_type(),
|
|
||||||
"array of number(mm), number(mm), number(in), ... with 4 values".to_string()
|
|
||||||
);
|
|
||||||
|
|
||||||
let empty_array = KclValue::HomArray {
|
|
||||||
value: vec![],
|
|
||||||
ty: RuntimeType::any(),
|
|
||||||
};
|
|
||||||
assert_eq!(empty_array.human_friendly_type(), "[any; 0]".to_string());
|
|
||||||
|
|
||||||
let array_nested = KclValue::HomArray {
|
|
||||||
value: vec![array2_mm.clone()],
|
|
||||||
ty: RuntimeType::any(),
|
|
||||||
};
|
|
||||||
assert_eq!(
|
|
||||||
array_nested.human_friendly_type(),
|
|
||||||
"array of [any; 2] with 1 value".to_string()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1044,7 +1044,6 @@ impl ExecutorContext {
|
|||||||
|
|
||||||
let root_imports = crate::walk::import_universe(
|
let root_imports = crate::walk::import_universe(
|
||||||
self,
|
self,
|
||||||
&ModulePath::Main,
|
|
||||||
&ModuleRepr::Kcl(program.ast.clone(), None),
|
&ModuleRepr::Kcl(program.ast.clone(), None),
|
||||||
&mut universe,
|
&mut universe,
|
||||||
exec_state,
|
exec_state,
|
||||||
@ -1212,10 +1211,15 @@ impl ExecutorContext {
|
|||||||
/// SAFETY: the current thread must have sole access to the memory referenced in exec_state.
|
/// SAFETY: the current thread must have sole access to the memory referenced in exec_state.
|
||||||
async fn eval_prelude(&self, exec_state: &mut ExecState, source_range: SourceRange) -> Result<(), KclError> {
|
async fn eval_prelude(&self, exec_state: &mut ExecState, source_range: SourceRange) -> Result<(), KclError> {
|
||||||
if exec_state.stack().memory.requires_std() {
|
if exec_state.stack().memory.requires_std() {
|
||||||
let path = vec!["std".to_owned(), "prelude".to_owned()];
|
|
||||||
let resolved_path = ModulePath::from_std_import_path(&path)?;
|
|
||||||
let id = self
|
let id = self
|
||||||
.open_module(&ImportPath::Std { path }, &[], &resolved_path, exec_state, source_range)
|
.open_module(
|
||||||
|
&ImportPath::Std {
|
||||||
|
path: vec!["std".to_owned(), "prelude".to_owned()],
|
||||||
|
},
|
||||||
|
&[],
|
||||||
|
exec_state,
|
||||||
|
source_range,
|
||||||
|
)
|
||||||
.await?;
|
.await?;
|
||||||
let (module_memory, _) = self.exec_module_for_items(id, exec_state, source_range).await?;
|
let (module_memory, _) = self.exec_module_for_items(id, exec_state, source_range).await?;
|
||||||
|
|
||||||
|
@ -85,14 +85,14 @@ pub(super) struct ModuleState {
|
|||||||
/// Settings specified from annotations.
|
/// Settings specified from annotations.
|
||||||
pub settings: MetaSettings,
|
pub settings: MetaSettings,
|
||||||
pub(super) explicit_length_units: bool,
|
pub(super) explicit_length_units: bool,
|
||||||
pub(super) path: ModulePath,
|
pub(super) std_path: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ExecState {
|
impl ExecState {
|
||||||
pub fn new(exec_context: &super::ExecutorContext) -> Self {
|
pub fn new(exec_context: &super::ExecutorContext) -> Self {
|
||||||
ExecState {
|
ExecState {
|
||||||
global: GlobalState::new(&exec_context.settings),
|
global: GlobalState::new(&exec_context.settings),
|
||||||
mod_local: ModuleState::new(ModulePath::Main, ProgramMemory::new(), Default::default()),
|
mod_local: ModuleState::new(None, ProgramMemory::new(), Default::default()),
|
||||||
exec_context: Some(exec_context.clone()),
|
exec_context: Some(exec_context.clone()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -102,7 +102,7 @@ impl ExecState {
|
|||||||
|
|
||||||
*self = ExecState {
|
*self = ExecState {
|
||||||
global,
|
global,
|
||||||
mod_local: ModuleState::new(self.mod_local.path.clone(), ProgramMemory::new(), Default::default()),
|
mod_local: ModuleState::new(None, ProgramMemory::new(), Default::default()),
|
||||||
exec_context: Some(exec_context.clone()),
|
exec_context: Some(exec_context.clone()),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -337,14 +337,14 @@ impl GlobalState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ModuleState {
|
impl ModuleState {
|
||||||
pub(super) fn new(path: ModulePath, memory: Arc<ProgramMemory>, module_id: Option<ModuleId>) -> Self {
|
pub(super) fn new(std_path: Option<String>, memory: Arc<ProgramMemory>, module_id: Option<ModuleId>) -> Self {
|
||||||
ModuleState {
|
ModuleState {
|
||||||
id_generator: IdGenerator::new(module_id),
|
id_generator: IdGenerator::new(module_id),
|
||||||
stack: memory.new_stack(),
|
stack: memory.new_stack(),
|
||||||
pipe_value: Default::default(),
|
pipe_value: Default::default(),
|
||||||
module_exports: Default::default(),
|
module_exports: Default::default(),
|
||||||
explicit_length_units: false,
|
explicit_length_units: false,
|
||||||
path,
|
std_path,
|
||||||
settings: MetaSettings {
|
settings: MetaSettings {
|
||||||
default_length_units: Default::default(),
|
default_length_units: Default::default(),
|
||||||
default_angle_units: Default::default(),
|
default_angle_units: Default::default(),
|
||||||
|
@ -1039,25 +1039,19 @@ impl KclValue {
|
|||||||
/// - result.principal_type().unwrap().subtype(ty)
|
/// - result.principal_type().unwrap().subtype(ty)
|
||||||
///
|
///
|
||||||
/// If self.principal_type() == ty then result == self
|
/// If self.principal_type() == ty then result == self
|
||||||
pub fn coerce(
|
pub fn coerce(&self, ty: &RuntimeType, exec_state: &mut ExecState) -> Result<KclValue, CoercionError> {
|
||||||
&self,
|
|
||||||
ty: &RuntimeType,
|
|
||||||
convert_units: bool,
|
|
||||||
exec_state: &mut ExecState,
|
|
||||||
) -> Result<KclValue, CoercionError> {
|
|
||||||
match ty {
|
match ty {
|
||||||
RuntimeType::Primitive(ty) => self.coerce_to_primitive_type(ty, convert_units, exec_state),
|
RuntimeType::Primitive(ty) => self.coerce_to_primitive_type(ty, exec_state),
|
||||||
RuntimeType::Array(ty, len) => self.coerce_to_array_type(ty, convert_units, *len, exec_state, false),
|
RuntimeType::Array(ty, len) => self.coerce_to_array_type(ty, *len, exec_state, false),
|
||||||
RuntimeType::Tuple(tys) => self.coerce_to_tuple_type(tys, convert_units, exec_state),
|
RuntimeType::Tuple(tys) => self.coerce_to_tuple_type(tys, exec_state),
|
||||||
RuntimeType::Union(tys) => self.coerce_to_union_type(tys, convert_units, exec_state),
|
RuntimeType::Union(tys) => self.coerce_to_union_type(tys, exec_state),
|
||||||
RuntimeType::Object(tys) => self.coerce_to_object_type(tys, convert_units, exec_state),
|
RuntimeType::Object(tys) => self.coerce_to_object_type(tys, exec_state),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn coerce_to_primitive_type(
|
fn coerce_to_primitive_type(
|
||||||
&self,
|
&self,
|
||||||
ty: &PrimitiveType,
|
ty: &PrimitiveType,
|
||||||
convert_units: bool,
|
|
||||||
exec_state: &mut ExecState,
|
exec_state: &mut ExecState,
|
||||||
) -> Result<KclValue, CoercionError> {
|
) -> Result<KclValue, CoercionError> {
|
||||||
let value = match self {
|
let value = match self {
|
||||||
@ -1066,29 +1060,7 @@ impl KclValue {
|
|||||||
};
|
};
|
||||||
match ty {
|
match ty {
|
||||||
PrimitiveType::Any => Ok(value.clone()),
|
PrimitiveType::Any => Ok(value.clone()),
|
||||||
PrimitiveType::Number(ty) => {
|
PrimitiveType::Number(ty) => ty.coerce(value),
|
||||||
if convert_units {
|
|
||||||
return ty.coerce(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Instead of converting units, reinterpret the number as having
|
|
||||||
// different units.
|
|
||||||
//
|
|
||||||
// If the user is explicitly specifying units, treat the value
|
|
||||||
// as having had its units erased, rather than forcing the user
|
|
||||||
// to explicitly erase them.
|
|
||||||
if let KclValue::Number { value: n, meta, .. } = &value {
|
|
||||||
if ty.is_fully_specified() {
|
|
||||||
let value = KclValue::Number {
|
|
||||||
ty: NumericType::Any,
|
|
||||||
value: *n,
|
|
||||||
meta: meta.clone(),
|
|
||||||
};
|
|
||||||
return ty.coerce(&value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ty.coerce(value)
|
|
||||||
}
|
|
||||||
PrimitiveType::String => match value {
|
PrimitiveType::String => match value {
|
||||||
KclValue::String { .. } => Ok(value.clone()),
|
KclValue::String { .. } => Ok(value.clone()),
|
||||||
_ => Err(self.into()),
|
_ => Err(self.into()),
|
||||||
@ -1181,22 +1153,10 @@ impl KclValue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let origin = values.get("origin").ok_or(self.into()).and_then(|p| {
|
let origin = values.get("origin").ok_or(self.into()).and_then(|p| {
|
||||||
p.coerce_to_array_type(
|
p.coerce_to_array_type(&RuntimeType::length(), ArrayLen::Known(2), exec_state, true)
|
||||||
&RuntimeType::length(),
|
|
||||||
convert_units,
|
|
||||||
ArrayLen::Known(2),
|
|
||||||
exec_state,
|
|
||||||
true,
|
|
||||||
)
|
|
||||||
})?;
|
})?;
|
||||||
let direction = values.get("direction").ok_or(self.into()).and_then(|p| {
|
let direction = values.get("direction").ok_or(self.into()).and_then(|p| {
|
||||||
p.coerce_to_array_type(
|
p.coerce_to_array_type(&RuntimeType::length(), ArrayLen::Known(2), exec_state, true)
|
||||||
&RuntimeType::length(),
|
|
||||||
convert_units,
|
|
||||||
ArrayLen::Known(2),
|
|
||||||
exec_state,
|
|
||||||
true,
|
|
||||||
)
|
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok(KclValue::Object {
|
Ok(KclValue::Object {
|
||||||
@ -1221,22 +1181,10 @@ impl KclValue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let origin = values.get("origin").ok_or(self.into()).and_then(|p| {
|
let origin = values.get("origin").ok_or(self.into()).and_then(|p| {
|
||||||
p.coerce_to_array_type(
|
p.coerce_to_array_type(&RuntimeType::length(), ArrayLen::Known(3), exec_state, true)
|
||||||
&RuntimeType::length(),
|
|
||||||
convert_units,
|
|
||||||
ArrayLen::Known(3),
|
|
||||||
exec_state,
|
|
||||||
true,
|
|
||||||
)
|
|
||||||
})?;
|
})?;
|
||||||
let direction = values.get("direction").ok_or(self.into()).and_then(|p| {
|
let direction = values.get("direction").ok_or(self.into()).and_then(|p| {
|
||||||
p.coerce_to_array_type(
|
p.coerce_to_array_type(&RuntimeType::length(), ArrayLen::Known(3), exec_state, true)
|
||||||
&RuntimeType::length(),
|
|
||||||
convert_units,
|
|
||||||
ArrayLen::Known(3),
|
|
||||||
exec_state,
|
|
||||||
true,
|
|
||||||
)
|
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok(KclValue::Object {
|
Ok(KclValue::Object {
|
||||||
@ -1273,7 +1221,6 @@ impl KclValue {
|
|||||||
fn coerce_to_array_type(
|
fn coerce_to_array_type(
|
||||||
&self,
|
&self,
|
||||||
ty: &RuntimeType,
|
ty: &RuntimeType,
|
||||||
convert_units: bool,
|
|
||||||
len: ArrayLen,
|
len: ArrayLen,
|
||||||
exec_state: &mut ExecState,
|
exec_state: &mut ExecState,
|
||||||
allow_shrink: bool,
|
allow_shrink: bool,
|
||||||
@ -1302,7 +1249,7 @@ impl KclValue {
|
|||||||
let value_result = value
|
let value_result = value
|
||||||
.iter()
|
.iter()
|
||||||
.take(satisfied_len)
|
.take(satisfied_len)
|
||||||
.map(|v| v.coerce(ty, convert_units, exec_state))
|
.map(|v| v.coerce(ty, exec_state))
|
||||||
.collect::<Result<Vec<_>, _>>();
|
.collect::<Result<Vec<_>, _>>();
|
||||||
|
|
||||||
if let Ok(value) = value_result {
|
if let Ok(value) = value_result {
|
||||||
@ -1317,10 +1264,10 @@ impl KclValue {
|
|||||||
if let KclValue::HomArray { value: inner_value, .. } = item {
|
if let KclValue::HomArray { value: inner_value, .. } = item {
|
||||||
// Flatten elements.
|
// Flatten elements.
|
||||||
for item in inner_value {
|
for item in inner_value {
|
||||||
values.push(item.coerce(ty, convert_units, exec_state)?);
|
values.push(item.coerce(ty, exec_state)?);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
values.push(item.coerce(ty, convert_units, exec_state)?);
|
values.push(item.coerce(ty, exec_state)?);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1350,7 +1297,7 @@ impl KclValue {
|
|||||||
.ok_or(CoercionError::from(self))?;
|
.ok_or(CoercionError::from(self))?;
|
||||||
let value = value
|
let value = value
|
||||||
.iter()
|
.iter()
|
||||||
.map(|item| item.coerce(ty, convert_units, exec_state))
|
.map(|item| item.coerce(ty, exec_state))
|
||||||
.take(len)
|
.take(len)
|
||||||
.collect::<Result<Vec<_>, _>>()?;
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
@ -1361,24 +1308,19 @@ impl KclValue {
|
|||||||
ty: ty.clone(),
|
ty: ty.clone(),
|
||||||
}),
|
}),
|
||||||
_ if len.satisfied(1, false).is_some() => Ok(KclValue::HomArray {
|
_ if len.satisfied(1, false).is_some() => Ok(KclValue::HomArray {
|
||||||
value: vec![self.coerce(ty, convert_units, exec_state)?],
|
value: vec![self.coerce(ty, exec_state)?],
|
||||||
ty: ty.clone(),
|
ty: ty.clone(),
|
||||||
}),
|
}),
|
||||||
_ => Err(self.into()),
|
_ => Err(self.into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn coerce_to_tuple_type(
|
fn coerce_to_tuple_type(&self, tys: &[RuntimeType], exec_state: &mut ExecState) -> Result<KclValue, CoercionError> {
|
||||||
&self,
|
|
||||||
tys: &[RuntimeType],
|
|
||||||
convert_units: bool,
|
|
||||||
exec_state: &mut ExecState,
|
|
||||||
) -> Result<KclValue, CoercionError> {
|
|
||||||
match self {
|
match self {
|
||||||
KclValue::Tuple { value, .. } | KclValue::HomArray { value, .. } if value.len() == tys.len() => {
|
KclValue::Tuple { value, .. } | KclValue::HomArray { value, .. } if value.len() == tys.len() => {
|
||||||
let mut result = Vec::new();
|
let mut result = Vec::new();
|
||||||
for (i, t) in tys.iter().enumerate() {
|
for (i, t) in tys.iter().enumerate() {
|
||||||
result.push(value[i].coerce(t, convert_units, exec_state)?);
|
result.push(value[i].coerce(t, exec_state)?);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(KclValue::Tuple {
|
Ok(KclValue::Tuple {
|
||||||
@ -1398,14 +1340,9 @@ impl KclValue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn coerce_to_union_type(
|
fn coerce_to_union_type(&self, tys: &[RuntimeType], exec_state: &mut ExecState) -> Result<KclValue, CoercionError> {
|
||||||
&self,
|
|
||||||
tys: &[RuntimeType],
|
|
||||||
convert_units: bool,
|
|
||||||
exec_state: &mut ExecState,
|
|
||||||
) -> Result<KclValue, CoercionError> {
|
|
||||||
for t in tys {
|
for t in tys {
|
||||||
if let Ok(v) = self.coerce(t, convert_units, exec_state) {
|
if let Ok(v) = self.coerce(t, exec_state) {
|
||||||
return Ok(v);
|
return Ok(v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1416,7 +1353,6 @@ impl KclValue {
|
|||||||
fn coerce_to_object_type(
|
fn coerce_to_object_type(
|
||||||
&self,
|
&self,
|
||||||
tys: &[(String, RuntimeType)],
|
tys: &[(String, RuntimeType)],
|
||||||
_convert_units: bool,
|
|
||||||
_exec_state: &mut ExecState,
|
_exec_state: &mut ExecState,
|
||||||
) -> Result<KclValue, CoercionError> {
|
) -> Result<KclValue, CoercionError> {
|
||||||
match self {
|
match self {
|
||||||
@ -1525,7 +1461,7 @@ mod test {
|
|||||||
exec_state: &mut ExecState,
|
exec_state: &mut ExecState,
|
||||||
) {
|
) {
|
||||||
let is_subtype = value == expected_value;
|
let is_subtype = value == expected_value;
|
||||||
assert_eq!(&value.coerce(super_type, true, exec_state).unwrap(), expected_value);
|
assert_eq!(&value.coerce(super_type, exec_state).unwrap(), expected_value);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
is_subtype,
|
is_subtype,
|
||||||
value.principal_type().is_some() && value.principal_type().unwrap().subtype(super_type),
|
value.principal_type().is_some() && value.principal_type().unwrap().subtype(super_type),
|
||||||
@ -1573,10 +1509,10 @@ mod test {
|
|||||||
);
|
);
|
||||||
// Coercing an empty tuple or array to an array of length 1
|
// Coercing an empty tuple or array to an array of length 1
|
||||||
// should fail.
|
// should fail.
|
||||||
v.coerce(&aty1, true, &mut exec_state).unwrap_err();
|
v.coerce(&aty1, &mut exec_state).unwrap_err();
|
||||||
// Coercing an empty tuple or array to an array that's
|
// Coercing an empty tuple or array to an array that's
|
||||||
// non-empty should fail.
|
// non-empty should fail.
|
||||||
v.coerce(&aty0, true, &mut exec_state).unwrap_err();
|
v.coerce(&aty0, &mut exec_state).unwrap_err();
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
assert_coerce_results(
|
assert_coerce_results(
|
||||||
@ -1624,7 +1560,7 @@ mod test {
|
|||||||
|
|
||||||
for v in &values[1..] {
|
for v in &values[1..] {
|
||||||
// Not a subtype
|
// Not a subtype
|
||||||
v.coerce(&RuntimeType::Primitive(PrimitiveType::Boolean), true, &mut exec_state)
|
v.coerce(&RuntimeType::Primitive(PrimitiveType::Boolean), &mut exec_state)
|
||||||
.unwrap_err();
|
.unwrap_err();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1659,8 +1595,8 @@ mod test {
|
|||||||
},
|
},
|
||||||
&mut exec_state,
|
&mut exec_state,
|
||||||
);
|
);
|
||||||
none.coerce(&aty1, true, &mut exec_state).unwrap_err();
|
none.coerce(&aty1, &mut exec_state).unwrap_err();
|
||||||
none.coerce(&aty1p, true, &mut exec_state).unwrap_err();
|
none.coerce(&aty1p, &mut exec_state).unwrap_err();
|
||||||
|
|
||||||
let tty = RuntimeType::Tuple(vec![]);
|
let tty = RuntimeType::Tuple(vec![]);
|
||||||
let tty1 = RuntimeType::Tuple(vec![RuntimeType::solid()]);
|
let tty1 = RuntimeType::Tuple(vec![RuntimeType::solid()]);
|
||||||
@ -1673,7 +1609,7 @@ mod test {
|
|||||||
},
|
},
|
||||||
&mut exec_state,
|
&mut exec_state,
|
||||||
);
|
);
|
||||||
none.coerce(&tty1, true, &mut exec_state).unwrap_err();
|
none.coerce(&tty1, &mut exec_state).unwrap_err();
|
||||||
|
|
||||||
let oty = RuntimeType::Object(vec![]);
|
let oty = RuntimeType::Object(vec![]);
|
||||||
assert_coerce_results(
|
assert_coerce_results(
|
||||||
@ -1742,7 +1678,7 @@ mod test {
|
|||||||
assert_coerce_results(&obj2, &ty0, &obj2, &mut exec_state);
|
assert_coerce_results(&obj2, &ty0, &obj2, &mut exec_state);
|
||||||
|
|
||||||
let ty1 = RuntimeType::Object(vec![("foo".to_owned(), RuntimeType::Primitive(PrimitiveType::Boolean))]);
|
let ty1 = RuntimeType::Object(vec![("foo".to_owned(), RuntimeType::Primitive(PrimitiveType::Boolean))]);
|
||||||
obj0.coerce(&ty1, true, &mut exec_state).unwrap_err();
|
obj0.coerce(&ty1, &mut exec_state).unwrap_err();
|
||||||
assert_coerce_results(&obj1, &ty1, &obj1, &mut exec_state);
|
assert_coerce_results(&obj1, &ty1, &obj1, &mut exec_state);
|
||||||
assert_coerce_results(&obj2, &ty1, &obj2, &mut exec_state);
|
assert_coerce_results(&obj2, &ty1, &obj2, &mut exec_state);
|
||||||
|
|
||||||
@ -1754,19 +1690,19 @@ mod test {
|
|||||||
),
|
),
|
||||||
("foo".to_owned(), RuntimeType::Primitive(PrimitiveType::Boolean)),
|
("foo".to_owned(), RuntimeType::Primitive(PrimitiveType::Boolean)),
|
||||||
]);
|
]);
|
||||||
obj0.coerce(&ty2, true, &mut exec_state).unwrap_err();
|
obj0.coerce(&ty2, &mut exec_state).unwrap_err();
|
||||||
obj1.coerce(&ty2, true, &mut exec_state).unwrap_err();
|
obj1.coerce(&ty2, &mut exec_state).unwrap_err();
|
||||||
assert_coerce_results(&obj2, &ty2, &obj2, &mut exec_state);
|
assert_coerce_results(&obj2, &ty2, &obj2, &mut exec_state);
|
||||||
|
|
||||||
// field not present
|
// field not present
|
||||||
let tyq = RuntimeType::Object(vec![("qux".to_owned(), RuntimeType::Primitive(PrimitiveType::Boolean))]);
|
let tyq = RuntimeType::Object(vec![("qux".to_owned(), RuntimeType::Primitive(PrimitiveType::Boolean))]);
|
||||||
obj0.coerce(&tyq, true, &mut exec_state).unwrap_err();
|
obj0.coerce(&tyq, &mut exec_state).unwrap_err();
|
||||||
obj1.coerce(&tyq, true, &mut exec_state).unwrap_err();
|
obj1.coerce(&tyq, &mut exec_state).unwrap_err();
|
||||||
obj2.coerce(&tyq, true, &mut exec_state).unwrap_err();
|
obj2.coerce(&tyq, &mut exec_state).unwrap_err();
|
||||||
|
|
||||||
// field with different type
|
// field with different type
|
||||||
let ty1 = RuntimeType::Object(vec![("bar".to_owned(), RuntimeType::Primitive(PrimitiveType::Boolean))]);
|
let ty1 = RuntimeType::Object(vec![("bar".to_owned(), RuntimeType::Primitive(PrimitiveType::Boolean))]);
|
||||||
obj2.coerce(&ty1, true, &mut exec_state).unwrap_err();
|
obj2.coerce(&ty1, &mut exec_state).unwrap_err();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
@ -1844,8 +1780,8 @@ mod test {
|
|||||||
assert_coerce_results(&hom_arr, &tyh, &hom_arr, &mut exec_state);
|
assert_coerce_results(&hom_arr, &tyh, &hom_arr, &mut exec_state);
|
||||||
assert_coerce_results(&mixed1, &tym1, &mixed1, &mut exec_state);
|
assert_coerce_results(&mixed1, &tym1, &mixed1, &mut exec_state);
|
||||||
assert_coerce_results(&mixed2, &tym2, &mixed2, &mut exec_state);
|
assert_coerce_results(&mixed2, &tym2, &mixed2, &mut exec_state);
|
||||||
mixed1.coerce(&tym2, true, &mut exec_state).unwrap_err();
|
mixed1.coerce(&tym2, &mut exec_state).unwrap_err();
|
||||||
mixed2.coerce(&tym1, true, &mut exec_state).unwrap_err();
|
mixed2.coerce(&tym1, &mut exec_state).unwrap_err();
|
||||||
|
|
||||||
// Length subtyping
|
// Length subtyping
|
||||||
let tyhn = RuntimeType::Array(
|
let tyhn = RuntimeType::Array(
|
||||||
@ -1862,15 +1798,15 @@ mod test {
|
|||||||
);
|
);
|
||||||
assert_coerce_results(&hom_arr, &tyhn, &hom_arr, &mut exec_state);
|
assert_coerce_results(&hom_arr, &tyhn, &hom_arr, &mut exec_state);
|
||||||
assert_coerce_results(&hom_arr, &tyh1, &hom_arr, &mut exec_state);
|
assert_coerce_results(&hom_arr, &tyh1, &hom_arr, &mut exec_state);
|
||||||
hom_arr.coerce(&tyh3, true, &mut exec_state).unwrap_err();
|
hom_arr.coerce(&tyh3, &mut exec_state).unwrap_err();
|
||||||
|
|
||||||
let hom_arr0 = KclValue::HomArray {
|
let hom_arr0 = KclValue::HomArray {
|
||||||
value: vec![],
|
value: vec![],
|
||||||
ty: RuntimeType::Primitive(PrimitiveType::Number(NumericType::count())),
|
ty: RuntimeType::Primitive(PrimitiveType::Number(NumericType::count())),
|
||||||
};
|
};
|
||||||
assert_coerce_results(&hom_arr0, &tyhn, &hom_arr0, &mut exec_state);
|
assert_coerce_results(&hom_arr0, &tyhn, &hom_arr0, &mut exec_state);
|
||||||
hom_arr0.coerce(&tyh1, true, &mut exec_state).unwrap_err();
|
hom_arr0.coerce(&tyh1, &mut exec_state).unwrap_err();
|
||||||
hom_arr0.coerce(&tyh3, true, &mut exec_state).unwrap_err();
|
hom_arr0.coerce(&tyh3, &mut exec_state).unwrap_err();
|
||||||
|
|
||||||
// Covariance
|
// Covariance
|
||||||
// let tyh = RuntimeType::Array(Box::new(RuntimeType::Primitive(PrimitiveType::Number(NumericType::Any))), ArrayLen::Known(4));
|
// let tyh = RuntimeType::Array(Box::new(RuntimeType::Primitive(PrimitiveType::Number(NumericType::Any))), ArrayLen::Known(4));
|
||||||
@ -1910,16 +1846,16 @@ mod test {
|
|||||||
assert_coerce_results(&mixed1, &tyhn, &hom_arr_2, &mut exec_state);
|
assert_coerce_results(&mixed1, &tyhn, &hom_arr_2, &mut exec_state);
|
||||||
assert_coerce_results(&mixed1, &tyh1, &hom_arr_2, &mut exec_state);
|
assert_coerce_results(&mixed1, &tyh1, &hom_arr_2, &mut exec_state);
|
||||||
assert_coerce_results(&mixed0, &tyhn, &hom_arr0, &mut exec_state);
|
assert_coerce_results(&mixed0, &tyhn, &hom_arr0, &mut exec_state);
|
||||||
mixed0.coerce(&tyh, true, &mut exec_state).unwrap_err();
|
mixed0.coerce(&tyh, &mut exec_state).unwrap_err();
|
||||||
mixed0.coerce(&tyh1, true, &mut exec_state).unwrap_err();
|
mixed0.coerce(&tyh1, &mut exec_state).unwrap_err();
|
||||||
|
|
||||||
// Homogehous to mixed
|
// Homogehous to mixed
|
||||||
assert_coerce_results(&hom_arr_2, &tym1, &mixed1, &mut exec_state);
|
assert_coerce_results(&hom_arr_2, &tym1, &mixed1, &mut exec_state);
|
||||||
hom_arr.coerce(&tym1, true, &mut exec_state).unwrap_err();
|
hom_arr.coerce(&tym1, &mut exec_state).unwrap_err();
|
||||||
hom_arr_2.coerce(&tym2, true, &mut exec_state).unwrap_err();
|
hom_arr_2.coerce(&tym2, &mut exec_state).unwrap_err();
|
||||||
|
|
||||||
mixed0.coerce(&tym1, true, &mut exec_state).unwrap_err();
|
mixed0.coerce(&tym1, &mut exec_state).unwrap_err();
|
||||||
mixed0.coerce(&tym2, true, &mut exec_state).unwrap_err();
|
mixed0.coerce(&tym2, &mut exec_state).unwrap_err();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
@ -1969,8 +1905,8 @@ mod test {
|
|||||||
RuntimeType::Primitive(PrimitiveType::Boolean),
|
RuntimeType::Primitive(PrimitiveType::Boolean),
|
||||||
RuntimeType::Primitive(PrimitiveType::String),
|
RuntimeType::Primitive(PrimitiveType::String),
|
||||||
]);
|
]);
|
||||||
count.coerce(&tyb, true, &mut exec_state).unwrap_err();
|
count.coerce(&tyb, &mut exec_state).unwrap_err();
|
||||||
count.coerce(&tyb2, true, &mut exec_state).unwrap_err();
|
count.coerce(&tyb2, &mut exec_state).unwrap_err();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
@ -2085,7 +2021,7 @@ mod test {
|
|||||||
assert_coerce_results(&a2d, &ty2d, &a2d, &mut exec_state);
|
assert_coerce_results(&a2d, &ty2d, &a2d, &mut exec_state);
|
||||||
assert_coerce_results(&a3d, &ty3d, &a3d, &mut exec_state);
|
assert_coerce_results(&a3d, &ty3d, &a3d, &mut exec_state);
|
||||||
assert_coerce_results(&a3d, &ty2d, &a2d, &mut exec_state);
|
assert_coerce_results(&a3d, &ty2d, &a2d, &mut exec_state);
|
||||||
a2d.coerce(&ty3d, true, &mut exec_state).unwrap_err();
|
a2d.coerce(&ty3d, &mut exec_state).unwrap_err();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
@ -2148,7 +2084,6 @@ mod test {
|
|||||||
angle: UnitAngle::default()
|
angle: UnitAngle::default()
|
||||||
}
|
}
|
||||||
.into(),
|
.into(),
|
||||||
true,
|
|
||||||
&mut exec_state
|
&mut exec_state
|
||||||
)
|
)
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
@ -2156,30 +2091,22 @@ mod test {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// No coercion
|
// No coercion
|
||||||
count
|
count.coerce(&NumericType::mm().into(), &mut exec_state).unwrap_err();
|
||||||
.coerce(&NumericType::mm().into(), true, &mut exec_state)
|
mm.coerce(&NumericType::count().into(), &mut exec_state).unwrap_err();
|
||||||
.unwrap_err();
|
unknown.coerce(&NumericType::mm().into(), &mut exec_state).unwrap_err();
|
||||||
mm.coerce(&NumericType::count().into(), true, &mut exec_state)
|
|
||||||
.unwrap_err();
|
|
||||||
unknown
|
unknown
|
||||||
.coerce(&NumericType::mm().into(), true, &mut exec_state)
|
.coerce(&NumericType::default().into(), &mut exec_state)
|
||||||
.unwrap_err();
|
|
||||||
unknown
|
|
||||||
.coerce(&NumericType::default().into(), true, &mut exec_state)
|
|
||||||
.unwrap_err();
|
.unwrap_err();
|
||||||
|
|
||||||
count
|
count.coerce(&NumericType::Unknown.into(), &mut exec_state).unwrap_err();
|
||||||
.coerce(&NumericType::Unknown.into(), true, &mut exec_state)
|
mm.coerce(&NumericType::Unknown.into(), &mut exec_state).unwrap_err();
|
||||||
.unwrap_err();
|
|
||||||
mm.coerce(&NumericType::Unknown.into(), true, &mut exec_state)
|
|
||||||
.unwrap_err();
|
|
||||||
default
|
default
|
||||||
.coerce(&NumericType::Unknown.into(), true, &mut exec_state)
|
.coerce(&NumericType::Unknown.into(), &mut exec_state)
|
||||||
.unwrap_err();
|
.unwrap_err();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
inches
|
inches
|
||||||
.coerce(&NumericType::mm().into(), true, &mut exec_state)
|
.coerce(&NumericType::mm().into(), &mut exec_state)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.as_f64()
|
.as_f64()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
@ -2189,7 +2116,6 @@ mod test {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
rads.coerce(
|
rads.coerce(
|
||||||
&NumericType::Known(UnitType::Angle(UnitAngle::Degrees)).into(),
|
&NumericType::Known(UnitType::Angle(UnitAngle::Degrees)).into(),
|
||||||
true,
|
|
||||||
&mut exec_state
|
&mut exec_state
|
||||||
)
|
)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
@ -2200,7 +2126,7 @@ mod test {
|
|||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
inches
|
inches
|
||||||
.coerce(&NumericType::default().into(), true, &mut exec_state)
|
.coerce(&NumericType::default().into(), &mut exec_state)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.as_f64()
|
.as_f64()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
@ -2208,7 +2134,7 @@ mod test {
|
|||||||
1.0
|
1.0
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
rads.coerce(&NumericType::default().into(), true, &mut exec_state)
|
rads.coerce(&NumericType::default().into(), &mut exec_state)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.as_f64()
|
.as_f64()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
@ -1189,7 +1189,6 @@ impl LanguageServer for Backend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn completion(&self, params: CompletionParams) -> RpcResult<Option<CompletionResponse>> {
|
async fn completion(&self, params: CompletionParams) -> RpcResult<Option<CompletionResponse>> {
|
||||||
// ADAM: This is the entrypoint.
|
|
||||||
let mut completions = vec![CompletionItem {
|
let mut completions = vec![CompletionItem {
|
||||||
label: PIPE_OPERATOR.to_string(),
|
label: PIPE_OPERATOR.to_string(),
|
||||||
label_details: None,
|
label_details: None,
|
||||||
@ -1687,29 +1686,31 @@ pub fn get_arg_maps_from_stdlib(
|
|||||||
let combined = stdlib.combined();
|
let combined = stdlib.combined();
|
||||||
|
|
||||||
for internal_fn in combined.values() {
|
for internal_fn in combined.values() {
|
||||||
let arg_map: HashMap<String, String> = internal_fn
|
if internal_fn.keyword_arguments() {
|
||||||
.args(false)
|
let arg_map: HashMap<String, String> = internal_fn
|
||||||
.into_iter()
|
.args(false)
|
||||||
.map(|data| {
|
.into_iter()
|
||||||
let mut tip = "```\n".to_owned();
|
.map(|data| {
|
||||||
tip.push_str(&data.name.clone());
|
let mut tip = "```\n".to_owned();
|
||||||
if !data.required {
|
tip.push_str(&data.name.clone());
|
||||||
tip.push('?');
|
if !data.required {
|
||||||
}
|
tip.push('?');
|
||||||
if !data.type_.is_empty() {
|
}
|
||||||
tip.push_str(": ");
|
if !data.type_.is_empty() {
|
||||||
tip.push_str(&data.type_);
|
tip.push_str(": ");
|
||||||
}
|
tip.push_str(&data.type_);
|
||||||
tip.push_str("\n```");
|
}
|
||||||
if !data.description.is_empty() {
|
tip.push_str("\n```");
|
||||||
tip.push_str("\n\n");
|
if !data.description.is_empty() {
|
||||||
tip.push_str(&data.description);
|
tip.push_str("\n\n");
|
||||||
}
|
tip.push_str(&data.description);
|
||||||
(data.name, tip)
|
}
|
||||||
})
|
(data.name, tip)
|
||||||
.collect();
|
})
|
||||||
if !arg_map.is_empty() {
|
.collect();
|
||||||
result.insert(internal_fn.name(), arg_map);
|
if !arg_map.is_empty() {
|
||||||
|
result.insert(internal_fn.name(), arg_map);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,7 +97,6 @@ pub(crate) fn read_std(mod_name: &str) -> Option<&'static str> {
|
|||||||
"units" => Some(include_str!("../std/units.kcl")),
|
"units" => Some(include_str!("../std/units.kcl")),
|
||||||
"array" => Some(include_str!("../std/array.kcl")),
|
"array" => Some(include_str!("../std/array.kcl")),
|
||||||
"sweep" => Some(include_str!("../std/sweep.kcl")),
|
"sweep" => Some(include_str!("../std/sweep.kcl")),
|
||||||
"appearance" => Some(include_str!("../std/appearance.kcl")),
|
|
||||||
"transform" => Some(include_str!("../std/transform.kcl")),
|
"transform" => Some(include_str!("../std/transform.kcl")),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
@ -154,6 +153,13 @@ impl ModulePath {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn std_path(&self) -> Option<String> {
|
||||||
|
match self {
|
||||||
|
ModulePath::Std { value: p } => Some(p.clone()),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) async fn source(&self, fs: &FileManager, source_range: SourceRange) -> Result<ModuleSource, KclError> {
|
pub(crate) async fn source(&self, fs: &FileManager, source_range: SourceRange) -> Result<ModuleSource, KclError> {
|
||||||
match self {
|
match self {
|
||||||
ModulePath::Local { value: p } => Ok(ModuleSource {
|
ModulePath::Local { value: p } => Ok(ModuleSource {
|
||||||
@ -175,52 +181,24 @@ impl ModulePath {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn from_import_path(
|
pub(crate) fn from_import_path(path: &ImportPath, project_directory: &Option<TypedPath>) -> Self {
|
||||||
path: &ImportPath,
|
|
||||||
project_directory: &Option<TypedPath>,
|
|
||||||
import_from: &ModulePath,
|
|
||||||
) -> Result<Self, KclError> {
|
|
||||||
match path {
|
match path {
|
||||||
ImportPath::Kcl { filename: path } | ImportPath::Foreign { path } => {
|
ImportPath::Kcl { filename: path } | ImportPath::Foreign { path } => {
|
||||||
let resolved_path = match import_from {
|
let resolved_path = if let Some(project_dir) = project_directory {
|
||||||
ModulePath::Main => {
|
project_dir.join_typed(path)
|
||||||
if let Some(project_dir) = project_directory {
|
} else {
|
||||||
project_dir.join_typed(path)
|
path.clone()
|
||||||
} else {
|
|
||||||
path.clone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ModulePath::Local { value } => {
|
|
||||||
let import_from_dir = value.parent();
|
|
||||||
let base = import_from_dir.as_ref().or(project_directory.as_ref());
|
|
||||||
if let Some(dir) = base {
|
|
||||||
dir.join_typed(path)
|
|
||||||
} else {
|
|
||||||
path.clone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ModulePath::Std { .. } => {
|
|
||||||
let message = format!("Cannot import a non-std KCL file from std: {path}.");
|
|
||||||
debug_assert!(false, "{}", &message);
|
|
||||||
return Err(KclError::Internal(KclErrorDetails::new(message, vec![])));
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
ModulePath::Local { value: resolved_path }
|
||||||
Ok(ModulePath::Local { value: resolved_path })
|
|
||||||
}
|
}
|
||||||
ImportPath::Std { path } => Self::from_std_import_path(path),
|
ImportPath::Std { path } => {
|
||||||
}
|
// For now we only support importing from singly-nested modules inside std.
|
||||||
}
|
assert_eq!(path.len(), 2);
|
||||||
|
assert_eq!(&path[0], "std");
|
||||||
|
|
||||||
pub(crate) fn from_std_import_path(path: &[String]) -> Result<Self, KclError> {
|
ModulePath::Std { value: path[1].clone() }
|
||||||
// For now we only support importing from singly-nested modules inside std.
|
}
|
||||||
if path.len() != 2 || path[0] != "std" {
|
|
||||||
let message = format!("Invalid std import path: {path:?}.");
|
|
||||||
debug_assert!(false, "{}", &message);
|
|
||||||
return Err(KclError::Internal(KclErrorDetails::new(message, vec![])));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(ModulePath::Std { value: path[1].clone() })
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -886,7 +886,7 @@ fn array_end_start(i: &mut TokenSlice) -> PResult<Node<ArrayRangeExpression>> {
|
|||||||
ignore_whitespace(i);
|
ignore_whitespace(i);
|
||||||
let start_element = expression.parse_next(i)?;
|
let start_element = expression.parse_next(i)?;
|
||||||
ignore_whitespace(i);
|
ignore_whitespace(i);
|
||||||
let end_inclusive = alt((end_inclusive_range.map(|_| true), end_exclusive_range.map(|_| false))).parse_next(i)?;
|
double_period.parse_next(i)?;
|
||||||
ignore_whitespace(i);
|
ignore_whitespace(i);
|
||||||
let end_element = expression.parse_next(i)?;
|
let end_element = expression.parse_next(i)?;
|
||||||
ignore_whitespace(i);
|
ignore_whitespace(i);
|
||||||
@ -895,7 +895,7 @@ fn array_end_start(i: &mut TokenSlice) -> PResult<Node<ArrayRangeExpression>> {
|
|||||||
ArrayRangeExpression {
|
ArrayRangeExpression {
|
||||||
start_element,
|
start_element,
|
||||||
end_element,
|
end_element,
|
||||||
end_inclusive,
|
end_inclusive: true,
|
||||||
digest: None,
|
digest: None,
|
||||||
},
|
},
|
||||||
start,
|
start,
|
||||||
@ -2705,7 +2705,7 @@ fn period(i: &mut TokenSlice) -> PResult<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn end_inclusive_range(i: &mut TokenSlice) -> PResult<Token> {
|
fn double_period(i: &mut TokenSlice) -> PResult<Token> {
|
||||||
any.try_map(|token: Token| {
|
any.try_map(|token: Token| {
|
||||||
if matches!(token.token_type, TokenType::DoublePeriod) {
|
if matches!(token.token_type, TokenType::DoublePeriod) {
|
||||||
Ok(token)
|
Ok(token)
|
||||||
@ -2724,21 +2724,6 @@ fn end_inclusive_range(i: &mut TokenSlice) -> PResult<Token> {
|
|||||||
.parse_next(i)
|
.parse_next(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn end_exclusive_range(i: &mut TokenSlice) -> PResult<Token> {
|
|
||||||
any.try_map(|token: Token| {
|
|
||||||
if matches!(token.token_type, TokenType::DoublePeriodLessThan) {
|
|
||||||
Ok(token)
|
|
||||||
} else {
|
|
||||||
Err(CompilationError::fatal(
|
|
||||||
token.as_source_range(),
|
|
||||||
format!("expected a '..<' but found {}", token.value.as_str()),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.context(expected("the ..< operator, used for array ranges like [0..<10]"))
|
|
||||||
.parse_next(i)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn colon(i: &mut TokenSlice) -> PResult<Token> {
|
fn colon(i: &mut TokenSlice) -> PResult<Token> {
|
||||||
TokenType::Colon.parse_from(i)
|
TokenType::Colon.parse_from(i)
|
||||||
}
|
}
|
||||||
@ -5359,6 +5344,7 @@ mod snapshot_tests {
|
|||||||
);
|
);
|
||||||
snapshot_test!(aa, r#"sg = -scale"#);
|
snapshot_test!(aa, r#"sg = -scale"#);
|
||||||
snapshot_test!(ab, "line(endAbsolute = [0, -1])");
|
snapshot_test!(ab, "line(endAbsolute = [0, -1])");
|
||||||
|
snapshot_test!(ac, "myArray = [0..10]");
|
||||||
snapshot_test!(
|
snapshot_test!(
|
||||||
ad,
|
ad,
|
||||||
r#"
|
r#"
|
||||||
@ -5499,11 +5485,6 @@ my14 = 4 ^ 2 - 3 ^ 2 * 2
|
|||||||
)"#
|
)"#
|
||||||
);
|
);
|
||||||
snapshot_test!(kw_function_in_binary_op, r#"val = f(x = 1) + 1"#);
|
snapshot_test!(kw_function_in_binary_op, r#"val = f(x = 1) + 1"#);
|
||||||
snapshot_test!(
|
|
||||||
array_ranges,
|
|
||||||
r#"incl = [1..10]
|
|
||||||
excl = [0..<10]"#
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
|
@ -1,117 +0,0 @@
|
|||||||
---
|
|
||||||
source: kcl-lib/src/parsing/parser.rs
|
|
||||||
expression: actual
|
|
||||||
---
|
|
||||||
{
|
|
||||||
"body": [
|
|
||||||
{
|
|
||||||
"commentStart": 0,
|
|
||||||
"declaration": {
|
|
||||||
"commentStart": 0,
|
|
||||||
"end": 14,
|
|
||||||
"id": {
|
|
||||||
"commentStart": 0,
|
|
||||||
"end": 4,
|
|
||||||
"name": "incl",
|
|
||||||
"start": 0,
|
|
||||||
"type": "Identifier"
|
|
||||||
},
|
|
||||||
"init": {
|
|
||||||
"commentStart": 7,
|
|
||||||
"end": 14,
|
|
||||||
"endElement": {
|
|
||||||
"commentStart": 11,
|
|
||||||
"end": 13,
|
|
||||||
"raw": "10",
|
|
||||||
"start": 11,
|
|
||||||
"type": "Literal",
|
|
||||||
"type": "Literal",
|
|
||||||
"value": {
|
|
||||||
"value": 10.0,
|
|
||||||
"suffix": "None"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"endInclusive": true,
|
|
||||||
"start": 7,
|
|
||||||
"startElement": {
|
|
||||||
"commentStart": 8,
|
|
||||||
"end": 9,
|
|
||||||
"raw": "1",
|
|
||||||
"start": 8,
|
|
||||||
"type": "Literal",
|
|
||||||
"type": "Literal",
|
|
||||||
"value": {
|
|
||||||
"value": 1.0,
|
|
||||||
"suffix": "None"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"type": "ArrayRangeExpression",
|
|
||||||
"type": "ArrayRangeExpression"
|
|
||||||
},
|
|
||||||
"start": 0,
|
|
||||||
"type": "VariableDeclarator"
|
|
||||||
},
|
|
||||||
"end": 14,
|
|
||||||
"kind": "const",
|
|
||||||
"start": 0,
|
|
||||||
"type": "VariableDeclaration",
|
|
||||||
"type": "VariableDeclaration"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"commentStart": 23,
|
|
||||||
"declaration": {
|
|
||||||
"commentStart": 23,
|
|
||||||
"end": 38,
|
|
||||||
"id": {
|
|
||||||
"commentStart": 23,
|
|
||||||
"end": 27,
|
|
||||||
"name": "excl",
|
|
||||||
"start": 23,
|
|
||||||
"type": "Identifier"
|
|
||||||
},
|
|
||||||
"init": {
|
|
||||||
"commentStart": 30,
|
|
||||||
"end": 38,
|
|
||||||
"endElement": {
|
|
||||||
"commentStart": 35,
|
|
||||||
"end": 37,
|
|
||||||
"raw": "10",
|
|
||||||
"start": 35,
|
|
||||||
"type": "Literal",
|
|
||||||
"type": "Literal",
|
|
||||||
"value": {
|
|
||||||
"value": 10.0,
|
|
||||||
"suffix": "None"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"endInclusive": false,
|
|
||||||
"start": 30,
|
|
||||||
"startElement": {
|
|
||||||
"commentStart": 31,
|
|
||||||
"end": 32,
|
|
||||||
"raw": "0",
|
|
||||||
"start": 31,
|
|
||||||
"type": "Literal",
|
|
||||||
"type": "Literal",
|
|
||||||
"value": {
|
|
||||||
"value": 0.0,
|
|
||||||
"suffix": "None"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"type": "ArrayRangeExpression",
|
|
||||||
"type": "ArrayRangeExpression"
|
|
||||||
},
|
|
||||||
"start": 23,
|
|
||||||
"type": "VariableDeclarator"
|
|
||||||
},
|
|
||||||
"end": 38,
|
|
||||||
"kind": "const",
|
|
||||||
"start": 23,
|
|
||||||
"type": "VariableDeclaration",
|
|
||||||
"type": "VariableDeclaration"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"commentStart": 0,
|
|
||||||
"end": 38,
|
|
||||||
"start": 0
|
|
||||||
}
|
|
@ -369,8 +369,6 @@ pub enum TokenType {
|
|||||||
Period,
|
Period,
|
||||||
/// A double period: `..`.
|
/// A double period: `..`.
|
||||||
DoublePeriod,
|
DoublePeriod,
|
||||||
/// A double period and a less than: `..<`.
|
|
||||||
DoublePeriodLessThan,
|
|
||||||
/// A line comment.
|
/// A line comment.
|
||||||
LineComment,
|
LineComment,
|
||||||
/// A block comment.
|
/// A block comment.
|
||||||
@ -412,7 +410,6 @@ impl TryFrom<TokenType> for SemanticTokenType {
|
|||||||
| TokenType::DoubleColon
|
| TokenType::DoubleColon
|
||||||
| TokenType::Period
|
| TokenType::Period
|
||||||
| TokenType::DoublePeriod
|
| TokenType::DoublePeriod
|
||||||
| TokenType::DoublePeriodLessThan
|
|
||||||
| TokenType::Hash
|
| TokenType::Hash
|
||||||
| TokenType::Dollar
|
| TokenType::Dollar
|
||||||
| TokenType::At
|
| TokenType::At
|
||||||
|
@ -87,7 +87,7 @@ pub(super) fn token(i: &mut Input<'_>) -> PResult<Token> {
|
|||||||
'0'..='9' => number,
|
'0'..='9' => number,
|
||||||
';' => semi_colon,
|
';' => semi_colon,
|
||||||
':' => alt((double_colon, colon)),
|
':' => alt((double_colon, colon)),
|
||||||
'.' => alt((number, double_period_less_than, double_period, period)),
|
'.' => alt((number, double_period, period)),
|
||||||
'#' => hash,
|
'#' => hash,
|
||||||
'$' => dollar,
|
'$' => dollar,
|
||||||
'!' => alt((operator, bang)),
|
'!' => alt((operator, bang)),
|
||||||
@ -320,16 +320,6 @@ fn double_period(i: &mut Input<'_>) -> PResult<Token> {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn double_period_less_than(i: &mut Input<'_>) -> PResult<Token> {
|
|
||||||
let (value, range) = "..<".with_span().parse_next(i)?;
|
|
||||||
Ok(Token::from_range(
|
|
||||||
range,
|
|
||||||
i.state.module_id,
|
|
||||||
TokenType::DoublePeriodLessThan,
|
|
||||||
value.to_string(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Zero or more of either:
|
/// Zero or more of either:
|
||||||
/// 1. Any character except " or \
|
/// 1. Any character except " or \
|
||||||
/// 2. Any character preceded by \
|
/// 2. Any character preceded by \
|
||||||
|
@ -26,10 +26,6 @@ struct Test {
|
|||||||
input_dir: PathBuf,
|
input_dir: PathBuf,
|
||||||
/// Expected snapshot output files are in this directory.
|
/// Expected snapshot output files are in this directory.
|
||||||
output_dir: PathBuf,
|
output_dir: PathBuf,
|
||||||
/// True to skip asserting the artifact graph and only write it. The default
|
|
||||||
/// is false and to assert it.
|
|
||||||
#[cfg_attr(not(feature = "artifact-graph"), expect(dead_code))]
|
|
||||||
skip_assert_artifact_graph: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) const RENDERED_MODEL_NAME: &str = "rendered_model.png";
|
pub(crate) const RENDERED_MODEL_NAME: &str = "rendered_model.png";
|
||||||
@ -41,7 +37,6 @@ impl Test {
|
|||||||
entry_point: Path::new("tests").join(name).join("input.kcl"),
|
entry_point: Path::new("tests").join(name).join("input.kcl"),
|
||||||
input_dir: Path::new("tests").join(name),
|
input_dir: Path::new("tests").join(name),
|
||||||
output_dir: Path::new("tests").join(name),
|
output_dir: Path::new("tests").join(name),
|
||||||
skip_assert_artifact_graph: false,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -304,28 +299,21 @@ fn assert_common_snapshots(
|
|||||||
})
|
})
|
||||||
}));
|
}));
|
||||||
let result3 = catch_unwind(AssertUnwindSafe(|| {
|
let result3 = catch_unwind(AssertUnwindSafe(|| {
|
||||||
// If the user is explicitly writing, we always want to run so that they
|
assert_snapshot(test, "Artifact graph flowchart", || {
|
||||||
// can save new expected output. There's no way to reliably determine
|
let mut artifact_graph = artifact_graph.clone();
|
||||||
// if insta will write, as far as I can tell, so we use our own
|
// Sort the map by artifact where we can.
|
||||||
// environment variable.
|
artifact_graph.sort();
|
||||||
let is_writing = matches!(std::env::var("ZOO_SIM_UPDATE").as_deref(), Ok("always"));
|
|
||||||
if !test.skip_assert_artifact_graph || is_writing {
|
|
||||||
assert_snapshot(test, "Artifact graph flowchart", || {
|
|
||||||
let mut artifact_graph = artifact_graph.clone();
|
|
||||||
// Sort the map by artifact where we can.
|
|
||||||
artifact_graph.sort();
|
|
||||||
|
|
||||||
let flowchart = artifact_graph
|
let flowchart = artifact_graph
|
||||||
.to_mermaid_flowchart()
|
.to_mermaid_flowchart()
|
||||||
.unwrap_or_else(|e| format!("Failed to convert artifact graph to flowchart: {e}"));
|
.unwrap_or_else(|e| format!("Failed to convert artifact graph to flowchart: {e}"));
|
||||||
// Change the snapshot suffix so that it is rendered as a Markdown file
|
// Change the snapshot suffix so that it is rendered as a Markdown file
|
||||||
// in GitHub.
|
// in GitHub.
|
||||||
// Ignore the cpu cooler for now because its being a little bitch.
|
// Ignore the cpu cooler for now because its being a little bitch.
|
||||||
if test.name != "cpu-cooler" && test.name != "subtract_regression10" {
|
if test.name != "cpu-cooler" {
|
||||||
insta::assert_binary_snapshot!("artifact_graph_flowchart.md", flowchart.as_bytes().to_owned());
|
insta::assert_binary_snapshot!("artifact_graph_flowchart.md", flowchart.as_bytes().to_owned());
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
result1.unwrap();
|
result1.unwrap();
|
||||||
@ -396,27 +384,6 @@ mod any_type {
|
|||||||
super::execute(TEST_NAME, false).await
|
super::execute(TEST_NAME, false).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mod error_with_point_shows_numeric_units {
|
|
||||||
const TEST_NAME: &str = "error_with_point_shows_numeric_units";
|
|
||||||
|
|
||||||
/// Test parsing KCL.
|
|
||||||
#[test]
|
|
||||||
fn parse() {
|
|
||||||
super::parse(TEST_NAME)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Test that parsing and unparsing KCL produces the original KCL input.
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
|
||||||
async fn unparse() {
|
|
||||||
super::unparse(TEST_NAME).await
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Test that KCL is executed correctly.
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
|
||||||
async fn kcl_test_execute() {
|
|
||||||
super::execute(TEST_NAME, true).await
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mod artifact_graph_example_code1 {
|
mod artifact_graph_example_code1 {
|
||||||
const TEST_NAME: &str = "artifact_graph_example_code1";
|
const TEST_NAME: &str = "artifact_graph_example_code1";
|
||||||
|
|
||||||
@ -3393,27 +3360,6 @@ mod nested_windows_main_kcl {
|
|||||||
super::execute(TEST_NAME, true).await
|
super::execute(TEST_NAME, true).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mod nested_assembly {
|
|
||||||
const TEST_NAME: &str = "nested_assembly";
|
|
||||||
|
|
||||||
/// Test parsing KCL.
|
|
||||||
#[test]
|
|
||||||
fn parse() {
|
|
||||||
super::parse(TEST_NAME)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Test that parsing and unparsing KCL produces the original KCL input.
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
|
||||||
async fn unparse() {
|
|
||||||
super::unparse(TEST_NAME).await
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Test that KCL is executed correctly.
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
|
||||||
async fn kcl_test_execute() {
|
|
||||||
super::execute(TEST_NAME, true).await
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mod subtract_regression11 {
|
mod subtract_regression11 {
|
||||||
const TEST_NAME: &str = "subtract_regression11";
|
const TEST_NAME: &str = "subtract_regression11";
|
||||||
|
|
||||||
|
@ -145,8 +145,6 @@ fn test(test_name: &str, entry_point: std::path::PathBuf) -> Test {
|
|||||||
entry_point: entry_point.clone(),
|
entry_point: entry_point.clone(),
|
||||||
input_dir: parent.to_path_buf(),
|
input_dir: parent.to_path_buf(),
|
||||||
output_dir: relative_output_dir,
|
output_dir: relative_output_dir,
|
||||||
// Skip is temporary while we have non-deterministic output.
|
|
||||||
skip_assert_artifact_graph: true,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,10 +10,7 @@ use rgba_simple::Hex;
|
|||||||
use super::args::TyF64;
|
use super::args::TyF64;
|
||||||
use crate::{
|
use crate::{
|
||||||
errors::{KclError, KclErrorDetails},
|
errors::{KclError, KclErrorDetails},
|
||||||
execution::{
|
execution::{types::RuntimeType, ExecState, KclValue, SolidOrImportedGeometry},
|
||||||
types::{ArrayLen, RuntimeType},
|
|
||||||
ExecState, KclValue, SolidOrImportedGeometry,
|
|
||||||
},
|
|
||||||
std::Args,
|
std::Args,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -21,34 +18,6 @@ lazy_static::lazy_static! {
|
|||||||
static ref HEX_REGEX: Regex = Regex::new(r"^#[0-9a-fA-F]{6}$").unwrap();
|
static ref HEX_REGEX: Regex = Regex::new(r"^#[0-9a-fA-F]{6}$").unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Construct a color from its red, blue and green components.
|
|
||||||
pub async fn hex_string(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
|
||||||
let rgb: [TyF64; 3] = args.get_unlabeled_kw_arg_typed(
|
|
||||||
"rgb",
|
|
||||||
&RuntimeType::Array(Box::new(RuntimeType::count()), ArrayLen::Known(3)),
|
|
||||||
exec_state,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// Make sure the color if set is valid.
|
|
||||||
if let Some(component) = rgb.iter().find(|component| component.n < 0.0 || component.n > 255.0) {
|
|
||||||
return Err(KclError::Semantic(KclErrorDetails::new(
|
|
||||||
format!("Colors are given between 0 and 255, so {} is invalid", component.n),
|
|
||||||
vec![args.source_range],
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
|
|
||||||
inner_hex_string(rgb, exec_state, args).await
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn inner_hex_string(rgb: [TyF64; 3], _: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
|
||||||
let [r, g, b] = rgb.map(|n| n.n.floor() as u32);
|
|
||||||
let s = format!("#{r:02x}{g:02x}{b:02x}");
|
|
||||||
Ok(KclValue::String {
|
|
||||||
value: s,
|
|
||||||
meta: args.into(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the appearance of a solid. This only works on solids, not sketches or individual paths.
|
/// Set the appearance of a solid. This only works on solids, not sketches or individual paths.
|
||||||
pub async fn appearance(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
pub async fn appearance(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||||
let solids = args.get_unlabeled_kw_arg_typed(
|
let solids = args.get_unlabeled_kw_arg_typed(
|
||||||
@ -291,6 +260,7 @@ pub async fn appearance(exec_state: &mut ExecState, args: Args) -> Result<KclVal
|
|||||||
/// ```
|
/// ```
|
||||||
#[stdlib {
|
#[stdlib {
|
||||||
name = "appearance",
|
name = "appearance",
|
||||||
|
keywords = true,
|
||||||
unlabeled_first = true,
|
unlabeled_first = true,
|
||||||
args = {
|
args = {
|
||||||
solids = { docs = "The solid(s) whose appearance is being set" },
|
solids = { docs = "The solid(s) whose appearance is being set" },
|
||||||
|
@ -177,7 +177,7 @@ impl Args {
|
|||||||
)));
|
)));
|
||||||
};
|
};
|
||||||
|
|
||||||
let arg = arg.value.coerce(ty, true, exec_state).map_err(|_| {
|
let arg = arg.value.coerce(ty, exec_state).map_err(|_| {
|
||||||
let actual_type = arg.value.principal_type();
|
let actual_type = arg.value.principal_type();
|
||||||
let actual_type_name = actual_type
|
let actual_type_name = actual_type
|
||||||
.as_ref()
|
.as_ref()
|
||||||
@ -297,7 +297,7 @@ impl Args {
|
|||||||
vec![self.source_range],
|
vec![self.source_range],
|
||||||
)))?;
|
)))?;
|
||||||
|
|
||||||
let arg = arg.value.coerce(ty, true, exec_state).map_err(|_| {
|
let arg = arg.value.coerce(ty, exec_state).map_err(|_| {
|
||||||
let actual_type = arg.value.principal_type();
|
let actual_type = arg.value.principal_type();
|
||||||
let actual_type_name = actual_type
|
let actual_type_name = actual_type
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
@ -49,6 +49,7 @@ pub async fn assert(_exec_state: &mut ExecState, args: Args) -> Result<KclValue,
|
|||||||
/// ```
|
/// ```
|
||||||
#[stdlib{
|
#[stdlib{
|
||||||
name = "assertIs",
|
name = "assertIs",
|
||||||
|
keywords = true,
|
||||||
unlabeled_first = true,
|
unlabeled_first = true,
|
||||||
args = {
|
args = {
|
||||||
actual = { docs = "Value to check. If this is the boolean value true, assert passes. Otherwise it fails." },
|
actual = { docs = "Value to check. If this is the boolean value true, assert passes. Otherwise it fails." },
|
||||||
@ -74,6 +75,7 @@ async fn inner_assert_is(actual: bool, error: Option<String>, args: &Args) -> Re
|
|||||||
/// ```
|
/// ```
|
||||||
#[stdlib {
|
#[stdlib {
|
||||||
name = "assert",
|
name = "assert",
|
||||||
|
keywords = true,
|
||||||
unlabeled_first = true,
|
unlabeled_first = true,
|
||||||
args = {
|
args = {
|
||||||
actual = { docs = "Value to check. It will be compared with one of the comparison arguments." },
|
actual = { docs = "Value to check. It will be compared with one of the comparison arguments." },
|
||||||
|
@ -106,6 +106,7 @@ pub async fn union(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
|||||||
#[stdlib {
|
#[stdlib {
|
||||||
name = "union",
|
name = "union",
|
||||||
feature_tree_operation = true,
|
feature_tree_operation = true,
|
||||||
|
keywords = true,
|
||||||
unlabeled_first = true,
|
unlabeled_first = true,
|
||||||
args = {
|
args = {
|
||||||
solids = {docs = "The solids to union."},
|
solids = {docs = "The solids to union."},
|
||||||
@ -231,6 +232,7 @@ pub async fn intersect(exec_state: &mut ExecState, args: Args) -> Result<KclValu
|
|||||||
#[stdlib {
|
#[stdlib {
|
||||||
name = "intersect",
|
name = "intersect",
|
||||||
feature_tree_operation = true,
|
feature_tree_operation = true,
|
||||||
|
keywords = true,
|
||||||
unlabeled_first = true,
|
unlabeled_first = true,
|
||||||
args = {
|
args = {
|
||||||
solids = {docs = "The solids to intersect."},
|
solids = {docs = "The solids to intersect."},
|
||||||
@ -350,6 +352,7 @@ pub async fn subtract(exec_state: &mut ExecState, args: Args) -> Result<KclValue
|
|||||||
#[stdlib {
|
#[stdlib {
|
||||||
name = "subtract",
|
name = "subtract",
|
||||||
feature_tree_operation = true,
|
feature_tree_operation = true,
|
||||||
|
keywords = true,
|
||||||
unlabeled_first = true,
|
unlabeled_first = true,
|
||||||
args = {
|
args = {
|
||||||
solids = {docs = "The solids to use as the base to subtract from."},
|
solids = {docs = "The solids to use as the base to subtract from."},
|
||||||
|
@ -53,6 +53,7 @@ pub async fn get_opposite_edge(exec_state: &mut ExecState, args: Args) -> Result
|
|||||||
/// ```
|
/// ```
|
||||||
#[stdlib {
|
#[stdlib {
|
||||||
name = "getOppositeEdge",
|
name = "getOppositeEdge",
|
||||||
|
keywords = true,
|
||||||
unlabeled_first = true,
|
unlabeled_first = true,
|
||||||
args = {
|
args = {
|
||||||
edge = { docs = "The tag of the edge you want to find the opposite edge of." },
|
edge = { docs = "The tag of the edge you want to find the opposite edge of." },
|
||||||
@ -136,6 +137,7 @@ pub async fn get_next_adjacent_edge(exec_state: &mut ExecState, args: Args) -> R
|
|||||||
/// ```
|
/// ```
|
||||||
#[stdlib {
|
#[stdlib {
|
||||||
name = "getNextAdjacentEdge",
|
name = "getNextAdjacentEdge",
|
||||||
|
keywords = true,
|
||||||
unlabeled_first = true,
|
unlabeled_first = true,
|
||||||
args = {
|
args = {
|
||||||
edge = { docs = "The tag of the edge you want to find the next adjacent edge of." },
|
edge = { docs = "The tag of the edge you want to find the next adjacent edge of." },
|
||||||
@ -228,6 +230,7 @@ pub async fn get_previous_adjacent_edge(exec_state: &mut ExecState, args: Args)
|
|||||||
/// ```
|
/// ```
|
||||||
#[stdlib {
|
#[stdlib {
|
||||||
name = "getPreviousAdjacentEdge",
|
name = "getPreviousAdjacentEdge",
|
||||||
|
keywords = true,
|
||||||
unlabeled_first = true,
|
unlabeled_first = true,
|
||||||
args = {
|
args = {
|
||||||
edge = { docs = "The tag of the edge you want to find the previous adjacent edge of." },
|
edge = { docs = "The tag of the edge you want to find the previous adjacent edge of." },
|
||||||
@ -315,6 +318,7 @@ pub async fn get_common_edge(exec_state: &mut ExecState, args: Args) -> Result<K
|
|||||||
#[stdlib {
|
#[stdlib {
|
||||||
name = "getCommonEdge",
|
name = "getCommonEdge",
|
||||||
feature_tree_operation = false,
|
feature_tree_operation = false,
|
||||||
|
keywords = true,
|
||||||
unlabeled_first = false,
|
unlabeled_first = false,
|
||||||
args = {
|
args = {
|
||||||
faces = { docs = "The tags of the faces you want to find the common edge between" },
|
faces = { docs = "The tags of the faces you want to find the common edge between" },
|
||||||
|
@ -148,6 +148,7 @@ pub async fn extrude(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
|
|||||||
#[stdlib {
|
#[stdlib {
|
||||||
name = "extrude",
|
name = "extrude",
|
||||||
feature_tree_operation = true,
|
feature_tree_operation = true,
|
||||||
|
keywords = true,
|
||||||
unlabeled_first = true,
|
unlabeled_first = true,
|
||||||
args = {
|
args = {
|
||||||
sketches = { docs = "Which sketch or sketches should be extruded"},
|
sketches = { docs = "Which sketch or sketches should be extruded"},
|
||||||
|
@ -82,67 +82,43 @@ async fn inner_fillet(
|
|||||||
exec_state: &mut ExecState,
|
exec_state: &mut ExecState,
|
||||||
args: Args,
|
args: Args,
|
||||||
) -> Result<Box<Solid>, KclError> {
|
) -> Result<Box<Solid>, KclError> {
|
||||||
// If you try and tag multiple edges with a tagged fillet, we want to return an
|
|
||||||
// error to the user that they can only tag one edge at a time.
|
|
||||||
if tag.is_some() && tags.len() > 1 {
|
|
||||||
return Err(KclError::Type(KclErrorDetails {
|
|
||||||
message: "You can only tag one edge at a time with a tagged fillet. Either delete the tag for the fillet fn if you don't need it OR separate into individual fillet functions for each tag.".to_string(),
|
|
||||||
source_ranges: vec![args.source_range],
|
|
||||||
backtrace: Default::default(),
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
if tags.is_empty() {
|
|
||||||
return Err(KclError::Semantic(KclErrorDetails {
|
|
||||||
source_ranges: vec![args.source_range],
|
|
||||||
message: "You must fillet at least one tag".to_owned(),
|
|
||||||
backtrace: Default::default(),
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut solid = solid.clone();
|
let mut solid = solid.clone();
|
||||||
let edge_ids = tags
|
for edge_tag in tags {
|
||||||
.into_iter()
|
let edge_id = edge_tag.get_engine_id(exec_state, &args)?;
|
||||||
.map(|edge_tag| edge_tag.get_engine_id(exec_state, &args))
|
|
||||||
.collect::<Result<Vec<_>, _>>()?;
|
|
||||||
|
|
||||||
let id = exec_state.next_uuid();
|
let id = exec_state.next_uuid();
|
||||||
let mut extra_face_ids = Vec::new();
|
args.batch_end_cmd(
|
||||||
let num_extra_ids = edge_ids.len() - 1;
|
id,
|
||||||
for _ in 0..num_extra_ids {
|
ModelingCmd::from(mcmd::Solid3dFilletEdge {
|
||||||
extra_face_ids.push(exec_state.next_uuid());
|
edge_id: None,
|
||||||
}
|
edge_ids: vec![edge_id],
|
||||||
args.batch_end_cmd(
|
extra_face_ids: vec![],
|
||||||
id,
|
strategy: Default::default(),
|
||||||
ModelingCmd::from(mcmd::Solid3dFilletEdge {
|
object_id: solid.id,
|
||||||
edge_id: None,
|
radius: LengthUnit(radius.to_mm()),
|
||||||
edge_ids: edge_ids.clone(),
|
tolerance: LengthUnit(tolerance.as_ref().map(|t| t.to_mm()).unwrap_or(DEFAULT_TOLERANCE)),
|
||||||
extra_face_ids,
|
cut_type: CutType::Fillet,
|
||||||
strategy: Default::default(),
|
}),
|
||||||
object_id: solid.id,
|
)
|
||||||
radius: LengthUnit(radius.to_mm()),
|
.await?;
|
||||||
tolerance: LengthUnit(tolerance.as_ref().map(|t| t.to_mm()).unwrap_or(DEFAULT_TOLERANCE)),
|
|
||||||
cut_type: CutType::Fillet,
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let new_edge_cuts = edge_ids.into_iter().map(|edge_id| EdgeCut::Fillet {
|
solid.edge_cuts.push(EdgeCut::Fillet {
|
||||||
id,
|
id,
|
||||||
edge_id,
|
edge_id,
|
||||||
radius: radius.clone(),
|
radius: radius.clone(),
|
||||||
tag: Box::new(tag.clone()),
|
tag: Box::new(tag.clone()),
|
||||||
});
|
});
|
||||||
solid.edge_cuts.extend(new_edge_cuts);
|
|
||||||
|
|
||||||
if let Some(ref tag) = tag {
|
if let Some(ref tag) = tag {
|
||||||
solid.value.push(ExtrudeSurface::Fillet(FilletSurface {
|
solid.value.push(ExtrudeSurface::Fillet(FilletSurface {
|
||||||
face_id: id,
|
face_id: id,
|
||||||
tag: Some(tag.clone()),
|
tag: Some(tag.clone()),
|
||||||
geo_meta: GeoMeta {
|
geo_meta: GeoMeta {
|
||||||
id,
|
id,
|
||||||
metadata: args.source_range.into(),
|
metadata: args.source_range.into(),
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(solid)
|
Ok(solid)
|
||||||
|
@ -121,6 +121,7 @@ pub async fn loft(exec_state: &mut ExecState, args: Args) -> Result<KclValue, Kc
|
|||||||
#[stdlib {
|
#[stdlib {
|
||||||
name = "loft",
|
name = "loft",
|
||||||
feature_tree_operation = true,
|
feature_tree_operation = true,
|
||||||
|
keywords = true,
|
||||||
unlabeled_first = true,
|
unlabeled_first = true,
|
||||||
args = {
|
args = {
|
||||||
sketches = {docs = "Which sketches to loft. Must include at least 2 sketches."},
|
sketches = {docs = "Which sketches to loft. Must include at least 2 sketches."},
|
||||||
|
@ -286,13 +286,7 @@ pub(crate) fn std_fn(path: &str, fn_name: &str) -> (crate::std::StdFn, StdFnProp
|
|||||||
|e, a| Box::pin(crate::std::patterns::pattern_transform_2d(e, a)),
|
|e, a| Box::pin(crate::std::patterns::pattern_transform_2d(e, a)),
|
||||||
StdFnProps::default("std::sketch::patternTransform2d"),
|
StdFnProps::default("std::sketch::patternTransform2d"),
|
||||||
),
|
),
|
||||||
("appearance", "hexString") => (
|
_ => unreachable!(),
|
||||||
|e, a| Box::pin(crate::std::appearance::hex_string(e, a)),
|
|
||||||
StdFnProps::default("std::appearance::hexString"),
|
|
||||||
),
|
|
||||||
(module, fn_name) => {
|
|
||||||
panic!("No implementation found for {module}::{fn_name}, please add it to this big match statement")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -344,7 +344,7 @@ fn array_to_point3d(
|
|||||||
source_ranges: Vec<SourceRange>,
|
source_ranges: Vec<SourceRange>,
|
||||||
exec_state: &mut ExecState,
|
exec_state: &mut ExecState,
|
||||||
) -> Result<[TyF64; 3], KclError> {
|
) -> Result<[TyF64; 3], KclError> {
|
||||||
val.coerce(&RuntimeType::point3d(), true, exec_state)
|
val.coerce(&RuntimeType::point3d(), exec_state)
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
KclError::Semantic(KclErrorDetails::new(
|
KclError::Semantic(KclErrorDetails::new(
|
||||||
format!(
|
format!(
|
||||||
@ -364,7 +364,7 @@ fn array_to_point2d(
|
|||||||
source_ranges: Vec<SourceRange>,
|
source_ranges: Vec<SourceRange>,
|
||||||
exec_state: &mut ExecState,
|
exec_state: &mut ExecState,
|
||||||
) -> Result<[TyF64; 2], KclError> {
|
) -> Result<[TyF64; 2], KclError> {
|
||||||
val.coerce(&RuntimeType::point2d(), true, exec_state)
|
val.coerce(&RuntimeType::point2d(), exec_state)
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
KclError::Semantic(KclErrorDetails::new(
|
KclError::Semantic(KclErrorDetails::new(
|
||||||
format!(
|
format!(
|
||||||
@ -578,12 +578,13 @@ pub async fn pattern_linear_2d(exec_state: &mut ExecState, args: Args) -> Result
|
|||||||
/// ```
|
/// ```
|
||||||
#[stdlib {
|
#[stdlib {
|
||||||
name = "patternLinear2d",
|
name = "patternLinear2d",
|
||||||
|
keywords = true,
|
||||||
unlabeled_first = true,
|
unlabeled_first = true,
|
||||||
args = {
|
args = {
|
||||||
sketches = { docs = "The sketch(es) to duplicate" },
|
sketches = { docs = "The sketch(es) to duplicate" },
|
||||||
instances = { docs = "The number of total instances. Must be greater than or equal to 1. This includes the original entity. For example, if instances is 2, there will be two copies -- the original, and one new copy. If instances is 1, this has no effect." },
|
instances = { docs = "The number of total instances. Must be greater than or equal to 1. This includes the original entity. For example, if instances is 2, there will be two copies -- the original, and one new copy. If instances is 1, this has no effect." },
|
||||||
distance = { docs = "Distance between each repetition. Also known as 'spacing'."},
|
distance = { docs = "Distance between each repetition. Also known as 'spacing'."},
|
||||||
axis = { docs = "The axis of the pattern. A 2D vector.", snippet_value_array = ["1", "0"] },
|
axis = { docs = "The axis of the pattern. A 2D vector." },
|
||||||
use_original = { docs = "If the target was sketched on an extrusion, setting this will use the original sketch as the target, not the entire joined solid. Defaults to false." },
|
use_original = { docs = "If the target was sketched on an extrusion, setting this will use the original sketch as the target, not the entire joined solid. Defaults to false." },
|
||||||
}
|
}
|
||||||
}]
|
}]
|
||||||
@ -741,12 +742,13 @@ pub async fn pattern_linear_3d(exec_state: &mut ExecState, args: Args) -> Result
|
|||||||
#[stdlib {
|
#[stdlib {
|
||||||
name = "patternLinear3d",
|
name = "patternLinear3d",
|
||||||
feature_tree_operation = true,
|
feature_tree_operation = true,
|
||||||
|
keywords = true,
|
||||||
unlabeled_first = true,
|
unlabeled_first = true,
|
||||||
args = {
|
args = {
|
||||||
solids = { docs = "The solid(s) to duplicate" },
|
solids = { docs = "The solid(s) to duplicate" },
|
||||||
instances = { docs = "The number of total instances. Must be greater than or equal to 1. This includes the original entity. For example, if instances is 2, there will be two copies -- the original, and one new copy. If instances is 1, this has no effect." },
|
instances = { docs = "The number of total instances. Must be greater than or equal to 1. This includes the original entity. For example, if instances is 2, there will be two copies -- the original, and one new copy. If instances is 1, this has no effect." },
|
||||||
distance = { docs = "Distance between each repetition. Also known as 'spacing'."},
|
distance = { docs = "Distance between each repetition. Also known as 'spacing'."},
|
||||||
axis = { docs = "The axis of the pattern. A 3D vector.", snippet_value_array = ["1", "0", "0"] },
|
axis = { docs = "The axis of the pattern. A 2D vector." },
|
||||||
use_original = { docs = "If the target was sketched on an extrusion, setting this will use the original sketch as the target, not the entire joined solid. Defaults to false." },
|
use_original = { docs = "If the target was sketched on an extrusion, setting this will use the original sketch as the target, not the entire joined solid. Defaults to false." },
|
||||||
},
|
},
|
||||||
tags = ["solid"]
|
tags = ["solid"]
|
||||||
@ -937,11 +939,12 @@ pub async fn pattern_circular_2d(exec_state: &mut ExecState, args: Args) -> Resu
|
|||||||
/// ```
|
/// ```
|
||||||
#[stdlib {
|
#[stdlib {
|
||||||
name = "patternCircular2d",
|
name = "patternCircular2d",
|
||||||
|
keywords = true,
|
||||||
unlabeled_first = true,
|
unlabeled_first = true,
|
||||||
args = {
|
args = {
|
||||||
sketch_set = { docs = "Which sketch(es) to pattern" },
|
sketch_set = { docs = "Which sketch(es) to pattern" },
|
||||||
instances = { docs = "The number of total instances. Must be greater than or equal to 1. This includes the original entity. For example, if instances is 2, there will be two copies -- the original, and one new copy. If instances is 1, this has no effect."},
|
instances = { docs = "The number of total instances. Must be greater than or equal to 1. This includes the original entity. For example, if instances is 2, there will be two copies -- the original, and one new copy. If instances is 1, this has no effect."},
|
||||||
center = { docs = "The center about which to make the pattern. This is a 2D vector.", snippet_value_array = ["0", "0"]},
|
center = { docs = "The center about which to make the pattern. This is a 2D vector."},
|
||||||
arc_degrees = { docs = "The arc angle (in degrees) to place the repetitions. Must be greater than 0. Defaults to 360."},
|
arc_degrees = { docs = "The arc angle (in degrees) to place the repetitions. Must be greater than 0. Defaults to 360."},
|
||||||
rotate_duplicates= { docs = "Whether or not to rotate the duplicates as they are copied. Defaults to true."},
|
rotate_duplicates= { docs = "Whether or not to rotate the duplicates as they are copied. Defaults to true."},
|
||||||
use_original= { docs = "If the target was sketched on an extrusion, setting this will use the original sketch as the target, not the entire joined solid. Defaults to false."},
|
use_original= { docs = "If the target was sketched on an extrusion, setting this will use the original sketch as the target, not the entire joined solid. Defaults to false."},
|
||||||
@ -1078,12 +1081,13 @@ pub async fn pattern_circular_3d(exec_state: &mut ExecState, args: Args) -> Resu
|
|||||||
#[stdlib {
|
#[stdlib {
|
||||||
name = "patternCircular3d",
|
name = "patternCircular3d",
|
||||||
feature_tree_operation = true,
|
feature_tree_operation = true,
|
||||||
|
keywords = true,
|
||||||
unlabeled_first = true,
|
unlabeled_first = true,
|
||||||
args = {
|
args = {
|
||||||
solids = { docs = "Which solid(s) to pattern" },
|
solids = { docs = "Which solid(s) to pattern" },
|
||||||
instances = { docs = "The number of total instances. Must be greater than or equal to 1. This includes the original entity. For example, if instances is 2, there will be two copies -- the original, and one new copy. If instances is 1, this has no effect."},
|
instances = { docs = "The number of total instances. Must be greater than or equal to 1. This includes the original entity. For example, if instances is 2, there will be two copies -- the original, and one new copy. If instances is 1, this has no effect."},
|
||||||
axis = { docs = "The axis around which to make the pattern. This is a 3D vector", snippet_value_array = ["1", "0", "0"]},
|
axis = { docs = "The axis around which to make the pattern. This is a 3D vector"},
|
||||||
center = { docs = "The center about which to make the pattern. This is a 3D vector.", snippet_value_array = ["0", "0", "0"]},
|
center = { docs = "The center about which to make the pattern. This is a 3D vector."},
|
||||||
arc_degrees = { docs = "The arc angle (in degrees) to place the repetitions. Must be greater than 0. Defaults to 360."},
|
arc_degrees = { docs = "The arc angle (in degrees) to place the repetitions. Must be greater than 0. Defaults to 360."},
|
||||||
rotate_duplicates = { docs = "Whether or not to rotate the duplicates as they are copied. Defaults to true."},
|
rotate_duplicates = { docs = "Whether or not to rotate the duplicates as they are copied. Defaults to true."},
|
||||||
use_original = { docs = "If the target was sketched on an extrusion, setting this will use the original sketch as the target, not the entire joined solid. Defaults to false."},
|
use_original = { docs = "If the target was sketched on an extrusion, setting this will use the original sketch as the target, not the entire joined solid. Defaults to false."},
|
||||||
|
@ -49,6 +49,7 @@ pub async fn segment_end(exec_state: &mut ExecState, args: Args) -> Result<KclVa
|
|||||||
/// ```
|
/// ```
|
||||||
#[stdlib {
|
#[stdlib {
|
||||||
name = "segEnd",
|
name = "segEnd",
|
||||||
|
keywords = true,
|
||||||
unlabeled_first = true,
|
unlabeled_first = true,
|
||||||
args = {
|
args = {
|
||||||
tag = { docs = "The line segment being queried by its tag"},
|
tag = { docs = "The line segment being queried by its tag"},
|
||||||
@ -93,6 +94,7 @@ pub async fn segment_end_x(exec_state: &mut ExecState, args: Args) -> Result<Kcl
|
|||||||
/// ```
|
/// ```
|
||||||
#[stdlib {
|
#[stdlib {
|
||||||
name = "segEndX",
|
name = "segEndX",
|
||||||
|
keywords = true,
|
||||||
unlabeled_first = true,
|
unlabeled_first = true,
|
||||||
args = {
|
args = {
|
||||||
tag = { docs = "The line segment being queried by its tag"},
|
tag = { docs = "The line segment being queried by its tag"},
|
||||||
@ -135,6 +137,7 @@ pub async fn segment_end_y(exec_state: &mut ExecState, args: Args) -> Result<Kcl
|
|||||||
/// ```
|
/// ```
|
||||||
#[stdlib {
|
#[stdlib {
|
||||||
name = "segEndY",
|
name = "segEndY",
|
||||||
|
keywords = true,
|
||||||
unlabeled_first = true,
|
unlabeled_first = true,
|
||||||
args = {
|
args = {
|
||||||
tag = { docs = "The line segment being queried by its tag"},
|
tag = { docs = "The line segment being queried by its tag"},
|
||||||
@ -188,6 +191,7 @@ pub async fn segment_start(exec_state: &mut ExecState, args: Args) -> Result<Kcl
|
|||||||
/// ```
|
/// ```
|
||||||
#[stdlib {
|
#[stdlib {
|
||||||
name = "segStart",
|
name = "segStart",
|
||||||
|
keywords = true,
|
||||||
unlabeled_first = true,
|
unlabeled_first = true,
|
||||||
args = {
|
args = {
|
||||||
tag = { docs = "The line segment being queried by its tag"},
|
tag = { docs = "The line segment being queried by its tag"},
|
||||||
@ -232,6 +236,7 @@ pub async fn segment_start_x(exec_state: &mut ExecState, args: Args) -> Result<K
|
|||||||
/// ```
|
/// ```
|
||||||
#[stdlib {
|
#[stdlib {
|
||||||
name = "segStartX",
|
name = "segStartX",
|
||||||
|
keywords = true,
|
||||||
unlabeled_first = true,
|
unlabeled_first = true,
|
||||||
args = {
|
args = {
|
||||||
tag = { docs = "The line segment being queried by its tag"},
|
tag = { docs = "The line segment being queried by its tag"},
|
||||||
@ -274,6 +279,7 @@ pub async fn segment_start_y(exec_state: &mut ExecState, args: Args) -> Result<K
|
|||||||
/// ```
|
/// ```
|
||||||
#[stdlib {
|
#[stdlib {
|
||||||
name = "segStartY",
|
name = "segStartY",
|
||||||
|
keywords = true,
|
||||||
unlabeled_first = true,
|
unlabeled_first = true,
|
||||||
args = {
|
args = {
|
||||||
tag = { docs = "The line segment being queried by its tag"},
|
tag = { docs = "The line segment being queried by its tag"},
|
||||||
@ -316,6 +322,7 @@ pub async fn last_segment_x(exec_state: &mut ExecState, args: Args) -> Result<Kc
|
|||||||
/// ```
|
/// ```
|
||||||
#[stdlib {
|
#[stdlib {
|
||||||
name = "lastSegX",
|
name = "lastSegX",
|
||||||
|
keywords = true,
|
||||||
unlabeled_first = true,
|
unlabeled_first = true,
|
||||||
args = {
|
args = {
|
||||||
sketch = { docs = "The sketch whose line segment is being queried"},
|
sketch = { docs = "The sketch whose line segment is being queried"},
|
||||||
@ -362,6 +369,7 @@ pub async fn last_segment_y(exec_state: &mut ExecState, args: Args) -> Result<Kc
|
|||||||
/// ```
|
/// ```
|
||||||
#[stdlib {
|
#[stdlib {
|
||||||
name = "lastSegY",
|
name = "lastSegY",
|
||||||
|
keywords = true,
|
||||||
unlabeled_first = true,
|
unlabeled_first = true,
|
||||||
args = {
|
args = {
|
||||||
sketch = { docs = "The sketch whose line segment is being queried"},
|
sketch = { docs = "The sketch whose line segment is being queried"},
|
||||||
@ -411,6 +419,7 @@ pub async fn segment_length(exec_state: &mut ExecState, args: Args) -> Result<Kc
|
|||||||
/// ```
|
/// ```
|
||||||
#[stdlib {
|
#[stdlib {
|
||||||
name = "segLen",
|
name = "segLen",
|
||||||
|
keywords = true,
|
||||||
unlabeled_first = true,
|
unlabeled_first = true,
|
||||||
args = {
|
args = {
|
||||||
tag = { docs = "The line segment being queried by its tag"},
|
tag = { docs = "The line segment being queried by its tag"},
|
||||||
@ -454,6 +463,7 @@ pub async fn segment_angle(exec_state: &mut ExecState, args: Args) -> Result<Kcl
|
|||||||
/// ```
|
/// ```
|
||||||
#[stdlib {
|
#[stdlib {
|
||||||
name = "segAng",
|
name = "segAng",
|
||||||
|
keywords = true,
|
||||||
unlabeled_first = true,
|
unlabeled_first = true,
|
||||||
args = {
|
args = {
|
||||||
tag = { docs = "The line segment being queried by its tag"},
|
tag = { docs = "The line segment being queried by its tag"},
|
||||||
@ -555,6 +565,7 @@ pub async fn tangent_to_end(exec_state: &mut ExecState, args: Args) -> Result<Kc
|
|||||||
/// ```
|
/// ```
|
||||||
#[stdlib {
|
#[stdlib {
|
||||||
name = "tangentToEnd",
|
name = "tangentToEnd",
|
||||||
|
keywords = true,
|
||||||
unlabeled_first = true,
|
unlabeled_first = true,
|
||||||
args = {
|
args = {
|
||||||
tag = { docs = "The line segment being queried by its tag"},
|
tag = { docs = "The line segment being queried by its tag"},
|
||||||
|
@ -167,6 +167,7 @@ pub async fn circle_three_point(exec_state: &mut ExecState, args: Args) -> Resul
|
|||||||
/// ```
|
/// ```
|
||||||
#[stdlib {
|
#[stdlib {
|
||||||
name = "circleThreePoint",
|
name = "circleThreePoint",
|
||||||
|
keywords = true,
|
||||||
unlabeled_first = true,
|
unlabeled_first = true,
|
||||||
args = {
|
args = {
|
||||||
sketch_surface_or_group = {docs = "Plane or surface to sketch on."},
|
sketch_surface_or_group = {docs = "Plane or surface to sketch on."},
|
||||||
@ -323,12 +324,13 @@ pub async fn polygon(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
|
|||||||
/// ```
|
/// ```
|
||||||
#[stdlib {
|
#[stdlib {
|
||||||
name = "polygon",
|
name = "polygon",
|
||||||
|
keywords = true,
|
||||||
unlabeled_first = true,
|
unlabeled_first = true,
|
||||||
args = {
|
args = {
|
||||||
sketch_surface_or_group = { docs = "Plane or surface to sketch on" },
|
sketch_surface_or_group = { docs = "Plane or surface to sketch on" },
|
||||||
radius = { docs = "The radius of the polygon", include_in_snippet = true },
|
radius = { docs = "The radius of the polygon", include_in_snippet = true },
|
||||||
num_sides = { docs = "The number of sides in the polygon", include_in_snippet = true },
|
num_sides = { docs = "The number of sides in the polygon", include_in_snippet = true },
|
||||||
center = { docs = "The center point of the polygon", snippet_value_array = ["0", "0"] },
|
center = { docs = "The center point of the polygon", include_in_snippet = true },
|
||||||
inscribed = { docs = "Whether the polygon is inscribed (true, the default) or circumscribed (false) about a circle with the specified radius" },
|
inscribed = { docs = "Whether the polygon is inscribed (true, the default) or circumscribed (false) about a circle with the specified radius" },
|
||||||
},
|
},
|
||||||
tags = ["sketch"]
|
tags = ["sketch"]
|
||||||
|
@ -133,6 +133,7 @@ fn involute_curve(radius: f64, angle: f64) -> (f64, f64) {
|
|||||||
/// ```
|
/// ```
|
||||||
#[stdlib {
|
#[stdlib {
|
||||||
name = "involuteCircular",
|
name = "involuteCircular",
|
||||||
|
keywords = true,
|
||||||
unlabeled_first = true,
|
unlabeled_first = true,
|
||||||
args = {
|
args = {
|
||||||
sketch = { docs = "Which sketch should this path be added to?"},
|
sketch = { docs = "Which sketch should this path be added to?"},
|
||||||
@ -253,6 +254,7 @@ pub async fn line(exec_state: &mut ExecState, args: Args) -> Result<KclValue, Kc
|
|||||||
/// ```
|
/// ```
|
||||||
#[stdlib {
|
#[stdlib {
|
||||||
name = "line",
|
name = "line",
|
||||||
|
keywords = true,
|
||||||
unlabeled_first = true,
|
unlabeled_first = true,
|
||||||
args = {
|
args = {
|
||||||
sketch = { docs = "Which sketch should this path be added to?"},
|
sketch = { docs = "Which sketch should this path be added to?"},
|
||||||
@ -424,6 +426,7 @@ pub async fn x_line(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
|
|||||||
/// ```
|
/// ```
|
||||||
#[stdlib {
|
#[stdlib {
|
||||||
name = "xLine",
|
name = "xLine",
|
||||||
|
keywords = true,
|
||||||
unlabeled_first = true,
|
unlabeled_first = true,
|
||||||
args = {
|
args = {
|
||||||
sketch = { docs = "Which sketch should this path be added to?"},
|
sketch = { docs = "Which sketch should this path be added to?"},
|
||||||
@ -489,6 +492,7 @@ pub async fn y_line(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
|
|||||||
/// ```
|
/// ```
|
||||||
#[stdlib {
|
#[stdlib {
|
||||||
name = "yLine",
|
name = "yLine",
|
||||||
|
keywords = true,
|
||||||
unlabeled_first = true,
|
unlabeled_first = true,
|
||||||
args = {
|
args = {
|
||||||
sketch = { docs = "Which sketch should this path be added to?"},
|
sketch = { docs = "Which sketch should this path be added to?"},
|
||||||
@ -571,6 +575,7 @@ pub async fn angled_line(exec_state: &mut ExecState, args: Args) -> Result<KclVa
|
|||||||
/// ```
|
/// ```
|
||||||
#[stdlib {
|
#[stdlib {
|
||||||
name = "angledLine",
|
name = "angledLine",
|
||||||
|
keywords = true,
|
||||||
unlabeled_first = true,
|
unlabeled_first = true,
|
||||||
args = {
|
args = {
|
||||||
sketch = { docs = "Which sketch should this path be added to?"},
|
sketch = { docs = "Which sketch should this path be added to?"},
|
||||||
@ -869,6 +874,7 @@ pub async fn angled_line_that_intersects(exec_state: &mut ExecState, args: Args)
|
|||||||
/// ```
|
/// ```
|
||||||
#[stdlib {
|
#[stdlib {
|
||||||
name = "angledLineThatIntersects",
|
name = "angledLineThatIntersects",
|
||||||
|
keywords = true,
|
||||||
unlabeled_first = true,
|
unlabeled_first = true,
|
||||||
args = {
|
args = {
|
||||||
sketch = { docs = "Which sketch should this path be added to?"},
|
sketch = { docs = "Which sketch should this path be added to?"},
|
||||||
@ -1146,6 +1152,7 @@ pub async fn start_sketch_on(exec_state: &mut ExecState, args: Args) -> Result<K
|
|||||||
#[stdlib {
|
#[stdlib {
|
||||||
name = "startSketchOn",
|
name = "startSketchOn",
|
||||||
feature_tree_operation = true,
|
feature_tree_operation = true,
|
||||||
|
keywords = true,
|
||||||
unlabeled_first = true,
|
unlabeled_first = true,
|
||||||
args = {
|
args = {
|
||||||
plane_or_solid = { docs = "The plane or solid to sketch on"},
|
plane_or_solid = { docs = "The plane or solid to sketch on"},
|
||||||
@ -1311,10 +1318,11 @@ pub async fn start_profile(exec_state: &mut ExecState, args: Args) -> Result<Kcl
|
|||||||
/// ```
|
/// ```
|
||||||
#[stdlib {
|
#[stdlib {
|
||||||
name = "startProfile",
|
name = "startProfile",
|
||||||
|
keywords = true,
|
||||||
unlabeled_first = true,
|
unlabeled_first = true,
|
||||||
args = {
|
args = {
|
||||||
sketch_surface = { docs = "What to start the profile on" },
|
sketch_surface = { docs = "What to start the profile on" },
|
||||||
at = { docs = "Where to start the profile. An absolute point.", snippet_value_array = ["0", "0"] },
|
at = { docs = "Where to start the profile. An absolute point." },
|
||||||
tag = { docs = "Tag this first starting point" },
|
tag = { docs = "Tag this first starting point" },
|
||||||
},
|
},
|
||||||
tags = ["sketch"]
|
tags = ["sketch"]
|
||||||
@ -1453,6 +1461,7 @@ pub async fn profile_start_x(exec_state: &mut ExecState, args: Args) -> Result<K
|
|||||||
/// ```
|
/// ```
|
||||||
#[stdlib {
|
#[stdlib {
|
||||||
name = "profileStartX",
|
name = "profileStartX",
|
||||||
|
keywords = true,
|
||||||
unlabeled_first = true,
|
unlabeled_first = true,
|
||||||
args = {
|
args = {
|
||||||
profile = {docs = "Profile whose start is being used"},
|
profile = {docs = "Profile whose start is being used"},
|
||||||
@ -1482,6 +1491,7 @@ pub async fn profile_start_y(exec_state: &mut ExecState, args: Args) -> Result<K
|
|||||||
/// ```
|
/// ```
|
||||||
#[stdlib {
|
#[stdlib {
|
||||||
name = "profileStartY",
|
name = "profileStartY",
|
||||||
|
keywords = true,
|
||||||
unlabeled_first = true,
|
unlabeled_first = true,
|
||||||
args = {
|
args = {
|
||||||
profile = {docs = "Profile whose start is being used"},
|
profile = {docs = "Profile whose start is being used"},
|
||||||
@ -1514,6 +1524,7 @@ pub async fn profile_start(exec_state: &mut ExecState, args: Args) -> Result<Kcl
|
|||||||
/// ```
|
/// ```
|
||||||
#[stdlib {
|
#[stdlib {
|
||||||
name = "profileStart",
|
name = "profileStart",
|
||||||
|
keywords = true,
|
||||||
unlabeled_first = true,
|
unlabeled_first = true,
|
||||||
args = {
|
args = {
|
||||||
profile = {docs = "Profile whose start is being used"},
|
profile = {docs = "Profile whose start is being used"},
|
||||||
@ -1558,6 +1569,7 @@ pub async fn close(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
|||||||
/// ```
|
/// ```
|
||||||
#[stdlib {
|
#[stdlib {
|
||||||
name = "close",
|
name = "close",
|
||||||
|
keywords = true,
|
||||||
unlabeled_first = true,
|
unlabeled_first = true,
|
||||||
args = {
|
args = {
|
||||||
sketch = { docs = "The sketch you want to close"},
|
sketch = { docs = "The sketch you want to close"},
|
||||||
@ -1667,6 +1679,7 @@ pub async fn arc(exec_state: &mut ExecState, args: Args) -> Result<KclValue, Kcl
|
|||||||
/// ```
|
/// ```
|
||||||
#[stdlib {
|
#[stdlib {
|
||||||
name = "arc",
|
name = "arc",
|
||||||
|
keywords = true,
|
||||||
unlabeled_first = true,
|
unlabeled_first = true,
|
||||||
args = {
|
args = {
|
||||||
sketch = { docs = "Which sketch should this path be added to?" },
|
sketch = { docs = "Which sketch should this path be added to?" },
|
||||||
@ -1909,6 +1922,7 @@ pub async fn tangential_arc(exec_state: &mut ExecState, args: Args) -> Result<Kc
|
|||||||
/// ```
|
/// ```
|
||||||
#[stdlib {
|
#[stdlib {
|
||||||
name = "tangentialArc",
|
name = "tangentialArc",
|
||||||
|
keywords = true,
|
||||||
unlabeled_first = true,
|
unlabeled_first = true,
|
||||||
args = {
|
args = {
|
||||||
sketch = { docs = "Which sketch should this path be added to?"},
|
sketch = { docs = "Which sketch should this path be added to?"},
|
||||||
@ -2154,27 +2168,12 @@ async fn inner_tangential_arc_to_point(
|
|||||||
pub async fn bezier_curve(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
pub async fn bezier_curve(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||||
let sketch =
|
let sketch =
|
||||||
args.get_unlabeled_kw_arg_typed("sketch", &RuntimeType::Primitive(PrimitiveType::Sketch), exec_state)?;
|
args.get_unlabeled_kw_arg_typed("sketch", &RuntimeType::Primitive(PrimitiveType::Sketch), exec_state)?;
|
||||||
let control1 = args.get_kw_arg_opt_typed("control1", &RuntimeType::point2d(), exec_state)?;
|
let end: [TyF64; 2] = args.get_kw_arg_typed("end", &RuntimeType::point2d(), exec_state)?;
|
||||||
let control2 = args.get_kw_arg_opt_typed("control2", &RuntimeType::point2d(), exec_state)?;
|
let control1: [TyF64; 2] = args.get_kw_arg_typed("control1", &RuntimeType::point2d(), exec_state)?;
|
||||||
let end = args.get_kw_arg_opt_typed("end", &RuntimeType::point2d(), exec_state)?;
|
let control2: [TyF64; 2] = args.get_kw_arg_typed("control2", &RuntimeType::point2d(), exec_state)?;
|
||||||
let control1_absolute = args.get_kw_arg_opt_typed("control1Absolute", &RuntimeType::point2d(), exec_state)?;
|
|
||||||
let control2_absolute = args.get_kw_arg_opt_typed("control2Absolute", &RuntimeType::point2d(), exec_state)?;
|
|
||||||
let end_absolute = args.get_kw_arg_opt_typed("endAbsolute", &RuntimeType::point2d(), exec_state)?;
|
|
||||||
let tag = args.get_kw_arg_opt("tag")?;
|
let tag = args.get_kw_arg_opt("tag")?;
|
||||||
|
|
||||||
let new_sketch = inner_bezier_curve(
|
let new_sketch = inner_bezier_curve(sketch, control1, control2, end, tag, exec_state, args).await?;
|
||||||
sketch,
|
|
||||||
control1,
|
|
||||||
control2,
|
|
||||||
end,
|
|
||||||
control1_absolute,
|
|
||||||
control2_absolute,
|
|
||||||
end_absolute,
|
|
||||||
tag,
|
|
||||||
exec_state,
|
|
||||||
args,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
Ok(KclValue::Sketch {
|
Ok(KclValue::Sketch {
|
||||||
value: Box::new(new_sketch),
|
value: Box::new(new_sketch),
|
||||||
})
|
})
|
||||||
@ -2185,7 +2184,6 @@ pub async fn bezier_curve(exec_state: &mut ExecState, args: Args) -> Result<KclV
|
|||||||
/// shape.
|
/// shape.
|
||||||
///
|
///
|
||||||
/// ```no_run
|
/// ```no_run
|
||||||
/// // Example using relative control points.
|
|
||||||
/// exampleSketch = startSketchOn(XZ)
|
/// exampleSketch = startSketchOn(XZ)
|
||||||
/// |> startProfile(at = [0, 0])
|
/// |> startProfile(at = [0, 0])
|
||||||
/// |> line(end = [0, 10])
|
/// |> line(end = [0, 10])
|
||||||
@ -2199,101 +2197,52 @@ pub async fn bezier_curve(exec_state: &mut ExecState, args: Args) -> Result<KclV
|
|||||||
///
|
///
|
||||||
/// example = extrude(exampleSketch, length = 10)
|
/// example = extrude(exampleSketch, length = 10)
|
||||||
/// ```
|
/// ```
|
||||||
/// ```no_run
|
|
||||||
/// // Example using absolute control points.
|
|
||||||
/// startSketchOn(XY)
|
|
||||||
/// |> startProfile(at = [300, 300])
|
|
||||||
/// |> bezierCurve(control1Absolute = [600, 300], control2Absolute = [-300, -100], endAbsolute = [600, 600])
|
|
||||||
/// |> close()
|
|
||||||
/// |> extrude(length = 10)
|
|
||||||
/// ```
|
|
||||||
#[stdlib {
|
#[stdlib {
|
||||||
name = "bezierCurve",
|
name = "bezierCurve",
|
||||||
|
keywords = true,
|
||||||
unlabeled_first = true,
|
unlabeled_first = true,
|
||||||
args = {
|
args = {
|
||||||
sketch = { docs = "Which sketch should this path be added to?"},
|
sketch = { docs = "Which sketch should this path be added to?"},
|
||||||
|
end = { docs = "How far away (along the X and Y axes) should this line go?" },
|
||||||
control1 = { docs = "First control point for the cubic" },
|
control1 = { docs = "First control point for the cubic" },
|
||||||
control2 = { docs = "Second control point for the cubic" },
|
control2 = { docs = "Second control point for the cubic" },
|
||||||
end = { docs = "How far away (along the X and Y axes) should this line go?" },
|
|
||||||
control1_absolute = { docs = "First control point for the cubic. Absolute point." },
|
|
||||||
control2_absolute = { docs = "Second control point for the cubic. Absolute point." },
|
|
||||||
end_absolute = { docs = "Coordinate on the plane at which this line should end." },
|
|
||||||
tag = { docs = "Create a new tag which refers to this line"},
|
tag = { docs = "Create a new tag which refers to this line"},
|
||||||
},
|
},
|
||||||
tags = ["sketch"]
|
tags = ["sketch"]
|
||||||
}]
|
}]
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
async fn inner_bezier_curve(
|
async fn inner_bezier_curve(
|
||||||
sketch: Sketch,
|
sketch: Sketch,
|
||||||
control1: Option<[TyF64; 2]>,
|
control1: [TyF64; 2],
|
||||||
control2: Option<[TyF64; 2]>,
|
control2: [TyF64; 2],
|
||||||
end: Option<[TyF64; 2]>,
|
end: [TyF64; 2],
|
||||||
control1_absolute: Option<[TyF64; 2]>,
|
|
||||||
control2_absolute: Option<[TyF64; 2]>,
|
|
||||||
end_absolute: Option<[TyF64; 2]>,
|
|
||||||
tag: Option<TagNode>,
|
tag: Option<TagNode>,
|
||||||
exec_state: &mut ExecState,
|
exec_state: &mut ExecState,
|
||||||
args: Args,
|
args: Args,
|
||||||
) -> Result<Sketch, KclError> {
|
) -> Result<Sketch, KclError> {
|
||||||
let from = sketch.current_pen_position()?;
|
let from = sketch.current_pen_position()?;
|
||||||
|
|
||||||
|
let relative = true;
|
||||||
|
let delta = end.clone();
|
||||||
|
let to = [
|
||||||
|
from.x + end[0].to_length_units(from.units),
|
||||||
|
from.y + end[1].to_length_units(from.units),
|
||||||
|
];
|
||||||
|
|
||||||
let id = exec_state.next_uuid();
|
let id = exec_state.next_uuid();
|
||||||
|
|
||||||
let to = match (
|
args.batch_modeling_cmd(
|
||||||
control1,
|
id,
|
||||||
control2,
|
ModelingCmd::from(mcmd::ExtendPath {
|
||||||
end,
|
path: sketch.id.into(),
|
||||||
control1_absolute,
|
segment: PathSegment::Bezier {
|
||||||
control2_absolute,
|
control1: KPoint2d::from(point_to_mm(control1)).with_z(0.0).map(LengthUnit),
|
||||||
end_absolute,
|
control2: KPoint2d::from(point_to_mm(control2)).with_z(0.0).map(LengthUnit),
|
||||||
) {
|
end: KPoint2d::from(point_to_mm(delta)).with_z(0.0).map(LengthUnit),
|
||||||
// Relative
|
relative,
|
||||||
(Some(control1), Some(control2), Some(end), None, None, None) => {
|
},
|
||||||
let delta = end.clone();
|
}),
|
||||||
let to = [
|
)
|
||||||
from.x + end[0].to_length_units(from.units),
|
.await?;
|
||||||
from.y + end[1].to_length_units(from.units),
|
|
||||||
];
|
|
||||||
|
|
||||||
args.batch_modeling_cmd(
|
|
||||||
id,
|
|
||||||
ModelingCmd::from(mcmd::ExtendPath {
|
|
||||||
path: sketch.id.into(),
|
|
||||||
segment: PathSegment::Bezier {
|
|
||||||
control1: KPoint2d::from(point_to_mm(control1)).with_z(0.0).map(LengthUnit),
|
|
||||||
control2: KPoint2d::from(point_to_mm(control2)).with_z(0.0).map(LengthUnit),
|
|
||||||
end: KPoint2d::from(point_to_mm(delta)).with_z(0.0).map(LengthUnit),
|
|
||||||
relative: true,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
to
|
|
||||||
}
|
|
||||||
// Absolute
|
|
||||||
(None, None, None, Some(control1), Some(control2), Some(end)) => {
|
|
||||||
let to = [end[0].to_length_units(from.units), end[1].to_length_units(from.units)];
|
|
||||||
args.batch_modeling_cmd(
|
|
||||||
id,
|
|
||||||
ModelingCmd::from(mcmd::ExtendPath {
|
|
||||||
path: sketch.id.into(),
|
|
||||||
segment: PathSegment::Bezier {
|
|
||||||
control1: KPoint2d::from(point_to_mm(control1)).with_z(0.0).map(LengthUnit),
|
|
||||||
control2: KPoint2d::from(point_to_mm(control2)).with_z(0.0).map(LengthUnit),
|
|
||||||
end: KPoint2d::from(point_to_mm(end)).with_z(0.0).map(LengthUnit),
|
|
||||||
relative: false,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
to
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
return Err(KclError::Semantic(KclErrorDetails::new(
|
|
||||||
"You must either give `control1`, `control2` and `end`, or `control1Absolute`, `control2Absolute` and `endAbsolute`.".to_owned(),
|
|
||||||
vec![args.source_range],
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let current_path = Path::ToPoint {
|
let current_path = Path::ToPoint {
|
||||||
base: BasePath {
|
base: BasePath {
|
||||||
@ -2372,6 +2321,7 @@ pub async fn subtract_2d(exec_state: &mut ExecState, args: Args) -> Result<KclVa
|
|||||||
#[stdlib {
|
#[stdlib {
|
||||||
name = "subtract2d",
|
name = "subtract2d",
|
||||||
feature_tree_operation = true,
|
feature_tree_operation = true,
|
||||||
|
keywords = true,
|
||||||
unlabeled_first = true,
|
unlabeled_first = true,
|
||||||
args = {
|
args = {
|
||||||
sketch = { docs = "Which sketch should this path be added to?" },
|
sketch = { docs = "Which sketch should this path be added to?" },
|
||||||
|
@ -160,6 +160,7 @@ pub async fn sweep(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
|||||||
#[stdlib {
|
#[stdlib {
|
||||||
name = "sweep",
|
name = "sweep",
|
||||||
feature_tree_operation = true,
|
feature_tree_operation = true,
|
||||||
|
keywords = true,
|
||||||
unlabeled_first = true,
|
unlabeled_first = true,
|
||||||
args = {
|
args = {
|
||||||
sketches = { docs = "The sketch or set of sketches that should be swept in space" },
|
sketches = { docs = "The sketch or set of sketches that should be swept in space" },
|
||||||
|
@ -147,6 +147,7 @@ pub async fn scale(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
|||||||
#[stdlib {
|
#[stdlib {
|
||||||
name = "scale",
|
name = "scale",
|
||||||
feature_tree_operation = false,
|
feature_tree_operation = false,
|
||||||
|
keywords = true,
|
||||||
unlabeled_first = true,
|
unlabeled_first = true,
|
||||||
args = {
|
args = {
|
||||||
objects = {docs = "The solid, sketch, or set of solids or sketches to scale."},
|
objects = {docs = "The solid, sketch, or set of solids or sketches to scale."},
|
||||||
@ -166,6 +167,12 @@ async fn inner_scale(
|
|||||||
exec_state: &mut ExecState,
|
exec_state: &mut ExecState,
|
||||||
args: Args,
|
args: Args,
|
||||||
) -> Result<SolidOrSketchOrImportedGeometry, KclError> {
|
) -> Result<SolidOrSketchOrImportedGeometry, KclError> {
|
||||||
|
// If we have a solid, flush the fillets and chamfers.
|
||||||
|
// Only transforms needs this, it is very odd, see: https://github.com/KittyCAD/modeling-app/issues/5880
|
||||||
|
if let SolidOrSketchOrImportedGeometry::SolidSet(solids) = &objects {
|
||||||
|
args.flush_batch_for_solids(exec_state, solids).await?;
|
||||||
|
}
|
||||||
|
|
||||||
let mut objects = objects.clone();
|
let mut objects = objects.clone();
|
||||||
for object_id in objects.ids(&args.ctx).await? {
|
for object_id in objects.ids(&args.ctx).await? {
|
||||||
let id = exec_state.next_uuid();
|
let id = exec_state.next_uuid();
|
||||||
@ -371,6 +378,7 @@ pub async fn translate(exec_state: &mut ExecState, args: Args) -> Result<KclValu
|
|||||||
#[stdlib {
|
#[stdlib {
|
||||||
name = "translate",
|
name = "translate",
|
||||||
feature_tree_operation = false,
|
feature_tree_operation = false,
|
||||||
|
keywords = true,
|
||||||
unlabeled_first = true,
|
unlabeled_first = true,
|
||||||
args = {
|
args = {
|
||||||
objects = {docs = "The solid, sketch, or set of solids or sketches to move."},
|
objects = {docs = "The solid, sketch, or set of solids or sketches to move."},
|
||||||
@ -390,6 +398,12 @@ async fn inner_translate(
|
|||||||
exec_state: &mut ExecState,
|
exec_state: &mut ExecState,
|
||||||
args: Args,
|
args: Args,
|
||||||
) -> Result<SolidOrSketchOrImportedGeometry, KclError> {
|
) -> Result<SolidOrSketchOrImportedGeometry, KclError> {
|
||||||
|
// If we have a solid, flush the fillets and chamfers.
|
||||||
|
// Only transforms needs this, it is very odd, see: https://github.com/KittyCAD/modeling-app/issues/5880
|
||||||
|
if let SolidOrSketchOrImportedGeometry::SolidSet(solids) = &objects {
|
||||||
|
args.flush_batch_for_solids(exec_state, solids).await?;
|
||||||
|
}
|
||||||
|
|
||||||
let mut objects = objects.clone();
|
let mut objects = objects.clone();
|
||||||
for object_id in objects.ids(&args.ctx).await? {
|
for object_id in objects.ids(&args.ctx).await? {
|
||||||
let id = exec_state.next_uuid();
|
let id = exec_state.next_uuid();
|
||||||
@ -768,6 +782,7 @@ pub async fn rotate(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
|
|||||||
#[stdlib {
|
#[stdlib {
|
||||||
name = "rotate",
|
name = "rotate",
|
||||||
feature_tree_operation = false,
|
feature_tree_operation = false,
|
||||||
|
keywords = true,
|
||||||
unlabeled_first = true,
|
unlabeled_first = true,
|
||||||
args = {
|
args = {
|
||||||
objects = {docs = "The solid, sketch, or set of solids or sketches to rotate."},
|
objects = {docs = "The solid, sketch, or set of solids or sketches to rotate."},
|
||||||
@ -792,6 +807,12 @@ async fn inner_rotate(
|
|||||||
exec_state: &mut ExecState,
|
exec_state: &mut ExecState,
|
||||||
args: Args,
|
args: Args,
|
||||||
) -> Result<SolidOrSketchOrImportedGeometry, KclError> {
|
) -> Result<SolidOrSketchOrImportedGeometry, KclError> {
|
||||||
|
// If we have a solid, flush the fillets and chamfers.
|
||||||
|
// Only transforms needs this, it is very odd, see: https://github.com/KittyCAD/modeling-app/issues/5880
|
||||||
|
if let SolidOrSketchOrImportedGeometry::SolidSet(solids) = &objects {
|
||||||
|
args.flush_batch_for_solids(exec_state, solids).await?;
|
||||||
|
}
|
||||||
|
|
||||||
let mut objects = objects.clone();
|
let mut objects = objects.clone();
|
||||||
for object_id in objects.ids(&args.ctx).await? {
|
for object_id in objects.ids(&args.ctx).await? {
|
||||||
let id = exec_state.next_uuid();
|
let id = exec_state.next_uuid();
|
||||||
|
@ -583,16 +583,14 @@ impl ArrayRangeExpression {
|
|||||||
let s1 = self.start_element.recast(options, 0, ExprContext::Other);
|
let s1 = self.start_element.recast(options, 0, ExprContext::Other);
|
||||||
let s2 = self.end_element.recast(options, 0, ExprContext::Other);
|
let s2 = self.end_element.recast(options, 0, ExprContext::Other);
|
||||||
|
|
||||||
let range_op = if self.end_inclusive { ".." } else { "..<" };
|
|
||||||
|
|
||||||
// Format these items into a one-line array. Put spaces around the `..` if either expression
|
// Format these items into a one-line array. Put spaces around the `..` if either expression
|
||||||
// is non-trivial. This is a bit arbitrary but people seem to like simple ranges to be formatted
|
// is non-trivial. This is a bit arbitrary but people seem to like simple ranges to be formatted
|
||||||
// tightly, but this is a misleading visual representation of the precedence if the range
|
// tightly, but this is a misleading visual representation of the precedence if the range
|
||||||
// components are compound expressions.
|
// components are compound expressions.
|
||||||
if expr_is_trivial(&self.start_element) && expr_is_trivial(&self.end_element) {
|
if expr_is_trivial(&self.start_element) && expr_is_trivial(&self.end_element) {
|
||||||
format!("[{s1}{range_op}{s2}]")
|
format!("[{s1}..{s2}]")
|
||||||
} else {
|
} else {
|
||||||
format!("[{s1} {range_op} {s2}]")
|
format!("[{s1} .. {s2}]")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assume a range expression fits on one line.
|
// Assume a range expression fits on one line.
|
||||||
@ -2799,12 +2797,4 @@ yo = 'bing'
|
|||||||
let recasted = ast.recast(&FormatOptions::new(), 0);
|
let recasted = ast.recast(&FormatOptions::new(), 0);
|
||||||
assert_eq!(recasted, code);
|
assert_eq!(recasted, code);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn array_range_end_exclusive() {
|
|
||||||
let code = "myArray = [0..<4]\n";
|
|
||||||
let ast = crate::parsing::top_level_parse(code).unwrap();
|
|
||||||
let recasted = ast.recast(&FormatOptions::new(), 0);
|
|
||||||
assert_eq!(recasted, code);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -34,9 +34,9 @@ pub(crate) type Universe = HashMap<String, DependencyInfo>;
|
|||||||
pub fn import_graph(progs: &Universe, ctx: &ExecutorContext) -> Result<Vec<Vec<String>>, KclError> {
|
pub fn import_graph(progs: &Universe, ctx: &ExecutorContext) -> Result<Vec<Vec<String>>, KclError> {
|
||||||
let mut graph = Graph::new();
|
let mut graph = Graph::new();
|
||||||
|
|
||||||
for (name, (_, _, path, repr)) in progs.iter() {
|
for (name, (_, _, _, repr)) in progs.iter() {
|
||||||
graph.extend(
|
graph.extend(
|
||||||
import_dependencies(path, repr, ctx)?
|
import_dependencies(repr, ctx)?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(dependency, _, _)| (name.clone(), dependency))
|
.map(|(dependency, _, _)| (name.clone(), dependency))
|
||||||
.collect::<Vec<_>>(),
|
.collect::<Vec<_>>(),
|
||||||
@ -120,26 +120,17 @@ fn topsort(all_modules: &[&str], graph: Graph) -> Result<Vec<Vec<String>>, KclEr
|
|||||||
|
|
||||||
type ImportDependencies = Vec<(String, AstNode<ImportStatement>, ModulePath)>;
|
type ImportDependencies = Vec<(String, AstNode<ImportStatement>, ModulePath)>;
|
||||||
|
|
||||||
pub(crate) fn import_dependencies(
|
pub(crate) fn import_dependencies(repr: &ModuleRepr, ctx: &ExecutorContext) -> Result<ImportDependencies, KclError> {
|
||||||
path: &ModulePath,
|
|
||||||
repr: &ModuleRepr,
|
|
||||||
ctx: &ExecutorContext,
|
|
||||||
) -> Result<ImportDependencies, KclError> {
|
|
||||||
let ModuleRepr::Kcl(prog, _) = repr else {
|
let ModuleRepr::Kcl(prog, _) = repr else {
|
||||||
// It has no dependencies, so return an empty list.
|
// It has no dependencies, so return an empty list.
|
||||||
return Ok(vec![]);
|
return Ok(vec![]);
|
||||||
};
|
};
|
||||||
|
|
||||||
let ret = Arc::new(Mutex::new(vec![]));
|
let ret = Arc::new(Mutex::new(vec![]));
|
||||||
fn walk(
|
fn walk(ret: Arc<Mutex<ImportDependencies>>, node: Node<'_>, ctx: &ExecutorContext) -> Result<(), KclError> {
|
||||||
ret: Arc<Mutex<ImportDependencies>>,
|
|
||||||
node: Node<'_>,
|
|
||||||
import_from: &ModulePath,
|
|
||||||
ctx: &ExecutorContext,
|
|
||||||
) -> Result<(), KclError> {
|
|
||||||
if let Node::ImportStatement(is) = node {
|
if let Node::ImportStatement(is) = node {
|
||||||
// We only care about Kcl and Foreign imports for now.
|
// We only care about Kcl and Foreign imports for now.
|
||||||
let resolved_path = ModulePath::from_import_path(&is.path, &ctx.settings.project_directory, import_from)?;
|
let resolved_path = ModulePath::from_import_path(&is.path, &ctx.settings.project_directory);
|
||||||
match &is.path {
|
match &is.path {
|
||||||
ImportPath::Kcl { filename } => {
|
ImportPath::Kcl { filename } => {
|
||||||
// We need to lock the mutex to push the dependency.
|
// We need to lock the mutex to push the dependency.
|
||||||
@ -169,13 +160,13 @@ pub(crate) fn import_dependencies(
|
|||||||
}
|
}
|
||||||
|
|
||||||
for child in node.children().iter() {
|
for child in node.children().iter() {
|
||||||
walk(ret.clone(), *child, import_from, ctx)?;
|
walk(ret.clone(), *child, ctx)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
walk(ret.clone(), prog.into(), path, ctx)?;
|
walk(ret.clone(), prog.into(), ctx)?;
|
||||||
|
|
||||||
let ret = ret.lock().map_err(|err| {
|
let ret = ret.lock().map_err(|err| {
|
||||||
KclError::Internal(KclErrorDetails::new(
|
KclError::Internal(KclErrorDetails::new(
|
||||||
@ -191,12 +182,11 @@ pub(crate) fn import_dependencies(
|
|||||||
/// only `repr`'s non-transitive imports.
|
/// only `repr`'s non-transitive imports.
|
||||||
pub(crate) async fn import_universe(
|
pub(crate) async fn import_universe(
|
||||||
ctx: &ExecutorContext,
|
ctx: &ExecutorContext,
|
||||||
path: &ModulePath,
|
|
||||||
repr: &ModuleRepr,
|
repr: &ModuleRepr,
|
||||||
out: &mut Universe,
|
out: &mut Universe,
|
||||||
exec_state: &mut ExecState,
|
exec_state: &mut ExecState,
|
||||||
) -> Result<UniverseMap, KclError> {
|
) -> Result<UniverseMap, KclError> {
|
||||||
let modules = import_dependencies(path, repr, ctx)?;
|
let modules = import_dependencies(repr, ctx)?;
|
||||||
let mut module_imports = HashMap::new();
|
let mut module_imports = HashMap::new();
|
||||||
for (filename, import_stmt, module_path) in modules {
|
for (filename, import_stmt, module_path) in modules {
|
||||||
match &module_path {
|
match &module_path {
|
||||||
@ -218,7 +208,7 @@ pub(crate) async fn import_universe(
|
|||||||
let source_range = SourceRange::from(&import_stmt);
|
let source_range = SourceRange::from(&import_stmt);
|
||||||
let attrs = &import_stmt.outer_attrs;
|
let attrs = &import_stmt.outer_attrs;
|
||||||
let module_id = ctx
|
let module_id = ctx
|
||||||
.open_module(&import_stmt.path, attrs, &module_path, exec_state, source_range)
|
.open_module(&import_stmt.path, attrs, exec_state, source_range)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let repr = {
|
let repr = {
|
||||||
@ -231,8 +221,8 @@ pub(crate) async fn import_universe(
|
|||||||
module_info.repr.clone()
|
module_info.repr.clone()
|
||||||
};
|
};
|
||||||
|
|
||||||
out.insert(filename, (import_stmt, module_id, module_path.clone(), repr.clone()));
|
out.insert(filename, (import_stmt, module_id, module_path, repr.clone()));
|
||||||
Box::pin(import_universe(ctx, &module_path, &repr, out, exec_state)).await?;
|
Box::pin(import_universe(ctx, &repr, out, exec_state)).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(module_imports)
|
Ok(module_imports)
|
||||||
|
@ -1,64 +0,0 @@
|
|||||||
/// Build a color from its red, green and blue components.
|
|
||||||
/// These must be between 0 and 255.
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// startSketchOn(-XZ)
|
|
||||||
/// |> circle(center = [0, 0], radius = 10)
|
|
||||||
/// |> extrude(length = 4)
|
|
||||||
/// |> appearance(color = appearance::hexString([50, 160, 160]))
|
|
||||||
/// ```
|
|
||||||
/// ```
|
|
||||||
/// sideLen = 30
|
|
||||||
/// n = 10
|
|
||||||
///
|
|
||||||
/// // The cubes become more green and less blue with each instance.
|
|
||||||
/// fn cube(i, center) {
|
|
||||||
/// g = 255 / n * i
|
|
||||||
/// b = 255 / n * (n - i)
|
|
||||||
/// return startSketchOn(XY)
|
|
||||||
/// |> polygon(radius = sideLen / 2, numSides = 4, center = [center, 0])
|
|
||||||
/// |> extrude(length = sideLen)
|
|
||||||
/// |> appearance(color = appearance::hexString([0, g, b]), metalness = 80, roughness = 20)
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// // Create n cubes, shifting each one over in a line.
|
|
||||||
/// map(
|
|
||||||
/// [0..n],
|
|
||||||
/// f = fn(@i) {
|
|
||||||
/// return cube(i, center = sideLen * i * 1.5)
|
|
||||||
/// },
|
|
||||||
/// )
|
|
||||||
/// ```
|
|
||||||
/// ```
|
|
||||||
/// sideLen = 30
|
|
||||||
/// n = 6
|
|
||||||
///
|
|
||||||
/// fn cube(offset, i, red) {
|
|
||||||
/// x = floor(i / n)
|
|
||||||
/// y = rem(i, divisor = n)
|
|
||||||
/// g = 255 / n * x
|
|
||||||
/// b = 255 / n * y
|
|
||||||
/// return startSketchOn(offsetPlane(XZ, offset))
|
|
||||||
/// |> circle(diameter = sideLen, center = [sideLen * x * 1.5, sideLen * y * 1.5])
|
|
||||||
/// |> extrude(length = sideLen)
|
|
||||||
/// |> appearance(color = appearance::hexString([red, g, b]), metalness = 80, roughness = 40)
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// fn grid(offset, red) {
|
|
||||||
/// return map(
|
|
||||||
/// [0 ..< n * n],
|
|
||||||
/// f = fn(@i) {
|
|
||||||
/// return cube(offset, i, red)
|
|
||||||
/// },
|
|
||||||
/// )
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// grid(offset = 0, red = 0)
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
@(impl = std_rust)
|
|
||||||
export fn hexString(
|
|
||||||
/// The red, blue and green components of the color.
|
|
||||||
/// Must be between 0 and 255.
|
|
||||||
@rgb: [number(_); 3],
|
|
||||||
): string {}
|
|
@ -22,7 +22,6 @@ export import * from "std::solid"
|
|||||||
export import * from "std::transform"
|
export import * from "std::transform"
|
||||||
export import "std::turns"
|
export import "std::turns"
|
||||||
export import "std::sweep"
|
export import "std::sweep"
|
||||||
export import "std::appearance"
|
|
||||||
|
|
||||||
/// An abstract 3d plane aligned with the X and Y axes. Its normal is the positive Z axis.
|
/// An abstract 3d plane aligned with the X and Y axes. Its normal is the positive Z axis.
|
||||||
export XY = {
|
export XY = {
|
||||||
|
@ -33,12 +33,10 @@ export fn circle(
|
|||||||
/// Sketch to extend, or plane or surface to sketch on.
|
/// Sketch to extend, or plane or surface to sketch on.
|
||||||
@sketch_or_surface: Sketch | Plane | Face,
|
@sketch_or_surface: Sketch | Plane | Face,
|
||||||
/// The center of the circle.
|
/// The center of the circle.
|
||||||
@(snippetArray = ["0", "0"])
|
|
||||||
center: Point2d,
|
center: Point2d,
|
||||||
/// The radius of the circle. Incompatible with `diameter`.
|
/// The radius of the circle. Incompatible with `diameter`.
|
||||||
radius?: number(Length),
|
radius?: number(Length),
|
||||||
/// The diameter of the circle. Incompatible with `radius`.
|
/// The diameter of the circle. Incompatible with `radius`.
|
||||||
@(include_in_snippet = true)
|
|
||||||
diameter?: number(Length),
|
diameter?: number(Length),
|
||||||
/// Create a new tag which refers to this circle.
|
/// Create a new tag which refers to this circle.
|
||||||
tag?: tag,
|
tag?: tag,
|
||||||
|
@ -4,8 +4,7 @@ description: Error from executing argument_error.kcl
|
|||||||
---
|
---
|
||||||
KCL Semantic error
|
KCL Semantic error
|
||||||
|
|
||||||
× semantic: f requires a value with type `fn(any): any`, but found array of
|
× semantic: f requires a value with type `fn(any): any`, but found [any; 2]
|
||||||
│ number(default units), number(default units) with 2 values
|
|
||||||
╭─[5:1]
|
╭─[5:1]
|
||||||
4 │
|
4 │
|
||||||
5 │ map(f, f = [0, 1])
|
5 │ map(f, f = [0, 1])
|
||||||
@ -16,7 +15,7 @@ KCL Semantic error
|
|||||||
╰─▶ KCL Semantic error
|
╰─▶ KCL Semantic error
|
||||||
|
|
||||||
× semantic: f requires a value with type `fn(any): any`, but found
|
× semantic: f requires a value with type `fn(any): any`, but found
|
||||||
│ array of number(default units), number(default units) with 2 values
|
│ [any; 2]
|
||||||
╭─[5:12]
|
╭─[5:12]
|
||||||
4 │
|
4 │
|
||||||
5 │ map(f, f = [0, 1])
|
5 │ map(f, f = [0, 1])
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
source: kcl-lib/src/simulation_tests.rs
|
source: kcl/src/simulation_tests.rs
|
||||||
description: Error from executing array_elem_pop_fail.kcl
|
description: Error from executing array_elem_pop_fail.kcl
|
||||||
---
|
---
|
||||||
KCL UndefinedValue error
|
KCL UndefinedValue error
|
||||||
|
@ -224,16 +224,30 @@ description: Artifact commands basic_fillet_cube_close_opposite.kcl
|
|||||||
"object_id": "[uuid]",
|
"object_id": "[uuid]",
|
||||||
"edge_id": null,
|
"edge_id": null,
|
||||||
"edge_ids": [
|
"edge_ids": [
|
||||||
"[uuid]",
|
|
||||||
"[uuid]"
|
"[uuid]"
|
||||||
],
|
],
|
||||||
"radius": 2.0,
|
"radius": 2.0,
|
||||||
"tolerance": 0.0000001,
|
"tolerance": 0.0000001,
|
||||||
"cut_type": "fillet",
|
"cut_type": "fillet",
|
||||||
"strategy": "automatic",
|
"strategy": "automatic",
|
||||||
"extra_face_ids": [
|
"extra_face_ids": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [],
|
||||||
|
"command": {
|
||||||
|
"type": "solid3d_fillet_edge",
|
||||||
|
"object_id": "[uuid]",
|
||||||
|
"edge_id": null,
|
||||||
|
"edge_ids": [
|
||||||
"[uuid]"
|
"[uuid]"
|
||||||
]
|
],
|
||||||
|
"radius": 2.0,
|
||||||
|
"tolerance": 0.0000001,
|
||||||
|
"cut_type": "fillet",
|
||||||
|
"strategy": "automatic",
|
||||||
|
"extra_face_ids": []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -39,6 +39,8 @@ flowchart LR
|
|||||||
22["SweepEdge Adjacent"]
|
22["SweepEdge Adjacent"]
|
||||||
23["EdgeCut Fillet<br>[221, 281, 0]"]
|
23["EdgeCut Fillet<br>[221, 281, 0]"]
|
||||||
%% [ProgramBodyItem { index: 0 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 7 }]
|
%% [ProgramBodyItem { index: 0 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 7 }]
|
||||||
|
24["EdgeCut Fillet<br>[221, 281, 0]"]
|
||||||
|
%% [ProgramBodyItem { index: 0 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 7 }]
|
||||||
1 --- 2
|
1 --- 2
|
||||||
2 --- 3
|
2 --- 3
|
||||||
2 --- 4
|
2 --- 4
|
||||||
@ -62,7 +64,7 @@ flowchart LR
|
|||||||
6 x--> 13
|
6 x--> 13
|
||||||
6 --- 15
|
6 --- 15
|
||||||
6 --- 19
|
6 --- 19
|
||||||
6 --- 23
|
6 --- 24
|
||||||
8 --- 9
|
8 --- 9
|
||||||
8 --- 10
|
8 --- 10
|
||||||
8 --- 11
|
8 --- 11
|
||||||
@ -93,4 +95,5 @@ flowchart LR
|
|||||||
16 <--x 14
|
16 <--x 14
|
||||||
17 <--x 14
|
17 <--x 14
|
||||||
18 <--x 14
|
18 <--x 14
|
||||||
|
15 <--x 23
|
||||||
```
|
```
|
||||||
|
@ -224,16 +224,30 @@ description: Artifact commands basic_fillet_cube_end.kcl
|
|||||||
"object_id": "[uuid]",
|
"object_id": "[uuid]",
|
||||||
"edge_id": null,
|
"edge_id": null,
|
||||||
"edge_ids": [
|
"edge_ids": [
|
||||||
"[uuid]",
|
|
||||||
"[uuid]"
|
"[uuid]"
|
||||||
],
|
],
|
||||||
"radius": 2.0,
|
"radius": 2.0,
|
||||||
"tolerance": 0.0000001,
|
"tolerance": 0.0000001,
|
||||||
"cut_type": "fillet",
|
"cut_type": "fillet",
|
||||||
"strategy": "automatic",
|
"strategy": "automatic",
|
||||||
"extra_face_ids": [
|
"extra_face_ids": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [],
|
||||||
|
"command": {
|
||||||
|
"type": "solid3d_fillet_edge",
|
||||||
|
"object_id": "[uuid]",
|
||||||
|
"edge_id": null,
|
||||||
|
"edge_ids": [
|
||||||
"[uuid]"
|
"[uuid]"
|
||||||
]
|
],
|
||||||
|
"radius": 2.0,
|
||||||
|
"tolerance": 0.0000001,
|
||||||
|
"cut_type": "fillet",
|
||||||
|
"strategy": "automatic",
|
||||||
|
"extra_face_ids": []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -39,6 +39,8 @@ flowchart LR
|
|||||||
22["SweepEdge Adjacent"]
|
22["SweepEdge Adjacent"]
|
||||||
23["EdgeCut Fillet<br>[209, 267, 0]"]
|
23["EdgeCut Fillet<br>[209, 267, 0]"]
|
||||||
%% [ProgramBodyItem { index: 0 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 7 }]
|
%% [ProgramBodyItem { index: 0 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 7 }]
|
||||||
|
24["EdgeCut Fillet<br>[209, 267, 0]"]
|
||||||
|
%% [ProgramBodyItem { index: 0 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 7 }]
|
||||||
1 --- 2
|
1 --- 2
|
||||||
2 --- 3
|
2 --- 3
|
||||||
2 --- 4
|
2 --- 4
|
||||||
@ -50,7 +52,7 @@ flowchart LR
|
|||||||
3 x--> 13
|
3 x--> 13
|
||||||
3 --- 18
|
3 --- 18
|
||||||
3 --- 22
|
3 --- 22
|
||||||
3 --- 23
|
3 --- 24
|
||||||
4 --- 10
|
4 --- 10
|
||||||
4 x--> 13
|
4 x--> 13
|
||||||
4 --- 17
|
4 --- 17
|
||||||
@ -93,4 +95,5 @@ flowchart LR
|
|||||||
16 <--x 14
|
16 <--x 14
|
||||||
17 <--x 14
|
17 <--x 14
|
||||||
18 <--x 14
|
18 <--x 14
|
||||||
|
18 <--x 23
|
||||||
```
|
```
|
||||||
|
@ -224,16 +224,30 @@ description: Artifact commands basic_fillet_cube_start.kcl
|
|||||||
"object_id": "[uuid]",
|
"object_id": "[uuid]",
|
||||||
"edge_id": null,
|
"edge_id": null,
|
||||||
"edge_ids": [
|
"edge_ids": [
|
||||||
"[uuid]",
|
|
||||||
"[uuid]"
|
"[uuid]"
|
||||||
],
|
],
|
||||||
"radius": 2.0,
|
"radius": 2.0,
|
||||||
"tolerance": 0.0000001,
|
"tolerance": 0.0000001,
|
||||||
"cut_type": "fillet",
|
"cut_type": "fillet",
|
||||||
"strategy": "automatic",
|
"strategy": "automatic",
|
||||||
"extra_face_ids": [
|
"extra_face_ids": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [],
|
||||||
|
"command": {
|
||||||
|
"type": "solid3d_fillet_edge",
|
||||||
|
"object_id": "[uuid]",
|
||||||
|
"edge_id": null,
|
||||||
|
"edge_ids": [
|
||||||
"[uuid]"
|
"[uuid]"
|
||||||
]
|
],
|
||||||
|
"radius": 2.0,
|
||||||
|
"tolerance": 0.0000001,
|
||||||
|
"cut_type": "fillet",
|
||||||
|
"strategy": "automatic",
|
||||||
|
"extra_face_ids": []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user