Compare commits
35 Commits
kcl-0.2.27
...
franknoiro
Author | SHA1 | Date | |
---|---|---|---|
f70847c407 | |||
441d957228 | |||
9e57034873 | |||
eb96d6539c | |||
513c76ecc8 | |||
51d9449280 | |||
6366bc4766 | |||
7a21918223 | |||
8072f1db63 | |||
18e1855fa9 | |||
7be53c7d4a | |||
2bf20988ef | |||
1495cc6d18 | |||
f876e6ca3c | |||
60a0c811ab | |||
cab0c1e6a1 | |||
417d720b22 | |||
77293952c0 | |||
ea3d604b73 | |||
023a659491 | |||
dd3a2b14f9 | |||
424b409cc1 | |||
82a58e69c2 | |||
776b420031 | |||
1087d4223b | |||
089d6df889 | |||
9510849a3a | |||
5b208356b4 | |||
84209e764e | |||
2b85e7abd6 | |||
9155a5efc8 | |||
2ccc27112a | |||
ee160b67f4 | |||
5117b6f5d6 | |||
bc8a7a364d |
22
.github/workflows/build-apps.yml
vendored
@ -362,6 +362,17 @@ jobs:
|
|||||||
- name: List artifacts
|
- name: List artifacts
|
||||||
run: "ls -R out"
|
run: "ls -R out"
|
||||||
|
|
||||||
|
- name: Set more complete nightly release notes
|
||||||
|
if: ${{ env.IS_NIGHTLY == 'true' }}
|
||||||
|
run: |
|
||||||
|
# Note: prefered going this way instead of a full clone in the checkout step,
|
||||||
|
# see https://github.com/actions/checkout/issues/1471
|
||||||
|
git fetch --prune --unshallow --tags
|
||||||
|
export TAG="nightly-${VERSION}"
|
||||||
|
export PREVIOUS_TAG=$(git describe --tags --match="nightly-v[0-9]*" --abbrev=0)
|
||||||
|
export NOTES=$(./scripts/get-nightly-changelog.sh)
|
||||||
|
yarn files:set-notes
|
||||||
|
|
||||||
- name: Authenticate to Google Cloud
|
- name: Authenticate to Google Cloud
|
||||||
if: ${{ env.IS_NIGHTLY == 'true' }}
|
if: ${{ env.IS_NIGHTLY == 'true' }}
|
||||||
uses: 'google-github-actions/auth@v2.1.7'
|
uses: 'google-github-actions/auth@v2.1.7'
|
||||||
@ -382,3 +393,14 @@ jobs:
|
|||||||
glob: '*'
|
glob: '*'
|
||||||
parent: false
|
parent: false
|
||||||
destination: 'dl.kittycad.io/releases/modeling-app/nightly'
|
destination: 'dl.kittycad.io/releases/modeling-app/nightly'
|
||||||
|
|
||||||
|
- name: Tag nightly commit
|
||||||
|
if: ${{ env.IS_NIGHTLY == 'true' }}
|
||||||
|
uses: actions/github-script@v7
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const { VERSION } = process.env
|
||||||
|
const { owner, repo } = context.repo
|
||||||
|
const { sha } = context
|
||||||
|
const ref = `refs/tags/nightly-${VERSION}`
|
||||||
|
github.rest.git.createRef({ owner, repo, sha, ref })
|
||||||
|
@ -99,7 +99,7 @@ yarn tron:start
|
|||||||
|
|
||||||
This will start the application and hot-reload on changes.
|
This will start the application and hot-reload on changes.
|
||||||
|
|
||||||
Devtools can be opened with the usual Cmd/Ctrl-Shift-I.
|
Devtools can be opened with the usual Cmd-Opt-I (Mac) or Ctrl-Shift-I (Linux and Windows).
|
||||||
|
|
||||||
To build, run `yarn tron:package`.
|
To build, run `yarn tron:package`.
|
||||||
|
|
||||||
|
@ -518,7 +518,10 @@ test.describe('Editor tests', () => {
|
|||||||
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||||
})
|
})
|
||||||
|
|
||||||
test('error with 2 source ranges gets 2 diagnostics', async ({ page }) => {
|
// TODO currently multiple source ranges are not supported
|
||||||
|
test.skip('error with 2 source ranges gets 2 diagnostics', async ({
|
||||||
|
page,
|
||||||
|
}) => {
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.addInitScript(async () => {
|
await page.addInitScript(async () => {
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
|
@ -6,6 +6,7 @@ export class ToolbarFixture {
|
|||||||
public page: Page
|
public page: Page
|
||||||
|
|
||||||
extrudeButton!: Locator
|
extrudeButton!: Locator
|
||||||
|
loftButton!: Locator
|
||||||
offsetPlaneButton!: Locator
|
offsetPlaneButton!: Locator
|
||||||
startSketchBtn!: Locator
|
startSketchBtn!: Locator
|
||||||
lineBtn!: Locator
|
lineBtn!: Locator
|
||||||
@ -26,6 +27,7 @@ export class ToolbarFixture {
|
|||||||
reConstruct = (page: Page) => {
|
reConstruct = (page: Page) => {
|
||||||
this.page = page
|
this.page = page
|
||||||
this.extrudeButton = page.getByTestId('extrude')
|
this.extrudeButton = page.getByTestId('extrude')
|
||||||
|
this.loftButton = page.getByTestId('loft')
|
||||||
this.offsetPlaneButton = page.getByTestId('plane-offset')
|
this.offsetPlaneButton = page.getByTestId('plane-offset')
|
||||||
this.startSketchBtn = page.getByTestId('sketch')
|
this.startSketchBtn = page.getByTestId('sketch')
|
||||||
this.lineBtn = page.getByTestId('line')
|
this.lineBtn = page.getByTestId('line')
|
||||||
|
@ -677,3 +677,94 @@ test(`Offset plane point-and-click`, async ({
|
|||||||
await scene.expectPixelColor([74, 74, 74], testPoint, 15)
|
await scene.expectPixelColor([74, 74, 74], testPoint, 15)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const loftPointAndClickCases = [
|
||||||
|
{ shouldPreselect: true },
|
||||||
|
{ shouldPreselect: false },
|
||||||
|
]
|
||||||
|
loftPointAndClickCases.forEach(({ shouldPreselect }) => {
|
||||||
|
test(`Loft point-and-click (preselected sketches: ${shouldPreselect})`, async ({
|
||||||
|
app,
|
||||||
|
page,
|
||||||
|
scene,
|
||||||
|
editor,
|
||||||
|
toolbar,
|
||||||
|
cmdBar,
|
||||||
|
}) => {
|
||||||
|
const initialCode = `sketch001 = startSketchOn('XZ')
|
||||||
|
|> circle({ center = [0, 0], radius = 30 }, %)
|
||||||
|
plane001 = offsetPlane('XZ', 50)
|
||||||
|
sketch002 = startSketchOn(plane001)
|
||||||
|
|> circle({ center = [0, 0], radius = 20 }, %)
|
||||||
|
`
|
||||||
|
await app.initialise(initialCode)
|
||||||
|
|
||||||
|
// One dumb hardcoded screen pixel value
|
||||||
|
const testPoint = { x: 575, y: 200 }
|
||||||
|
const [clickOnSketch1] = scene.makeMouseHelpers(testPoint.x, testPoint.y)
|
||||||
|
const [clickOnSketch2] = scene.makeMouseHelpers(
|
||||||
|
testPoint.x,
|
||||||
|
testPoint.y + 80
|
||||||
|
)
|
||||||
|
const loftDeclaration = 'loft001 = loft([sketch001, sketch002])'
|
||||||
|
|
||||||
|
await test.step(`Look for the white of the sketch001 shape`, async () => {
|
||||||
|
await scene.expectPixelColor([254, 254, 254], testPoint, 15)
|
||||||
|
})
|
||||||
|
|
||||||
|
async function selectSketches() {
|
||||||
|
await clickOnSketch1()
|
||||||
|
await page.keyboard.down('Shift')
|
||||||
|
await clickOnSketch2()
|
||||||
|
await app.page.waitForTimeout(500)
|
||||||
|
await page.keyboard.up('Shift')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!shouldPreselect) {
|
||||||
|
await test.step(`Go through the command bar flow without preselected sketches`, async () => {
|
||||||
|
await toolbar.loftButton.click()
|
||||||
|
await cmdBar.expectState({
|
||||||
|
stage: 'arguments',
|
||||||
|
currentArgKey: 'selection',
|
||||||
|
currentArgValue: '',
|
||||||
|
headerArguments: { Selection: '' },
|
||||||
|
highlightedHeaderArg: 'selection',
|
||||||
|
commandName: 'Loft',
|
||||||
|
})
|
||||||
|
await selectSketches()
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await cmdBar.expectState({
|
||||||
|
stage: 'review',
|
||||||
|
headerArguments: { Selection: '2 faces' },
|
||||||
|
commandName: 'Loft',
|
||||||
|
})
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
await test.step(`Preselect the two sketches`, async () => {
|
||||||
|
await selectSketches()
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step(`Go through the command bar flow with preselected sketches`, async () => {
|
||||||
|
await toolbar.loftButton.click()
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await cmdBar.expectState({
|
||||||
|
stage: 'review',
|
||||||
|
headerArguments: { Selection: '2 faces' },
|
||||||
|
commandName: 'Loft',
|
||||||
|
})
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
await test.step(`Confirm code is added to the editor, scene has changed`, async () => {
|
||||||
|
await editor.expectEditor.toContain(loftDeclaration)
|
||||||
|
await editor.expectState({
|
||||||
|
diagnostics: [],
|
||||||
|
activeLines: [loftDeclaration],
|
||||||
|
highlightedCode: '',
|
||||||
|
})
|
||||||
|
await scene.expectPixelColor([89, 89, 89], testPoint, 15)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
@ -550,7 +550,7 @@ sketch001 = startSketchAt([-0, -0])
|
|||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
|
|
||||||
// Constants and locators
|
// Constants and locators
|
||||||
const planeColor: [number, number, number] = [170, 220, 170]
|
const planeColor: [number, number, number] = [161, 220, 155]
|
||||||
const bgColor: [number, number, number] = [27, 27, 27]
|
const bgColor: [number, number, number] = [27, 27, 27]
|
||||||
const middlePixelIsColor = async (color: [number, number, number]) => {
|
const middlePixelIsColor = async (color: [number, number, number]) => {
|
||||||
return u.getGreatestPixDiff({ x: 600, y: 250 }, color)
|
return u.getGreatestPixDiff({ x: 600, y: 250 }, color)
|
||||||
|
@ -7,6 +7,8 @@ try {
|
|||||||
.split('\n')
|
.split('\n')
|
||||||
.filter((line) => line && line.length > 1)
|
.filter((line) => line && line.length > 1)
|
||||||
.forEach((line) => {
|
.forEach((line) => {
|
||||||
|
// Allow line comments.
|
||||||
|
if (line.trimStart().startsWith('#')) return
|
||||||
const [key, value] = line.split('=')
|
const [key, value] = line.split('=')
|
||||||
// prefer env vars over secrets file
|
// prefer env vars over secrets file
|
||||||
secrets[key] = process.env[key] || (value as any).replaceAll('"', '')
|
secrets[key] = process.env[key] || (value as any).replaceAll('"', '')
|
||||||
|
@ -943,6 +943,110 @@ sketch002 = startSketchOn(extrude001, 'END')
|
|||||||
`.replace(/\s/g, '')
|
`.replace(/\s/g, '')
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
/* TODO: once we fix bug turn on.
|
||||||
|
test('empty-scene default-planes act as expected when spaces in file', async ({
|
||||||
|
page,
|
||||||
|
browserName,
|
||||||
|
}) => {
|
||||||
|
|
||||||
|
const u = await getUtils(page)
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
await u.openDebugPanel()
|
||||||
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
|
await u.closeDebugPanel()
|
||||||
|
|
||||||
|
const XYPlanePoint = { x: 774, y: 116 } as const
|
||||||
|
const unHoveredColor: [number, number, number] = [47, 47, 93]
|
||||||
|
expect(
|
||||||
|
await u.getGreatestPixDiff(XYPlanePoint, unHoveredColor)
|
||||||
|
).toBeLessThan(8)
|
||||||
|
|
||||||
|
await page.mouse.move(XYPlanePoint.x, XYPlanePoint.y)
|
||||||
|
await page.waitForTimeout(200)
|
||||||
|
|
||||||
|
// color should not change for having been hovered
|
||||||
|
expect(
|
||||||
|
await u.getGreatestPixDiff(XYPlanePoint, unHoveredColor)
|
||||||
|
).toBeLessThan(8)
|
||||||
|
|
||||||
|
await u.openAndClearDebugPanel()
|
||||||
|
|
||||||
|
// Fill with spaces
|
||||||
|
await u.codeLocator.fill(`
|
||||||
|
`)
|
||||||
|
|
||||||
|
await u.openDebugPanel()
|
||||||
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
|
await u.closeDebugPanel()
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await u.getGreatestPixDiff(XYPlanePoint, unHoveredColor)
|
||||||
|
).toBeLessThan(8)
|
||||||
|
|
||||||
|
await page.mouse.move(XYPlanePoint.x, XYPlanePoint.y)
|
||||||
|
await page.waitForTimeout(200)
|
||||||
|
|
||||||
|
// color should not change for having been hovered
|
||||||
|
expect(
|
||||||
|
await u.getGreatestPixDiff(XYPlanePoint, unHoveredColor)
|
||||||
|
).toBeLessThan(8)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('empty-scene default-planes act as expected when only code comments in file', async ({
|
||||||
|
page,
|
||||||
|
browserName,
|
||||||
|
}) => {
|
||||||
|
|
||||||
|
const u = await getUtils(page)
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
await u.openDebugPanel()
|
||||||
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
|
await u.closeDebugPanel()
|
||||||
|
|
||||||
|
const XYPlanePoint = { x: 774, y: 116 } as const
|
||||||
|
const unHoveredColor: [number, number, number] = [47, 47, 93]
|
||||||
|
expect(
|
||||||
|
await u.getGreatestPixDiff(XYPlanePoint, unHoveredColor)
|
||||||
|
).toBeLessThan(8)
|
||||||
|
|
||||||
|
await page.mouse.move(XYPlanePoint.x, XYPlanePoint.y)
|
||||||
|
await page.waitForTimeout(200)
|
||||||
|
|
||||||
|
// color should not change for having been hovered
|
||||||
|
expect(
|
||||||
|
await u.getGreatestPixDiff(XYPlanePoint, unHoveredColor)
|
||||||
|
).toBeLessThan(8)
|
||||||
|
|
||||||
|
await u.openAndClearDebugPanel()
|
||||||
|
|
||||||
|
// Fill with spaces
|
||||||
|
await u.codeLocator.fill(`// this is a code comments ya nerds
|
||||||
|
`)
|
||||||
|
|
||||||
|
await u.openDebugPanel()
|
||||||
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
|
await u.closeDebugPanel()
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await u.getGreatestPixDiff(XYPlanePoint, unHoveredColor)
|
||||||
|
).toBeLessThan(8)
|
||||||
|
|
||||||
|
await page.mouse.move(XYPlanePoint.x, XYPlanePoint.y)
|
||||||
|
await page.waitForTimeout(200)
|
||||||
|
|
||||||
|
// color should not change for having been hovered
|
||||||
|
expect(
|
||||||
|
await u.getGreatestPixDiff(XYPlanePoint, unHoveredColor)
|
||||||
|
).toBeLessThan(8)
|
||||||
|
})*/
|
||||||
|
|
||||||
test('empty-scene default-planes act as expected', async ({
|
test('empty-scene default-planes act as expected', async ({
|
||||||
page,
|
page,
|
||||||
browserName,
|
browserName,
|
||||||
|
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 33 KiB |
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 50 KiB |
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 51 KiB |
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 40 KiB |
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 40 KiB |
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 39 KiB |
@ -23,7 +23,7 @@ test.describe('Test toggling perspective', () => {
|
|||||||
y: screenHeight * 0.4,
|
y: screenHeight * 0.4,
|
||||||
}
|
}
|
||||||
const backgroundColor: [number, number, number] = [29, 29, 29]
|
const backgroundColor: [number, number, number] = [29, 29, 29]
|
||||||
const xzPlaneColor: [number, number, number] = [50, 50, 99]
|
const xzPlaneColor: [number, number, number] = [82, 55, 96]
|
||||||
const locationToHaveColor = async (color: [number, number, number]) => {
|
const locationToHaveColor = async (color: [number, number, number]) => {
|
||||||
return u.getGreatestPixDiff(checkedScreenLocation, color)
|
return u.getGreatestPixDiff(checkedScreenLocation, color)
|
||||||
}
|
}
|
||||||
|
@ -212,7 +212,7 @@
|
|||||||
"vite-tsconfig-paths": "^4.3.2",
|
"vite-tsconfig-paths": "^4.3.2",
|
||||||
"vitest": "^1.6.0",
|
"vitest": "^1.6.0",
|
||||||
"vitest-webgl-canvas-mock": "^1.1.0",
|
"vitest-webgl-canvas-mock": "^1.1.0",
|
||||||
"wasm-pack": "^0.13.0",
|
"wasm-pack": "^0.13.1",
|
||||||
"ws": "^8.17.0",
|
"ws": "^8.17.0",
|
||||||
"yarn": "^1.22.22"
|
"yarn": "^1.22.22"
|
||||||
}
|
}
|
||||||
|
@ -149,11 +149,6 @@
|
|||||||
"title": "Tire",
|
"title": "Tire",
|
||||||
"description": "A tire is a critical component of a vehicle that provides the necessary traction and grip between the car and the road. It supports the vehicle's weight and absorbs shocks from road irregularities."
|
"description": "A tire is a critical component of a vehicle that provides the necessary traction and grip between the car and the road. It supports the vehicle's weight and absorbs shocks from road irregularities."
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"file": "wall-e.kcl",
|
|
||||||
"title": "WALL-E",
|
|
||||||
"description": "An inquisitive trash compacting robot imagined by Disney Pixar Animation Studios"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"file": "washer.kcl",
|
"file": "washer.kcl",
|
||||||
"title": "Washer",
|
"title": "Washer",
|
||||||
|
5
scripts/get-nightly-changelog.sh
Executable file
@ -0,0 +1,5 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
echo "## What's Changed"
|
||||||
|
git log ${PREVIOUS_TAG}..HEAD --oneline --pretty=format:%s | grep -v Bump | awk '{print "* "toupper(substr($0,0,1))substr($0,2)}'
|
||||||
|
echo ""
|
||||||
|
echo "**Full Changelog**: https://github.com/KittyCAD/modeling-app/compare/${PREVIOUS_TAG}...${TAG}"
|
39
src/App.tsx
@ -3,7 +3,7 @@ import { useHotKeyListener } from './hooks/useHotKeyListener'
|
|||||||
import { Stream } from './components/Stream'
|
import { Stream } from './components/Stream'
|
||||||
import { AppHeader } from './components/AppHeader'
|
import { AppHeader } from './components/AppHeader'
|
||||||
import { useHotkeys } from 'react-hotkeys-hook'
|
import { useHotkeys } from 'react-hotkeys-hook'
|
||||||
import { useLoaderData, useNavigate } from 'react-router-dom'
|
import { useLoaderData, useLocation, useNavigate } from 'react-router-dom'
|
||||||
import { type IndexLoaderData } from 'lib/types'
|
import { type IndexLoaderData } from 'lib/types'
|
||||||
import { PATHS } from 'lib/paths'
|
import { PATHS } from 'lib/paths'
|
||||||
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
||||||
@ -22,7 +22,14 @@ import Gizmo from 'components/Gizmo'
|
|||||||
import { CoreDumpManager } from 'lib/coredump'
|
import { CoreDumpManager } from 'lib/coredump'
|
||||||
import { UnitsMenu } from 'components/UnitsMenu'
|
import { UnitsMenu } from 'components/UnitsMenu'
|
||||||
import { CameraProjectionToggle } from 'components/CameraProjectionToggle'
|
import { CameraProjectionToggle } from 'components/CameraProjectionToggle'
|
||||||
|
import { homeDefaultStatusBarItems } from 'components/statusBar/homeDefaultStatusBarItems'
|
||||||
|
import { StatusBar } from 'components/StatusBar'
|
||||||
|
import { useModelStateStatus } from 'components/ModelStateIndicator'
|
||||||
|
import { useNetworkHealthStatus } from 'components/NetworkHealthIndicator'
|
||||||
|
import { useModelingContext } from 'hooks/useModelingContext'
|
||||||
|
import { xStateValueToString } from 'lib/xStateValueToString'
|
||||||
import { maybeWriteToDisk } from 'lib/telemetry'
|
import { maybeWriteToDisk } from 'lib/telemetry'
|
||||||
|
import { useNetworkMachineStatus } from 'components/NetworkMachineIndicator'
|
||||||
maybeWriteToDisk()
|
maybeWriteToDisk()
|
||||||
.then(() => {})
|
.then(() => {})
|
||||||
.catch(() => {})
|
.catch(() => {})
|
||||||
@ -31,11 +38,10 @@ export function App() {
|
|||||||
const { project, file } = useLoaderData() as IndexLoaderData
|
const { project, file } = useLoaderData() as IndexLoaderData
|
||||||
useRefreshSettings(PATHS.FILE + 'SETTINGS')
|
useRefreshSettings(PATHS.FILE + 'SETTINGS')
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
|
const location = useLocation()
|
||||||
const filePath = useAbsoluteFilePath()
|
const filePath = useAbsoluteFilePath()
|
||||||
const { onProjectOpen } = useLspContext()
|
const { onProjectOpen } = useLspContext()
|
||||||
// We need the ref for the outermost div so we can screenshot the app for
|
const { state: modelingState, streamRef } = useModelingContext()
|
||||||
// the coredump.
|
|
||||||
const ref = useRef<HTMLDivElement>(null)
|
|
||||||
|
|
||||||
const projectName = project?.name || null
|
const projectName = project?.name || null
|
||||||
const projectPath = project?.path || null
|
const projectPath = project?.path || null
|
||||||
@ -77,7 +83,8 @@ export function App() {
|
|||||||
useEngineConnectionSubscriptions()
|
useEngineConnectionSubscriptions()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative h-full flex flex-col" ref={ref}>
|
<div className="h-screen flex flex-col overflow-hidden select-none">
|
||||||
|
<div className="relative flex flex-1 flex-col" ref={streamRef}>
|
||||||
<AppHeader
|
<AppHeader
|
||||||
className={'transition-opacity transition-duration-75 ' + paneOpacity}
|
className={'transition-opacity transition-duration-75 ' + paneOpacity}
|
||||||
project={{ project, file }}
|
project={{ project, file }}
|
||||||
@ -93,5 +100,27 @@ export function App() {
|
|||||||
<CameraProjectionToggle />
|
<CameraProjectionToggle />
|
||||||
</LowerRightControls>
|
</LowerRightControls>
|
||||||
</div>
|
</div>
|
||||||
|
<StatusBar
|
||||||
|
globalItems={[
|
||||||
|
useNetworkHealthStatus(),
|
||||||
|
useNetworkMachineStatus(),
|
||||||
|
...homeDefaultStatusBarItems({ coreDumpManager, location }),
|
||||||
|
]}
|
||||||
|
localItems={[
|
||||||
|
{
|
||||||
|
id: 'modeling-state',
|
||||||
|
element: 'text',
|
||||||
|
label:
|
||||||
|
modelingState.value instanceof Object
|
||||||
|
? xStateValueToString(modelingState.value) ?? ''
|
||||||
|
: modelingState.value,
|
||||||
|
toolTip: {
|
||||||
|
children: 'The current state of the modeler',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
useModelStateStatus(),
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -155,7 +155,6 @@ export class CameraControls {
|
|||||||
this.camera.zoom = camProps.zoom || 1
|
this.camera.zoom = camProps.zoom || 1
|
||||||
}
|
}
|
||||||
this.camera.updateProjectionMatrix()
|
this.camera.updateProjectionMatrix()
|
||||||
console.log('doing this thing', camProps)
|
|
||||||
this.update(true)
|
this.update(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -273,14 +272,26 @@ export class CameraControls {
|
|||||||
camSettings.center.y,
|
camSettings.center.y,
|
||||||
camSettings.center.z
|
camSettings.center.z
|
||||||
)
|
)
|
||||||
const quat = new Quaternion(
|
const orientation = new Quaternion(
|
||||||
camSettings.orientation.x,
|
camSettings.orientation.x,
|
||||||
camSettings.orientation.y,
|
camSettings.orientation.y,
|
||||||
camSettings.orientation.z,
|
camSettings.orientation.z,
|
||||||
camSettings.orientation.w
|
camSettings.orientation.w
|
||||||
).invert()
|
).invert()
|
||||||
|
|
||||||
this.camera.up.copy(new Vector3(0, 1, 0).applyQuaternion(quat))
|
const newUp = new Vector3(
|
||||||
|
camSettings.up.x,
|
||||||
|
camSettings.up.y,
|
||||||
|
camSettings.up.z
|
||||||
|
)
|
||||||
|
this.camera.quaternion.set(
|
||||||
|
orientation.x,
|
||||||
|
orientation.y,
|
||||||
|
orientation.z,
|
||||||
|
orientation.w
|
||||||
|
)
|
||||||
|
this.camera.up.copy(newUp)
|
||||||
|
this.camera.updateProjectionMatrix()
|
||||||
if (this.camera instanceof PerspectiveCamera && camSettings.ortho) {
|
if (this.camera instanceof PerspectiveCamera && camSettings.ortho) {
|
||||||
this.useOrthographicCamera()
|
this.useOrthographicCamera()
|
||||||
}
|
}
|
||||||
@ -1164,7 +1175,7 @@ export class CameraControls {
|
|||||||
this.camera.updateProjectionMatrix()
|
this.camera.updateProjectionMatrix()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.syncDirection === 'clientToEngine' || forceUpdate)
|
if (this.syncDirection === 'clientToEngine' || forceUpdate) {
|
||||||
this.throttledUpdateEngineCamera({
|
this.throttledUpdateEngineCamera({
|
||||||
quaternion: this.camera.quaternion,
|
quaternion: this.camera.quaternion,
|
||||||
position: this.camera.position,
|
position: this.camera.position,
|
||||||
@ -1172,6 +1183,7 @@ export class CameraControls {
|
|||||||
isPerspective: this.isPerspective,
|
isPerspective: this.isPerspective,
|
||||||
target: this.target,
|
target: this.target,
|
||||||
})
|
})
|
||||||
|
}
|
||||||
this.deferReactUpdate(this.reactCameraProperties)
|
this.deferReactUpdate(this.reactCameraProperties)
|
||||||
Object.values(this._camChangeCallbacks).forEach((cb) => cb())
|
Object.values(this._camChangeCallbacks).forEach((cb) => cb())
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,9 @@ import {
|
|||||||
Expr,
|
Expr,
|
||||||
parse,
|
parse,
|
||||||
recast,
|
recast,
|
||||||
|
defaultSourceRange,
|
||||||
|
resultIsOk,
|
||||||
|
ProgramMemory,
|
||||||
} from 'lang/wasm'
|
} from 'lang/wasm'
|
||||||
import { CustomIcon, CustomIconName } from 'components/CustomIcon'
|
import { CustomIcon, CustomIconName } from 'components/CustomIcon'
|
||||||
import { ConstrainInfo } from 'lang/std/stdTypes'
|
import { ConstrainInfo } from 'lang/std/stdTypes'
|
||||||
@ -412,14 +415,15 @@ export async function deleteSegment({
|
|||||||
if (err(modifiedAst)) return Promise.reject(modifiedAst)
|
if (err(modifiedAst)) return Promise.reject(modifiedAst)
|
||||||
|
|
||||||
const newCode = recast(modifiedAst)
|
const newCode = recast(modifiedAst)
|
||||||
modifiedAst = parse(newCode)
|
const pResult = parse(newCode)
|
||||||
if (err(modifiedAst)) return Promise.reject(modifiedAst)
|
if (err(pResult) || !resultIsOk(pResult)) return Promise.reject(pResult)
|
||||||
|
modifiedAst = pResult.program
|
||||||
|
|
||||||
const testExecute = await executeAst({
|
const testExecute = await executeAst({
|
||||||
ast: modifiedAst,
|
ast: modifiedAst,
|
||||||
idGenerator: kclManager.execState.idGenerator,
|
|
||||||
useFakeExecutor: true,
|
|
||||||
engineCommandManager: engineCommandManager,
|
engineCommandManager: engineCommandManager,
|
||||||
|
// We make sure to send an empty program memory to denote we mean mock mode.
|
||||||
|
programMemoryOverride: ProgramMemory.empty(),
|
||||||
})
|
})
|
||||||
if (testExecute.errors.length) {
|
if (testExecute.errors.length) {
|
||||||
toast.error('Segment tag used outside of current Sketch. Could not delete.')
|
toast.error('Segment tag used outside of current Sketch. Could not delete.')
|
||||||
@ -590,7 +594,9 @@ const ConstraintSymbol = ({
|
|||||||
if (err(_node)) return
|
if (err(_node)) return
|
||||||
const node = _node.node
|
const node = _node.node
|
||||||
|
|
||||||
const range: SourceRange = node ? [node.start, node.end] : [0, 0]
|
const range: SourceRange = node
|
||||||
|
? [node.start, node.end, true]
|
||||||
|
: defaultSourceRange()
|
||||||
|
|
||||||
if (_type === 'intersectionTag') return null
|
if (_type === 'intersectionTag') return null
|
||||||
|
|
||||||
@ -612,7 +618,7 @@ const ConstraintSymbol = ({
|
|||||||
editorManager.setHighlightRange([range])
|
editorManager.setHighlightRange([range])
|
||||||
}}
|
}}
|
||||||
onMouseLeave={() => {
|
onMouseLeave={() => {
|
||||||
editorManager.setHighlightRange([[0, 0]])
|
editorManager.setHighlightRange([defaultSourceRange()])
|
||||||
}}
|
}}
|
||||||
// disabled={isConstrained || !convertToVarEnabled}
|
// disabled={isConstrained || !convertToVarEnabled}
|
||||||
// disabled={implicitDesc} TODO why does this change styles that are hard to override?
|
// disabled={implicitDesc} TODO why does this change styles that are hard to override?
|
||||||
@ -627,10 +633,12 @@ const ConstraintSymbol = ({
|
|||||||
})
|
})
|
||||||
} else if (isConstrained) {
|
} else if (isConstrained) {
|
||||||
try {
|
try {
|
||||||
const parsed = parse(recast(kclManager.ast))
|
const pResult = parse(recast(kclManager.ast))
|
||||||
if (trap(parsed)) return Promise.reject(parsed)
|
if (trap(pResult) || !resultIsOk(pResult))
|
||||||
|
return Promise.reject(pResult)
|
||||||
|
|
||||||
const _node1 = getNodeFromPath<CallExpression>(
|
const _node1 = getNodeFromPath<CallExpression>(
|
||||||
parsed,
|
pResult.program!,
|
||||||
pathToNode,
|
pathToNode,
|
||||||
'CallExpression',
|
'CallExpression',
|
||||||
true
|
true
|
||||||
|
@ -48,6 +48,9 @@ import {
|
|||||||
VariableDeclarator,
|
VariableDeclarator,
|
||||||
sketchFromKclValue,
|
sketchFromKclValue,
|
||||||
sketchFromKclValueOptional,
|
sketchFromKclValueOptional,
|
||||||
|
defaultSourceRange,
|
||||||
|
sourceRangeFromRust,
|
||||||
|
resultIsOk,
|
||||||
} from 'lang/wasm'
|
} from 'lang/wasm'
|
||||||
import {
|
import {
|
||||||
engineCommandManager,
|
engineCommandManager,
|
||||||
@ -495,10 +498,9 @@ export class SceneEntities {
|
|||||||
|
|
||||||
const { execState } = await executeAst({
|
const { execState } = await executeAst({
|
||||||
ast: truncatedAst,
|
ast: truncatedAst,
|
||||||
useFakeExecutor: true,
|
|
||||||
engineCommandManager: this.engineCommandManager,
|
engineCommandManager: this.engineCommandManager,
|
||||||
|
// We make sure to send an empty program memory to denote we mean mock mode.
|
||||||
programMemoryOverride,
|
programMemoryOverride,
|
||||||
idGenerator: kclManager.execState.idGenerator,
|
|
||||||
})
|
})
|
||||||
const programMemory = execState.memory
|
const programMemory = execState.memory
|
||||||
const sketch = sketchFromPathToNode({
|
const sketch = sketchFromPathToNode({
|
||||||
@ -530,7 +532,7 @@ export class SceneEntities {
|
|||||||
|
|
||||||
const segPathToNode = getNodePathFromSourceRange(
|
const segPathToNode = getNodePathFromSourceRange(
|
||||||
maybeModdedAst,
|
maybeModdedAst,
|
||||||
sketch.start.__geoMeta.sourceRange
|
sourceRangeFromRust(sketch.start.__geoMeta.sourceRange)
|
||||||
)
|
)
|
||||||
if (sketch?.paths?.[0]?.type !== 'Circle') {
|
if (sketch?.paths?.[0]?.type !== 'Circle') {
|
||||||
const _profileStart = createProfileStartHandle({
|
const _profileStart = createProfileStartHandle({
|
||||||
@ -552,7 +554,7 @@ export class SceneEntities {
|
|||||||
sketch.paths.forEach((segment, index) => {
|
sketch.paths.forEach((segment, index) => {
|
||||||
let segPathToNode = getNodePathFromSourceRange(
|
let segPathToNode = getNodePathFromSourceRange(
|
||||||
maybeModdedAst,
|
maybeModdedAst,
|
||||||
segment.__geoMeta.sourceRange
|
sourceRangeFromRust(segment.__geoMeta.sourceRange)
|
||||||
)
|
)
|
||||||
if (
|
if (
|
||||||
draftExpressionsIndices &&
|
draftExpressionsIndices &&
|
||||||
@ -561,12 +563,12 @@ export class SceneEntities {
|
|||||||
const previousSegment = sketch.paths[index - 1] || sketch.start
|
const previousSegment = sketch.paths[index - 1] || sketch.start
|
||||||
const previousSegmentPathToNode = getNodePathFromSourceRange(
|
const previousSegmentPathToNode = getNodePathFromSourceRange(
|
||||||
maybeModdedAst,
|
maybeModdedAst,
|
||||||
previousSegment.__geoMeta.sourceRange
|
sourceRangeFromRust(previousSegment.__geoMeta.sourceRange)
|
||||||
)
|
)
|
||||||
const bodyIndex = previousSegmentPathToNode[1][0]
|
const bodyIndex = previousSegmentPathToNode[1][0]
|
||||||
segPathToNode = getNodePathFromSourceRange(
|
segPathToNode = getNodePathFromSourceRange(
|
||||||
truncatedAst,
|
truncatedAst,
|
||||||
segment.__geoMeta.sourceRange
|
sourceRangeFromRust(segment.__geoMeta.sourceRange)
|
||||||
)
|
)
|
||||||
segPathToNode[1][0] = bodyIndex
|
segPathToNode[1][0] = bodyIndex
|
||||||
}
|
}
|
||||||
@ -575,7 +577,10 @@ export class SceneEntities {
|
|||||||
index <= draftExpressionsIndices.end &&
|
index <= draftExpressionsIndices.end &&
|
||||||
index >= draftExpressionsIndices.start
|
index >= draftExpressionsIndices.start
|
||||||
const isSelected = selectionRanges?.graphSelections.some((selection) =>
|
const isSelected = selectionRanges?.graphSelections.some((selection) =>
|
||||||
isOverlap(selection?.codeRef?.range, segment.__geoMeta.sourceRange)
|
isOverlap(
|
||||||
|
selection?.codeRef?.range,
|
||||||
|
sourceRangeFromRust(segment.__geoMeta.sourceRange)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
let seg: Group
|
let seg: Group
|
||||||
@ -657,13 +662,11 @@ export class SceneEntities {
|
|||||||
}
|
}
|
||||||
updateAstAndRejigSketch = async (
|
updateAstAndRejigSketch = async (
|
||||||
sketchPathToNode: PathToNode,
|
sketchPathToNode: PathToNode,
|
||||||
modifiedAst: Node<Program> | Error,
|
modifiedAst: Node<Program>,
|
||||||
forward: [number, number, number],
|
forward: [number, number, number],
|
||||||
up: [number, number, number],
|
up: [number, number, number],
|
||||||
origin: [number, number, number]
|
origin: [number, number, number]
|
||||||
) => {
|
) => {
|
||||||
if (err(modifiedAst)) return modifiedAst
|
|
||||||
|
|
||||||
const nextAst = await kclManager.updateAst(modifiedAst, false)
|
const nextAst = await kclManager.updateAst(modifiedAst, false)
|
||||||
await this.tearDownSketch({ removeAxis: false })
|
await this.tearDownSketch({ removeAxis: false })
|
||||||
sceneInfra.resetMouseListeners()
|
sceneInfra.resetMouseListeners()
|
||||||
@ -721,8 +724,9 @@ export class SceneEntities {
|
|||||||
pathToNode: sketchPathToNode,
|
pathToNode: sketchPathToNode,
|
||||||
})
|
})
|
||||||
if (trap(mod)) return Promise.reject(mod)
|
if (trap(mod)) return Promise.reject(mod)
|
||||||
const modifiedAst = parse(recast(mod.modifiedAst))
|
const pResult = parse(recast(mod.modifiedAst))
|
||||||
if (trap(modifiedAst)) return Promise.reject(modifiedAst)
|
if (trap(pResult) || !resultIsOk(pResult)) return Promise.reject(pResult)
|
||||||
|
const modifiedAst = pResult.program
|
||||||
|
|
||||||
const draftExpressionsIndices = { start: index, end: index }
|
const draftExpressionsIndices = { start: index, end: index }
|
||||||
|
|
||||||
@ -914,9 +918,9 @@ export class SceneEntities {
|
|||||||
...getRectangleCallExpressions(rectangleOrigin, tags),
|
...getRectangleCallExpressions(rectangleOrigin, tags),
|
||||||
])
|
])
|
||||||
|
|
||||||
let _recastAst = parse(recast(_ast))
|
const pResult = parse(recast(_ast))
|
||||||
if (trap(_recastAst)) return Promise.reject(_recastAst)
|
if (trap(pResult) || !resultIsOk(pResult)) return Promise.reject(pResult)
|
||||||
_ast = _recastAst
|
_ast = pResult.program
|
||||||
|
|
||||||
const { programMemoryOverride, truncatedAst } = await this.setupSketch({
|
const { programMemoryOverride, truncatedAst } = await this.setupSketch({
|
||||||
sketchPathToNode,
|
sketchPathToNode,
|
||||||
@ -950,10 +954,9 @@ export class SceneEntities {
|
|||||||
|
|
||||||
const { execState } = await executeAst({
|
const { execState } = await executeAst({
|
||||||
ast: truncatedAst,
|
ast: truncatedAst,
|
||||||
useFakeExecutor: true,
|
|
||||||
engineCommandManager: this.engineCommandManager,
|
engineCommandManager: this.engineCommandManager,
|
||||||
|
// We make sure to send an empty program memory to denote we mean mock mode.
|
||||||
programMemoryOverride,
|
programMemoryOverride,
|
||||||
idGenerator: kclManager.execState.idGenerator,
|
|
||||||
})
|
})
|
||||||
const programMemory = execState.memory
|
const programMemory = execState.memory
|
||||||
this.sceneProgramMemory = programMemory
|
this.sceneProgramMemory = programMemory
|
||||||
@ -998,9 +1001,10 @@ export class SceneEntities {
|
|||||||
updateRectangleSketch(sketchInit, x, y, tags[0])
|
updateRectangleSketch(sketchInit, x, y, tags[0])
|
||||||
|
|
||||||
const newCode = recast(_ast)
|
const newCode = recast(_ast)
|
||||||
let _recastAst = parse(newCode)
|
const pResult = parse(newCode)
|
||||||
if (trap(_recastAst)) return
|
if (trap(pResult) || !resultIsOk(pResult))
|
||||||
_ast = _recastAst
|
return Promise.reject(pResult)
|
||||||
|
_ast = pResult.program
|
||||||
|
|
||||||
// Update the primary AST and unequip the rectangle tool
|
// Update the primary AST and unequip the rectangle tool
|
||||||
await kclManager.executeAstMock(_ast)
|
await kclManager.executeAstMock(_ast)
|
||||||
@ -1013,10 +1017,9 @@ export class SceneEntities {
|
|||||||
|
|
||||||
const { execState } = await executeAst({
|
const { execState } = await executeAst({
|
||||||
ast: _ast,
|
ast: _ast,
|
||||||
useFakeExecutor: true,
|
|
||||||
engineCommandManager: this.engineCommandManager,
|
engineCommandManager: this.engineCommandManager,
|
||||||
|
// We make sure to send an empty program memory to denote we mean mock mode.
|
||||||
programMemoryOverride,
|
programMemoryOverride,
|
||||||
idGenerator: kclManager.execState.idGenerator,
|
|
||||||
})
|
})
|
||||||
const programMemory = execState.memory
|
const programMemory = execState.memory
|
||||||
|
|
||||||
@ -1071,9 +1074,9 @@ export class SceneEntities {
|
|||||||
...getRectangleCallExpressions(rectangleOrigin, tags),
|
...getRectangleCallExpressions(rectangleOrigin, tags),
|
||||||
])
|
])
|
||||||
|
|
||||||
let _recastAst = parse(recast(_ast))
|
const pResult = parse(recast(_ast))
|
||||||
if (trap(_recastAst)) return Promise.reject(_recastAst)
|
if (trap(pResult) || !resultIsOk(pResult)) return Promise.reject(pResult)
|
||||||
_ast = _recastAst
|
_ast = pResult.program
|
||||||
|
|
||||||
const { programMemoryOverride, truncatedAst } = await this.setupSketch({
|
const { programMemoryOverride, truncatedAst } = await this.setupSketch({
|
||||||
sketchPathToNode,
|
sketchPathToNode,
|
||||||
@ -1114,10 +1117,9 @@ export class SceneEntities {
|
|||||||
|
|
||||||
const { execState } = await executeAst({
|
const { execState } = await executeAst({
|
||||||
ast: truncatedAst,
|
ast: truncatedAst,
|
||||||
useFakeExecutor: true,
|
|
||||||
engineCommandManager: this.engineCommandManager,
|
engineCommandManager: this.engineCommandManager,
|
||||||
|
// We make sure to send an empty program memory to denote we mean mock mode.
|
||||||
programMemoryOverride,
|
programMemoryOverride,
|
||||||
idGenerator: kclManager.execState.idGenerator,
|
|
||||||
})
|
})
|
||||||
const programMemory = execState.memory
|
const programMemory = execState.memory
|
||||||
this.sceneProgramMemory = programMemory
|
this.sceneProgramMemory = programMemory
|
||||||
@ -1165,9 +1167,10 @@ export class SceneEntities {
|
|||||||
rectangleOrigin[1]
|
rectangleOrigin[1]
|
||||||
)
|
)
|
||||||
|
|
||||||
let _recastAst = parse(recast(_ast))
|
const pResult = parse(recast(_ast))
|
||||||
if (trap(_recastAst)) return
|
if (trap(pResult) || !resultIsOk(pResult))
|
||||||
_ast = _recastAst
|
return Promise.reject(pResult)
|
||||||
|
_ast = pResult.program
|
||||||
|
|
||||||
// Update the primary AST and unequip the rectangle tool
|
// Update the primary AST and unequip the rectangle tool
|
||||||
await kclManager.executeAstMock(_ast)
|
await kclManager.executeAstMock(_ast)
|
||||||
@ -1180,10 +1183,9 @@ export class SceneEntities {
|
|||||||
|
|
||||||
const { execState } = await executeAst({
|
const { execState } = await executeAst({
|
||||||
ast: _ast,
|
ast: _ast,
|
||||||
useFakeExecutor: true,
|
|
||||||
engineCommandManager: this.engineCommandManager,
|
engineCommandManager: this.engineCommandManager,
|
||||||
|
// We make sure to send an empty program memory to denote we mean mock mode.
|
||||||
programMemoryOverride,
|
programMemoryOverride,
|
||||||
idGenerator: kclManager.execState.idGenerator,
|
|
||||||
})
|
})
|
||||||
const programMemory = execState.memory
|
const programMemory = execState.memory
|
||||||
|
|
||||||
@ -1241,9 +1243,9 @@ export class SceneEntities {
|
|||||||
]),
|
]),
|
||||||
])
|
])
|
||||||
|
|
||||||
let _recastAst = parse(recast(_ast))
|
const pResult = parse(recast(_ast))
|
||||||
if (trap(_recastAst)) return Promise.reject(_recastAst)
|
if (trap(pResult) || !resultIsOk(pResult)) return Promise.reject(pResult)
|
||||||
_ast = _recastAst
|
_ast = pResult.program
|
||||||
|
|
||||||
// do a quick mock execution to get the program memory up-to-date
|
// do a quick mock execution to get the program memory up-to-date
|
||||||
await kclManager.executeAstMock(_ast)
|
await kclManager.executeAstMock(_ast)
|
||||||
@ -1299,10 +1301,9 @@ export class SceneEntities {
|
|||||||
|
|
||||||
const { execState } = await executeAst({
|
const { execState } = await executeAst({
|
||||||
ast: modded,
|
ast: modded,
|
||||||
useFakeExecutor: true,
|
|
||||||
engineCommandManager: this.engineCommandManager,
|
engineCommandManager: this.engineCommandManager,
|
||||||
|
// We make sure to send an empty program memory to denote we mean mock mode.
|
||||||
programMemoryOverride,
|
programMemoryOverride,
|
||||||
idGenerator: kclManager.execState.idGenerator,
|
|
||||||
})
|
})
|
||||||
const programMemory = execState.memory
|
const programMemory = execState.memory
|
||||||
this.sceneProgramMemory = programMemory
|
this.sceneProgramMemory = programMemory
|
||||||
@ -1365,9 +1366,10 @@ export class SceneEntities {
|
|||||||
|
|
||||||
const newCode = recast(modded)
|
const newCode = recast(modded)
|
||||||
if (err(newCode)) return
|
if (err(newCode)) return
|
||||||
let _recastAst = parse(newCode)
|
const pResult = parse(newCode)
|
||||||
if (trap(_recastAst)) return Promise.reject(_recastAst)
|
if (trap(pResult) || !resultIsOk(pResult))
|
||||||
_ast = _recastAst
|
return Promise.reject(pResult)
|
||||||
|
_ast = pResult.program
|
||||||
|
|
||||||
// Update the primary AST and unequip the rectangle tool
|
// Update the primary AST and unequip the rectangle tool
|
||||||
await kclManager.executeAstMock(_ast)
|
await kclManager.executeAstMock(_ast)
|
||||||
@ -1660,7 +1662,7 @@ export class SceneEntities {
|
|||||||
kclManager.programMemory,
|
kclManager.programMemory,
|
||||||
{
|
{
|
||||||
type: 'sourceRange',
|
type: 'sourceRange',
|
||||||
sourceRange: [node.start, node.end],
|
sourceRange: [node.start, node.end, true],
|
||||||
},
|
},
|
||||||
getChangeSketchInput()
|
getChangeSketchInput()
|
||||||
)
|
)
|
||||||
@ -1683,10 +1685,9 @@ export class SceneEntities {
|
|||||||
codeManager.updateCodeEditor(code)
|
codeManager.updateCodeEditor(code)
|
||||||
const { execState } = await executeAst({
|
const { execState } = await executeAst({
|
||||||
ast: truncatedAst,
|
ast: truncatedAst,
|
||||||
useFakeExecutor: true,
|
|
||||||
engineCommandManager: this.engineCommandManager,
|
engineCommandManager: this.engineCommandManager,
|
||||||
|
// We make sure to send an empty program memory to denote we mean mock mode.
|
||||||
programMemoryOverride,
|
programMemoryOverride,
|
||||||
idGenerator: kclManager.execState.idGenerator,
|
|
||||||
})
|
})
|
||||||
const programMemory = execState.memory
|
const programMemory = execState.memory
|
||||||
this.sceneProgramMemory = programMemory
|
this.sceneProgramMemory = programMemory
|
||||||
@ -1750,7 +1751,7 @@ export class SceneEntities {
|
|||||||
): (() => SegmentOverlayPayload | null) => {
|
): (() => SegmentOverlayPayload | null) => {
|
||||||
const segPathToNode = getNodePathFromSourceRange(
|
const segPathToNode = getNodePathFromSourceRange(
|
||||||
modifiedAst,
|
modifiedAst,
|
||||||
segment.__geoMeta.sourceRange
|
sourceRangeFromRust(segment.__geoMeta.sourceRange)
|
||||||
)
|
)
|
||||||
const sgPaths = sketch.paths
|
const sgPaths = sketch.paths
|
||||||
const originalPathToNodeStr = JSON.stringify(segPathToNode)
|
const originalPathToNodeStr = JSON.stringify(segPathToNode)
|
||||||
@ -1901,8 +1902,10 @@ export class SceneEntities {
|
|||||||
SEGMENT_BODIES_PLUS_PROFILE_START
|
SEGMENT_BODIES_PLUS_PROFILE_START
|
||||||
)
|
)
|
||||||
if (parent?.userData?.pathToNode) {
|
if (parent?.userData?.pathToNode) {
|
||||||
const updatedAst = parse(recast(kclManager.ast))
|
const pResult = parse(recast(kclManager.ast))
|
||||||
if (trap(updatedAst)) return
|
if (trap(pResult) || !resultIsOk(pResult))
|
||||||
|
return Promise.reject(pResult)
|
||||||
|
const updatedAst = pResult.program
|
||||||
const _node = getNodeFromPath<Node<CallExpression>>(
|
const _node = getNodeFromPath<Node<CallExpression>>(
|
||||||
updatedAst,
|
updatedAst,
|
||||||
parent.userData.pathToNode,
|
parent.userData.pathToNode,
|
||||||
@ -1910,7 +1913,7 @@ export class SceneEntities {
|
|||||||
)
|
)
|
||||||
if (trap(_node, { suppress: true })) return
|
if (trap(_node, { suppress: true })) return
|
||||||
const node = _node.node
|
const node = _node.node
|
||||||
editorManager.setHighlightRange([[node.start, node.end]])
|
editorManager.setHighlightRange([[node.start, node.end, true]])
|
||||||
const yellow = 0xffff00
|
const yellow = 0xffff00
|
||||||
colorSegment(selected, yellow)
|
colorSegment(selected, yellow)
|
||||||
const extraSegmentGroup = parent.getObjectByName(EXTRA_SEGMENT_HANDLE)
|
const extraSegmentGroup = parent.getObjectByName(EXTRA_SEGMENT_HANDLE)
|
||||||
@ -1955,10 +1958,10 @@ export class SceneEntities {
|
|||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
editorManager.setHighlightRange([[0, 0]])
|
editorManager.setHighlightRange([defaultSourceRange()])
|
||||||
},
|
},
|
||||||
onMouseLeave: ({ selected, ...rest }: OnMouseEnterLeaveArgs) => {
|
onMouseLeave: ({ selected, ...rest }: OnMouseEnterLeaveArgs) => {
|
||||||
editorManager.setHighlightRange([[0, 0]])
|
editorManager.setHighlightRange([defaultSourceRange()])
|
||||||
const parent = getParentGroup(
|
const parent = getParentGroup(
|
||||||
selected,
|
selected,
|
||||||
SEGMENT_BODIES_PLUS_PROFILE_START
|
SEGMENT_BODIES_PLUS_PROFILE_START
|
||||||
@ -2087,8 +2090,10 @@ function prepareTruncatedMemoryAndAst(
|
|||||||
).body.push(newSegment)
|
).body.push(newSegment)
|
||||||
// update source ranges to section we just added.
|
// update source ranges to section we just added.
|
||||||
// hacks like this wouldn't be needed if the AST put pathToNode info in memory/sketch segments
|
// hacks like this wouldn't be needed if the AST put pathToNode info in memory/sketch segments
|
||||||
const updatedSrcRangeAst = parse(recast(_ast)) // get source ranges correct since unfortunately we still rely on them
|
const pResult = parse(recast(_ast)) // get source ranges correct since unfortunately we still rely on them
|
||||||
if (err(updatedSrcRangeAst)) return updatedSrcRangeAst
|
if (trap(pResult) || !resultIsOk(pResult))
|
||||||
|
return Error('Unexpected compilation error')
|
||||||
|
const updatedSrcRangeAst = pResult.program
|
||||||
|
|
||||||
const lastPipeItem = (
|
const lastPipeItem = (
|
||||||
(updatedSrcRangeAst.body[bodyIndex] as VariableDeclaration)
|
(updatedSrcRangeAst.body[bodyIndex] as VariableDeclaration)
|
||||||
|
@ -5,6 +5,7 @@ import { useEffect, useRef, useState } from 'react'
|
|||||||
import { trap } from 'lib/trap'
|
import { trap } from 'lib/trap'
|
||||||
import { codeToIdSelections } from 'lib/selections'
|
import { codeToIdSelections } from 'lib/selections'
|
||||||
import { codeRefFromRange } from 'lang/std/artifactGraph'
|
import { codeRefFromRange } from 'lang/std/artifactGraph'
|
||||||
|
import { defaultSourceRange } from 'lang/wasm'
|
||||||
|
|
||||||
export function AstExplorer() {
|
export function AstExplorer() {
|
||||||
const { context } = useModelingContext()
|
const { context } = useModelingContext()
|
||||||
@ -46,7 +47,7 @@ export function AstExplorer() {
|
|||||||
<div
|
<div
|
||||||
className="h-full relative"
|
className="h-full relative"
|
||||||
onMouseLeave={(e) => {
|
onMouseLeave={(e) => {
|
||||||
editorManager.setHighlightRange([[0, 0]])
|
editorManager.setHighlightRange([defaultSourceRange()])
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<pre className="text-xs">
|
<pre className="text-xs">
|
||||||
@ -115,15 +116,19 @@ function DisplayObj({
|
|||||||
hasCursor ? 'bg-violet-100/80 dark:bg-violet-100/25' : ''
|
hasCursor ? 'bg-violet-100/80 dark:bg-violet-100/25' : ''
|
||||||
}`}
|
}`}
|
||||||
onMouseEnter={(e) => {
|
onMouseEnter={(e) => {
|
||||||
editorManager.setHighlightRange([[obj?.start || 0, obj.end]])
|
editorManager.setHighlightRange([[obj?.start || 0, obj.end, true]])
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
}}
|
}}
|
||||||
onMouseMove={(e) => {
|
onMouseMove={(e) => {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
editorManager.setHighlightRange([[obj?.start || 0, obj.end]])
|
editorManager.setHighlightRange([[obj?.start || 0, obj.end, true]])
|
||||||
}}
|
}}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
const range: [number, number] = [obj?.start || 0, obj.end || 0]
|
const range: [number, number, boolean] = [
|
||||||
|
obj?.start || 0,
|
||||||
|
obj.end || 0,
|
||||||
|
true,
|
||||||
|
]
|
||||||
const idInfo = codeToIdSelections([
|
const idInfo = codeToIdSelections([
|
||||||
{ codeRef: codeRefFromRange(range, kclManager.ast) },
|
{ codeRef: codeRefFromRange(range, kclManager.ast) },
|
||||||
])[0]
|
])[0]
|
||||||
|
@ -1,5 +1,11 @@
|
|||||||
import { useEffect, useState, useRef } from 'react'
|
import { useEffect, useState, useRef } from 'react'
|
||||||
import { parse, BinaryPart, Expr, ProgramMemory } from '../lang/wasm'
|
import {
|
||||||
|
parse,
|
||||||
|
BinaryPart,
|
||||||
|
Expr,
|
||||||
|
ProgramMemory,
|
||||||
|
resultIsOk,
|
||||||
|
} from '../lang/wasm'
|
||||||
import {
|
import {
|
||||||
createIdentifier,
|
createIdentifier,
|
||||||
createLiteral,
|
createLiteral,
|
||||||
@ -141,8 +147,9 @@ export function useCalc({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
try {
|
try {
|
||||||
const code = `const __result__ = ${value}`
|
const code = `const __result__ = ${value}`
|
||||||
const ast = parse(code)
|
const pResult = parse(code)
|
||||||
if (trap(ast)) return
|
if (trap(pResult) || !resultIsOk(pResult)) return
|
||||||
|
const ast = pResult.program
|
||||||
const _programMem: ProgramMemory = ProgramMemory.empty()
|
const _programMem: ProgramMemory = ProgramMemory.empty()
|
||||||
for (const { key, value } of availableVarInfo.variables) {
|
for (const { key, value } of availableVarInfo.variables) {
|
||||||
const error = _programMem.set(key, {
|
const error = _programMem.set(key, {
|
||||||
@ -156,9 +163,8 @@ export function useCalc({
|
|||||||
executeAst({
|
executeAst({
|
||||||
ast,
|
ast,
|
||||||
engineCommandManager,
|
engineCommandManager,
|
||||||
useFakeExecutor: true,
|
// We make sure to send an empty program memory to denote we mean mock mode.
|
||||||
programMemoryOverride: kclManager.programMemory.clone(),
|
programMemoryOverride: kclManager.programMemory.clone(),
|
||||||
idGenerator: kclManager.execState.idGenerator,
|
|
||||||
}).then(({ execState }) => {
|
}).then(({ execState }) => {
|
||||||
const resultDeclaration = ast.body.find(
|
const resultDeclaration = ast.body.find(
|
||||||
(a) =>
|
(a) =>
|
||||||
|
@ -636,6 +636,16 @@ const CustomIconMap = {
|
|||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
),
|
),
|
||||||
|
loading: (
|
||||||
|
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
clipRule="evenodd"
|
||||||
|
d="M12.5001 6.25839C11.76 5.76392 10.89 5.5 10 5.5V4.5C11.0878 4.5 12.1512 4.82257 13.0556 5.42692C13.9601 6.03126 14.6651 6.89025 15.0813 7.89524C15.4976 8.90023 15.6065 10.0061 15.3943 11.073C15.1821 12.1399 14.6583 13.1199 13.8891 13.8891C13.1199 14.6583 12.1399 15.1821 11.073 15.3943C10.0061 15.6065 8.90023 15.4976 7.89524 15.0813C6.89025 14.6651 6.03126 13.9601 5.42692 13.0556C4.82257 12.1512 4.5 11.0878 4.5 10H5.5C5.5 10.89 5.76392 11.76 6.25839 12.5001C6.75285 13.2401 7.45566 13.8169 8.27792 14.1575C9.10019 14.4981 10.005 14.5872 10.8779 14.4135C11.7508 14.2399 12.5526 13.8113 13.182 13.182C13.8113 12.5526 14.2399 11.7508 14.4135 10.8779C14.5872 10.005 14.4981 9.10019 14.1575 8.27792C13.8169 7.45566 13.2401 6.75285 12.5001 6.25839Z"
|
||||||
|
fill="currentColor"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
lockClosed: (
|
lockClosed: (
|
||||||
<svg
|
<svg
|
||||||
viewBox="0 0 20 20"
|
viewBox="0 0 20 20"
|
||||||
|
@ -1,141 +1,14 @@
|
|||||||
import { APP_VERSION } from 'routes/Settings'
|
|
||||||
import { CustomIcon } from 'components/CustomIcon'
|
|
||||||
import Tooltip from 'components/Tooltip'
|
|
||||||
import { PATHS } from 'lib/paths'
|
|
||||||
import { NetworkHealthIndicator } from 'components/NetworkHealthIndicator'
|
|
||||||
import { HelpMenu } from './HelpMenu'
|
|
||||||
import { Link, useLocation } from 'react-router-dom'
|
|
||||||
import { useAbsoluteFilePath } from 'hooks/useAbsoluteFilePath'
|
|
||||||
import { coreDump } from 'lang/wasm'
|
|
||||||
import toast from 'react-hot-toast'
|
|
||||||
import { CoreDumpManager } from 'lib/coredump'
|
import { CoreDumpManager } from 'lib/coredump'
|
||||||
import openWindow, { openExternalBrowserIfDesktop } from 'lib/openWindow'
|
|
||||||
import { NetworkMachineIndicator } from './NetworkMachineIndicator'
|
|
||||||
import { ModelStateIndicator } from './ModelStateIndicator'
|
|
||||||
import { reportRejection } from 'lib/trap'
|
|
||||||
|
|
||||||
export function LowerRightControls({
|
export function LowerRightControls({
|
||||||
children,
|
children,
|
||||||
coreDumpManager,
|
|
||||||
}: {
|
}: {
|
||||||
children?: React.ReactNode
|
children?: React.ReactNode
|
||||||
coreDumpManager?: CoreDumpManager
|
coreDumpManager?: CoreDumpManager
|
||||||
}) {
|
}) {
|
||||||
const location = useLocation()
|
|
||||||
const filePath = useAbsoluteFilePath()
|
|
||||||
|
|
||||||
const linkOverrideClassName =
|
|
||||||
'!text-chalkboard-70 hover:!text-chalkboard-80 dark:!text-chalkboard-40 dark:hover:!text-chalkboard-30'
|
|
||||||
|
|
||||||
function reportbug(event: {
|
|
||||||
preventDefault: () => void
|
|
||||||
stopPropagation: () => void
|
|
||||||
}) {
|
|
||||||
event?.preventDefault()
|
|
||||||
event?.stopPropagation()
|
|
||||||
|
|
||||||
if (!coreDumpManager) {
|
|
||||||
// open default reporting option
|
|
||||||
openWindow(
|
|
||||||
'https://github.com/KittyCAD/modeling-app/issues/new/choose'
|
|
||||||
).catch(reportRejection)
|
|
||||||
} else {
|
|
||||||
toast
|
|
||||||
.promise(
|
|
||||||
coreDump(coreDumpManager, true),
|
|
||||||
{
|
|
||||||
loading: 'Preparing bug report...',
|
|
||||||
success: 'Bug report opened in new window',
|
|
||||||
error: 'Unable to export a core dump. Using default reporting.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
success: {
|
|
||||||
// Note: this extended duration is especially important for Playwright e2e testing
|
|
||||||
// default duration is 2000 - https://react-hot-toast.com/docs/toast#default-durations
|
|
||||||
duration: 6000,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.catch((err: Error) => {
|
|
||||||
if (err) {
|
|
||||||
openWindow(
|
|
||||||
'https://github.com/KittyCAD/modeling-app/issues/new/choose'
|
|
||||||
).catch(reportRejection)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="fixed bottom-2 right-2 flex flex-col items-end gap-3 pointer-events-none">
|
<section className="absolute bottom-2 right-2 flex flex-col items-end gap-3 pointer-events-none">
|
||||||
{children}
|
{children}
|
||||||
<menu className="flex items-center justify-end gap-3 pointer-events-auto">
|
|
||||||
{!location.pathname.startsWith(PATHS.HOME) && <ModelStateIndicator />}
|
|
||||||
<a
|
|
||||||
onClick={openExternalBrowserIfDesktop(
|
|
||||||
`https://github.com/KittyCAD/modeling-app/releases/tag/v${APP_VERSION}`
|
|
||||||
)}
|
|
||||||
href={`https://github.com/KittyCAD/modeling-app/releases/tag/v${APP_VERSION}`}
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
className={'!no-underline font-mono text-xs ' + linkOverrideClassName}
|
|
||||||
>
|
|
||||||
v{APP_VERSION}
|
|
||||||
</a>
|
|
||||||
<a
|
|
||||||
onClick={reportbug}
|
|
||||||
href="https://github.com/KittyCAD/modeling-app/issues/new/choose"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
<CustomIcon
|
|
||||||
name="bug"
|
|
||||||
className={`w-5 h-5 ${linkOverrideClassName}`}
|
|
||||||
/>
|
|
||||||
<Tooltip position="top" contentClassName="text-xs">
|
|
||||||
Report a bug
|
|
||||||
</Tooltip>
|
|
||||||
</a>
|
|
||||||
<Link
|
|
||||||
to={
|
|
||||||
location.pathname.includes(PATHS.FILE)
|
|
||||||
? filePath + PATHS.TELEMETRY + '?tab=project'
|
|
||||||
: PATHS.HOME + PATHS.TELEMETRY
|
|
||||||
}
|
|
||||||
data-testid="telemetry-link"
|
|
||||||
>
|
|
||||||
<CustomIcon
|
|
||||||
name="stopwatch"
|
|
||||||
className={`w-5 h-5 ${linkOverrideClassName}`}
|
|
||||||
/>
|
|
||||||
<span className="sr-only">Telemetry</span>
|
|
||||||
<Tooltip position="top" contentClassName="text-xs">
|
|
||||||
Telemetry
|
|
||||||
</Tooltip>
|
|
||||||
</Link>
|
|
||||||
<Link
|
|
||||||
to={
|
|
||||||
location.pathname.includes(PATHS.FILE)
|
|
||||||
? filePath + PATHS.SETTINGS + '?tab=project'
|
|
||||||
: PATHS.HOME + PATHS.SETTINGS
|
|
||||||
}
|
|
||||||
data-testid="settings-link"
|
|
||||||
>
|
|
||||||
<CustomIcon
|
|
||||||
name="settings"
|
|
||||||
className={`w-5 h-5 ${linkOverrideClassName}`}
|
|
||||||
/>
|
|
||||||
<span className="sr-only">Settings</span>
|
|
||||||
<Tooltip position="top" contentClassName="text-xs">
|
|
||||||
Settings
|
|
||||||
</Tooltip>
|
|
||||||
</Link>
|
|
||||||
<NetworkMachineIndicator className={linkOverrideClassName} />
|
|
||||||
{!location.pathname.startsWith(PATHS.HOME) && (
|
|
||||||
<NetworkHealthIndicator />
|
|
||||||
)}
|
|
||||||
<HelpMenu />
|
|
||||||
</menu>
|
|
||||||
</section>
|
</section>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,39 @@
|
|||||||
import { useEngineCommands } from './EngineCommands'
|
import { useEngineCommands } from './EngineCommands'
|
||||||
import { Spinner } from './Spinner'
|
import { Spinner } from './Spinner'
|
||||||
import { CustomIcon } from './CustomIcon'
|
import { CustomIcon } from './CustomIcon'
|
||||||
|
import { StatusBarItemType } from './statusBar/statusBarTypes'
|
||||||
|
|
||||||
|
export const useModelStateStatus = (): StatusBarItemType => {
|
||||||
|
const [commands] = useEngineCommands()
|
||||||
|
const lastCommandType = commands[commands.length - 1]?.type
|
||||||
|
|
||||||
|
let icon: StatusBarItemType['icon'] = 'loading'
|
||||||
|
const baseDataTestId = 'model-state-indicator'
|
||||||
|
let dataTestId = baseDataTestId
|
||||||
|
|
||||||
|
if (lastCommandType === 'receive-reliable') {
|
||||||
|
icon = 'checkmark'
|
||||||
|
dataTestId = `${baseDataTestId}-receive-reliable`
|
||||||
|
} else if (lastCommandType === 'execution-done') {
|
||||||
|
icon = 'checkmark'
|
||||||
|
dataTestId = `${baseDataTestId}-execution-done`
|
||||||
|
} else if (lastCommandType === 'export-done') {
|
||||||
|
icon = 'checkmark'
|
||||||
|
dataTestId = `${baseDataTestId}-export-done`
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: 'model-state-indicator',
|
||||||
|
label: '',
|
||||||
|
icon,
|
||||||
|
toolTip: {
|
||||||
|
children: 'Model state indicator',
|
||||||
|
},
|
||||||
|
element: 'button',
|
||||||
|
onClick: () => {},
|
||||||
|
'data-testid': dataTestId,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const ModelStateIndicator = () => {
|
export const ModelStateIndicator = () => {
|
||||||
const [commands] = useEngineCommands()
|
const [commands] = useEngineCommands()
|
||||||
|
@ -50,6 +50,7 @@ import {
|
|||||||
isSketchPipe,
|
isSketchPipe,
|
||||||
Selections,
|
Selections,
|
||||||
updateSelections,
|
updateSelections,
|
||||||
|
canLoftSelection,
|
||||||
} from 'lib/selections'
|
} from 'lib/selections'
|
||||||
import { applyConstraintIntersect } from './Toolbar/Intersect'
|
import { applyConstraintIntersect } from './Toolbar/Intersect'
|
||||||
import { applyConstraintAbsDistance } from './Toolbar/SetAbsDistance'
|
import { applyConstraintAbsDistance } from './Toolbar/SetAbsDistance'
|
||||||
@ -66,7 +67,7 @@ import {
|
|||||||
sketchOnOffsetPlane,
|
sketchOnOffsetPlane,
|
||||||
startSketchOnDefault,
|
startSketchOnDefault,
|
||||||
} from 'lang/modifyAst'
|
} from 'lang/modifyAst'
|
||||||
import { Program, parse, recast } from 'lang/wasm'
|
import { Program, parse, recast, resultIsOk } from 'lang/wasm'
|
||||||
import {
|
import {
|
||||||
doesSceneHaveSweepableSketch,
|
doesSceneHaveSweepableSketch,
|
||||||
getNodePathFromSourceRange,
|
getNodePathFromSourceRange,
|
||||||
@ -82,7 +83,7 @@ import { getVarNameModal } from 'hooks/useToolbarGuards'
|
|||||||
import { err, reportRejection, trap } from 'lib/trap'
|
import { err, reportRejection, trap } from 'lib/trap'
|
||||||
import { useCommandsContext } from 'hooks/useCommandsContext'
|
import { useCommandsContext } from 'hooks/useCommandsContext'
|
||||||
import { modelingMachineEvent } from 'editor/manager'
|
import { modelingMachineEvent } from 'editor/manager'
|
||||||
import { hasValidEdgeTreatmentSelection } from 'lang/modifyAst/addFillet'
|
import { hasValidEdgeTreatmentSelection } from 'lang/modifyAst/addEdgeTreatment'
|
||||||
import {
|
import {
|
||||||
ExportIntent,
|
ExportIntent,
|
||||||
EngineConnectionStateType,
|
EngineConnectionStateType,
|
||||||
@ -98,6 +99,7 @@ type MachineContext<T extends AnyStateMachine> = {
|
|||||||
state: StateFrom<T>
|
state: StateFrom<T>
|
||||||
context: ContextFrom<T>
|
context: ContextFrom<T>
|
||||||
send: Prop<Actor<T>, 'send'>
|
send: Prop<Actor<T>, 'send'>
|
||||||
|
streamRef: React.RefObject<HTMLDivElement>
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ModelingMachineContext = createContext(
|
export const ModelingMachineContext = createContext(
|
||||||
@ -569,6 +571,21 @@ export const ModelingMachineProvider = ({
|
|||||||
if (err(canSweep)) return false
|
if (err(canSweep)) return false
|
||||||
return canSweep
|
return canSweep
|
||||||
},
|
},
|
||||||
|
'has valid loft selection': ({ context: { selectionRanges } }) => {
|
||||||
|
const hasNoSelection =
|
||||||
|
selectionRanges.graphSelections.length === 0 ||
|
||||||
|
isRangeBetweenCharacters(selectionRanges) ||
|
||||||
|
isSelectionLastLine(selectionRanges, codeManager.code)
|
||||||
|
|
||||||
|
if (hasNoSelection) {
|
||||||
|
const count = 2
|
||||||
|
return doesSceneHaveSweepableSketch(kclManager.ast, count)
|
||||||
|
}
|
||||||
|
|
||||||
|
const canLoft = canLoftSelection(selectionRanges)
|
||||||
|
if (err(canLoft)) return false
|
||||||
|
return canLoft
|
||||||
|
},
|
||||||
'has valid selection for deletion': ({
|
'has valid selection for deletion': ({
|
||||||
context: { selectionRanges },
|
context: { selectionRanges },
|
||||||
}) => {
|
}) => {
|
||||||
@ -596,15 +613,11 @@ export const ModelingMachineProvider = ({
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
'Has exportable geometry': () => {
|
'Has exportable geometry': () => {
|
||||||
if (
|
if (!kclManager.hasErrors() && kclManager.ast.body.length > 0)
|
||||||
kclManager.kclErrors.length === 0 &&
|
|
||||||
kclManager.ast.body.length > 0
|
|
||||||
)
|
|
||||||
return true
|
return true
|
||||||
else {
|
else {
|
||||||
let errorMessage = 'Unable to Export '
|
let errorMessage = 'Unable to Export '
|
||||||
if (kclManager.kclErrors.length > 0)
|
if (kclManager.hasErrors()) errorMessage += 'due to KCL Errors'
|
||||||
errorMessage += 'due to KCL Errors'
|
|
||||||
else if (kclManager.ast.body.length === 0)
|
else if (kclManager.ast.body.length === 0)
|
||||||
errorMessage += 'due to Empty Scene'
|
errorMessage += 'due to Empty Scene'
|
||||||
console.error(errorMessage)
|
console.error(errorMessage)
|
||||||
@ -722,7 +735,11 @@ export const ModelingMachineProvider = ({
|
|||||||
constraint: 'setHorzDistance',
|
constraint: 'setHorzDistance',
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
})
|
})
|
||||||
const _modifiedAst = parse(recast(modifiedAst))
|
const pResult = parse(recast(modifiedAst))
|
||||||
|
if (trap(pResult) || !resultIsOk(pResult))
|
||||||
|
return Promise.reject(new Error('Unexpected compilation error'))
|
||||||
|
const _modifiedAst = pResult.program
|
||||||
|
|
||||||
if (!sketchDetails)
|
if (!sketchDetails)
|
||||||
return Promise.reject(new Error('No sketch details'))
|
return Promise.reject(new Error('No sketch details'))
|
||||||
const updatedPathToNode = updatePathToNodeFromMap(
|
const updatedPathToNode = updatePathToNodeFromMap(
|
||||||
@ -763,7 +780,10 @@ export const ModelingMachineProvider = ({
|
|||||||
constraint: 'setVertDistance',
|
constraint: 'setVertDistance',
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
})
|
})
|
||||||
const _modifiedAst = parse(recast(modifiedAst))
|
const pResult = parse(recast(modifiedAst))
|
||||||
|
if (trap(pResult) || !resultIsOk(pResult))
|
||||||
|
return Promise.reject(new Error('Unexpected compilation error'))
|
||||||
|
const _modifiedAst = pResult.program
|
||||||
if (!sketchDetails)
|
if (!sketchDetails)
|
||||||
return Promise.reject(new Error('No sketch details'))
|
return Promise.reject(new Error('No sketch details'))
|
||||||
const updatedPathToNode = updatePathToNodeFromMap(
|
const updatedPathToNode = updatePathToNodeFromMap(
|
||||||
@ -811,7 +831,10 @@ export const ModelingMachineProvider = ({
|
|||||||
selectionRanges,
|
selectionRanges,
|
||||||
angleOrLength: 'setAngle',
|
angleOrLength: 'setAngle',
|
||||||
}))
|
}))
|
||||||
const _modifiedAst = parse(recast(modifiedAst))
|
const pResult = parse(recast(modifiedAst))
|
||||||
|
if (trap(pResult) || !resultIsOk(pResult))
|
||||||
|
return Promise.reject(new Error('Unexpected compilation error'))
|
||||||
|
const _modifiedAst = pResult.program
|
||||||
if (err(_modifiedAst)) return Promise.reject(_modifiedAst)
|
if (err(_modifiedAst)) return Promise.reject(_modifiedAst)
|
||||||
|
|
||||||
if (!sketchDetails)
|
if (!sketchDetails)
|
||||||
@ -853,7 +876,10 @@ export const ModelingMachineProvider = ({
|
|||||||
await applyConstraintAngleLength({
|
await applyConstraintAngleLength({
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
})
|
})
|
||||||
const _modifiedAst = parse(recast(modifiedAst))
|
const pResult = parse(recast(modifiedAst))
|
||||||
|
if (trap(pResult) || !resultIsOk(pResult))
|
||||||
|
return Promise.reject(new Error('Unexpected compilation error'))
|
||||||
|
const _modifiedAst = pResult.program
|
||||||
if (!sketchDetails)
|
if (!sketchDetails)
|
||||||
return Promise.reject(new Error('No sketch details'))
|
return Promise.reject(new Error('No sketch details'))
|
||||||
const updatedPathToNode = updatePathToNodeFromMap(
|
const updatedPathToNode = updatePathToNodeFromMap(
|
||||||
@ -893,7 +919,10 @@ export const ModelingMachineProvider = ({
|
|||||||
await applyConstraintIntersect({
|
await applyConstraintIntersect({
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
})
|
})
|
||||||
const _modifiedAst = parse(recast(modifiedAst))
|
const pResult = parse(recast(modifiedAst))
|
||||||
|
if (trap(pResult) || !resultIsOk(pResult))
|
||||||
|
return Promise.reject(new Error('Unexpected compilation error'))
|
||||||
|
const _modifiedAst = pResult.program
|
||||||
if (!sketchDetails)
|
if (!sketchDetails)
|
||||||
return Promise.reject(new Error('No sketch details'))
|
return Promise.reject(new Error('No sketch details'))
|
||||||
const updatedPathToNode = updatePathToNodeFromMap(
|
const updatedPathToNode = updatePathToNodeFromMap(
|
||||||
@ -934,7 +963,10 @@ export const ModelingMachineProvider = ({
|
|||||||
constraint: 'xAbs',
|
constraint: 'xAbs',
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
})
|
})
|
||||||
const _modifiedAst = parse(recast(modifiedAst))
|
const pResult = parse(recast(modifiedAst))
|
||||||
|
if (trap(pResult) || !resultIsOk(pResult))
|
||||||
|
return Promise.reject(new Error('Unexpected compilation error'))
|
||||||
|
const _modifiedAst = pResult.program
|
||||||
if (!sketchDetails)
|
if (!sketchDetails)
|
||||||
return Promise.reject(new Error('No sketch details'))
|
return Promise.reject(new Error('No sketch details'))
|
||||||
const updatedPathToNode = updatePathToNodeFromMap(
|
const updatedPathToNode = updatePathToNodeFromMap(
|
||||||
@ -975,7 +1007,10 @@ export const ModelingMachineProvider = ({
|
|||||||
constraint: 'yAbs',
|
constraint: 'yAbs',
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
})
|
})
|
||||||
const _modifiedAst = parse(recast(modifiedAst))
|
const pResult = parse(recast(modifiedAst))
|
||||||
|
if (trap(pResult) || !resultIsOk(pResult))
|
||||||
|
return Promise.reject(new Error('Unexpected compilation error'))
|
||||||
|
const _modifiedAst = pResult.program
|
||||||
if (!sketchDetails)
|
if (!sketchDetails)
|
||||||
return Promise.reject(new Error('No sketch details'))
|
return Promise.reject(new Error('No sketch details'))
|
||||||
const updatedPathToNode = updatePathToNodeFromMap(
|
const updatedPathToNode = updatePathToNodeFromMap(
|
||||||
@ -1016,9 +1051,10 @@ export const ModelingMachineProvider = ({
|
|||||||
const { variableName } = await getVarNameModal({
|
const { variableName } = await getVarNameModal({
|
||||||
valueName: data?.variableName || 'var',
|
valueName: data?.variableName || 'var',
|
||||||
})
|
})
|
||||||
let parsed = parse(recast(kclManager.ast))
|
let pResult = parse(recast(kclManager.ast))
|
||||||
if (trap(parsed)) return Promise.reject(parsed)
|
if (trap(pResult) || !resultIsOk(pResult))
|
||||||
parsed = parsed as Node<Program>
|
return Promise.reject(new Error('Unexpected compilation error'))
|
||||||
|
let parsed = pResult.program
|
||||||
|
|
||||||
const { modifiedAst: _modifiedAst, pathToReplacedNode } =
|
const { modifiedAst: _modifiedAst, pathToReplacedNode } =
|
||||||
moveValueIntoNewVariablePath(
|
moveValueIntoNewVariablePath(
|
||||||
@ -1027,7 +1063,11 @@ export const ModelingMachineProvider = ({
|
|||||||
data?.pathToNode || [],
|
data?.pathToNode || [],
|
||||||
variableName
|
variableName
|
||||||
)
|
)
|
||||||
parsed = parse(recast(_modifiedAst))
|
pResult = parse(recast(_modifiedAst))
|
||||||
|
if (trap(pResult) || !resultIsOk(pResult))
|
||||||
|
return Promise.reject(new Error('Unexpected compilation error'))
|
||||||
|
parsed = pResult.program
|
||||||
|
|
||||||
if (trap(parsed)) return Promise.reject(parsed)
|
if (trap(parsed)) return Promise.reject(parsed)
|
||||||
parsed = parsed as Node<Program>
|
parsed = parsed as Node<Program>
|
||||||
if (!pathToReplacedNode)
|
if (!pathToReplacedNode)
|
||||||
@ -1166,13 +1206,10 @@ export const ModelingMachineProvider = ({
|
|||||||
state: modelingState,
|
state: modelingState,
|
||||||
context: modelingState.context,
|
context: modelingState.context,
|
||||||
send: modelingSend,
|
send: modelingSend,
|
||||||
|
streamRef,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{/* TODO #818: maybe pass reff down to children/app.ts or render app.tsx directly?
|
|
||||||
since realistically it won't ever have generic children that isn't app.tsx */}
|
|
||||||
<div className="h-screen overflow-hidden select-none" ref={streamRef}>
|
|
||||||
{children}
|
{children}
|
||||||
</div>
|
|
||||||
</ModelingMachineContext.Provider>
|
</ModelingMachineContext.Provider>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { processMemory } from './MemoryPane'
|
import { processMemory } from './MemoryPane'
|
||||||
import { enginelessExecutor } from '../../../lib/testHelpers'
|
import { enginelessExecutor } from '../../../lib/testHelpers'
|
||||||
import { initPromise, parse, ProgramMemory } from '../../../lang/wasm'
|
import { assertParse, initPromise, ProgramMemory } from '../../../lang/wasm'
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
await initPromise
|
await initPromise
|
||||||
@ -28,12 +28,16 @@ describe('processMemory', () => {
|
|||||||
|> lineTo([0.98, 5.16], %)
|
|> lineTo([0.98, 5.16], %)
|
||||||
|> lineTo([2.15, 4.32], %)
|
|> lineTo([2.15, 4.32], %)
|
||||||
// |> rx(90, %)`
|
// |> rx(90, %)`
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
const execState = await enginelessExecutor(ast, ProgramMemory.empty())
|
const execState = await enginelessExecutor(ast, ProgramMemory.empty())
|
||||||
const output = processMemory(execState.memory)
|
const output = processMemory(execState.memory)
|
||||||
expect(output.myVar).toEqual(5)
|
expect(output.myVar).toEqual(5)
|
||||||
expect(output.otherVar).toEqual(3)
|
expect(output.otherVar).toEqual(3)
|
||||||
expect(output).toEqual({
|
expect(output).toEqual({
|
||||||
|
HALF_TURN: 180,
|
||||||
|
QUARTER_TURN: 90,
|
||||||
|
THREE_QUARTER_TURN: 270,
|
||||||
|
ZERO: 0,
|
||||||
myVar: 5,
|
myVar: 5,
|
||||||
myFn: '__function(a)__',
|
myFn: '__function(a)__',
|
||||||
otherVar: 3,
|
otherVar: 3,
|
||||||
|
@ -90,7 +90,7 @@ export const sidebarPanes: SidebarPane[] = [
|
|||||||
keybinding: 'Shift + C',
|
keybinding: 'Shift + C',
|
||||||
showBadge: {
|
showBadge: {
|
||||||
value: ({ kclContext }) => {
|
value: ({ kclContext }) => {
|
||||||
return kclContext.errors.length
|
return kclContext.diagnostics.length
|
||||||
},
|
},
|
||||||
onClick: (e) => {
|
onClick: (e) => {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
@ -53,7 +53,7 @@ export function ModelingSidebar({ paneOpacity }: ModelingSidebarProps) {
|
|||||||
settings: settings.context,
|
settings: settings.context,
|
||||||
platform: getPlatformString(),
|
platform: getPlatformString(),
|
||||||
}),
|
}),
|
||||||
[kclContext.errors, settings.context]
|
[kclContext.diagnostics, settings.context]
|
||||||
)
|
)
|
||||||
|
|
||||||
const sidebarActions: SidebarAction[] = [
|
const sidebarActions: SidebarAction[] = [
|
||||||
|
@ -6,6 +6,7 @@ import { useNetworkContext } from '../hooks/useNetworkContext'
|
|||||||
import { NetworkHealthState } from '../hooks/useNetworkStatus'
|
import { NetworkHealthState } from '../hooks/useNetworkStatus'
|
||||||
import { toSync } from 'lib/utils'
|
import { toSync } from 'lib/utils'
|
||||||
import { reportRejection } from 'lib/trap'
|
import { reportRejection } from 'lib/trap'
|
||||||
|
import { StatusBarItemType } from './statusBar/statusBarTypes'
|
||||||
|
|
||||||
export const NETWORK_HEALTH_TEXT: Record<NetworkHealthState, string> = {
|
export const NETWORK_HEALTH_TEXT: Record<NetworkHealthState, string> = {
|
||||||
[NetworkHealthState.Ok]: 'Connected',
|
[NetworkHealthState.Ok]: 'Connected',
|
||||||
@ -64,14 +65,28 @@ const overallConnectionStateColor: Record<NetworkHealthState, IconColorConfig> =
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
const overallConnectionStateIcon: Record<
|
const overallConnectionStateIcon = {
|
||||||
NetworkHealthState,
|
|
||||||
ActionIconProps['icon']
|
|
||||||
> = {
|
|
||||||
[NetworkHealthState.Ok]: 'network',
|
[NetworkHealthState.Ok]: 'network',
|
||||||
[NetworkHealthState.Weak]: 'network',
|
[NetworkHealthState.Weak]: 'network',
|
||||||
[NetworkHealthState.Issue]: 'networkCrossedOut',
|
[NetworkHealthState.Issue]: 'networkCrossedOut',
|
||||||
[NetworkHealthState.Disconnected]: 'networkCrossedOut',
|
[NetworkHealthState.Disconnected]: 'networkCrossedOut',
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export const useNetworkHealthStatus = (): StatusBarItemType => {
|
||||||
|
const { overallState } = useNetworkContext()
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: 'network-health',
|
||||||
|
label: `Network health (${NETWORK_HEALTH_TEXT[overallState]})`,
|
||||||
|
hideLabel: true,
|
||||||
|
element: 'popover',
|
||||||
|
className: overallConnectionStateColor[overallState].icon,
|
||||||
|
toolTip: {
|
||||||
|
children: `Network health (${NETWORK_HEALTH_TEXT[overallState]})`,
|
||||||
|
},
|
||||||
|
icon: overallConnectionStateIcon[overallState],
|
||||||
|
popoverContent: <NetworkHealthPopoverContent />,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const NetworkHealthIndicator = () => {
|
export const NetworkHealthIndicator = () => {
|
||||||
@ -109,8 +124,28 @@ export const NetworkHealthIndicator = () => {
|
|||||||
Network health ({NETWORK_HEALTH_TEXT[overallState]})
|
Network health ({NETWORK_HEALTH_TEXT[overallState]})
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Popover.Button>
|
</Popover.Button>
|
||||||
<Popover.Panel
|
<Popover.Panel>
|
||||||
className="absolute right-0 left-auto bottom-full mb-1 w-64 flex flex-col gap-1 align-stretch bg-chalkboard-10 dark:bg-chalkboard-90 rounded shadow-lg border border-solid border-chalkboard-20/50 dark:border-chalkboard-80/50 text-sm"
|
<NetworkHealthPopoverContent />
|
||||||
|
</Popover.Panel>
|
||||||
|
</Popover>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const NetworkHealthPopoverContent = () => {
|
||||||
|
const {
|
||||||
|
hasIssues,
|
||||||
|
overallState,
|
||||||
|
internetConnected,
|
||||||
|
steps,
|
||||||
|
issues,
|
||||||
|
error,
|
||||||
|
setHasCopied,
|
||||||
|
hasCopied,
|
||||||
|
} = useNetworkContext()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="absolute left-2 bottom-full mb-1 w-64 flex flex-col gap-1 align-stretch bg-chalkboard-10 dark:bg-chalkboard-90 rounded shadow-lg border border-solid border-chalkboard-20/50 dark:border-chalkboard-80/50 text-sm"
|
||||||
data-testid="network-popover"
|
data-testid="network-popover"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@ -126,19 +161,14 @@ export const NetworkHealthIndicator = () => {
|
|||||||
</div>
|
</div>
|
||||||
<ul className="divide-y divide-chalkboard-20 dark:divide-chalkboard-80">
|
<ul className="divide-y divide-chalkboard-20 dark:divide-chalkboard-80">
|
||||||
{Object.keys(steps).map((name) => (
|
{Object.keys(steps).map((name) => (
|
||||||
<li
|
<li key={name} className={'flex flex-col px-2 py-4 gap-1 last:mb-0 '}>
|
||||||
key={name}
|
|
||||||
className={'flex flex-col px-2 py-4 gap-1 last:mb-0 '}
|
|
||||||
>
|
|
||||||
<div className="flex items-center text-left gap-1">
|
<div className="flex items-center text-left gap-1">
|
||||||
<p className="flex-1">{name}</p>
|
<p className="flex-1">{name}</p>
|
||||||
{internetConnected ? (
|
{internetConnected ? (
|
||||||
<ActionIcon
|
<ActionIcon
|
||||||
size="lg"
|
size="lg"
|
||||||
icon={
|
icon={
|
||||||
hasIssueToIcon[
|
hasIssueToIcon[String(issues[name as ConnectingTypeGroup])]
|
||||||
String(issues[name as ConnectingTypeGroup])
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
iconClassName={
|
iconClassName={
|
||||||
hasIssueToIconColors[
|
hasIssueToIconColors[
|
||||||
@ -183,7 +213,6 @@ export const NetworkHealthIndicator = () => {
|
|||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
</Popover.Panel>
|
</div>
|
||||||
</Popover>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import { isDesktop } from 'lib/isDesktop'
|
|||||||
import { components } from 'lib/machine-api'
|
import { components } from 'lib/machine-api'
|
||||||
import { MachineManagerContext } from 'components/MachineManagerProvider'
|
import { MachineManagerContext } from 'components/MachineManagerProvider'
|
||||||
import { CustomIcon } from './CustomIcon'
|
import { CustomIcon } from './CustomIcon'
|
||||||
|
import { StatusBarItemType } from './statusBar/statusBarTypes'
|
||||||
|
|
||||||
export const NetworkMachineIndicator = ({
|
export const NetworkMachineIndicator = ({
|
||||||
className,
|
className,
|
||||||
@ -27,12 +28,7 @@ export const NetworkMachineIndicator = ({
|
|||||||
}
|
}
|
||||||
data-testid="network-machine-toggle"
|
data-testid="network-machine-toggle"
|
||||||
>
|
>
|
||||||
<CustomIcon name="printer3d" className="w-5 h-5" />
|
<NetworkMachinesIcon machineCount={machineCount} />
|
||||||
{machineCount > 0 && (
|
|
||||||
<p aria-hidden className="flex items-center justify-center text-xs">
|
|
||||||
{machineCount}
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
<Tooltip position="top-right" wrapperClassName="ui-open:hidden">
|
<Tooltip position="top-right" wrapperClassName="ui-open:hidden">
|
||||||
Network machines ({machineCount}) {reason && `: ${reason}`}
|
Network machines ({machineCount}) {reason && `: ${reason}`}
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
@ -41,16 +37,59 @@ export const NetworkMachineIndicator = ({
|
|||||||
className="absolute right-0 left-auto bottom-full mb-1 w-64 flex flex-col gap-1 align-stretch bg-chalkboard-10 dark:bg-chalkboard-90 rounded shadow-lg border border-solid border-chalkboard-20/50 dark:border-chalkboard-80/50 text-sm"
|
className="absolute right-0 left-auto bottom-full mb-1 w-64 flex flex-col gap-1 align-stretch bg-chalkboard-10 dark:bg-chalkboard-90 rounded shadow-lg border border-solid border-chalkboard-20/50 dark:border-chalkboard-80/50 text-sm"
|
||||||
data-testid="network-popover"
|
data-testid="network-popover"
|
||||||
>
|
>
|
||||||
|
<NetworkMachinesPopoverContent machines={machines} />
|
||||||
|
</Popover.Panel>
|
||||||
|
</Popover>
|
||||||
|
) : null
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useNetworkMachineStatus = (): StatusBarItemType => {
|
||||||
|
const {
|
||||||
|
noMachinesReason,
|
||||||
|
machines,
|
||||||
|
machines: { length: machineCount },
|
||||||
|
} = useContext(MachineManagerContext)
|
||||||
|
const reason = noMachinesReason()
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: 'network-machines',
|
||||||
|
label: `Network machines (${machineCount}) ${reason && `: ${reason}`}`,
|
||||||
|
hideLabel: true,
|
||||||
|
element: 'popover',
|
||||||
|
toolTip: {
|
||||||
|
children: `Network machines (${machineCount}) ${reason && `: ${reason}`}`,
|
||||||
|
},
|
||||||
|
icon: 'printer3d',
|
||||||
|
popoverContent: <NetworkMachinesPopoverContent machines={machines} />,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function NetworkMachinesIcon({ machineCount }: { machineCount: number }) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<CustomIcon name="printer3d" className="w-5 h-5" />
|
||||||
|
{machineCount > 0 && (
|
||||||
|
<p aria-hidden className="flex items-center justify-center text-xs">
|
||||||
|
{machineCount}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function NetworkMachinesPopoverContent({ machines }: { machines: components['schemas']['MachineInfoResponse'][] }) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
<div className="flex items-center justify-between p-2 rounded-t-sm bg-chalkboard-20 dark:bg-chalkboard-80">
|
<div className="flex items-center justify-between p-2 rounded-t-sm bg-chalkboard-20 dark:bg-chalkboard-80">
|
||||||
<h2 className="text-sm font-sans font-normal">Network machines</h2>
|
<h2 className="text-sm font-sans font-normal">Network machines</h2>
|
||||||
<p
|
<p
|
||||||
data-testid="network"
|
data-testid="network"
|
||||||
className="font-bold text-xs uppercase px-2 py-1 rounded-sm"
|
className="font-bold text-xs uppercase px-2 py-1 rounded-sm"
|
||||||
>
|
>
|
||||||
{machineCount}
|
{machines.length}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
{machineCount > 0 && (
|
{machines.length > 0 && (
|
||||||
<ul className="divide-y divide-chalkboard-20 dark:divide-chalkboard-80">
|
<ul className="divide-y divide-chalkboard-20 dark:divide-chalkboard-80">
|
||||||
{machines.map(
|
{machines.map(
|
||||||
(machine: components['schemas']['MachineInfoResponse']) => {
|
(machine: components['schemas']['MachineInfoResponse']) => {
|
||||||
@ -84,7 +123,6 @@ export const NetworkMachineIndicator = ({
|
|||||||
)}
|
)}
|
||||||
</ul>
|
</ul>
|
||||||
)}
|
)}
|
||||||
</Popover.Panel>
|
</>
|
||||||
</Popover>
|
)
|
||||||
) : null
|
|
||||||
}
|
}
|
@ -68,8 +68,8 @@ function AppLogoLink({
|
|||||||
data-testid="app-logo"
|
data-testid="app-logo"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
onProjectClose(file || null, project?.path || null, false)
|
onProjectClose(file || null, project?.path || null, false)
|
||||||
// Clear the scene and end the session.
|
// Clear the scene.
|
||||||
engineCommandManager.endSession()
|
engineCommandManager.clearScene()
|
||||||
}}
|
}}
|
||||||
to={PATHS.HOME}
|
to={PATHS.HOME}
|
||||||
className={wrapperClassName + ' hover:before:brightness-110'}
|
className={wrapperClassName + ' hover:before:brightness-110'}
|
||||||
@ -190,8 +190,8 @@ function ProjectMenuPopover({
|
|||||||
className: !isDesktop() ? 'hidden' : '',
|
className: !isDesktop() ? 'hidden' : '',
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
onProjectClose(file || null, project?.path || null, true)
|
onProjectClose(file || null, project?.path || null, true)
|
||||||
// Clear the scene and end the session.
|
// Clear the scene.
|
||||||
engineCommandManager.endSession()
|
engineCommandManager.clearScene()
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
].filter(
|
].filter(
|
||||||
|
@ -13,7 +13,7 @@ import { isDesktop } from 'lib/isDesktop'
|
|||||||
import { ActionButton } from 'components/ActionButton'
|
import { ActionButton } from 'components/ActionButton'
|
||||||
import { SettingsFieldInput } from './SettingsFieldInput'
|
import { SettingsFieldInput } from './SettingsFieldInput'
|
||||||
import toast from 'react-hot-toast'
|
import toast from 'react-hot-toast'
|
||||||
import { APP_VERSION, PACKAGE_NAME } from 'routes/Settings'
|
import { APP_VERSION } from 'lib/appVersion'
|
||||||
import { PATHS } from 'lib/paths'
|
import { PATHS } from 'lib/paths'
|
||||||
import {
|
import {
|
||||||
createAndOpenNewTutorialProject,
|
createAndOpenNewTutorialProject,
|
||||||
@ -25,6 +25,7 @@ import { useLspContext } from 'components/LspProvider'
|
|||||||
import { toSync } from 'lib/utils'
|
import { toSync } from 'lib/utils'
|
||||||
import { reportRejection } from 'lib/trap'
|
import { reportRejection } from 'lib/trap'
|
||||||
import { openExternalBrowserIfDesktop } from 'lib/openWindow'
|
import { openExternalBrowserIfDesktop } from 'lib/openWindow'
|
||||||
|
import { PACKAGE_NAME } from 'routes/Settings'
|
||||||
|
|
||||||
interface AllSettingsFieldsProps {
|
interface AllSettingsFieldsProps {
|
||||||
searchParamTab: SettingsLevel
|
searchParamTab: SettingsLevel
|
||||||
|
148
src/components/StatusBar.tsx
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
import { useEffect } from 'react'
|
||||||
|
import { ActionButton } from './ActionButton'
|
||||||
|
import { StatusBarItemType } from './statusBar/statusBarTypes'
|
||||||
|
import Tooltip, { TooltipProps } from './Tooltip'
|
||||||
|
import { ActionIcon } from './ActionIcon'
|
||||||
|
import { Popover } from '@headlessui/react'
|
||||||
|
|
||||||
|
export function StatusBar({
|
||||||
|
globalItems,
|
||||||
|
localItems,
|
||||||
|
}: {
|
||||||
|
globalItems: StatusBarItemType[]
|
||||||
|
localItems: StatusBarItemType[]
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<footer
|
||||||
|
id="statusbar"
|
||||||
|
className="relative z-10 flex justify-between items-center bg-chalkboard-20 dark:bg-chalkboard-90 text-chalkboard-80 dark:text-chalkboard-30 border-t border-t-chalkboard-30 dark:border-t-chalkboard-80"
|
||||||
|
>
|
||||||
|
<menu id="statusbar-globals" className="flex items-stretch">
|
||||||
|
{globalItems.map((item) => (
|
||||||
|
<StatusBarItem key={item.id} {...item} position="left" />
|
||||||
|
))}
|
||||||
|
</menu>
|
||||||
|
<menu id="statusbar-locals" className="flex items-stretch">
|
||||||
|
{localItems.map((item) => (
|
||||||
|
<StatusBarItem key={item.id} {...item} position="right" />
|
||||||
|
))}
|
||||||
|
</menu>
|
||||||
|
</footer>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function StatusBarItem(
|
||||||
|
props: StatusBarItemType & { position: 'left' | 'middle' | 'right' }
|
||||||
|
) {
|
||||||
|
const defaultClassNames = `px-2 py-1 text-xs text-chalkboard-80 dark:text-chalkboard-30 rounded-none border-none hover:bg-chalkboard-30 dark:hover:bg-chalkboard-80 focus:bg-chalkboard-30 dark:focus:bg-chalkboard-80 hover:text-chalkboard-100 dark:hover:text-chalkboard-10 focustext-chalkboard-100 dark:focus:text-chalkboard-10 focus:outline-none focus-visible:ring-2 focus:ring-primary focus:ring-opacity-50`
|
||||||
|
const tooltipPosition: TooltipProps['position'] =
|
||||||
|
props.position === 'middle' ? 'top' : `top-${props.position}`
|
||||||
|
|
||||||
|
switch (props.element) {
|
||||||
|
case 'button':
|
||||||
|
return (
|
||||||
|
<ActionButton
|
||||||
|
Element="button"
|
||||||
|
iconStart={
|
||||||
|
props.icon && {
|
||||||
|
icon: props.icon,
|
||||||
|
iconClassName: props.icon === 'loading' ? 'animate-spin' : '',
|
||||||
|
bgClassName: 'bg-transparent dark:bg-transparent',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
className={defaultClassNames + ' ' + props.className}
|
||||||
|
data-testid={props['data-testid']}
|
||||||
|
>
|
||||||
|
{props.label && (
|
||||||
|
<span className={props.hideLabel ? 'sr-only' : ''}>
|
||||||
|
{props.label}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{props.toolTip && (
|
||||||
|
<Tooltip {...props.toolTip} position={tooltipPosition} />
|
||||||
|
)}
|
||||||
|
</ActionButton>
|
||||||
|
)
|
||||||
|
case 'popover':
|
||||||
|
return (
|
||||||
|
<Popover className="relative">
|
||||||
|
<Popover.Button
|
||||||
|
as={ActionButton}
|
||||||
|
Element="button"
|
||||||
|
iconStart={
|
||||||
|
props.icon && {
|
||||||
|
icon: props.icon,
|
||||||
|
iconClassName: props.icon === 'loading' ? 'animate-spin' : '',
|
||||||
|
bgClassName: 'bg-transparent dark:bg-transparent',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
className={defaultClassNames + ' ' + props.className}
|
||||||
|
data-testid={props['data-testid']}
|
||||||
|
>
|
||||||
|
{props.label && (
|
||||||
|
<span className={props.hideLabel ? 'sr-only' : ''}>
|
||||||
|
{props.label}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{props.toolTip && (
|
||||||
|
<Tooltip
|
||||||
|
{...props.toolTip}
|
||||||
|
wrapperClassName={`${
|
||||||
|
props.toolTip?.wrapperClassName || ''
|
||||||
|
} ui-open:hidden`}
|
||||||
|
position={tooltipPosition}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Popover.Button>
|
||||||
|
<Popover.Panel>{props.popoverContent}</Popover.Panel>
|
||||||
|
</Popover>
|
||||||
|
)
|
||||||
|
case 'text':
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
role="tooltip"
|
||||||
|
className={defaultClassNames + ' ' + props.className}
|
||||||
|
>
|
||||||
|
{props.icon && (
|
||||||
|
<ActionIcon
|
||||||
|
icon={props.icon}
|
||||||
|
iconClassName={props.icon === 'loading' ? 'animate-spin' : ''}
|
||||||
|
bgClassName="bg-transparent dark:bg-transparent"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{props.label && (
|
||||||
|
<span className={props.hideLabel ? 'sr-only' : ''}>
|
||||||
|
{props.label}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{props.toolTip && (
|
||||||
|
<Tooltip {...props.toolTip} position={tooltipPosition} />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
default:
|
||||||
|
return (
|
||||||
|
<ActionButton
|
||||||
|
Element={props.element}
|
||||||
|
to={props.href}
|
||||||
|
iconStart={
|
||||||
|
props.icon && {
|
||||||
|
icon: props.icon,
|
||||||
|
bgClassName: 'bg-transparent dark:bg-transparent',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
className={defaultClassNames + ' ' + props.className}
|
||||||
|
data-testid={props['data-testid']}
|
||||||
|
>
|
||||||
|
{props.label && (
|
||||||
|
<span className={props.hideLabel ? 'sr-only' : ''}>
|
||||||
|
{props.label}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{props.toolTip && (
|
||||||
|
<Tooltip {...props.toolTip} position={tooltipPosition} />
|
||||||
|
)}
|
||||||
|
</ActionButton>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -40,7 +40,10 @@ export function removeConstrainingValuesInfo({
|
|||||||
otherSelections: [],
|
otherSelections: [],
|
||||||
graphSelections: nodes.map(
|
graphSelections: nodes.map(
|
||||||
(node): Selection => ({
|
(node): Selection => ({
|
||||||
codeRef: codeRefFromRange([node.start, node.end], kclManager.ast),
|
codeRef: codeRefFromRange(
|
||||||
|
[node.start, node.end, true],
|
||||||
|
kclManager.ast
|
||||||
|
),
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ type LeftOrRight = 'left' | 'right'
|
|||||||
type Corner = `${TopOrBottom}-${LeftOrRight}`
|
type Corner = `${TopOrBottom}-${LeftOrRight}`
|
||||||
type TooltipPosition = TopOrBottom | LeftOrRight | Corner
|
type TooltipPosition = TopOrBottom | LeftOrRight | Corner
|
||||||
|
|
||||||
interface TooltipProps extends React.PropsWithChildren {
|
export interface TooltipProps extends React.PropsWithChildren {
|
||||||
position?: TooltipPosition
|
position?: TooltipPosition
|
||||||
wrapperClassName?: string
|
wrapperClassName?: string
|
||||||
contentClassName?: string
|
contentClassName?: string
|
||||||
|
96
src/components/statusBar/homeDefaultStatusBarItems.ts
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
import openWindow from 'lib/openWindow'
|
||||||
|
import { StatusBarItemType } from './statusBarTypes'
|
||||||
|
import { reportRejection } from 'lib/trap'
|
||||||
|
import { CoreDumpManager } from 'lib/coredump'
|
||||||
|
import toast from 'react-hot-toast'
|
||||||
|
import { coreDump } from 'lang/wasm'
|
||||||
|
import { APP_VERSION } from 'lib/appVersion'
|
||||||
|
import { Location } from 'react-router-dom'
|
||||||
|
import { PATHS } from 'lib/paths'
|
||||||
|
|
||||||
|
export const homeDefaultStatusBarItems = ({
|
||||||
|
coreDumpManager,
|
||||||
|
location,
|
||||||
|
}: {
|
||||||
|
coreDumpManager?: CoreDumpManager
|
||||||
|
location: Location
|
||||||
|
}): StatusBarItemType[] => [
|
||||||
|
{
|
||||||
|
id: 'version',
|
||||||
|
element: 'externalLink',
|
||||||
|
label: `v${APP_VERSION}`,
|
||||||
|
href: `https://github.com/KittyCAD/modeling-app/releases/tag/v${APP_VERSION}`,
|
||||||
|
toolTip: {
|
||||||
|
children: 'View the release notes on GitHub',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'report-bug',
|
||||||
|
element: 'button',
|
||||||
|
icon: 'bug',
|
||||||
|
label: 'Report a bug',
|
||||||
|
onClick: (event) => reportBug(event, { coreDumpManager }),
|
||||||
|
toolTip: {
|
||||||
|
children: 'Send your current app state to the developers for debugging',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'settings',
|
||||||
|
element: 'link',
|
||||||
|
icon: 'settings',
|
||||||
|
href:
|
||||||
|
'.' +
|
||||||
|
PATHS.SETTINGS +
|
||||||
|
(location.pathname.includes(PATHS.FILE) ? '?tab=project' : ''),
|
||||||
|
'data-testid': 'settings-link',
|
||||||
|
label: 'Settings',
|
||||||
|
toolTip: {
|
||||||
|
children: 'Settings',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
function reportBug(
|
||||||
|
event: {
|
||||||
|
preventDefault: () => void
|
||||||
|
stopPropagation: () => void
|
||||||
|
},
|
||||||
|
dependencies: {
|
||||||
|
coreDumpManager: CoreDumpManager | undefined
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
event?.preventDefault()
|
||||||
|
event?.stopPropagation()
|
||||||
|
const { coreDumpManager } = dependencies
|
||||||
|
|
||||||
|
if (!coreDumpManager) {
|
||||||
|
// open default reporting option
|
||||||
|
openWindow(
|
||||||
|
'https://github.com/KittyCAD/modeling-app/issues/new/choose'
|
||||||
|
).catch(reportRejection)
|
||||||
|
} else {
|
||||||
|
toast
|
||||||
|
.promise(
|
||||||
|
coreDump(coreDumpManager, true),
|
||||||
|
{
|
||||||
|
loading: 'Preparing bug report...',
|
||||||
|
success: 'Bug report opened in new window',
|
||||||
|
error: 'Unable to export a core dump. Using default reporting.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
success: {
|
||||||
|
// Note: this extended duration is especially important for Playwright e2e testing
|
||||||
|
// default duration is 2000 - https://react-hot-toast.com/docs/toast#default-durations
|
||||||
|
duration: 6000,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.catch((err: Error) => {
|
||||||
|
if (err) {
|
||||||
|
openWindow(
|
||||||
|
'https://github.com/KittyCAD/modeling-app/issues/new/choose'
|
||||||
|
).catch(reportRejection)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
28
src/components/statusBar/statusBarTypes.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import { CustomIconName } from 'components/CustomIcon'
|
||||||
|
import { TooltipProps } from 'components/Tooltip'
|
||||||
|
|
||||||
|
export type StatusBarItemType = {
|
||||||
|
id: string
|
||||||
|
label: string
|
||||||
|
icon?: CustomIconName
|
||||||
|
hideLabel?: boolean
|
||||||
|
toolTip?: Omit<TooltipProps, 'position'>
|
||||||
|
className?: string
|
||||||
|
['data-testid']?: string
|
||||||
|
} & (
|
||||||
|
| {
|
||||||
|
element: 'button'
|
||||||
|
onClick: (event: React.MouseEvent<HTMLButtonElement>) => void
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
element: 'popover'
|
||||||
|
popoverContent: React.ReactNode
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
element: 'link' | 'externalLink'
|
||||||
|
href: string
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
element: 'text'
|
||||||
|
}
|
||||||
|
)
|
@ -139,7 +139,9 @@ export default class EditorManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setHighlightRange(range: Array<Selection['codeRef']['range']>): void {
|
setHighlightRange(range: Array<Selection['codeRef']['range']>): void {
|
||||||
this._highlightRange = range
|
this._highlightRange = range.map((s): [number, number] => {
|
||||||
|
return [s[0], s[1]]
|
||||||
|
})
|
||||||
|
|
||||||
const selectionsWithSafeEnds = range.map((s): [number, number] => {
|
const selectionsWithSafeEnds = range.map((s): [number, number] => {
|
||||||
const safeEnd = Math.min(s[1], this._editorView?.state.doc.length || s[1])
|
const safeEnd = Math.min(s[1], this._editorView?.state.doc.length || s[1])
|
||||||
|
@ -18,7 +18,7 @@ import {
|
|||||||
import { err, reportRejection } from 'lib/trap'
|
import { err, reportRejection } from 'lib/trap'
|
||||||
import { DefaultPlaneStr, getFaceDetails } from 'clientSideScene/sceneEntities'
|
import { DefaultPlaneStr, getFaceDetails } from 'clientSideScene/sceneEntities'
|
||||||
import { getNodeFromPath, getNodePathFromSourceRange } from 'lang/queryAst'
|
import { getNodeFromPath, getNodePathFromSourceRange } from 'lang/queryAst'
|
||||||
import { CallExpression } from 'lang/wasm'
|
import { CallExpression, defaultSourceRange } from 'lang/wasm'
|
||||||
import { EdgeCutInfo, ExtrudeFacePlane } from 'machines/modelingMachine'
|
import { EdgeCutInfo, ExtrudeFacePlane } from 'machines/modelingMachine'
|
||||||
|
|
||||||
export function useEngineConnectionSubscriptions() {
|
export function useEngineConnectionSubscriptions() {
|
||||||
@ -46,7 +46,7 @@ export function useEngineConnectionSubscriptions() {
|
|||||||
(editorManager.highlightRange[0][0] !== 0 &&
|
(editorManager.highlightRange[0][0] !== 0 &&
|
||||||
editorManager.highlightRange[0][1] !== 0)
|
editorManager.highlightRange[0][1] !== 0)
|
||||||
) {
|
) {
|
||||||
editorManager.setHighlightRange([[0, 0]])
|
editorManager.setHighlightRange([defaultSourceRange()])
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@ -201,7 +201,7 @@ export function useEngineConnectionSubscriptions() {
|
|||||||
const { z_axis, y_axis, origin } = faceInfo
|
const { z_axis, y_axis, origin } = faceInfo
|
||||||
const sketchPathToNode = getNodePathFromSourceRange(
|
const sketchPathToNode = getNodePathFromSourceRange(
|
||||||
kclManager.ast,
|
kclManager.ast,
|
||||||
err(codeRef) ? [0, 0] : codeRef.range
|
err(codeRef) ? defaultSourceRange() : codeRef.range
|
||||||
)
|
)
|
||||||
|
|
||||||
const getEdgeCutMeta = (): null | EdgeCutInfo => {
|
const getEdgeCutMeta = (): null | EdgeCutInfo => {
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
import { KCLError } from './errors'
|
|
||||||
import { createContext, useContext, useEffect, useState } from 'react'
|
import { createContext, useContext, useEffect, useState } from 'react'
|
||||||
import { type IndexLoaderData } from 'lib/types'
|
import { type IndexLoaderData } from 'lib/types'
|
||||||
import { useLoaderData } from 'react-router-dom'
|
import { useLoaderData } from 'react-router-dom'
|
||||||
import { codeManager, kclManager } from 'lib/singletons'
|
import { codeManager, kclManager } from 'lib/singletons'
|
||||||
|
import { Diagnostic } from '@codemirror/lint'
|
||||||
|
|
||||||
const KclContext = createContext({
|
const KclContext = createContext({
|
||||||
code: codeManager?.code || '',
|
code: codeManager?.code || '',
|
||||||
programMemory: kclManager?.programMemory,
|
programMemory: kclManager?.programMemory,
|
||||||
ast: kclManager?.ast,
|
ast: kclManager?.ast,
|
||||||
isExecuting: kclManager?.isExecuting,
|
isExecuting: kclManager?.isExecuting,
|
||||||
errors: kclManager?.kclErrors,
|
diagnostics: kclManager?.diagnostics,
|
||||||
logs: kclManager?.logs,
|
logs: kclManager?.logs,
|
||||||
wasmInitFailed: kclManager?.wasmInitFailed,
|
wasmInitFailed: kclManager?.wasmInitFailed,
|
||||||
})
|
})
|
||||||
@ -32,7 +32,7 @@ export function KclContextProvider({
|
|||||||
const [programMemory, setProgramMemory] = useState(kclManager.programMemory)
|
const [programMemory, setProgramMemory] = useState(kclManager.programMemory)
|
||||||
const [ast, setAst] = useState(kclManager.ast)
|
const [ast, setAst] = useState(kclManager.ast)
|
||||||
const [isExecuting, setIsExecuting] = useState(false)
|
const [isExecuting, setIsExecuting] = useState(false)
|
||||||
const [errors, setErrors] = useState<KCLError[]>([])
|
const [diagnostics, setErrors] = useState<Diagnostic[]>([])
|
||||||
const [logs, setLogs] = useState<string[]>([])
|
const [logs, setLogs] = useState<string[]>([])
|
||||||
const [wasmInitFailed, setWasmInitFailed] = useState(false)
|
const [wasmInitFailed, setWasmInitFailed] = useState(false)
|
||||||
|
|
||||||
@ -57,7 +57,7 @@ export function KclContextProvider({
|
|||||||
programMemory,
|
programMemory,
|
||||||
ast,
|
ast,
|
||||||
isExecuting,
|
isExecuting,
|
||||||
errors,
|
diagnostics,
|
||||||
logs,
|
logs,
|
||||||
wasmInitFailed,
|
wasmInitFailed,
|
||||||
}}
|
}}
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
import { executeAst, lintAst } from 'lang/langHelpers'
|
import { executeAst, lintAst } from 'lang/langHelpers'
|
||||||
import { Selections } from 'lib/selections'
|
import { Selections } from 'lib/selections'
|
||||||
import { KCLError, kclErrorsToDiagnostics } from './errors'
|
import {
|
||||||
|
KCLError,
|
||||||
|
complilationErrorsToDiagnostics,
|
||||||
|
kclErrorsToDiagnostics,
|
||||||
|
} from './errors'
|
||||||
import { uuidv4 } from 'lib/utils'
|
import { uuidv4 } from 'lib/utils'
|
||||||
import { EngineCommandManager } from './std/engineConnection'
|
import { EngineCommandManager } from './std/engineConnection'
|
||||||
import { err } from 'lib/trap'
|
import { err } from 'lib/trap'
|
||||||
@ -51,11 +55,11 @@ export class KclManager {
|
|||||||
private _programMemory: ProgramMemory = ProgramMemory.empty()
|
private _programMemory: ProgramMemory = ProgramMemory.empty()
|
||||||
lastSuccessfulProgramMemory: ProgramMemory = ProgramMemory.empty()
|
lastSuccessfulProgramMemory: ProgramMemory = ProgramMemory.empty()
|
||||||
private _logs: string[] = []
|
private _logs: string[] = []
|
||||||
private _lints: Diagnostic[] = []
|
private _diagnostics: Diagnostic[] = []
|
||||||
private _kclErrors: KCLError[] = []
|
|
||||||
private _isExecuting = false
|
private _isExecuting = false
|
||||||
private _executeIsStale: ExecuteArgs | null = null
|
private _executeIsStale: ExecuteArgs | null = null
|
||||||
private _wasmInitFailed = true
|
private _wasmInitFailed = true
|
||||||
|
private _hasErrors = false
|
||||||
|
|
||||||
engineCommandManager: EngineCommandManager
|
engineCommandManager: EngineCommandManager
|
||||||
|
|
||||||
@ -63,7 +67,7 @@ export class KclManager {
|
|||||||
private _astCallBack: (arg: Node<Program>) => void = () => {}
|
private _astCallBack: (arg: Node<Program>) => void = () => {}
|
||||||
private _programMemoryCallBack: (arg: ProgramMemory) => void = () => {}
|
private _programMemoryCallBack: (arg: ProgramMemory) => void = () => {}
|
||||||
private _logsCallBack: (arg: string[]) => void = () => {}
|
private _logsCallBack: (arg: string[]) => void = () => {}
|
||||||
private _kclErrorsCallBack: (arg: KCLError[]) => void = () => {}
|
private _kclErrorsCallBack: (errors: Diagnostic[]) => void = () => {}
|
||||||
private _wasmInitFailedCallback: (arg: boolean) => void = () => {}
|
private _wasmInitFailedCallback: (arg: boolean) => void = () => {}
|
||||||
private _executeCallback: () => void = () => {}
|
private _executeCallback: () => void = () => {}
|
||||||
|
|
||||||
@ -84,7 +88,7 @@ export class KclManager {
|
|||||||
this._programMemoryCallBack(programMemory)
|
this._programMemoryCallBack(programMemory)
|
||||||
}
|
}
|
||||||
|
|
||||||
set execState(execState) {
|
private set execState(execState) {
|
||||||
this._execState = execState
|
this._execState = execState
|
||||||
this.programMemory = execState.memory
|
this.programMemory = execState.memory
|
||||||
}
|
}
|
||||||
@ -101,38 +105,28 @@ export class KclManager {
|
|||||||
this._logsCallBack(logs)
|
this._logsCallBack(logs)
|
||||||
}
|
}
|
||||||
|
|
||||||
get lints() {
|
get diagnostics() {
|
||||||
return this._lints
|
return this._diagnostics
|
||||||
}
|
}
|
||||||
|
|
||||||
set lints(lints) {
|
set diagnostics(ds) {
|
||||||
if (lints === this._lints) return
|
if (ds === this._diagnostics) return
|
||||||
this._lints = lints
|
this._diagnostics = ds
|
||||||
// Run the lints through the diagnostics.
|
|
||||||
this.kclErrors = this._kclErrors
|
|
||||||
}
|
|
||||||
|
|
||||||
get kclErrors() {
|
|
||||||
return this._kclErrors
|
|
||||||
}
|
|
||||||
set kclErrors(kclErrors) {
|
|
||||||
if (kclErrors === this._kclErrors && this.lints.length === 0) return
|
|
||||||
this._kclErrors = kclErrors
|
|
||||||
this.setDiagnosticsForCurrentErrors()
|
this.setDiagnosticsForCurrentErrors()
|
||||||
this._kclErrorsCallBack(kclErrors)
|
}
|
||||||
|
|
||||||
|
addDiagnostics(ds: Diagnostic[]) {
|
||||||
|
if (ds.length === 0) return
|
||||||
|
this.diagnostics = this.diagnostics.concat(ds)
|
||||||
|
}
|
||||||
|
|
||||||
|
hasErrors(): boolean {
|
||||||
|
return this._hasErrors
|
||||||
}
|
}
|
||||||
|
|
||||||
setDiagnosticsForCurrentErrors() {
|
setDiagnosticsForCurrentErrors() {
|
||||||
let diagnostics = kclErrorsToDiagnostics(this.kclErrors)
|
editorManager?.setDiagnostics(this.diagnostics)
|
||||||
if (this.lints.length > 0) {
|
this._kclErrorsCallBack(this.diagnostics)
|
||||||
diagnostics = diagnostics.concat(this.lints)
|
|
||||||
}
|
|
||||||
editorManager?.setDiagnostics(diagnostics)
|
|
||||||
}
|
|
||||||
|
|
||||||
addKclErrors(kclErrors: KCLError[]) {
|
|
||||||
if (kclErrors.length === 0) return
|
|
||||||
this.kclErrors = this.kclErrors.concat(kclErrors)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get isExecuting() {
|
get isExecuting() {
|
||||||
@ -188,7 +182,7 @@ export class KclManager {
|
|||||||
setProgramMemory: (arg: ProgramMemory) => void
|
setProgramMemory: (arg: ProgramMemory) => void
|
||||||
setAst: (arg: Node<Program>) => void
|
setAst: (arg: Node<Program>) => void
|
||||||
setLogs: (arg: string[]) => void
|
setLogs: (arg: string[]) => void
|
||||||
setKclErrors: (arg: KCLError[]) => void
|
setKclErrors: (errors: Diagnostic[]) => void
|
||||||
setIsExecuting: (arg: boolean) => void
|
setIsExecuting: (arg: boolean) => void
|
||||||
setWasmInitFailed: (arg: boolean) => void
|
setWasmInitFailed: (arg: boolean) => void
|
||||||
}) {
|
}) {
|
||||||
@ -218,19 +212,28 @@ export class KclManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
safeParse(code: string): Node<Program> | null {
|
safeParse(code: string): Node<Program> | null {
|
||||||
const ast = parse(code)
|
const result = parse(code)
|
||||||
this.lints = []
|
this.diagnostics = []
|
||||||
this.kclErrors = []
|
this._hasErrors = false
|
||||||
if (!err(ast)) return ast
|
|
||||||
const kclerror: KCLError = ast as KCLError
|
|
||||||
|
|
||||||
this.addKclErrors([kclerror])
|
if (err(result)) {
|
||||||
// TODO: re-eval if session should end?
|
const kclerror: KCLError = result as KCLError
|
||||||
if (kclerror.msg === 'file is empty')
|
this.diagnostics = kclErrorsToDiagnostics([kclerror])
|
||||||
this.engineCommandManager?.endSession()
|
this._hasErrors = true
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.addDiagnostics(complilationErrorsToDiagnostics(result.errors))
|
||||||
|
this.addDiagnostics(complilationErrorsToDiagnostics(result.warnings))
|
||||||
|
if (result.errors.length > 0) {
|
||||||
|
this._hasErrors = true
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.program
|
||||||
|
}
|
||||||
|
|
||||||
async ensureWasmInit() {
|
async ensureWasmInit() {
|
||||||
try {
|
try {
|
||||||
await initPromise
|
await initPromise
|
||||||
@ -267,19 +270,16 @@ export class KclManager {
|
|||||||
this._cancelTokens.set(currentExecutionId, false)
|
this._cancelTokens.set(currentExecutionId, false)
|
||||||
|
|
||||||
this.isExecuting = true
|
this.isExecuting = true
|
||||||
// Make sure we clear before starting again. End session will do this.
|
|
||||||
this.engineCommandManager?.endSession()
|
|
||||||
await this.ensureWasmInit()
|
await this.ensureWasmInit()
|
||||||
const { logs, errors, execState, isInterrupted } = await executeAst({
|
const { logs, errors, execState, isInterrupted } = await executeAst({
|
||||||
ast,
|
ast,
|
||||||
idGenerator: this.execState.idGenerator,
|
|
||||||
engineCommandManager: this.engineCommandManager,
|
engineCommandManager: this.engineCommandManager,
|
||||||
})
|
})
|
||||||
|
|
||||||
// Program was not interrupted, setup the scene
|
// Program was not interrupted, setup the scene
|
||||||
// Do not send send scene commands if the program was interrupted, go to clean up
|
// Do not send send scene commands if the program was interrupted, go to clean up
|
||||||
if (!isInterrupted) {
|
if (!isInterrupted) {
|
||||||
this.lints = await lintAst({ ast: ast })
|
this.addDiagnostics(await lintAst({ ast: ast }))
|
||||||
|
|
||||||
sceneInfra.modelingSend({ type: 'code edit during sketch' })
|
sceneInfra.modelingSend({ type: 'code edit during sketch' })
|
||||||
setSelectionFilterToDefault(execState.memory, this.engineCommandManager)
|
setSelectionFilterToDefault(execState.memory, this.engineCommandManager)
|
||||||
@ -321,9 +321,7 @@ export class KclManager {
|
|||||||
|
|
||||||
this.logs = logs
|
this.logs = logs
|
||||||
// Do not add the errors since the program was interrupted and the error is not a real KCL error
|
// Do not add the errors since the program was interrupted and the error is not a real KCL error
|
||||||
this.addKclErrors(isInterrupted ? [] : errors)
|
this.addDiagnostics(isInterrupted ? [] : kclErrorsToDiagnostics(errors))
|
||||||
// Reset the next ID index so that we reuse the previous IDs next time.
|
|
||||||
execState.idGenerator.nextId = 0
|
|
||||||
this.execState = execState
|
this.execState = execState
|
||||||
if (!errors.length) {
|
if (!errors.length) {
|
||||||
this.lastSuccessfulProgramMemory = execState.memory
|
this.lastSuccessfulProgramMemory = execState.memory
|
||||||
@ -364,13 +362,13 @@ export class KclManager {
|
|||||||
|
|
||||||
const { logs, errors, execState } = await executeAst({
|
const { logs, errors, execState } = await executeAst({
|
||||||
ast: newAst,
|
ast: newAst,
|
||||||
idGenerator: this.execState.idGenerator,
|
|
||||||
engineCommandManager: this.engineCommandManager,
|
engineCommandManager: this.engineCommandManager,
|
||||||
useFakeExecutor: true,
|
// We make sure to send an empty program memory to denote we mean mock mode.
|
||||||
|
programMemoryOverride: ProgramMemory.empty(),
|
||||||
})
|
})
|
||||||
|
|
||||||
this._logs = logs
|
this._logs = logs
|
||||||
this._kclErrors = errors
|
this.addDiagnostics(kclErrorsToDiagnostics(errors))
|
||||||
this._execState = execState
|
this._execState = execState
|
||||||
this._programMemory = execState.memory
|
this._programMemory = execState.memory
|
||||||
if (!errors.length) {
|
if (!errors.length) {
|
||||||
@ -398,7 +396,7 @@ export class KclManager {
|
|||||||
...artifact,
|
...artifact,
|
||||||
codeRef: {
|
codeRef: {
|
||||||
...artifact.codeRef,
|
...artifact.codeRef,
|
||||||
range: [node.start, node.end],
|
range: [node.start, node.end, true],
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -490,7 +488,7 @@ export class KclManager {
|
|||||||
if (start && end) {
|
if (start && end) {
|
||||||
returnVal.graphSelections.push({
|
returnVal.graphSelections.push({
|
||||||
codeRef: {
|
codeRef: {
|
||||||
range: [start, end],
|
range: [start, end, true],
|
||||||
pathToNode: path,
|
pathToNode: path,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { parse, initPromise } from './wasm'
|
import { assertParse, initPromise } from './wasm'
|
||||||
import { enginelessExecutor } from '../lib/testHelpers'
|
import { enginelessExecutor } from '../lib/testHelpers'
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
@ -14,7 +14,7 @@ const mySketch001 = startSketchOn('XY')
|
|||||||
|> lineTo([-1.59, -1.54], %)
|
|> lineTo([-1.59, -1.54], %)
|
||||||
|> lineTo([0.46, -5.82], %)
|
|> lineTo([0.46, -5.82], %)
|
||||||
// |> rx(45, %)`
|
// |> rx(45, %)`
|
||||||
const execState = await enginelessExecutor(parse(code))
|
const execState = await enginelessExecutor(assertParse(code))
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const sketch001 = execState.memory.get('mySketch001')
|
const sketch001 = execState.memory.get('mySketch001')
|
||||||
expect(sketch001).toEqual({
|
expect(sketch001).toEqual({
|
||||||
@ -67,7 +67,7 @@ const mySketch001 = startSketchOn('XY')
|
|||||||
|> lineTo([0.46, -5.82], %)
|
|> lineTo([0.46, -5.82], %)
|
||||||
// |> rx(45, %)
|
// |> rx(45, %)
|
||||||
|> extrude(2, %)`
|
|> extrude(2, %)`
|
||||||
const execState = await enginelessExecutor(parse(code))
|
const execState = await enginelessExecutor(assertParse(code))
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const sketch001 = execState.memory.get('mySketch001')
|
const sketch001 = execState.memory.get('mySketch001')
|
||||||
expect(sketch001).toEqual({
|
expect(sketch001).toEqual({
|
||||||
@ -147,7 +147,7 @@ const sk2 = startSketchOn('XY')
|
|||||||
|> extrude(2, %)
|
|> extrude(2, %)
|
||||||
|
|
||||||
`
|
`
|
||||||
const execState = await enginelessExecutor(parse(code))
|
const execState = await enginelessExecutor(assertParse(code))
|
||||||
const programMemory = execState.memory
|
const programMemory = execState.memory
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const geos = [programMemory.get('theExtrude'), programMemory.get('sk2')]
|
const geos = [programMemory.get('theExtrude'), programMemory.get('sk2')]
|
||||||
|
@ -8,20 +8,14 @@ describe('test kclErrToDiagnostic', () => {
|
|||||||
message: '',
|
message: '',
|
||||||
kind: 'semantic',
|
kind: 'semantic',
|
||||||
msg: 'Semantic error',
|
msg: 'Semantic error',
|
||||||
sourceRanges: [
|
sourceRange: [0, 1, true],
|
||||||
[0, 1, 0],
|
|
||||||
[2, 3, 0],
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: '',
|
name: '',
|
||||||
message: '',
|
message: '',
|
||||||
kind: 'type',
|
kind: 'type',
|
||||||
msg: 'Type error',
|
msg: 'Type error',
|
||||||
sourceRanges: [
|
sourceRange: [4, 5, true],
|
||||||
[4, 5, 0],
|
|
||||||
[6, 7, 0],
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
const diagnostics = kclErrorsToDiagnostics(errors)
|
const diagnostics = kclErrorsToDiagnostics(errors)
|
||||||
@ -32,24 +26,12 @@ describe('test kclErrToDiagnostic', () => {
|
|||||||
message: 'Semantic error',
|
message: 'Semantic error',
|
||||||
severity: 'error',
|
severity: 'error',
|
||||||
},
|
},
|
||||||
{
|
|
||||||
from: 2,
|
|
||||||
to: 3,
|
|
||||||
message: 'Semantic error',
|
|
||||||
severity: 'error',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
from: 4,
|
from: 4,
|
||||||
to: 5,
|
to: 5,
|
||||||
message: 'Type error',
|
message: 'Type error',
|
||||||
severity: 'error',
|
severity: 'error',
|
||||||
},
|
},
|
||||||
{
|
|
||||||
from: 6,
|
|
||||||
to: 7,
|
|
||||||
message: 'Type error',
|
|
||||||
severity: 'error',
|
|
||||||
},
|
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -1,88 +1,90 @@
|
|||||||
import { KclError as RustKclError } from '../wasm-lib/kcl/bindings/KclError'
|
import { KclError as RustKclError } from '../wasm-lib/kcl/bindings/KclError'
|
||||||
|
import { CompilationError } from 'wasm-lib/kcl/bindings/CompilationError'
|
||||||
import { Diagnostic as CodeMirrorDiagnostic } from '@codemirror/lint'
|
import { Diagnostic as CodeMirrorDiagnostic } from '@codemirror/lint'
|
||||||
import { posToOffset } from '@kittycad/codemirror-lsp-client'
|
import { posToOffset } from '@kittycad/codemirror-lsp-client'
|
||||||
import { Diagnostic as LspDiagnostic } from 'vscode-languageserver-protocol'
|
import { Diagnostic as LspDiagnostic } from 'vscode-languageserver-protocol'
|
||||||
import { Text } from '@codemirror/state'
|
import { Text } from '@codemirror/state'
|
||||||
|
import { EditorView } from 'codemirror'
|
||||||
const TOP_LEVEL_MODULE_ID = 0
|
import { SourceRange } from 'lang/wasm'
|
||||||
|
|
||||||
type ExtractKind<T> = T extends { kind: infer K } ? K : never
|
type ExtractKind<T> = T extends { kind: infer K } ? K : never
|
||||||
export class KCLError extends Error {
|
export class KCLError extends Error {
|
||||||
kind: ExtractKind<RustKclError> | 'name'
|
kind: ExtractKind<RustKclError> | 'name'
|
||||||
sourceRanges: [number, number, number][]
|
sourceRange: SourceRange
|
||||||
msg: string
|
msg: string
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
kind: ExtractKind<RustKclError> | 'name',
|
kind: ExtractKind<RustKclError> | 'name',
|
||||||
msg: string,
|
msg: string,
|
||||||
sourceRanges: [number, number, number][]
|
sourceRange: SourceRange
|
||||||
) {
|
) {
|
||||||
super()
|
super()
|
||||||
this.kind = kind
|
this.kind = kind
|
||||||
this.msg = msg
|
this.msg = msg
|
||||||
this.sourceRanges = sourceRanges
|
this.sourceRange = sourceRange
|
||||||
Object.setPrototypeOf(this, KCLError.prototype)
|
Object.setPrototypeOf(this, KCLError.prototype)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class KCLLexicalError extends KCLError {
|
export class KCLLexicalError extends KCLError {
|
||||||
constructor(msg: string, sourceRanges: [number, number, number][]) {
|
constructor(msg: string, sourceRange: SourceRange) {
|
||||||
super('lexical', msg, sourceRanges)
|
super('lexical', msg, sourceRange)
|
||||||
Object.setPrototypeOf(this, KCLSyntaxError.prototype)
|
Object.setPrototypeOf(this, KCLSyntaxError.prototype)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class KCLInternalError extends KCLError {
|
export class KCLInternalError extends KCLError {
|
||||||
constructor(msg: string, sourceRanges: [number, number, number][]) {
|
constructor(msg: string, sourceRange: SourceRange) {
|
||||||
super('internal', msg, sourceRanges)
|
super('internal', msg, sourceRange)
|
||||||
Object.setPrototypeOf(this, KCLSyntaxError.prototype)
|
Object.setPrototypeOf(this, KCLSyntaxError.prototype)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class KCLSyntaxError extends KCLError {
|
export class KCLSyntaxError extends KCLError {
|
||||||
constructor(msg: string, sourceRanges: [number, number, number][]) {
|
constructor(msg: string, sourceRange: SourceRange) {
|
||||||
super('syntax', msg, sourceRanges)
|
super('syntax', msg, sourceRange)
|
||||||
Object.setPrototypeOf(this, KCLSyntaxError.prototype)
|
Object.setPrototypeOf(this, KCLSyntaxError.prototype)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class KCLSemanticError extends KCLError {
|
export class KCLSemanticError extends KCLError {
|
||||||
constructor(msg: string, sourceRanges: [number, number, number][]) {
|
constructor(msg: string, sourceRange: SourceRange) {
|
||||||
super('semantic', msg, sourceRanges)
|
super('semantic', msg, sourceRange)
|
||||||
Object.setPrototypeOf(this, KCLSemanticError.prototype)
|
Object.setPrototypeOf(this, KCLSemanticError.prototype)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class KCLTypeError extends KCLError {
|
export class KCLTypeError extends KCLError {
|
||||||
constructor(msg: string, sourceRanges: [number, number, number][]) {
|
constructor(msg: string, sourceRange: SourceRange) {
|
||||||
super('type', msg, sourceRanges)
|
super('type', msg, sourceRange)
|
||||||
Object.setPrototypeOf(this, KCLTypeError.prototype)
|
Object.setPrototypeOf(this, KCLTypeError.prototype)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class KCLUnimplementedError extends KCLError {
|
export class KCLUnimplementedError extends KCLError {
|
||||||
constructor(msg: string, sourceRanges: [number, number, number][]) {
|
constructor(msg: string, sourceRange: SourceRange) {
|
||||||
super('unimplemented', msg, sourceRanges)
|
super('unimplemented', msg, sourceRange)
|
||||||
Object.setPrototypeOf(this, KCLUnimplementedError.prototype)
|
Object.setPrototypeOf(this, KCLUnimplementedError.prototype)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class KCLUnexpectedError extends KCLError {
|
export class KCLUnexpectedError extends KCLError {
|
||||||
constructor(msg: string, sourceRanges: [number, number, number][]) {
|
constructor(msg: string, sourceRange: SourceRange) {
|
||||||
super('unexpected', msg, sourceRanges)
|
super('unexpected', msg, sourceRange)
|
||||||
Object.setPrototypeOf(this, KCLUnexpectedError.prototype)
|
Object.setPrototypeOf(this, KCLUnexpectedError.prototype)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class KCLValueAlreadyDefined extends KCLError {
|
export class KCLValueAlreadyDefined extends KCLError {
|
||||||
constructor(key: string, sourceRanges: [number, number, number][]) {
|
constructor(key: string, sourceRange: SourceRange) {
|
||||||
super('name', `Key ${key} was already defined elsewhere`, sourceRanges)
|
super('name', `Key ${key} was already defined elsewhere`, sourceRange)
|
||||||
Object.setPrototypeOf(this, KCLValueAlreadyDefined.prototype)
|
Object.setPrototypeOf(this, KCLValueAlreadyDefined.prototype)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class KCLUndefinedValueError extends KCLError {
|
export class KCLUndefinedValueError extends KCLError {
|
||||||
constructor(key: string, sourceRanges: [number, number, number][]) {
|
constructor(key: string, sourceRange: SourceRange) {
|
||||||
super('name', `Key ${key} has not been defined`, sourceRanges)
|
super('name', `Key ${key} has not been defined`, sourceRange)
|
||||||
Object.setPrototypeOf(this, KCLUndefinedValueError.prototype)
|
Object.setPrototypeOf(this, KCLUndefinedValueError.prototype)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -99,27 +101,14 @@ export function lspDiagnosticsToKclErrors(
|
|||||||
.flatMap(
|
.flatMap(
|
||||||
({ range, message }) =>
|
({ range, message }) =>
|
||||||
new KCLError('unexpected', message, [
|
new KCLError('unexpected', message, [
|
||||||
[
|
|
||||||
posToOffset(doc, range.start)!,
|
posToOffset(doc, range.start)!,
|
||||||
posToOffset(doc, range.end)!,
|
posToOffset(doc, range.end)!,
|
||||||
TOP_LEVEL_MODULE_ID,
|
true,
|
||||||
],
|
|
||||||
])
|
])
|
||||||
)
|
)
|
||||||
.filter(({ sourceRanges }) => {
|
|
||||||
const [from, to, moduleId] = sourceRanges[0]
|
|
||||||
return (
|
|
||||||
from !== null &&
|
|
||||||
to !== null &&
|
|
||||||
from !== undefined &&
|
|
||||||
to !== undefined &&
|
|
||||||
// Filter out errors that are not from the top-level module.
|
|
||||||
moduleId === TOP_LEVEL_MODULE_ID
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.sort((a, b) => {
|
.sort((a, b) => {
|
||||||
const c = a.sourceRanges[0][0]
|
const c = a.sourceRange[0]
|
||||||
const d = b.sourceRanges[0][0]
|
const d = b.sourceRange[0]
|
||||||
switch (true) {
|
switch (true) {
|
||||||
case c < d:
|
case c < d:
|
||||||
return -1
|
return -1
|
||||||
@ -137,17 +126,48 @@ export function lspDiagnosticsToKclErrors(
|
|||||||
export function kclErrorsToDiagnostics(
|
export function kclErrorsToDiagnostics(
|
||||||
errors: KCLError[]
|
errors: KCLError[]
|
||||||
): CodeMirrorDiagnostic[] {
|
): CodeMirrorDiagnostic[] {
|
||||||
return errors?.flatMap((err) => {
|
return errors
|
||||||
const sourceRanges: CodeMirrorDiagnostic[] = err.sourceRanges
|
?.filter((err) => err.sourceRange[2])
|
||||||
// Filter out errors that are not from the top-level module.
|
.map((err) => {
|
||||||
.filter(([_start, _end, moduleId]) => moduleId === TOP_LEVEL_MODULE_ID)
|
return {
|
||||||
.map(([from, to]) => {
|
from: err.sourceRange[0],
|
||||||
return { from, to, message: err.msg, severity: 'error' }
|
to: err.sourceRange[1],
|
||||||
})
|
message: err.msg,
|
||||||
// Make sure we didn't filter out all the source ranges.
|
severity: 'error',
|
||||||
if (sourceRanges.length === 0) {
|
}
|
||||||
sourceRanges.push({ from: 0, to: 0, message: err.msg, severity: 'error' })
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function complilationErrorsToDiagnostics(
|
||||||
|
errors: CompilationError[]
|
||||||
|
): CodeMirrorDiagnostic[] {
|
||||||
|
return errors
|
||||||
|
?.filter((err) => err.sourceRange[2] === 0)
|
||||||
|
.map((err) => {
|
||||||
|
let severity: any = 'error'
|
||||||
|
if (err.severity === 'Warning') {
|
||||||
|
severity = 'warning'
|
||||||
|
}
|
||||||
|
let actions
|
||||||
|
const suggestion = err.suggestion
|
||||||
|
if (suggestion) {
|
||||||
|
actions = [
|
||||||
|
{
|
||||||
|
name: suggestion.title,
|
||||||
|
apply: (view: EditorView, from: number, to: number) => {
|
||||||
|
view.dispatch({
|
||||||
|
changes: { from, to, insert: suggestion.insert },
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
from: err.sourceRange[0],
|
||||||
|
to: err.sourceRange[1],
|
||||||
|
message: err.message,
|
||||||
|
severity,
|
||||||
|
actions,
|
||||||
}
|
}
|
||||||
return sourceRanges
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import fs from 'node:fs'
|
import fs from 'node:fs'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
parse,
|
assertParse,
|
||||||
ProgramMemory,
|
ProgramMemory,
|
||||||
Sketch,
|
Sketch,
|
||||||
initPromise,
|
initPromise,
|
||||||
@ -472,7 +472,7 @@ describe('Testing Errors', () => {
|
|||||||
const theExtrude = startSketchOn('XY')
|
const theExtrude = startSketchOn('XY')
|
||||||
|> startProfileAt([0, 0], %)
|
|> startProfileAt([0, 0], %)
|
||||||
|> line([-2.4, 5], %)
|
|> line([-2.4, 5], %)
|
||||||
|> line([-0.76], myVarZ, %)
|
|> line(myVarZ, %)
|
||||||
|> line([5,5], %)
|
|> line([5,5], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
|> extrude(4, %)`
|
|> extrude(4, %)`
|
||||||
@ -480,7 +480,7 @@ const theExtrude = startSketchOn('XY')
|
|||||||
new KCLError(
|
new KCLError(
|
||||||
'undefined_value',
|
'undefined_value',
|
||||||
'memory item key `myVarZ` is not defined',
|
'memory item key `myVarZ` is not defined',
|
||||||
[[129, 135, 0]]
|
[129, 135, true]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@ -492,7 +492,7 @@ async function exe(
|
|||||||
code: string,
|
code: string,
|
||||||
programMemory: ProgramMemory = ProgramMemory.empty()
|
programMemory: ProgramMemory = ProgramMemory.empty()
|
||||||
) {
|
) {
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
|
|
||||||
const execState = await enginelessExecutor(ast, programMemory)
|
const execState = await enginelessExecutor(ast, programMemory)
|
||||||
return execState.memory
|
return execState.memory
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { getNodePathFromSourceRange, getNodeFromPath } from './queryAst'
|
import { getNodePathFromSourceRange, getNodeFromPath } from './queryAst'
|
||||||
import { Identifier, parse, initPromise, Parameter } from './wasm'
|
import { Identifier, assertParse, initPromise, Parameter } from './wasm'
|
||||||
import { err } from 'lib/trap'
|
import { err } from 'lib/trap'
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
@ -17,19 +17,19 @@ const sk3 = startSketchAt([0, 0])
|
|||||||
`
|
`
|
||||||
const subStr = 'lineTo([3, 4], %, $yo)'
|
const subStr = 'lineTo([3, 4], %, $yo)'
|
||||||
const lineToSubstringIndex = code.indexOf(subStr)
|
const lineToSubstringIndex = code.indexOf(subStr)
|
||||||
const sourceRange: [number, number] = [
|
const sourceRange: [number, number, boolean] = [
|
||||||
lineToSubstringIndex,
|
lineToSubstringIndex,
|
||||||
lineToSubstringIndex + subStr.length,
|
lineToSubstringIndex + subStr.length,
|
||||||
|
true,
|
||||||
]
|
]
|
||||||
|
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(ast)) throw ast
|
|
||||||
const nodePath = getNodePathFromSourceRange(ast, sourceRange)
|
const nodePath = getNodePathFromSourceRange(ast, sourceRange)
|
||||||
const _node = getNodeFromPath<any>(ast, nodePath)
|
const _node = getNodeFromPath<any>(ast, nodePath)
|
||||||
if (err(_node)) throw _node
|
if (err(_node)) throw _node
|
||||||
const { node } = _node
|
const { node } = _node
|
||||||
|
|
||||||
expect([node.start, node.end]).toEqual(sourceRange)
|
expect([node.start, node.end, true]).toEqual(sourceRange)
|
||||||
expect(node.type).toBe('CallExpression')
|
expect(node.type).toBe('CallExpression')
|
||||||
})
|
})
|
||||||
it('gets path right for function definition params', () => {
|
it('gets path right for function definition params', () => {
|
||||||
@ -45,13 +45,13 @@ const sk3 = startSketchAt([0, 0])
|
|||||||
const b1 = cube([0,0], 10)`
|
const b1 = cube([0,0], 10)`
|
||||||
const subStr = 'pos, scale'
|
const subStr = 'pos, scale'
|
||||||
const subStrIndex = code.indexOf(subStr)
|
const subStrIndex = code.indexOf(subStr)
|
||||||
const sourceRange: [number, number] = [
|
const sourceRange: [number, number, boolean] = [
|
||||||
subStrIndex,
|
subStrIndex,
|
||||||
subStrIndex + 'pos'.length,
|
subStrIndex + 'pos'.length,
|
||||||
|
true,
|
||||||
]
|
]
|
||||||
|
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(ast)) throw ast
|
|
||||||
const nodePath = getNodePathFromSourceRange(ast, sourceRange)
|
const nodePath = getNodePathFromSourceRange(ast, sourceRange)
|
||||||
const _node = getNodeFromPath<Parameter>(ast, nodePath)
|
const _node = getNodeFromPath<Parameter>(ast, nodePath)
|
||||||
if (err(_node)) throw _node
|
if (err(_node)) throw _node
|
||||||
@ -82,13 +82,13 @@ const b1 = cube([0,0], 10)`
|
|||||||
const b1 = cube([0,0], 10)`
|
const b1 = cube([0,0], 10)`
|
||||||
const subStr = 'scale, 0'
|
const subStr = 'scale, 0'
|
||||||
const subStrIndex = code.indexOf(subStr)
|
const subStrIndex = code.indexOf(subStr)
|
||||||
const sourceRange: [number, number] = [
|
const sourceRange: [number, number, boolean] = [
|
||||||
subStrIndex,
|
subStrIndex,
|
||||||
subStrIndex + 'scale'.length,
|
subStrIndex + 'scale'.length,
|
||||||
|
true,
|
||||||
]
|
]
|
||||||
|
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(ast)) throw ast
|
|
||||||
const nodePath = getNodePathFromSourceRange(ast, sourceRange)
|
const nodePath = getNodePathFromSourceRange(ast, sourceRange)
|
||||||
const _node = getNodeFromPath<Identifier>(ast, nodePath)
|
const _node = getNodeFromPath<Identifier>(ast, nodePath)
|
||||||
if (err(_node)) throw _node
|
if (err(_node)) throw _node
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { parse, initPromise, programMemoryInit } from './wasm'
|
import { assertParse, initPromise, programMemoryInit } from './wasm'
|
||||||
import { enginelessExecutor } from '../lib/testHelpers'
|
import { enginelessExecutor } from '../lib/testHelpers'
|
||||||
import { assert } from 'vitest'
|
|
||||||
// These unit tests makes web requests to a public github repository.
|
// These unit tests makes web requests to a public github repository.
|
||||||
|
|
||||||
interface KclSampleFile {
|
interface KclSampleFile {
|
||||||
@ -58,8 +57,7 @@ describe('Test KCL Samples from public Github repository', () => {
|
|||||||
files.forEach((file: KclSampleFile) => {
|
files.forEach((file: KclSampleFile) => {
|
||||||
it(`should parse ${file.filename} without errors`, async () => {
|
it(`should parse ${file.filename} without errors`, async () => {
|
||||||
const code = await getKclSampleCodeFromGithub(file.filename)
|
const code = await getKclSampleCodeFromGithub(file.filename)
|
||||||
const parsed = parse(code)
|
assertParse(code)
|
||||||
assert(!(parsed instanceof Error))
|
|
||||||
}, 1000)
|
}, 1000)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -71,9 +69,8 @@ describe('Test KCL Samples from public Github repository', () => {
|
|||||||
for (let i = 0; i < files.length; i++) {
|
for (let i = 0; i < files.length; i++) {
|
||||||
const file: KclSampleFile = files[i]
|
const file: KclSampleFile = files[i]
|
||||||
const code = await getKclSampleCodeFromGithub(file.filename)
|
const code = await getKclSampleCodeFromGithub(file.filename)
|
||||||
const parsed = parse(code)
|
const ast = assertParse(code)
|
||||||
assert(!(parsed instanceof Error))
|
await enginelessExecutor(ast, programMemoryInit())
|
||||||
await enginelessExecutor(parsed, programMemoryInit())
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
files.length * 1000
|
files.length * 1000
|
||||||
|
@ -2,7 +2,6 @@ import {
|
|||||||
Program,
|
Program,
|
||||||
_executor,
|
_executor,
|
||||||
ProgramMemory,
|
ProgramMemory,
|
||||||
programMemoryInit,
|
|
||||||
kclLint,
|
kclLint,
|
||||||
emptyExecState,
|
emptyExecState,
|
||||||
ExecState,
|
ExecState,
|
||||||
@ -11,7 +10,6 @@ import { enginelessExecutor } from 'lib/testHelpers'
|
|||||||
import { EngineCommandManager } from 'lang/std/engineConnection'
|
import { EngineCommandManager } from 'lang/std/engineConnection'
|
||||||
import { KCLError } from 'lang/errors'
|
import { KCLError } from 'lang/errors'
|
||||||
import { Diagnostic } from '@codemirror/lint'
|
import { Diagnostic } from '@codemirror/lint'
|
||||||
import { IdGenerator } from 'wasm-lib/kcl/bindings/IdGenerator'
|
|
||||||
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||||
|
|
||||||
export type ToolTip =
|
export type ToolTip =
|
||||||
@ -49,15 +47,13 @@ export const toolTips: Array<ToolTip> = [
|
|||||||
export async function executeAst({
|
export async function executeAst({
|
||||||
ast,
|
ast,
|
||||||
engineCommandManager,
|
engineCommandManager,
|
||||||
useFakeExecutor = false,
|
// If you set programMemoryOverride we assume you mean mock mode. Since that
|
||||||
|
// is the only way to go about it.
|
||||||
programMemoryOverride,
|
programMemoryOverride,
|
||||||
idGenerator,
|
|
||||||
}: {
|
}: {
|
||||||
ast: Node<Program>
|
ast: Node<Program>
|
||||||
engineCommandManager: EngineCommandManager
|
engineCommandManager: EngineCommandManager
|
||||||
useFakeExecutor?: boolean
|
|
||||||
programMemoryOverride?: ProgramMemory
|
programMemoryOverride?: ProgramMemory
|
||||||
idGenerator?: IdGenerator
|
|
||||||
isInterrupted?: boolean
|
isInterrupted?: boolean
|
||||||
}): Promise<{
|
}): Promise<{
|
||||||
logs: string[]
|
logs: string[]
|
||||||
@ -66,22 +62,14 @@ export async function executeAst({
|
|||||||
isInterrupted: boolean
|
isInterrupted: boolean
|
||||||
}> {
|
}> {
|
||||||
try {
|
try {
|
||||||
if (!useFakeExecutor) {
|
const execState = await (programMemoryOverride
|
||||||
engineCommandManager.endSession()
|
? enginelessExecutor(ast, programMemoryOverride)
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
: _executor(ast, engineCommandManager))
|
||||||
engineCommandManager.startNewSession()
|
|
||||||
}
|
await engineCommandManager.waitForAllCommands(
|
||||||
const execState = await (useFakeExecutor
|
programMemoryOverride !== undefined
|
||||||
? enginelessExecutor(ast, programMemoryOverride || programMemoryInit())
|
)
|
||||||
: _executor(
|
|
||||||
ast,
|
|
||||||
programMemoryInit(),
|
|
||||||
idGenerator,
|
|
||||||
engineCommandManager,
|
|
||||||
false
|
|
||||||
))
|
|
||||||
|
|
||||||
await engineCommandManager.waitForAllCommands(useFakeExecutor)
|
|
||||||
return {
|
return {
|
||||||
logs: [],
|
logs: [],
|
||||||
errors: [],
|
errors: [],
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { parse, recast, initPromise, Identifier } from './wasm'
|
import { assertParse, recast, initPromise, Identifier } from './wasm'
|
||||||
import {
|
import {
|
||||||
createLiteral,
|
createLiteral,
|
||||||
createIdentifier,
|
createIdentifier,
|
||||||
@ -146,10 +146,13 @@ function giveSketchFnCallTagTestHelper(
|
|||||||
// giveSketchFnCallTag inputs and outputs an ast, which is very verbose for testing
|
// giveSketchFnCallTag inputs and outputs an ast, which is very verbose for testing
|
||||||
// this wrapper changes the input and output to code
|
// this wrapper changes the input and output to code
|
||||||
// making it more of an integration test, but easier to read the test intention is the goal
|
// making it more of an integration test, but easier to read the test intention is the goal
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(ast)) throw ast
|
|
||||||
const start = code.indexOf(searchStr)
|
const start = code.indexOf(searchStr)
|
||||||
const range: [number, number] = [start, start + searchStr.length]
|
const range: [number, number, boolean] = [
|
||||||
|
start,
|
||||||
|
start + searchStr.length,
|
||||||
|
true,
|
||||||
|
]
|
||||||
const sketchRes = giveSketchFnCallTag(ast, range)
|
const sketchRes = giveSketchFnCallTag(ast, range)
|
||||||
if (err(sketchRes)) throw sketchRes
|
if (err(sketchRes)) throw sketchRes
|
||||||
const { modifiedAst, tag, isTagExisting } = sketchRes
|
const { modifiedAst, tag, isTagExisting } = sketchRes
|
||||||
@ -221,14 +224,13 @@ part001 = startSketchOn('XY')
|
|||||||
|> angledLine([jkl(yo) + 2, 3.09], %)
|
|> angledLine([jkl(yo) + 2, 3.09], %)
|
||||||
yo2 = hmm([identifierGuy + 5])`
|
yo2 = hmm([identifierGuy + 5])`
|
||||||
it('should move a binary expression into a new variable', async () => {
|
it('should move a binary expression into a new variable', async () => {
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(ast)) throw ast
|
|
||||||
const execState = await enginelessExecutor(ast)
|
const execState = await enginelessExecutor(ast)
|
||||||
const startIndex = code.indexOf('100 + 100') + 1
|
const startIndex = code.indexOf('100 + 100') + 1
|
||||||
const { modifiedAst } = moveValueIntoNewVariable(
|
const { modifiedAst } = moveValueIntoNewVariable(
|
||||||
ast,
|
ast,
|
||||||
execState.memory,
|
execState.memory,
|
||||||
[startIndex, startIndex],
|
[startIndex, startIndex, true],
|
||||||
'newVar'
|
'newVar'
|
||||||
)
|
)
|
||||||
const newCode = recast(modifiedAst)
|
const newCode = recast(modifiedAst)
|
||||||
@ -236,14 +238,13 @@ yo2 = hmm([identifierGuy + 5])`
|
|||||||
expect(newCode).toContain(`angledLine([newVar, 3.09], %)`)
|
expect(newCode).toContain(`angledLine([newVar, 3.09], %)`)
|
||||||
})
|
})
|
||||||
it('should move a value into a new variable', async () => {
|
it('should move a value into a new variable', async () => {
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(ast)) throw ast
|
|
||||||
const execState = await enginelessExecutor(ast)
|
const execState = await enginelessExecutor(ast)
|
||||||
const startIndex = code.indexOf('2.8') + 1
|
const startIndex = code.indexOf('2.8') + 1
|
||||||
const { modifiedAst } = moveValueIntoNewVariable(
|
const { modifiedAst } = moveValueIntoNewVariable(
|
||||||
ast,
|
ast,
|
||||||
execState.memory,
|
execState.memory,
|
||||||
[startIndex, startIndex],
|
[startIndex, startIndex, true],
|
||||||
'newVar'
|
'newVar'
|
||||||
)
|
)
|
||||||
const newCode = recast(modifiedAst)
|
const newCode = recast(modifiedAst)
|
||||||
@ -251,14 +252,13 @@ yo2 = hmm([identifierGuy + 5])`
|
|||||||
expect(newCode).toContain(`line([newVar, 0], %)`)
|
expect(newCode).toContain(`line([newVar, 0], %)`)
|
||||||
})
|
})
|
||||||
it('should move a callExpression into a new variable', async () => {
|
it('should move a callExpression into a new variable', async () => {
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(ast)) throw ast
|
|
||||||
const execState = await enginelessExecutor(ast)
|
const execState = await enginelessExecutor(ast)
|
||||||
const startIndex = code.indexOf('def(')
|
const startIndex = code.indexOf('def(')
|
||||||
const { modifiedAst } = moveValueIntoNewVariable(
|
const { modifiedAst } = moveValueIntoNewVariable(
|
||||||
ast,
|
ast,
|
||||||
execState.memory,
|
execState.memory,
|
||||||
[startIndex, startIndex],
|
[startIndex, startIndex, true],
|
||||||
'newVar'
|
'newVar'
|
||||||
)
|
)
|
||||||
const newCode = recast(modifiedAst)
|
const newCode = recast(modifiedAst)
|
||||||
@ -266,14 +266,13 @@ yo2 = hmm([identifierGuy + 5])`
|
|||||||
expect(newCode).toContain(`angledLine([newVar, 3.09], %)`)
|
expect(newCode).toContain(`angledLine([newVar, 3.09], %)`)
|
||||||
})
|
})
|
||||||
it('should move a binary expression with call expression into a new variable', async () => {
|
it('should move a binary expression with call expression into a new variable', async () => {
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(ast)) throw ast
|
|
||||||
const execState = await enginelessExecutor(ast)
|
const execState = await enginelessExecutor(ast)
|
||||||
const startIndex = code.indexOf('jkl(') + 1
|
const startIndex = code.indexOf('jkl(') + 1
|
||||||
const { modifiedAst } = moveValueIntoNewVariable(
|
const { modifiedAst } = moveValueIntoNewVariable(
|
||||||
ast,
|
ast,
|
||||||
execState.memory,
|
execState.memory,
|
||||||
[startIndex, startIndex],
|
[startIndex, startIndex, true],
|
||||||
'newVar'
|
'newVar'
|
||||||
)
|
)
|
||||||
const newCode = recast(modifiedAst)
|
const newCode = recast(modifiedAst)
|
||||||
@ -281,14 +280,13 @@ yo2 = hmm([identifierGuy + 5])`
|
|||||||
expect(newCode).toContain(`angledLine([newVar, 3.09], %)`)
|
expect(newCode).toContain(`angledLine([newVar, 3.09], %)`)
|
||||||
})
|
})
|
||||||
it('should move a identifier into a new variable', async () => {
|
it('should move a identifier into a new variable', async () => {
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(ast)) throw ast
|
|
||||||
const execState = await enginelessExecutor(ast)
|
const execState = await enginelessExecutor(ast)
|
||||||
const startIndex = code.indexOf('identifierGuy +') + 1
|
const startIndex = code.indexOf('identifierGuy +') + 1
|
||||||
const { modifiedAst } = moveValueIntoNewVariable(
|
const { modifiedAst } = moveValueIntoNewVariable(
|
||||||
ast,
|
ast,
|
||||||
execState.memory,
|
execState.memory,
|
||||||
[startIndex, startIndex],
|
[startIndex, startIndex, true],
|
||||||
'newVar'
|
'newVar'
|
||||||
)
|
)
|
||||||
const newCode = recast(modifiedAst)
|
const newCode = recast(modifiedAst)
|
||||||
@ -305,19 +303,20 @@ describe('testing sketchOnExtrudedFace', () => {
|
|||||||
|> line([8.62, -9.57], %)
|
|> line([8.62, -9.57], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
|> extrude(5 + 7, %)`
|
|> extrude(5 + 7, %)`
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(ast)) throw ast
|
|
||||||
|
|
||||||
const segmentSnippet = `line([9.7, 9.19], %)`
|
const segmentSnippet = `line([9.7, 9.19], %)`
|
||||||
const segmentRange: [number, number] = [
|
const segmentRange: [number, number, boolean] = [
|
||||||
code.indexOf(segmentSnippet),
|
code.indexOf(segmentSnippet),
|
||||||
code.indexOf(segmentSnippet) + segmentSnippet.length,
|
code.indexOf(segmentSnippet) + segmentSnippet.length,
|
||||||
|
true,
|
||||||
]
|
]
|
||||||
const segmentPathToNode = getNodePathFromSourceRange(ast, segmentRange)
|
const segmentPathToNode = getNodePathFromSourceRange(ast, segmentRange)
|
||||||
const extrudeSnippet = `extrude(5 + 7, %)`
|
const extrudeSnippet = `extrude(5 + 7, %)`
|
||||||
const extrudeRange: [number, number] = [
|
const extrudeRange: [number, number, boolean] = [
|
||||||
code.indexOf(extrudeSnippet),
|
code.indexOf(extrudeSnippet),
|
||||||
code.indexOf(extrudeSnippet) + extrudeSnippet.length,
|
code.indexOf(extrudeSnippet) + extrudeSnippet.length,
|
||||||
|
true,
|
||||||
]
|
]
|
||||||
const extrudePathToNode = getNodePathFromSourceRange(ast, extrudeRange)
|
const extrudePathToNode = getNodePathFromSourceRange(ast, extrudeRange)
|
||||||
|
|
||||||
@ -345,18 +344,19 @@ sketch001 = startSketchOn(part001, seg01)`)
|
|||||||
|> line([8.62, -9.57], %)
|
|> line([8.62, -9.57], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
|> extrude(5 + 7, %)`
|
|> extrude(5 + 7, %)`
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(ast)) throw ast
|
|
||||||
const segmentSnippet = `close(%)`
|
const segmentSnippet = `close(%)`
|
||||||
const segmentRange: [number, number] = [
|
const segmentRange: [number, number, boolean] = [
|
||||||
code.indexOf(segmentSnippet),
|
code.indexOf(segmentSnippet),
|
||||||
code.indexOf(segmentSnippet) + segmentSnippet.length,
|
code.indexOf(segmentSnippet) + segmentSnippet.length,
|
||||||
|
true,
|
||||||
]
|
]
|
||||||
const segmentPathToNode = getNodePathFromSourceRange(ast, segmentRange)
|
const segmentPathToNode = getNodePathFromSourceRange(ast, segmentRange)
|
||||||
const extrudeSnippet = `extrude(5 + 7, %)`
|
const extrudeSnippet = `extrude(5 + 7, %)`
|
||||||
const extrudeRange: [number, number] = [
|
const extrudeRange: [number, number, boolean] = [
|
||||||
code.indexOf(extrudeSnippet),
|
code.indexOf(extrudeSnippet),
|
||||||
code.indexOf(extrudeSnippet) + extrudeSnippet.length,
|
code.indexOf(extrudeSnippet) + extrudeSnippet.length,
|
||||||
|
true,
|
||||||
]
|
]
|
||||||
const extrudePathToNode = getNodePathFromSourceRange(ast, extrudeRange)
|
const extrudePathToNode = getNodePathFromSourceRange(ast, extrudeRange)
|
||||||
|
|
||||||
@ -384,18 +384,19 @@ sketch001 = startSketchOn(part001, seg01)`)
|
|||||||
|> line([8.62, -9.57], %)
|
|> line([8.62, -9.57], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
|> extrude(5 + 7, %)`
|
|> extrude(5 + 7, %)`
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(ast)) throw ast
|
|
||||||
const sketchSnippet = `startProfileAt([3.58, 2.06], %)`
|
const sketchSnippet = `startProfileAt([3.58, 2.06], %)`
|
||||||
const sketchRange: [number, number] = [
|
const sketchRange: [number, number, boolean] = [
|
||||||
code.indexOf(sketchSnippet),
|
code.indexOf(sketchSnippet),
|
||||||
code.indexOf(sketchSnippet) + sketchSnippet.length,
|
code.indexOf(sketchSnippet) + sketchSnippet.length,
|
||||||
|
true,
|
||||||
]
|
]
|
||||||
const sketchPathToNode = getNodePathFromSourceRange(ast, sketchRange)
|
const sketchPathToNode = getNodePathFromSourceRange(ast, sketchRange)
|
||||||
const extrudeSnippet = `extrude(5 + 7, %)`
|
const extrudeSnippet = `extrude(5 + 7, %)`
|
||||||
const extrudeRange: [number, number] = [
|
const extrudeRange: [number, number, boolean] = [
|
||||||
code.indexOf(extrudeSnippet),
|
code.indexOf(extrudeSnippet),
|
||||||
code.indexOf(extrudeSnippet) + extrudeSnippet.length,
|
code.indexOf(extrudeSnippet) + extrudeSnippet.length,
|
||||||
|
true,
|
||||||
]
|
]
|
||||||
const extrudePathToNode = getNodePathFromSourceRange(ast, extrudeRange)
|
const extrudePathToNode = getNodePathFromSourceRange(ast, extrudeRange)
|
||||||
|
|
||||||
@ -432,18 +433,19 @@ sketch001 = startSketchOn(part001, 'END')`)
|
|||||||
|> line([-17.67, 0.85], %)
|
|> line([-17.67, 0.85], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
part001 = extrude(5 + 7, sketch001)`
|
part001 = extrude(5 + 7, sketch001)`
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(ast)) throw ast
|
|
||||||
const segmentSnippet = `line([4.99, -0.46], %)`
|
const segmentSnippet = `line([4.99, -0.46], %)`
|
||||||
const segmentRange: [number, number] = [
|
const segmentRange: [number, number, boolean] = [
|
||||||
code.indexOf(segmentSnippet),
|
code.indexOf(segmentSnippet),
|
||||||
code.indexOf(segmentSnippet) + segmentSnippet.length,
|
code.indexOf(segmentSnippet) + segmentSnippet.length,
|
||||||
|
true,
|
||||||
]
|
]
|
||||||
const segmentPathToNode = getNodePathFromSourceRange(ast, segmentRange)
|
const segmentPathToNode = getNodePathFromSourceRange(ast, segmentRange)
|
||||||
const extrudeSnippet = `extrude(5 + 7, sketch001)`
|
const extrudeSnippet = `extrude(5 + 7, sketch001)`
|
||||||
const extrudeRange: [number, number] = [
|
const extrudeRange: [number, number, boolean] = [
|
||||||
code.indexOf(extrudeSnippet),
|
code.indexOf(extrudeSnippet),
|
||||||
code.indexOf(extrudeSnippet) + extrudeSnippet.length,
|
code.indexOf(extrudeSnippet) + extrudeSnippet.length,
|
||||||
|
true,
|
||||||
]
|
]
|
||||||
const extrudePathToNode = getNodePathFromSourceRange(ast, extrudeRange)
|
const extrudePathToNode = getNodePathFromSourceRange(ast, extrudeRange)
|
||||||
|
|
||||||
@ -466,13 +468,13 @@ describe('Testing deleteSegmentFromPipeExpression', () => {
|
|||||||
|> line([306.21, 198.82], %)
|
|> line([306.21, 198.82], %)
|
||||||
|> line([306.21, 198.85], %, $a)
|
|> line([306.21, 198.85], %, $a)
|
||||||
|> line([306.21, 198.87], %)`
|
|> line([306.21, 198.87], %)`
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(ast)) throw ast
|
|
||||||
const execState = await enginelessExecutor(ast)
|
const execState = await enginelessExecutor(ast)
|
||||||
const lineOfInterest = 'line([306.21, 198.85], %, $a)'
|
const lineOfInterest = 'line([306.21, 198.85], %, $a)'
|
||||||
const range: [number, number] = [
|
const range: [number, number, boolean] = [
|
||||||
code.indexOf(lineOfInterest),
|
code.indexOf(lineOfInterest),
|
||||||
code.indexOf(lineOfInterest) + lineOfInterest.length,
|
code.indexOf(lineOfInterest) + lineOfInterest.length,
|
||||||
|
true,
|
||||||
]
|
]
|
||||||
const pathToNode = getNodePathFromSourceRange(ast, range)
|
const pathToNode = getNodePathFromSourceRange(ast, range)
|
||||||
const modifiedAst = deleteSegmentFromPipeExpression(
|
const modifiedAst = deleteSegmentFromPipeExpression(
|
||||||
@ -544,13 +546,13 @@ ${!replace1 ? ` |> ${line}\n` : ''} |> angledLine([-65, ${
|
|||||||
],
|
],
|
||||||
])(`%s`, async (_, line, [replace1, replace2]) => {
|
])(`%s`, async (_, line, [replace1, replace2]) => {
|
||||||
const code = makeCode(line)
|
const code = makeCode(line)
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(ast)) throw ast
|
|
||||||
const execState = await enginelessExecutor(ast)
|
const execState = await enginelessExecutor(ast)
|
||||||
const lineOfInterest = line
|
const lineOfInterest = line
|
||||||
const range: [number, number] = [
|
const range: [number, number, boolean] = [
|
||||||
code.indexOf(lineOfInterest),
|
code.indexOf(lineOfInterest),
|
||||||
code.indexOf(lineOfInterest) + lineOfInterest.length,
|
code.indexOf(lineOfInterest) + lineOfInterest.length,
|
||||||
|
true,
|
||||||
]
|
]
|
||||||
const pathToNode = getNodePathFromSourceRange(ast, range)
|
const pathToNode = getNodePathFromSourceRange(ast, range)
|
||||||
const dependentSegments = findUsesOfTagInPipe(ast, pathToNode)
|
const dependentSegments = findUsesOfTagInPipe(ast, pathToNode)
|
||||||
@ -632,14 +634,14 @@ describe('Testing removeSingleConstraintInfo', () => {
|
|||||||
],
|
],
|
||||||
['tangentialArcTo([3.14 + 0, 13.14], %)', 'arrayIndex', 1],
|
['tangentialArcTo([3.14 + 0, 13.14], %)', 'arrayIndex', 1],
|
||||||
] as const)('stdlib fn: %s', async (expectedFinish, key, value) => {
|
] as const)('stdlib fn: %s', async (expectedFinish, key, value) => {
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(ast)) throw ast
|
|
||||||
|
|
||||||
const execState = await enginelessExecutor(ast)
|
const execState = await enginelessExecutor(ast)
|
||||||
const lineOfInterest = expectedFinish.split('(')[0] + '('
|
const lineOfInterest = expectedFinish.split('(')[0] + '('
|
||||||
const range: [number, number] = [
|
const range: [number, number, boolean] = [
|
||||||
code.indexOf(lineOfInterest) + 1,
|
code.indexOf(lineOfInterest) + 1,
|
||||||
code.indexOf(lineOfInterest) + lineOfInterest.length,
|
code.indexOf(lineOfInterest) + lineOfInterest.length,
|
||||||
|
true,
|
||||||
]
|
]
|
||||||
const pathToNode = getNodePathFromSourceRange(ast, range)
|
const pathToNode = getNodePathFromSourceRange(ast, range)
|
||||||
let argPosition: SimplifiedArgDetails
|
let argPosition: SimplifiedArgDetails
|
||||||
@ -686,14 +688,14 @@ describe('Testing removeSingleConstraintInfo', () => {
|
|||||||
['angledLineToX([12.14 + 0, 12], %)', 'arrayIndex', 1],
|
['angledLineToX([12.14 + 0, 12], %)', 'arrayIndex', 1],
|
||||||
['angledLineToY([30, 10.14 + 0], %)', 'arrayIndex', 0],
|
['angledLineToY([30, 10.14 + 0], %)', 'arrayIndex', 0],
|
||||||
])('stdlib fn: %s', async (expectedFinish, key, value) => {
|
])('stdlib fn: %s', async (expectedFinish, key, value) => {
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(ast)) throw ast
|
|
||||||
|
|
||||||
const execState = await enginelessExecutor(ast)
|
const execState = await enginelessExecutor(ast)
|
||||||
const lineOfInterest = expectedFinish.split('(')[0] + '('
|
const lineOfInterest = expectedFinish.split('(')[0] + '('
|
||||||
const range: [number, number] = [
|
const range: [number, number, boolean] = [
|
||||||
code.indexOf(lineOfInterest) + 1,
|
code.indexOf(lineOfInterest) + 1,
|
||||||
code.indexOf(lineOfInterest) + lineOfInterest.length,
|
code.indexOf(lineOfInterest) + lineOfInterest.length,
|
||||||
|
true,
|
||||||
]
|
]
|
||||||
let argPosition: SimplifiedArgDetails
|
let argPosition: SimplifiedArgDetails
|
||||||
if (key === 'arrayIndex' && typeof value === 'number') {
|
if (key === 'arrayIndex' && typeof value === 'number') {
|
||||||
@ -883,14 +885,14 @@ sketch002 = startSketchOn({
|
|||||||
'%s',
|
'%s',
|
||||||
async (name, { codeBefore, codeAfter, lineOfInterest, type }) => {
|
async (name, { codeBefore, codeAfter, lineOfInterest, type }) => {
|
||||||
// const lineOfInterest = 'line([-2.94, 2.7], %)'
|
// const lineOfInterest = 'line([-2.94, 2.7], %)'
|
||||||
const ast = parse(codeBefore)
|
const ast = assertParse(codeBefore)
|
||||||
if (err(ast)) throw ast
|
|
||||||
const execState = await enginelessExecutor(ast)
|
const execState = await enginelessExecutor(ast)
|
||||||
|
|
||||||
// deleteFromSelection
|
// deleteFromSelection
|
||||||
const range: [number, number] = [
|
const range: [number, number, boolean] = [
|
||||||
codeBefore.indexOf(lineOfInterest),
|
codeBefore.indexOf(lineOfInterest),
|
||||||
codeBefore.indexOf(lineOfInterest) + lineOfInterest.length,
|
codeBefore.indexOf(lineOfInterest) + lineOfInterest.length,
|
||||||
|
true,
|
||||||
]
|
]
|
||||||
const artifact = { type } as Artifact
|
const artifact = { type } as Artifact
|
||||||
const newAst = await deleteFromSelection(
|
const newAst = await deleteFromSelection(
|
||||||
|
@ -346,6 +346,37 @@ export function extrudeSketch(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function loftSketches(
|
||||||
|
node: Node<Program>,
|
||||||
|
declarators: VariableDeclarator[]
|
||||||
|
): {
|
||||||
|
modifiedAst: Node<Program>
|
||||||
|
pathToNode: PathToNode
|
||||||
|
} {
|
||||||
|
const modifiedAst = structuredClone(node)
|
||||||
|
const name = findUniqueName(node, KCL_DEFAULT_CONSTANT_PREFIXES.LOFT)
|
||||||
|
const elements = declarators.map((d) => createIdentifier(d.id.name))
|
||||||
|
const loft = createCallExpressionStdLib('loft', [
|
||||||
|
createArrayExpression(elements),
|
||||||
|
])
|
||||||
|
const declaration = createVariableDeclaration(name, loft)
|
||||||
|
modifiedAst.body.push(declaration)
|
||||||
|
const pathToNode: PathToNode = [
|
||||||
|
['body', ''],
|
||||||
|
[modifiedAst.body.length - 1, 'index'],
|
||||||
|
['declarations', 'VariableDeclaration'],
|
||||||
|
['0', 'index'],
|
||||||
|
['init', 'VariableDeclarator'],
|
||||||
|
['arguments', 'CallExpression'],
|
||||||
|
[0, 'index'],
|
||||||
|
]
|
||||||
|
|
||||||
|
return {
|
||||||
|
modifiedAst,
|
||||||
|
pathToNode,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function revolveSketch(
|
export function revolveSketch(
|
||||||
node: Node<Program>,
|
node: Node<Program>,
|
||||||
pathToNode: PathToNode,
|
pathToNode: PathToNode,
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import {
|
import {
|
||||||
parse,
|
assertParse,
|
||||||
recast,
|
recast,
|
||||||
initPromise,
|
initPromise,
|
||||||
PathToNode,
|
PathToNode,
|
||||||
@ -18,7 +18,7 @@ import {
|
|||||||
FilletParameters,
|
FilletParameters,
|
||||||
ChamferParameters,
|
ChamferParameters,
|
||||||
EdgeTreatmentParameters,
|
EdgeTreatmentParameters,
|
||||||
} from './addFillet'
|
} from './addEdgeTreatment'
|
||||||
import { getNodeFromPath, getNodePathFromSourceRange } from '../queryAst'
|
import { getNodeFromPath, getNodePathFromSourceRange } from '../queryAst'
|
||||||
import { createLiteral } from 'lang/modifyAst'
|
import { createLiteral } from 'lang/modifyAst'
|
||||||
import { err } from 'lib/trap'
|
import { err } from 'lib/trap'
|
||||||
@ -78,9 +78,10 @@ const runGetPathToExtrudeForSegmentSelectionTest = async (
|
|||||||
code: string,
|
code: string,
|
||||||
expectedExtrudeSnippet: string
|
expectedExtrudeSnippet: string
|
||||||
): CallExpression | PipeExpression | Error {
|
): CallExpression | PipeExpression | Error {
|
||||||
const extrudeRange: [number, number] = [
|
const extrudeRange: [number, number, boolean] = [
|
||||||
code.indexOf(expectedExtrudeSnippet),
|
code.indexOf(expectedExtrudeSnippet),
|
||||||
code.indexOf(expectedExtrudeSnippet) + expectedExtrudeSnippet.length,
|
code.indexOf(expectedExtrudeSnippet) + expectedExtrudeSnippet.length,
|
||||||
|
true,
|
||||||
]
|
]
|
||||||
const expectedExtrudePath = getNodePathFromSourceRange(ast, extrudeRange)
|
const expectedExtrudePath = getNodePathFromSourceRange(ast, extrudeRange)
|
||||||
const expectedExtrudeNodeResult = getNodeFromPath<
|
const expectedExtrudeNodeResult = getNodeFromPath<
|
||||||
@ -109,14 +110,13 @@ const runGetPathToExtrudeForSegmentSelectionTest = async (
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ast
|
// ast
|
||||||
const astOrError = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(astOrError)) return new Error('AST not found')
|
|
||||||
const ast = astOrError
|
|
||||||
|
|
||||||
// selection
|
// selection
|
||||||
const segmentRange: [number, number] = [
|
const segmentRange: [number, number, boolean] = [
|
||||||
code.indexOf(selectedSegmentSnippet),
|
code.indexOf(selectedSegmentSnippet),
|
||||||
code.indexOf(selectedSegmentSnippet) + selectedSegmentSnippet.length,
|
code.indexOf(selectedSegmentSnippet) + selectedSegmentSnippet.length,
|
||||||
|
true,
|
||||||
]
|
]
|
||||||
const selection: Selections = {
|
const selection: Selections = {
|
||||||
graphSelections: [
|
graphSelections: [
|
||||||
@ -263,17 +263,14 @@ const runModifyAstCloneWithEdgeTreatmentAndTag = async (
|
|||||||
expectedCode: string
|
expectedCode: string
|
||||||
) => {
|
) => {
|
||||||
// ast
|
// ast
|
||||||
const astOrError = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(astOrError)) {
|
|
||||||
return new Error('AST not found')
|
|
||||||
}
|
|
||||||
const ast = astOrError
|
|
||||||
|
|
||||||
// selection
|
// selection
|
||||||
const segmentRanges: Array<[number, number]> = selectionSnippets.map(
|
const segmentRanges: Array<[number, number, boolean]> = selectionSnippets.map(
|
||||||
(selectionSnippet) => [
|
(selectionSnippet) => [
|
||||||
code.indexOf(selectionSnippet),
|
code.indexOf(selectionSnippet),
|
||||||
code.indexOf(selectionSnippet) + selectionSnippet.length,
|
code.indexOf(selectionSnippet) + selectionSnippet.length,
|
||||||
|
true,
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -603,12 +600,12 @@ extrude001 = extrude(-5, sketch001)
|
|||||||
}, %)
|
}, %)
|
||||||
`
|
`
|
||||||
it('should correctly identify getOppositeEdge and baseEdge edges', () => {
|
it('should correctly identify getOppositeEdge and baseEdge edges', () => {
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(ast)) return
|
|
||||||
const lineOfInterest = `line([7.11, 3.48], %, $seg01)`
|
const lineOfInterest = `line([7.11, 3.48], %, $seg01)`
|
||||||
const range: [number, number] = [
|
const range: [number, number, boolean] = [
|
||||||
code.indexOf(lineOfInterest),
|
code.indexOf(lineOfInterest),
|
||||||
code.indexOf(lineOfInterest) + lineOfInterest.length,
|
code.indexOf(lineOfInterest) + lineOfInterest.length,
|
||||||
|
true,
|
||||||
]
|
]
|
||||||
const pathToNode = getNodePathFromSourceRange(ast, range)
|
const pathToNode = getNodePathFromSourceRange(ast, range)
|
||||||
if (err(pathToNode)) return
|
if (err(pathToNode)) return
|
||||||
@ -622,12 +619,12 @@ extrude001 = extrude(-5, sketch001)
|
|||||||
expect(edges).toEqual(['getOppositeEdge', 'baseEdge'])
|
expect(edges).toEqual(['getOppositeEdge', 'baseEdge'])
|
||||||
})
|
})
|
||||||
it('should correctly identify getPreviousAdjacentEdge edges', () => {
|
it('should correctly identify getPreviousAdjacentEdge edges', () => {
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(ast)) return
|
|
||||||
const lineOfInterest = `line([-6.37, 3.88], %, $seg02)`
|
const lineOfInterest = `line([-6.37, 3.88], %, $seg02)`
|
||||||
const range: [number, number] = [
|
const range: [number, number, boolean] = [
|
||||||
code.indexOf(lineOfInterest),
|
code.indexOf(lineOfInterest),
|
||||||
code.indexOf(lineOfInterest) + lineOfInterest.length,
|
code.indexOf(lineOfInterest) + lineOfInterest.length,
|
||||||
|
true,
|
||||||
]
|
]
|
||||||
const pathToNode = getNodePathFromSourceRange(ast, range)
|
const pathToNode = getNodePathFromSourceRange(ast, range)
|
||||||
if (err(pathToNode)) return
|
if (err(pathToNode)) return
|
||||||
@ -641,12 +638,12 @@ extrude001 = extrude(-5, sketch001)
|
|||||||
expect(edges).toEqual(['getPreviousAdjacentEdge'])
|
expect(edges).toEqual(['getPreviousAdjacentEdge'])
|
||||||
})
|
})
|
||||||
it('should correctly identify no edges', () => {
|
it('should correctly identify no edges', () => {
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(ast)) return
|
|
||||||
const lineOfInterest = `line([-3.29, -13.85], %)`
|
const lineOfInterest = `line([-3.29, -13.85], %)`
|
||||||
const range: [number, number] = [
|
const range: [number, number, boolean] = [
|
||||||
code.indexOf(lineOfInterest),
|
code.indexOf(lineOfInterest),
|
||||||
code.indexOf(lineOfInterest) + lineOfInterest.length,
|
code.indexOf(lineOfInterest) + lineOfInterest.length,
|
||||||
|
true,
|
||||||
]
|
]
|
||||||
const pathToNode = getNodePathFromSourceRange(ast, range)
|
const pathToNode = getNodePathFromSourceRange(ast, range)
|
||||||
if (err(pathToNode)) return
|
if (err(pathToNode)) return
|
||||||
@ -667,19 +664,15 @@ describe('Testing button states', () => {
|
|||||||
segmentSnippet: string,
|
segmentSnippet: string,
|
||||||
expectedState: boolean
|
expectedState: boolean
|
||||||
) => {
|
) => {
|
||||||
// ast
|
const ast = assertParse(code)
|
||||||
const astOrError = parse(code)
|
|
||||||
if (err(astOrError)) {
|
|
||||||
return new Error('AST not found')
|
|
||||||
}
|
|
||||||
const ast = astOrError
|
|
||||||
|
|
||||||
const range: [number, number] = segmentSnippet
|
const range: [number, number, boolean] = segmentSnippet
|
||||||
? [
|
? [
|
||||||
code.indexOf(segmentSnippet),
|
code.indexOf(segmentSnippet),
|
||||||
code.indexOf(segmentSnippet) + segmentSnippet.length,
|
code.indexOf(segmentSnippet) + segmentSnippet.length,
|
||||||
|
true,
|
||||||
]
|
]
|
||||||
: [ast.end, ast.end] // empty line in the end of the code
|
: [ast.end, ast.end, true] // empty line in the end of the code
|
||||||
|
|
||||||
const selectionRanges: Selections = {
|
const selectionRanges: Selections = {
|
||||||
graphSelections: [
|
graphSelections: [
|
@ -1,4 +1,10 @@
|
|||||||
import { parse, recast, initPromise, PathToNode, Identifier } from './wasm'
|
import {
|
||||||
|
assertParse,
|
||||||
|
recast,
|
||||||
|
initPromise,
|
||||||
|
PathToNode,
|
||||||
|
Identifier,
|
||||||
|
} from './wasm'
|
||||||
import {
|
import {
|
||||||
findAllPreviousVariables,
|
findAllPreviousVariables,
|
||||||
isNodeSafeToReplace,
|
isNodeSafeToReplace,
|
||||||
@ -45,14 +51,13 @@ part001 = startSketchOn('XY')
|
|||||||
variableBelowShouldNotBeIncluded = 3
|
variableBelowShouldNotBeIncluded = 3
|
||||||
`
|
`
|
||||||
const rangeStart = code.indexOf('// selection-range-7ish-before-this') - 7
|
const rangeStart = code.indexOf('// selection-range-7ish-before-this') - 7
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(ast)) throw ast
|
|
||||||
const execState = await enginelessExecutor(ast)
|
const execState = await enginelessExecutor(ast)
|
||||||
|
|
||||||
const { variables, bodyPath, insertIndex } = findAllPreviousVariables(
|
const { variables, bodyPath, insertIndex } = findAllPreviousVariables(
|
||||||
ast,
|
ast,
|
||||||
execState.memory,
|
execState.memory,
|
||||||
[rangeStart, rangeStart]
|
[rangeStart, rangeStart, true]
|
||||||
)
|
)
|
||||||
expect(variables).toEqual([
|
expect(variables).toEqual([
|
||||||
{ key: 'baseThick', value: 1 },
|
{ key: 'baseThick', value: 1 },
|
||||||
@ -80,10 +85,9 @@ describe('testing argIsNotIdentifier', () => {
|
|||||||
yo = 5 + 6
|
yo = 5 + 6
|
||||||
yo2 = hmm([identifierGuy + 5])`
|
yo2 = hmm([identifierGuy + 5])`
|
||||||
it('find a safe binaryExpression', () => {
|
it('find a safe binaryExpression', () => {
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(ast)) throw ast
|
|
||||||
const rangeStart = code.indexOf('100 + 100') + 2
|
const rangeStart = code.indexOf('100 + 100') + 2
|
||||||
const result = isNodeSafeToReplace(ast, [rangeStart, rangeStart])
|
const result = isNodeSafeToReplace(ast, [rangeStart, rangeStart, true])
|
||||||
if (err(result)) throw result
|
if (err(result)) throw result
|
||||||
expect(result.isSafe).toBe(true)
|
expect(result.isSafe).toBe(true)
|
||||||
expect(result.value?.type).toBe('BinaryExpression')
|
expect(result.value?.type).toBe('BinaryExpression')
|
||||||
@ -94,20 +98,18 @@ yo2 = hmm([identifierGuy + 5])`
|
|||||||
expect(outCode).toContain(`angledLine([replaceName, 3.09], %)`)
|
expect(outCode).toContain(`angledLine([replaceName, 3.09], %)`)
|
||||||
})
|
})
|
||||||
it('find a safe Identifier', () => {
|
it('find a safe Identifier', () => {
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(ast)) throw ast
|
|
||||||
const rangeStart = code.indexOf('abc')
|
const rangeStart = code.indexOf('abc')
|
||||||
const result = isNodeSafeToReplace(ast, [rangeStart, rangeStart])
|
const result = isNodeSafeToReplace(ast, [rangeStart, rangeStart, true])
|
||||||
if (err(result)) throw result
|
if (err(result)) throw result
|
||||||
expect(result.isSafe).toBe(true)
|
expect(result.isSafe).toBe(true)
|
||||||
expect(result.value?.type).toBe('Identifier')
|
expect(result.value?.type).toBe('Identifier')
|
||||||
expect(code.slice(result.value.start, result.value.end)).toBe('abc')
|
expect(code.slice(result.value.start, result.value.end)).toBe('abc')
|
||||||
})
|
})
|
||||||
it('find a safe CallExpression', () => {
|
it('find a safe CallExpression', () => {
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(ast)) throw ast
|
|
||||||
const rangeStart = code.indexOf('def')
|
const rangeStart = code.indexOf('def')
|
||||||
const result = isNodeSafeToReplace(ast, [rangeStart, rangeStart])
|
const result = isNodeSafeToReplace(ast, [rangeStart, rangeStart, true])
|
||||||
if (err(result)) throw result
|
if (err(result)) throw result
|
||||||
expect(result.isSafe).toBe(true)
|
expect(result.isSafe).toBe(true)
|
||||||
expect(result.value?.type).toBe('CallExpression')
|
expect(result.value?.type).toBe('CallExpression')
|
||||||
@ -118,10 +120,9 @@ yo2 = hmm([identifierGuy + 5])`
|
|||||||
expect(outCode).toContain(`angledLine([replaceName, 3.09], %)`)
|
expect(outCode).toContain(`angledLine([replaceName, 3.09], %)`)
|
||||||
})
|
})
|
||||||
it('find an UNsafe CallExpression, as it has a PipeSubstitution', () => {
|
it('find an UNsafe CallExpression, as it has a PipeSubstitution', () => {
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(ast)) throw ast
|
|
||||||
const rangeStart = code.indexOf('ghi')
|
const rangeStart = code.indexOf('ghi')
|
||||||
const range: [number, number] = [rangeStart, rangeStart]
|
const range: [number, number, boolean] = [rangeStart, rangeStart, true]
|
||||||
const result = isNodeSafeToReplace(ast, range)
|
const result = isNodeSafeToReplace(ast, range)
|
||||||
if (err(result)) throw result
|
if (err(result)) throw result
|
||||||
expect(result.isSafe).toBe(false)
|
expect(result.isSafe).toBe(false)
|
||||||
@ -129,10 +130,9 @@ yo2 = hmm([identifierGuy + 5])`
|
|||||||
expect(code.slice(result.value.start, result.value.end)).toBe('ghi(%)')
|
expect(code.slice(result.value.start, result.value.end)).toBe('ghi(%)')
|
||||||
})
|
})
|
||||||
it('find an UNsafe Identifier, as it is a callee', () => {
|
it('find an UNsafe Identifier, as it is a callee', () => {
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(ast)) throw ast
|
|
||||||
const rangeStart = code.indexOf('ine([2.8,')
|
const rangeStart = code.indexOf('ine([2.8,')
|
||||||
const result = isNodeSafeToReplace(ast, [rangeStart, rangeStart])
|
const result = isNodeSafeToReplace(ast, [rangeStart, rangeStart, true])
|
||||||
if (err(result)) throw result
|
if (err(result)) throw result
|
||||||
expect(result.isSafe).toBe(false)
|
expect(result.isSafe).toBe(false)
|
||||||
expect(result.value?.type).toBe('CallExpression')
|
expect(result.value?.type).toBe('CallExpression')
|
||||||
@ -141,10 +141,9 @@ yo2 = hmm([identifierGuy + 5])`
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
it("find a safe BinaryExpression that's assigned to a variable", () => {
|
it("find a safe BinaryExpression that's assigned to a variable", () => {
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(ast)) throw ast
|
|
||||||
const rangeStart = code.indexOf('5 + 6') + 1
|
const rangeStart = code.indexOf('5 + 6') + 1
|
||||||
const result = isNodeSafeToReplace(ast, [rangeStart, rangeStart])
|
const result = isNodeSafeToReplace(ast, [rangeStart, rangeStart, true])
|
||||||
if (err(result)) throw result
|
if (err(result)) throw result
|
||||||
expect(result.isSafe).toBe(true)
|
expect(result.isSafe).toBe(true)
|
||||||
expect(result.value?.type).toBe('BinaryExpression')
|
expect(result.value?.type).toBe('BinaryExpression')
|
||||||
@ -155,10 +154,9 @@ yo2 = hmm([identifierGuy + 5])`
|
|||||||
expect(outCode).toContain(`yo = replaceName`)
|
expect(outCode).toContain(`yo = replaceName`)
|
||||||
})
|
})
|
||||||
it('find a safe BinaryExpression that has a CallExpression within', () => {
|
it('find a safe BinaryExpression that has a CallExpression within', () => {
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(ast)) throw ast
|
|
||||||
const rangeStart = code.indexOf('jkl') + 1
|
const rangeStart = code.indexOf('jkl') + 1
|
||||||
const result = isNodeSafeToReplace(ast, [rangeStart, rangeStart])
|
const result = isNodeSafeToReplace(ast, [rangeStart, rangeStart, true])
|
||||||
if (err(result)) throw result
|
if (err(result)) throw result
|
||||||
expect(result.isSafe).toBe(true)
|
expect(result.isSafe).toBe(true)
|
||||||
expect(result.value?.type).toBe('BinaryExpression')
|
expect(result.value?.type).toBe('BinaryExpression')
|
||||||
@ -172,11 +170,10 @@ yo2 = hmm([identifierGuy + 5])`
|
|||||||
expect(outCode).toContain(`angledLine([replaceName, 3.09], %)`)
|
expect(outCode).toContain(`angledLine([replaceName, 3.09], %)`)
|
||||||
})
|
})
|
||||||
it('find a safe BinaryExpression within a CallExpression', () => {
|
it('find a safe BinaryExpression within a CallExpression', () => {
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(ast)) throw ast
|
|
||||||
|
|
||||||
const rangeStart = code.indexOf('identifierGuy') + 1
|
const rangeStart = code.indexOf('identifierGuy') + 1
|
||||||
const result = isNodeSafeToReplace(ast, [rangeStart, rangeStart])
|
const result = isNodeSafeToReplace(ast, [rangeStart, rangeStart, true])
|
||||||
if (err(result)) throw result
|
if (err(result)) throw result
|
||||||
|
|
||||||
expect(result.isSafe).toBe(true)
|
expect(result.isSafe).toBe(true)
|
||||||
@ -223,10 +220,13 @@ describe('testing getNodePathFromSourceRange', () => {
|
|||||||
it('finds the second line when cursor is put at the end', () => {
|
it('finds the second line when cursor is put at the end', () => {
|
||||||
const searchLn = `line([0.94, 2.61], %)`
|
const searchLn = `line([0.94, 2.61], %)`
|
||||||
const sourceIndex = code.indexOf(searchLn) + searchLn.length
|
const sourceIndex = code.indexOf(searchLn) + searchLn.length
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(ast)) throw ast
|
|
||||||
|
|
||||||
const result = getNodePathFromSourceRange(ast, [sourceIndex, sourceIndex])
|
const result = getNodePathFromSourceRange(ast, [
|
||||||
|
sourceIndex,
|
||||||
|
sourceIndex,
|
||||||
|
true,
|
||||||
|
])
|
||||||
expect(result).toEqual([
|
expect(result).toEqual([
|
||||||
['body', ''],
|
['body', ''],
|
||||||
[0, 'index'],
|
[0, 'index'],
|
||||||
@ -240,10 +240,13 @@ describe('testing getNodePathFromSourceRange', () => {
|
|||||||
it('finds the last line when cursor is put at the end', () => {
|
it('finds the last line when cursor is put at the end', () => {
|
||||||
const searchLn = `line([-0.21, -1.4], %)`
|
const searchLn = `line([-0.21, -1.4], %)`
|
||||||
const sourceIndex = code.indexOf(searchLn) + searchLn.length
|
const sourceIndex = code.indexOf(searchLn) + searchLn.length
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(ast)) throw ast
|
|
||||||
|
|
||||||
const result = getNodePathFromSourceRange(ast, [sourceIndex, sourceIndex])
|
const result = getNodePathFromSourceRange(ast, [
|
||||||
|
sourceIndex,
|
||||||
|
sourceIndex,
|
||||||
|
true,
|
||||||
|
])
|
||||||
const expected = [
|
const expected = [
|
||||||
['body', ''],
|
['body', ''],
|
||||||
[0, 'index'],
|
[0, 'index'],
|
||||||
@ -259,12 +262,14 @@ describe('testing getNodePathFromSourceRange', () => {
|
|||||||
const startResult = getNodePathFromSourceRange(ast, [
|
const startResult = getNodePathFromSourceRange(ast, [
|
||||||
startSourceIndex,
|
startSourceIndex,
|
||||||
startSourceIndex,
|
startSourceIndex,
|
||||||
|
true,
|
||||||
])
|
])
|
||||||
expect(startResult).toEqual([...expected, ['callee', 'CallExpression']])
|
expect(startResult).toEqual([...expected, ['callee', 'CallExpression']])
|
||||||
// expect similar result when whole line is selected
|
// expect similar result when whole line is selected
|
||||||
const selectWholeThing = getNodePathFromSourceRange(ast, [
|
const selectWholeThing = getNodePathFromSourceRange(ast, [
|
||||||
startSourceIndex,
|
startSourceIndex,
|
||||||
sourceIndex,
|
sourceIndex,
|
||||||
|
true,
|
||||||
])
|
])
|
||||||
expect(selectWholeThing).toEqual(expected)
|
expect(selectWholeThing).toEqual(expected)
|
||||||
})
|
})
|
||||||
@ -278,10 +283,13 @@ describe('testing getNodePathFromSourceRange', () => {
|
|||||||
}`
|
}`
|
||||||
const searchLn = `x > y`
|
const searchLn = `x > y`
|
||||||
const sourceIndex = code.indexOf(searchLn)
|
const sourceIndex = code.indexOf(searchLn)
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(ast)) throw ast
|
|
||||||
|
|
||||||
const result = getNodePathFromSourceRange(ast, [sourceIndex, sourceIndex])
|
const result = getNodePathFromSourceRange(ast, [
|
||||||
|
sourceIndex,
|
||||||
|
sourceIndex,
|
||||||
|
true,
|
||||||
|
])
|
||||||
expect(result).toEqual([
|
expect(result).toEqual([
|
||||||
['body', ''],
|
['body', ''],
|
||||||
[1, 'index'],
|
[1, 'index'],
|
||||||
@ -306,10 +314,13 @@ describe('testing getNodePathFromSourceRange', () => {
|
|||||||
}`
|
}`
|
||||||
const searchLn = `x + 1`
|
const searchLn = `x + 1`
|
||||||
const sourceIndex = code.indexOf(searchLn)
|
const sourceIndex = code.indexOf(searchLn)
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(ast)) throw ast
|
|
||||||
|
|
||||||
const result = getNodePathFromSourceRange(ast, [sourceIndex, sourceIndex])
|
const result = getNodePathFromSourceRange(ast, [
|
||||||
|
sourceIndex,
|
||||||
|
sourceIndex,
|
||||||
|
true,
|
||||||
|
])
|
||||||
expect(result).toEqual([
|
expect(result).toEqual([
|
||||||
['body', ''],
|
['body', ''],
|
||||||
[1, 'index'],
|
[1, 'index'],
|
||||||
@ -332,10 +343,13 @@ describe('testing getNodePathFromSourceRange', () => {
|
|||||||
const code = `import foo, bar as baz from 'thing.kcl'`
|
const code = `import foo, bar as baz from 'thing.kcl'`
|
||||||
const searchLn = `bar`
|
const searchLn = `bar`
|
||||||
const sourceIndex = code.indexOf(searchLn)
|
const sourceIndex = code.indexOf(searchLn)
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(ast)) throw ast
|
|
||||||
|
|
||||||
const result = getNodePathFromSourceRange(ast, [sourceIndex, sourceIndex])
|
const result = getNodePathFromSourceRange(ast, [
|
||||||
|
sourceIndex,
|
||||||
|
sourceIndex,
|
||||||
|
true,
|
||||||
|
])
|
||||||
expect(result).toEqual([
|
expect(result).toEqual([
|
||||||
['body', ''],
|
['body', ''],
|
||||||
[0, 'index'],
|
[0, 'index'],
|
||||||
@ -360,14 +374,13 @@ part001 = startSketchAt([-1.41, 3.46])
|
|||||||
|> angledLine([-175, segLen(seg01)], %)
|
|> angledLine([-175, segLen(seg01)], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
`
|
`
|
||||||
const ast = parse(exampleCode)
|
const ast = assertParse(exampleCode)
|
||||||
if (err(ast)) throw ast
|
|
||||||
|
|
||||||
const result = doesPipeHaveCallExp({
|
const result = doesPipeHaveCallExp({
|
||||||
calleeName: 'close',
|
calleeName: 'close',
|
||||||
ast,
|
ast,
|
||||||
selection: {
|
selection: {
|
||||||
codeRef: codeRefFromRange([100, 101], ast),
|
codeRef: codeRefFromRange([100, 101, true], ast),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
expect(result).toEqual(true)
|
expect(result).toEqual(true)
|
||||||
@ -382,14 +395,13 @@ part001 = startSketchAt([-1.41, 3.46])
|
|||||||
|> close(%)
|
|> close(%)
|
||||||
|> extrude(1, %)
|
|> extrude(1, %)
|
||||||
`
|
`
|
||||||
const ast = parse(exampleCode)
|
const ast = assertParse(exampleCode)
|
||||||
if (err(ast)) throw ast
|
|
||||||
|
|
||||||
const result = doesPipeHaveCallExp({
|
const result = doesPipeHaveCallExp({
|
||||||
calleeName: 'extrude',
|
calleeName: 'extrude',
|
||||||
ast,
|
ast,
|
||||||
selection: {
|
selection: {
|
||||||
codeRef: codeRefFromRange([100, 101], ast),
|
codeRef: codeRefFromRange([100, 101, true], ast),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
expect(result).toEqual(true)
|
expect(result).toEqual(true)
|
||||||
@ -402,28 +414,26 @@ part001 = startSketchAt([-1.41, 3.46])
|
|||||||
|> line([-3.22, -7.36], %)
|
|> line([-3.22, -7.36], %)
|
||||||
|> angledLine([-175, segLen(seg01)], %)
|
|> angledLine([-175, segLen(seg01)], %)
|
||||||
`
|
`
|
||||||
const ast = parse(exampleCode)
|
const ast = assertParse(exampleCode)
|
||||||
if (err(ast)) throw ast
|
|
||||||
|
|
||||||
const result = doesPipeHaveCallExp({
|
const result = doesPipeHaveCallExp({
|
||||||
calleeName: 'close',
|
calleeName: 'close',
|
||||||
ast,
|
ast,
|
||||||
selection: {
|
selection: {
|
||||||
codeRef: codeRefFromRange([100, 101], ast),
|
codeRef: codeRefFromRange([100, 101, true], ast),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
expect(result).toEqual(false)
|
expect(result).toEqual(false)
|
||||||
})
|
})
|
||||||
it('returns false if not a pipe', () => {
|
it('returns false if not a pipe', () => {
|
||||||
const exampleCode = `length001 = 2`
|
const exampleCode = `length001 = 2`
|
||||||
const ast = parse(exampleCode)
|
const ast = assertParse(exampleCode)
|
||||||
if (err(ast)) throw ast
|
|
||||||
|
|
||||||
const result = doesPipeHaveCallExp({
|
const result = doesPipeHaveCallExp({
|
||||||
calleeName: 'close',
|
calleeName: 'close',
|
||||||
ast,
|
ast,
|
||||||
selection: {
|
selection: {
|
||||||
codeRef: codeRefFromRange([9, 10], ast),
|
codeRef: codeRefFromRange([9, 10, true], ast),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
expect(result).toEqual(false)
|
expect(result).toEqual(false)
|
||||||
@ -438,14 +448,13 @@ part001 = startSketchAt([-1.41, 3.46])
|
|||||||
|> angledLine([-35, length001], %)
|
|> angledLine([-35, length001], %)
|
||||||
|> line([-3.22, -7.36], %)
|
|> line([-3.22, -7.36], %)
|
||||||
|> angledLine([-175, segLen(seg01)], %)`
|
|> angledLine([-175, segLen(seg01)], %)`
|
||||||
const ast = parse(exampleCode)
|
const ast = assertParse(exampleCode)
|
||||||
if (err(ast)) throw ast
|
|
||||||
|
|
||||||
const execState = await enginelessExecutor(ast)
|
const execState = await enginelessExecutor(ast)
|
||||||
const result = hasExtrudeSketch({
|
const result = hasExtrudeSketch({
|
||||||
ast,
|
ast,
|
||||||
selection: {
|
selection: {
|
||||||
codeRef: codeRefFromRange([100, 101], ast),
|
codeRef: codeRefFromRange([100, 101, true], ast),
|
||||||
},
|
},
|
||||||
programMemory: execState.memory,
|
programMemory: execState.memory,
|
||||||
})
|
})
|
||||||
@ -459,14 +468,13 @@ part001 = startSketchAt([-1.41, 3.46])
|
|||||||
|> line([-3.22, -7.36], %)
|
|> line([-3.22, -7.36], %)
|
||||||
|> angledLine([-175, segLen(seg01)], %)
|
|> angledLine([-175, segLen(seg01)], %)
|
||||||
|> extrude(1, %)`
|
|> extrude(1, %)`
|
||||||
const ast = parse(exampleCode)
|
const ast = assertParse(exampleCode)
|
||||||
if (err(ast)) throw ast
|
|
||||||
|
|
||||||
const execState = await enginelessExecutor(ast)
|
const execState = await enginelessExecutor(ast)
|
||||||
const result = hasExtrudeSketch({
|
const result = hasExtrudeSketch({
|
||||||
ast,
|
ast,
|
||||||
selection: {
|
selection: {
|
||||||
codeRef: codeRefFromRange([100, 101], ast),
|
codeRef: codeRefFromRange([100, 101, true], ast),
|
||||||
},
|
},
|
||||||
programMemory: execState.memory,
|
programMemory: execState.memory,
|
||||||
})
|
})
|
||||||
@ -474,14 +482,13 @@ part001 = startSketchAt([-1.41, 3.46])
|
|||||||
})
|
})
|
||||||
it('finds nothing', async () => {
|
it('finds nothing', async () => {
|
||||||
const exampleCode = `length001 = 2`
|
const exampleCode = `length001 = 2`
|
||||||
const ast = parse(exampleCode)
|
const ast = assertParse(exampleCode)
|
||||||
if (err(ast)) throw ast
|
|
||||||
|
|
||||||
const execState = await enginelessExecutor(ast)
|
const execState = await enginelessExecutor(ast)
|
||||||
const result = hasExtrudeSketch({
|
const result = hasExtrudeSketch({
|
||||||
ast,
|
ast,
|
||||||
selection: {
|
selection: {
|
||||||
codeRef: codeRefFromRange([10, 11], ast),
|
codeRef: codeRefFromRange([10, 11, true], ast),
|
||||||
},
|
},
|
||||||
programMemory: execState.memory,
|
programMemory: execState.memory,
|
||||||
})
|
})
|
||||||
@ -498,8 +505,7 @@ describe('Testing findUsesOfTagInPipe', () => {
|
|||||||
|> line([306.21, 198.87], %)
|
|> line([306.21, 198.87], %)
|
||||||
|> angledLine([65, segLen(seg01)], %)`
|
|> angledLine([65, segLen(seg01)], %)`
|
||||||
it('finds the current segment', async () => {
|
it('finds the current segment', async () => {
|
||||||
const ast = parse(exampleCode)
|
const ast = assertParse(exampleCode)
|
||||||
if (err(ast)) throw ast
|
|
||||||
|
|
||||||
const lineOfInterest = `198.85], %, $seg01`
|
const lineOfInterest = `198.85], %, $seg01`
|
||||||
const characterIndex =
|
const characterIndex =
|
||||||
@ -507,6 +513,7 @@ describe('Testing findUsesOfTagInPipe', () => {
|
|||||||
const pathToNode = getNodePathFromSourceRange(ast, [
|
const pathToNode = getNodePathFromSourceRange(ast, [
|
||||||
characterIndex,
|
characterIndex,
|
||||||
characterIndex,
|
characterIndex,
|
||||||
|
true,
|
||||||
])
|
])
|
||||||
const result = findUsesOfTagInPipe(ast, pathToNode)
|
const result = findUsesOfTagInPipe(ast, pathToNode)
|
||||||
expect(result).toHaveLength(2)
|
expect(result).toHaveLength(2)
|
||||||
@ -515,8 +522,7 @@ describe('Testing findUsesOfTagInPipe', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
it('find no tag if line has no tag', () => {
|
it('find no tag if line has no tag', () => {
|
||||||
const ast = parse(exampleCode)
|
const ast = assertParse(exampleCode)
|
||||||
if (err(ast)) throw ast
|
|
||||||
|
|
||||||
const lineOfInterest = `line([306.21, 198.82], %)`
|
const lineOfInterest = `line([306.21, 198.82], %)`
|
||||||
const characterIndex =
|
const characterIndex =
|
||||||
@ -524,6 +530,7 @@ describe('Testing findUsesOfTagInPipe', () => {
|
|||||||
const pathToNode = getNodePathFromSourceRange(ast, [
|
const pathToNode = getNodePathFromSourceRange(ast, [
|
||||||
characterIndex,
|
characterIndex,
|
||||||
characterIndex,
|
characterIndex,
|
||||||
|
true,
|
||||||
])
|
])
|
||||||
const result = findUsesOfTagInPipe(ast, pathToNode)
|
const result = findUsesOfTagInPipe(ast, pathToNode)
|
||||||
expect(result).toHaveLength(0)
|
expect(result).toHaveLength(0)
|
||||||
@ -564,42 +571,39 @@ sketch003 = startSketchOn(extrude001, 'END')
|
|||||||
|> extrude(3.14, %)
|
|> extrude(3.14, %)
|
||||||
`
|
`
|
||||||
it('identifies sketch001 pipe as extruded (extrusion after pipe)', async () => {
|
it('identifies sketch001 pipe as extruded (extrusion after pipe)', async () => {
|
||||||
const ast = parse(exampleCode)
|
const ast = assertParse(exampleCode)
|
||||||
if (err(ast)) throw ast
|
|
||||||
const lineOfInterest = `line([4.99, -0.46], %, $seg01)`
|
const lineOfInterest = `line([4.99, -0.46], %, $seg01)`
|
||||||
const characterIndex =
|
const characterIndex =
|
||||||
exampleCode.indexOf(lineOfInterest) + lineOfInterest.length
|
exampleCode.indexOf(lineOfInterest) + lineOfInterest.length
|
||||||
const extruded = hasSketchPipeBeenExtruded(
|
const extruded = hasSketchPipeBeenExtruded(
|
||||||
{
|
{
|
||||||
codeRef: codeRefFromRange([characterIndex, characterIndex], ast),
|
codeRef: codeRefFromRange([characterIndex, characterIndex, true], ast),
|
||||||
},
|
},
|
||||||
ast
|
ast
|
||||||
)
|
)
|
||||||
expect(extruded).toBeTruthy()
|
expect(extruded).toBeTruthy()
|
||||||
})
|
})
|
||||||
it('identifies sketch002 pipe as not extruded', async () => {
|
it('identifies sketch002 pipe as not extruded', async () => {
|
||||||
const ast = parse(exampleCode)
|
const ast = assertParse(exampleCode)
|
||||||
if (err(ast)) throw ast
|
|
||||||
const lineOfInterest = `line([2.45, -0.2], %)`
|
const lineOfInterest = `line([2.45, -0.2], %)`
|
||||||
const characterIndex =
|
const characterIndex =
|
||||||
exampleCode.indexOf(lineOfInterest) + lineOfInterest.length
|
exampleCode.indexOf(lineOfInterest) + lineOfInterest.length
|
||||||
const extruded = hasSketchPipeBeenExtruded(
|
const extruded = hasSketchPipeBeenExtruded(
|
||||||
{
|
{
|
||||||
codeRef: codeRefFromRange([characterIndex, characterIndex], ast),
|
codeRef: codeRefFromRange([characterIndex, characterIndex, true], ast),
|
||||||
},
|
},
|
||||||
ast
|
ast
|
||||||
)
|
)
|
||||||
expect(extruded).toBeFalsy()
|
expect(extruded).toBeFalsy()
|
||||||
})
|
})
|
||||||
it('identifies sketch003 pipe as extruded (extrusion within pipe)', async () => {
|
it('identifies sketch003 pipe as extruded (extrusion within pipe)', async () => {
|
||||||
const ast = parse(exampleCode)
|
const ast = assertParse(exampleCode)
|
||||||
if (err(ast)) throw ast
|
|
||||||
const lineOfInterest = `|> line([3.12, 1.74], %)`
|
const lineOfInterest = `|> line([3.12, 1.74], %)`
|
||||||
const characterIndex =
|
const characterIndex =
|
||||||
exampleCode.indexOf(lineOfInterest) + lineOfInterest.length
|
exampleCode.indexOf(lineOfInterest) + lineOfInterest.length
|
||||||
const extruded = hasSketchPipeBeenExtruded(
|
const extruded = hasSketchPipeBeenExtruded(
|
||||||
{
|
{
|
||||||
codeRef: codeRefFromRange([characterIndex, characterIndex], ast),
|
codeRef: codeRefFromRange([characterIndex, characterIndex, true], ast),
|
||||||
},
|
},
|
||||||
ast
|
ast
|
||||||
)
|
)
|
||||||
@ -623,11 +627,21 @@ sketch002 = startSketchOn(extrude001, $seg01)
|
|||||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
`
|
`
|
||||||
const ast = parse(exampleCode)
|
const ast = assertParse(exampleCode)
|
||||||
if (err(ast)) throw ast
|
|
||||||
const extrudable = doesSceneHaveSweepableSketch(ast)
|
const extrudable = doesSceneHaveSweepableSketch(ast)
|
||||||
expect(extrudable).toBeTruthy()
|
expect(extrudable).toBeTruthy()
|
||||||
})
|
})
|
||||||
|
it('finds sketch001 and sketch002 pipes to be lofted', async () => {
|
||||||
|
const exampleCode = `sketch001 = startSketchOn('XZ')
|
||||||
|
|> circle({ center = [0, 0], radius = 1 }, %)
|
||||||
|
plane001 = offsetPlane('XZ', 2)
|
||||||
|
sketch002 = startSketchOn(plane001)
|
||||||
|
|> circle({ center = [0, 0], radius = 3 }, %)
|
||||||
|
`
|
||||||
|
const ast = assertParse(exampleCode)
|
||||||
|
const extrudable = doesSceneHaveSweepableSketch(ast, 2)
|
||||||
|
expect(extrudable).toBeTruthy()
|
||||||
|
})
|
||||||
it('find sketch002 NOT pipe to be extruded', async () => {
|
it('find sketch002 NOT pipe to be extruded', async () => {
|
||||||
const exampleCode = `sketch001 = startSketchOn('XZ')
|
const exampleCode = `sketch001 = startSketchOn('XZ')
|
||||||
|> startProfileAt([3.29, 7.86], %)
|
|> startProfileAt([3.29, 7.86], %)
|
||||||
@ -637,8 +651,7 @@ sketch002 = startSketchOn(extrude001, $seg01)
|
|||||||
|> close(%)
|
|> close(%)
|
||||||
extrude001 = extrude(10, sketch001)
|
extrude001 = extrude(10, sketch001)
|
||||||
`
|
`
|
||||||
const ast = parse(exampleCode)
|
const ast = assertParse(exampleCode)
|
||||||
if (err(ast)) throw ast
|
|
||||||
const extrudable = doesSceneHaveSweepableSketch(ast)
|
const extrudable = doesSceneHaveSweepableSketch(ast)
|
||||||
expect(extrudable).toBeFalsy()
|
expect(extrudable).toBeFalsy()
|
||||||
})
|
})
|
||||||
@ -666,8 +679,7 @@ myNestedVar = [
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
`
|
`
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(ast)) throw ast
|
|
||||||
let pathToNode: PathToNode = []
|
let pathToNode: PathToNode = []
|
||||||
traverse(ast, {
|
traverse(ast, {
|
||||||
enter: (node, path) => {
|
enter: (node, path) => {
|
||||||
@ -689,6 +701,7 @@ myNestedVar = [
|
|||||||
const pathToNode2 = getNodePathFromSourceRange(ast, [
|
const pathToNode2 = getNodePathFromSourceRange(ast, [
|
||||||
literalIndex + 2,
|
literalIndex + 2,
|
||||||
literalIndex + 2,
|
literalIndex + 2,
|
||||||
|
true,
|
||||||
])
|
])
|
||||||
expect(pathToNode).toEqual(pathToNode2)
|
expect(pathToNode).toEqual(pathToNode2)
|
||||||
})
|
})
|
||||||
|
@ -16,6 +16,7 @@ import {
|
|||||||
sketchFromKclValue,
|
sketchFromKclValue,
|
||||||
sketchFromKclValueOptional,
|
sketchFromKclValueOptional,
|
||||||
SourceRange,
|
SourceRange,
|
||||||
|
sourceRangeFromRust,
|
||||||
SyntaxType,
|
SyntaxType,
|
||||||
VariableDeclaration,
|
VariableDeclaration,
|
||||||
VariableDeclarator,
|
VariableDeclarator,
|
||||||
@ -173,6 +174,30 @@ function moreNodePathFromSourceRange(
|
|||||||
}
|
}
|
||||||
return path
|
return path
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_node.type === 'CallExpressionKw' && isInRange) {
|
||||||
|
const { callee, arguments: args } = _node
|
||||||
|
if (
|
||||||
|
callee.type === 'Identifier' &&
|
||||||
|
callee.start <= start &&
|
||||||
|
callee.end >= end
|
||||||
|
) {
|
||||||
|
path.push(['callee', 'CallExpressionKw'])
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
if (args.length > 0) {
|
||||||
|
for (let argIndex = 0; argIndex < args.length; argIndex++) {
|
||||||
|
const arg = args[argIndex].arg
|
||||||
|
if (arg.start <= start && arg.end >= end) {
|
||||||
|
path.push(['arguments', 'CallExpressionKw'])
|
||||||
|
path.push([argIndex, 'index'])
|
||||||
|
return moreNodePathFromSourceRange(arg, sourceRange, path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
|
||||||
if (_node.type === 'BinaryExpression' && isInRange) {
|
if (_node.type === 'BinaryExpression' && isInRange) {
|
||||||
const { left, right } = _node
|
const { left, right } = _node
|
||||||
if (left.start <= start && left.end >= end) {
|
if (left.start <= start && left.end >= end) {
|
||||||
@ -645,7 +670,7 @@ export function isNodeSafeToReplacePath(
|
|||||||
|
|
||||||
export function isNodeSafeToReplace(
|
export function isNodeSafeToReplace(
|
||||||
ast: Node<Program>,
|
ast: Node<Program>,
|
||||||
sourceRange: [number, number]
|
sourceRange: SourceRange
|
||||||
):
|
):
|
||||||
| {
|
| {
|
||||||
isSafe: boolean
|
isSafe: boolean
|
||||||
@ -797,7 +822,7 @@ export function isLinesParallelAndConstrained(
|
|||||||
return {
|
return {
|
||||||
isParallelAndConstrained,
|
isParallelAndConstrained,
|
||||||
selection: {
|
selection: {
|
||||||
codeRef: codeRefFromRange(prevSourceRange, ast),
|
codeRef: codeRefFromRange(sourceRangeFromRust(prevSourceRange), ast),
|
||||||
artifact: artifactGraph.get(prevSegment.__geoMeta.id),
|
artifact: artifactGraph.get(prevSegment.__geoMeta.id),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -933,7 +958,8 @@ export function findUsesOfTagInPipe(
|
|||||||
return
|
return
|
||||||
const tagArgValue =
|
const tagArgValue =
|
||||||
tagArg.type === 'TagDeclarator' ? String(tagArg.value) : tagArg.name
|
tagArg.type === 'TagDeclarator' ? String(tagArg.value) : tagArg.name
|
||||||
if (tagArgValue === tag) dependentRanges.push([node.start, node.end])
|
if (tagArgValue === tag)
|
||||||
|
dependentRanges.push([node.start, node.end, true])
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
return dependentRanges
|
return dependentRanges
|
||||||
@ -975,7 +1001,9 @@ export function hasSketchPipeBeenExtruded(selection: Selection, ast: Program) {
|
|||||||
if (
|
if (
|
||||||
node.type === 'CallExpression' &&
|
node.type === 'CallExpression' &&
|
||||||
node.callee.type === 'Identifier' &&
|
node.callee.type === 'Identifier' &&
|
||||||
(node.callee.name === 'extrude' || node.callee.name === 'revolve') &&
|
(node.callee.name === 'extrude' ||
|
||||||
|
node.callee.name === 'revolve' ||
|
||||||
|
node.callee.name === 'loft') &&
|
||||||
node.arguments?.[1]?.type === 'Identifier' &&
|
node.arguments?.[1]?.type === 'Identifier' &&
|
||||||
node.arguments[1].name === varDec.id.name
|
node.arguments[1].name === varDec.id.name
|
||||||
) {
|
) {
|
||||||
@ -988,7 +1016,7 @@ export function hasSketchPipeBeenExtruded(selection: Selection, ast: Program) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** File must contain at least one sketch that has not been extruded already */
|
/** File must contain at least one sketch that has not been extruded already */
|
||||||
export function doesSceneHaveSweepableSketch(ast: Node<Program>) {
|
export function doesSceneHaveSweepableSketch(ast: Node<Program>, count = 1) {
|
||||||
const theMap: any = {}
|
const theMap: any = {}
|
||||||
traverse(ast as any, {
|
traverse(ast as any, {
|
||||||
enter(node) {
|
enter(node) {
|
||||||
@ -1037,7 +1065,7 @@ export function doesSceneHaveSweepableSketch(ast: Node<Program>) {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
return Object.keys(theMap).length > 0
|
return Object.keys(theMap).length >= count
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getObjExprProperty(
|
export function getObjExprProperty(
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { parse, Program, recast, initPromise } from './wasm'
|
import { assertParse, Program, recast, initPromise } from './wasm'
|
||||||
import fs from 'node:fs'
|
import fs from 'node:fs'
|
||||||
import { err } from 'lib/trap'
|
import { err } from 'lib/trap'
|
||||||
|
|
||||||
@ -394,8 +394,6 @@ describe('it recasts binary expression using brackets where needed', () => {
|
|||||||
// helpers
|
// helpers
|
||||||
|
|
||||||
function code2ast(code: string): { ast: Program } {
|
function code2ast(code: string): { ast: Program } {
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
// eslint-ignore-next-line
|
|
||||||
if (err(ast)) throw ast
|
|
||||||
return { ast }
|
return { ast }
|
||||||
}
|
}
|
||||||
|
@ -11,8 +11,8 @@ Map {
|
|||||||
],
|
],
|
||||||
],
|
],
|
||||||
"range": [
|
"range": [
|
||||||
37,
|
12,
|
||||||
64,
|
31,
|
||||||
0,
|
0,
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { makeDefaultPlanes, parse, initPromise, Program } from 'lang/wasm'
|
import { makeDefaultPlanes, assertParse, initPromise, Program } from 'lang/wasm'
|
||||||
import { Models } from '@kittycad/lib'
|
import { Models } from '@kittycad/lib'
|
||||||
import {
|
import {
|
||||||
OrderedCommand,
|
OrderedCommand,
|
||||||
@ -148,11 +148,7 @@ beforeAll(async () => {
|
|||||||
][]
|
][]
|
||||||
const cacheToWriteToFileTemp: Partial<CacheShape> = {}
|
const cacheToWriteToFileTemp: Partial<CacheShape> = {}
|
||||||
for (const [codeKey, code] of cacheEntries) {
|
for (const [codeKey, code] of cacheEntries) {
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(ast)) {
|
|
||||||
console.error(ast)
|
|
||||||
return Promise.reject(ast)
|
|
||||||
}
|
|
||||||
await kclManager.executeAst({ ast })
|
await kclManager.executeAst({ ast })
|
||||||
|
|
||||||
cacheToWriteToFileTemp[codeKey] = {
|
cacheToWriteToFileTemp[codeKey] = {
|
||||||
@ -403,11 +399,7 @@ describe('capture graph of sketchOnFaceOnFace...', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
function getCommands(codeKey: CodeKey): CacheShape[CodeKey] & { ast: Program } {
|
function getCommands(codeKey: CodeKey): CacheShape[CodeKey] & { ast: Program } {
|
||||||
const ast = parse(codeKey)
|
const ast = assertParse(codeKey)
|
||||||
if (err(ast)) {
|
|
||||||
console.error(ast)
|
|
||||||
throw ast
|
|
||||||
}
|
|
||||||
const file = fs.readFileSync(fullPath, 'utf-8')
|
const file = fs.readFileSync(fullPath, 'utf-8')
|
||||||
const parsed: CacheShape = JSON.parse(file)
|
const parsed: CacheShape = JSON.parse(file)
|
||||||
// these either already exist from the last run, or were created in
|
// these either already exist from the last run, or were created in
|
||||||
|
Before Width: | Height: | Size: 378 KiB After Width: | Height: | Size: 357 KiB |
Before Width: | Height: | Size: 92 KiB After Width: | Height: | Size: 88 KiB |
Before Width: | Height: | Size: 613 KiB After Width: | Height: | Size: 577 KiB |
@ -1,4 +1,4 @@
|
|||||||
import { SourceRange } from 'lang/wasm'
|
import { defaultSourceRange, SourceRange } from 'lang/wasm'
|
||||||
import { VITE_KC_API_WS_MODELING_URL, VITE_KC_DEV_TOKEN } from 'env'
|
import { VITE_KC_API_WS_MODELING_URL, VITE_KC_DEV_TOKEN } from 'env'
|
||||||
import { Models } from '@kittycad/lib'
|
import { Models } from '@kittycad/lib'
|
||||||
import { exportSave } from 'lib/exportSave'
|
import { exportSave } from 'lib/exportSave'
|
||||||
@ -1879,7 +1879,7 @@ export class EngineCommandManager extends EventTarget {
|
|||||||
}
|
}
|
||||||
return JSON.stringify(this.defaultPlanes)
|
return JSON.stringify(this.defaultPlanes)
|
||||||
}
|
}
|
||||||
endSession() {
|
clearScene(): void {
|
||||||
const deleteCmd: EngineCommand = {
|
const deleteCmd: EngineCommand = {
|
||||||
type: 'modeling_cmd_req',
|
type: 'modeling_cmd_req',
|
||||||
cmd_id: uuidv4(),
|
cmd_id: uuidv4(),
|
||||||
@ -2014,7 +2014,7 @@ export class EngineCommandManager extends EventTarget {
|
|||||||
{
|
{
|
||||||
command,
|
command,
|
||||||
idToRangeMap: {},
|
idToRangeMap: {},
|
||||||
range: [0, 0],
|
range: defaultSourceRange(),
|
||||||
},
|
},
|
||||||
true // isSceneCommand
|
true // isSceneCommand
|
||||||
)
|
)
|
||||||
|
@ -8,7 +8,7 @@ import {
|
|||||||
getConstraintInfo,
|
getConstraintInfo,
|
||||||
} from './sketch'
|
} from './sketch'
|
||||||
import {
|
import {
|
||||||
parse,
|
assertParse,
|
||||||
recast,
|
recast,
|
||||||
initPromise,
|
initPromise,
|
||||||
SourceRange,
|
SourceRange,
|
||||||
@ -115,8 +115,7 @@ describe('testing changeSketchArguments', () => {
|
|||||||
`
|
`
|
||||||
const code = genCode(lineToChange)
|
const code = genCode(lineToChange)
|
||||||
const expectedCode = genCode(lineAfterChange)
|
const expectedCode = genCode(lineAfterChange)
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(ast)) return ast
|
|
||||||
|
|
||||||
const execState = await enginelessExecutor(ast)
|
const execState = await enginelessExecutor(ast)
|
||||||
const sourceStart = code.indexOf(lineToChange)
|
const sourceStart = code.indexOf(lineToChange)
|
||||||
@ -125,7 +124,7 @@ describe('testing changeSketchArguments', () => {
|
|||||||
execState.memory,
|
execState.memory,
|
||||||
{
|
{
|
||||||
type: 'sourceRange',
|
type: 'sourceRange',
|
||||||
sourceRange: [sourceStart, sourceStart + lineToChange.length],
|
sourceRange: [sourceStart, sourceStart + lineToChange.length, true],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'straight-segment',
|
type: 'straight-segment',
|
||||||
@ -148,8 +147,7 @@ mySketch001 = startSketchOn('XY')
|
|||||||
// |> rx(45, %)
|
// |> rx(45, %)
|
||||||
|> lineTo([-1.59, -1.54], %)
|
|> lineTo([-1.59, -1.54], %)
|
||||||
|> lineTo([0.46, -5.82], %)`
|
|> lineTo([0.46, -5.82], %)`
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(ast)) return ast
|
|
||||||
|
|
||||||
const execState = await enginelessExecutor(ast)
|
const execState = await enginelessExecutor(ast)
|
||||||
const sourceStart = code.indexOf(lineToChange)
|
const sourceStart = code.indexOf(lineToChange)
|
||||||
@ -220,12 +218,13 @@ describe('testing addTagForSketchOnFace', () => {
|
|||||||
|> lineTo([0.46, -5.82], %)
|
|> lineTo([0.46, -5.82], %)
|
||||||
`
|
`
|
||||||
const code = genCode(originalLine)
|
const code = genCode(originalLine)
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
await enginelessExecutor(ast)
|
await enginelessExecutor(ast)
|
||||||
const sourceStart = code.indexOf(originalLine)
|
const sourceStart = code.indexOf(originalLine)
|
||||||
const sourceRange: [number, number] = [
|
const sourceRange: [number, number, boolean] = [
|
||||||
sourceStart,
|
sourceStart,
|
||||||
sourceStart + originalLine.length,
|
sourceStart + originalLine.length,
|
||||||
|
true,
|
||||||
]
|
]
|
||||||
if (err(ast)) return ast
|
if (err(ast)) return ast
|
||||||
const pathToNode = getNodePathFromSourceRange(ast, sourceRange)
|
const pathToNode = getNodePathFromSourceRange(ast, sourceRange)
|
||||||
@ -291,13 +290,14 @@ extrude001 = extrude(100, sketch001)
|
|||||||
${insertCode}
|
${insertCode}
|
||||||
`
|
`
|
||||||
const code = genCode(originalChamfer)
|
const code = genCode(originalChamfer)
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
await enginelessExecutor(ast)
|
await enginelessExecutor(ast)
|
||||||
const sourceStart = code.indexOf(originalChamfer)
|
const sourceStart = code.indexOf(originalChamfer)
|
||||||
const extraChars = originalChamfer.indexOf('chamfer')
|
const extraChars = originalChamfer.indexOf('chamfer')
|
||||||
const sourceRange: [number, number] = [
|
const sourceRange: [number, number, boolean] = [
|
||||||
sourceStart + extraChars,
|
sourceStart + extraChars,
|
||||||
sourceStart + originalChamfer.length - extraChars,
|
sourceStart + originalChamfer.length - extraChars,
|
||||||
|
true,
|
||||||
]
|
]
|
||||||
|
|
||||||
if (err(ast)) throw ast
|
if (err(ast)) throw ast
|
||||||
@ -335,7 +335,7 @@ describe('testing getConstraintInfo', () => {
|
|||||||
|> lineTo([6.14, 3.14], %)
|
|> lineTo([6.14, 3.14], %)
|
||||||
|> xLineTo(8, %)
|
|> xLineTo(8, %)
|
||||||
|> yLineTo(5, %)
|
|> yLineTo(5, %)
|
||||||
|> yLine(3.14, %, 'a')
|
|> yLine(3.14, %, $a)
|
||||||
|> xLine(3.14, %)
|
|> xLine(3.14, %)
|
||||||
|> angledLineOfXLength({
|
|> angledLineOfXLength({
|
||||||
angle = 3.14,
|
angle = 3.14,
|
||||||
@ -355,11 +355,11 @@ describe('testing getConstraintInfo', () => {
|
|||||||
}, %)
|
}, %)
|
||||||
|> angledLineThatIntersects({
|
|> angledLineThatIntersects({
|
||||||
angle = 3.14,
|
angle = 3.14,
|
||||||
intersectTag = 'a',
|
intersectTag = a,
|
||||||
offset = 0
|
offset = 0
|
||||||
}, %)
|
}, %)
|
||||||
|> tangentialArcTo([3.14, 13.14], %)`
|
|> tangentialArcTo([3.14, 13.14], %)`
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
test.each([
|
test.each([
|
||||||
[
|
[
|
||||||
'line',
|
'line',
|
||||||
@ -368,7 +368,7 @@ describe('testing getConstraintInfo', () => {
|
|||||||
type: 'xRelative',
|
type: 'xRelative',
|
||||||
isConstrained: false,
|
isConstrained: false,
|
||||||
value: '3',
|
value: '3',
|
||||||
sourceRange: [78, 79],
|
sourceRange: [78, 79, true],
|
||||||
argPosition: { type: 'arrayItem', index: 0 },
|
argPosition: { type: 'arrayItem', index: 0 },
|
||||||
pathToNode: expect.any(Array),
|
pathToNode: expect.any(Array),
|
||||||
stdLibFnName: 'line',
|
stdLibFnName: 'line',
|
||||||
@ -377,7 +377,7 @@ describe('testing getConstraintInfo', () => {
|
|||||||
type: 'yRelative',
|
type: 'yRelative',
|
||||||
isConstrained: false,
|
isConstrained: false,
|
||||||
value: '4',
|
value: '4',
|
||||||
sourceRange: [81, 82],
|
sourceRange: [81, 82, true],
|
||||||
argPosition: { type: 'arrayItem', index: 1 },
|
argPosition: { type: 'arrayItem', index: 1 },
|
||||||
pathToNode: expect.any(Array),
|
pathToNode: expect.any(Array),
|
||||||
stdLibFnName: 'line',
|
stdLibFnName: 'line',
|
||||||
@ -391,7 +391,7 @@ describe('testing getConstraintInfo', () => {
|
|||||||
type: 'angle',
|
type: 'angle',
|
||||||
isConstrained: false,
|
isConstrained: false,
|
||||||
value: '3.14',
|
value: '3.14',
|
||||||
sourceRange: [117, 121],
|
sourceRange: [118, 122, true],
|
||||||
argPosition: { type: 'objectProperty', key: 'angle' },
|
argPosition: { type: 'objectProperty', key: 'angle' },
|
||||||
pathToNode: expect.any(Array),
|
pathToNode: expect.any(Array),
|
||||||
stdLibFnName: 'angledLine',
|
stdLibFnName: 'angledLine',
|
||||||
@ -400,7 +400,7 @@ describe('testing getConstraintInfo', () => {
|
|||||||
type: 'length',
|
type: 'length',
|
||||||
isConstrained: false,
|
isConstrained: false,
|
||||||
value: '3.14',
|
value: '3.14',
|
||||||
sourceRange: [135, 139],
|
sourceRange: [137, 141, true],
|
||||||
argPosition: { type: 'objectProperty', key: 'length' },
|
argPosition: { type: 'objectProperty', key: 'length' },
|
||||||
pathToNode: expect.any(Array),
|
pathToNode: expect.any(Array),
|
||||||
stdLibFnName: 'angledLine',
|
stdLibFnName: 'angledLine',
|
||||||
@ -414,7 +414,7 @@ describe('testing getConstraintInfo', () => {
|
|||||||
type: 'xAbsolute',
|
type: 'xAbsolute',
|
||||||
isConstrained: false,
|
isConstrained: false,
|
||||||
value: '6.14',
|
value: '6.14',
|
||||||
sourceRange: [162, 166],
|
sourceRange: [164, 168, true],
|
||||||
argPosition: { type: 'arrayItem', index: 0 },
|
argPosition: { type: 'arrayItem', index: 0 },
|
||||||
pathToNode: expect.any(Array),
|
pathToNode: expect.any(Array),
|
||||||
stdLibFnName: 'lineTo',
|
stdLibFnName: 'lineTo',
|
||||||
@ -423,7 +423,7 @@ describe('testing getConstraintInfo', () => {
|
|||||||
type: 'yAbsolute',
|
type: 'yAbsolute',
|
||||||
isConstrained: false,
|
isConstrained: false,
|
||||||
value: '3.14',
|
value: '3.14',
|
||||||
sourceRange: [168, 172],
|
sourceRange: [170, 174, true],
|
||||||
argPosition: { type: 'arrayItem', index: 1 },
|
argPosition: { type: 'arrayItem', index: 1 },
|
||||||
pathToNode: expect.any(Array),
|
pathToNode: expect.any(Array),
|
||||||
stdLibFnName: 'lineTo',
|
stdLibFnName: 'lineTo',
|
||||||
@ -437,7 +437,7 @@ describe('testing getConstraintInfo', () => {
|
|||||||
type: 'horizontal',
|
type: 'horizontal',
|
||||||
isConstrained: true,
|
isConstrained: true,
|
||||||
value: 'xLineTo',
|
value: 'xLineTo',
|
||||||
sourceRange: [183, 190],
|
sourceRange: [185, 192, true],
|
||||||
argPosition: undefined,
|
argPosition: undefined,
|
||||||
pathToNode: expect.any(Array),
|
pathToNode: expect.any(Array),
|
||||||
stdLibFnName: 'xLineTo',
|
stdLibFnName: 'xLineTo',
|
||||||
@ -446,7 +446,7 @@ describe('testing getConstraintInfo', () => {
|
|||||||
type: 'xAbsolute',
|
type: 'xAbsolute',
|
||||||
isConstrained: false,
|
isConstrained: false,
|
||||||
value: '8',
|
value: '8',
|
||||||
sourceRange: [191, 192],
|
sourceRange: [193, 194, true],
|
||||||
argPosition: { type: 'singleValue' },
|
argPosition: { type: 'singleValue' },
|
||||||
pathToNode: expect.any(Array),
|
pathToNode: expect.any(Array),
|
||||||
stdLibFnName: 'xLineTo',
|
stdLibFnName: 'xLineTo',
|
||||||
@ -460,7 +460,7 @@ describe('testing getConstraintInfo', () => {
|
|||||||
type: 'vertical',
|
type: 'vertical',
|
||||||
isConstrained: true,
|
isConstrained: true,
|
||||||
value: 'yLineTo',
|
value: 'yLineTo',
|
||||||
sourceRange: [202, 209],
|
sourceRange: [204, 211, true],
|
||||||
argPosition: undefined,
|
argPosition: undefined,
|
||||||
pathToNode: expect.any(Array),
|
pathToNode: expect.any(Array),
|
||||||
stdLibFnName: 'yLineTo',
|
stdLibFnName: 'yLineTo',
|
||||||
@ -469,7 +469,7 @@ describe('testing getConstraintInfo', () => {
|
|||||||
type: 'yAbsolute',
|
type: 'yAbsolute',
|
||||||
isConstrained: false,
|
isConstrained: false,
|
||||||
value: '5',
|
value: '5',
|
||||||
sourceRange: [210, 211],
|
sourceRange: [212, 213, true],
|
||||||
argPosition: { type: 'singleValue' },
|
argPosition: { type: 'singleValue' },
|
||||||
pathToNode: expect.any(Array),
|
pathToNode: expect.any(Array),
|
||||||
stdLibFnName: 'yLineTo',
|
stdLibFnName: 'yLineTo',
|
||||||
@ -483,7 +483,7 @@ describe('testing getConstraintInfo', () => {
|
|||||||
type: 'vertical',
|
type: 'vertical',
|
||||||
isConstrained: true,
|
isConstrained: true,
|
||||||
value: 'yLine',
|
value: 'yLine',
|
||||||
sourceRange: [221, 226],
|
sourceRange: [223, 228, true],
|
||||||
argPosition: undefined,
|
argPosition: undefined,
|
||||||
pathToNode: expect.any(Array),
|
pathToNode: expect.any(Array),
|
||||||
stdLibFnName: 'yLine',
|
stdLibFnName: 'yLine',
|
||||||
@ -492,7 +492,7 @@ describe('testing getConstraintInfo', () => {
|
|||||||
type: 'yRelative',
|
type: 'yRelative',
|
||||||
isConstrained: false,
|
isConstrained: false,
|
||||||
value: '3.14',
|
value: '3.14',
|
||||||
sourceRange: [227, 231],
|
sourceRange: [229, 233, true],
|
||||||
argPosition: { type: 'singleValue' },
|
argPosition: { type: 'singleValue' },
|
||||||
pathToNode: expect.any(Array),
|
pathToNode: expect.any(Array),
|
||||||
stdLibFnName: 'yLine',
|
stdLibFnName: 'yLine',
|
||||||
@ -506,7 +506,7 @@ describe('testing getConstraintInfo', () => {
|
|||||||
type: 'horizontal',
|
type: 'horizontal',
|
||||||
isConstrained: true,
|
isConstrained: true,
|
||||||
value: 'xLine',
|
value: 'xLine',
|
||||||
sourceRange: [246, 251],
|
sourceRange: [247, 252, true],
|
||||||
argPosition: undefined,
|
argPosition: undefined,
|
||||||
pathToNode: expect.any(Array),
|
pathToNode: expect.any(Array),
|
||||||
stdLibFnName: 'xLine',
|
stdLibFnName: 'xLine',
|
||||||
@ -515,7 +515,7 @@ describe('testing getConstraintInfo', () => {
|
|||||||
type: 'xRelative',
|
type: 'xRelative',
|
||||||
isConstrained: false,
|
isConstrained: false,
|
||||||
value: '3.14',
|
value: '3.14',
|
||||||
sourceRange: [252, 256],
|
sourceRange: [253, 257, true],
|
||||||
argPosition: { type: 'singleValue' },
|
argPosition: { type: 'singleValue' },
|
||||||
pathToNode: expect.any(Array),
|
pathToNode: expect.any(Array),
|
||||||
stdLibFnName: 'xLine',
|
stdLibFnName: 'xLine',
|
||||||
@ -529,7 +529,7 @@ describe('testing getConstraintInfo', () => {
|
|||||||
type: 'angle',
|
type: 'angle',
|
||||||
isConstrained: false,
|
isConstrained: false,
|
||||||
value: '3.14',
|
value: '3.14',
|
||||||
sourceRange: [299, 303],
|
sourceRange: [301, 305, true],
|
||||||
argPosition: { type: 'objectProperty', key: 'angle' },
|
argPosition: { type: 'objectProperty', key: 'angle' },
|
||||||
pathToNode: expect.any(Array),
|
pathToNode: expect.any(Array),
|
||||||
stdLibFnName: 'angledLineOfXLength',
|
stdLibFnName: 'angledLineOfXLength',
|
||||||
@ -538,7 +538,7 @@ describe('testing getConstraintInfo', () => {
|
|||||||
type: 'xRelative',
|
type: 'xRelative',
|
||||||
isConstrained: false,
|
isConstrained: false,
|
||||||
value: '3.14',
|
value: '3.14',
|
||||||
sourceRange: [317, 321],
|
sourceRange: [320, 324, true],
|
||||||
argPosition: { type: 'objectProperty', key: 'length' },
|
argPosition: { type: 'objectProperty', key: 'length' },
|
||||||
pathToNode: expect.any(Array),
|
pathToNode: expect.any(Array),
|
||||||
stdLibFnName: 'angledLineOfXLength',
|
stdLibFnName: 'angledLineOfXLength',
|
||||||
@ -552,7 +552,7 @@ describe('testing getConstraintInfo', () => {
|
|||||||
type: 'angle',
|
type: 'angle',
|
||||||
isConstrained: false,
|
isConstrained: false,
|
||||||
value: '30',
|
value: '30',
|
||||||
sourceRange: [369, 371],
|
sourceRange: [373, 375, true],
|
||||||
argPosition: { type: 'objectProperty', key: 'angle' },
|
argPosition: { type: 'objectProperty', key: 'angle' },
|
||||||
pathToNode: expect.any(Array),
|
pathToNode: expect.any(Array),
|
||||||
stdLibFnName: 'angledLineOfYLength',
|
stdLibFnName: 'angledLineOfYLength',
|
||||||
@ -561,7 +561,7 @@ describe('testing getConstraintInfo', () => {
|
|||||||
type: 'yRelative',
|
type: 'yRelative',
|
||||||
isConstrained: false,
|
isConstrained: false,
|
||||||
value: '3',
|
value: '3',
|
||||||
sourceRange: [385, 386],
|
sourceRange: [390, 391, true],
|
||||||
argPosition: { type: 'objectProperty', key: 'length' },
|
argPosition: { type: 'objectProperty', key: 'length' },
|
||||||
pathToNode: expect.any(Array),
|
pathToNode: expect.any(Array),
|
||||||
stdLibFnName: 'angledLineOfYLength',
|
stdLibFnName: 'angledLineOfYLength',
|
||||||
@ -575,7 +575,7 @@ describe('testing getConstraintInfo', () => {
|
|||||||
type: 'angle',
|
type: 'angle',
|
||||||
isConstrained: false,
|
isConstrained: false,
|
||||||
value: '12.14',
|
value: '12.14',
|
||||||
sourceRange: [428, 433],
|
sourceRange: [434, 439, true],
|
||||||
argPosition: { type: 'objectProperty', key: 'angle' },
|
argPosition: { type: 'objectProperty', key: 'angle' },
|
||||||
pathToNode: expect.any(Array),
|
pathToNode: expect.any(Array),
|
||||||
stdLibFnName: 'angledLineToX',
|
stdLibFnName: 'angledLineToX',
|
||||||
@ -584,7 +584,7 @@ describe('testing getConstraintInfo', () => {
|
|||||||
type: 'xAbsolute',
|
type: 'xAbsolute',
|
||||||
isConstrained: false,
|
isConstrained: false,
|
||||||
value: '12',
|
value: '12',
|
||||||
sourceRange: [443, 445],
|
sourceRange: [450, 452, true],
|
||||||
argPosition: { type: 'objectProperty', key: 'to' },
|
argPosition: { type: 'objectProperty', key: 'to' },
|
||||||
pathToNode: expect.any(Array),
|
pathToNode: expect.any(Array),
|
||||||
stdLibFnName: 'angledLineToX',
|
stdLibFnName: 'angledLineToX',
|
||||||
@ -598,7 +598,7 @@ describe('testing getConstraintInfo', () => {
|
|||||||
type: 'angle',
|
type: 'angle',
|
||||||
isConstrained: false,
|
isConstrained: false,
|
||||||
value: '30',
|
value: '30',
|
||||||
sourceRange: [487, 489],
|
sourceRange: [495, 497, true],
|
||||||
argPosition: { type: 'objectProperty', key: 'angle' },
|
argPosition: { type: 'objectProperty', key: 'angle' },
|
||||||
pathToNode: expect.any(Array),
|
pathToNode: expect.any(Array),
|
||||||
stdLibFnName: 'angledLineToY',
|
stdLibFnName: 'angledLineToY',
|
||||||
@ -607,7 +607,7 @@ describe('testing getConstraintInfo', () => {
|
|||||||
type: 'yAbsolute',
|
type: 'yAbsolute',
|
||||||
isConstrained: false,
|
isConstrained: false,
|
||||||
value: '10.14',
|
value: '10.14',
|
||||||
sourceRange: [499, 504],
|
sourceRange: [508, 513, true],
|
||||||
argPosition: { type: 'objectProperty', key: 'to' },
|
argPosition: { type: 'objectProperty', key: 'to' },
|
||||||
pathToNode: expect.any(Array),
|
pathToNode: expect.any(Array),
|
||||||
stdLibFnName: 'angledLineToY',
|
stdLibFnName: 'angledLineToY',
|
||||||
@ -621,7 +621,7 @@ describe('testing getConstraintInfo', () => {
|
|||||||
type: 'angle',
|
type: 'angle',
|
||||||
isConstrained: false,
|
isConstrained: false,
|
||||||
value: '3.14',
|
value: '3.14',
|
||||||
sourceRange: [557, 561],
|
sourceRange: [567, 571, true],
|
||||||
argPosition: { type: 'objectProperty', key: 'angle' },
|
argPosition: { type: 'objectProperty', key: 'angle' },
|
||||||
pathToNode: expect.any(Array),
|
pathToNode: expect.any(Array),
|
||||||
stdLibFnName: 'angledLineThatIntersects',
|
stdLibFnName: 'angledLineThatIntersects',
|
||||||
@ -630,7 +630,7 @@ describe('testing getConstraintInfo', () => {
|
|||||||
type: 'intersectionOffset',
|
type: 'intersectionOffset',
|
||||||
isConstrained: false,
|
isConstrained: false,
|
||||||
value: '0',
|
value: '0',
|
||||||
sourceRange: [598, 599],
|
sourceRange: [608, 609, true],
|
||||||
argPosition: { type: 'objectProperty', key: 'offset' },
|
argPosition: { type: 'objectProperty', key: 'offset' },
|
||||||
pathToNode: expect.any(Array),
|
pathToNode: expect.any(Array),
|
||||||
stdLibFnName: 'angledLineThatIntersects',
|
stdLibFnName: 'angledLineThatIntersects',
|
||||||
@ -638,8 +638,8 @@ describe('testing getConstraintInfo', () => {
|
|||||||
{
|
{
|
||||||
type: 'intersectionTag',
|
type: 'intersectionTag',
|
||||||
isConstrained: false,
|
isConstrained: false,
|
||||||
value: "'a'",
|
value: 'a',
|
||||||
sourceRange: [581, 584],
|
sourceRange: [592, 593, true],
|
||||||
argPosition: {
|
argPosition: {
|
||||||
key: 'intersectTag',
|
key: 'intersectTag',
|
||||||
type: 'objectProperty',
|
type: 'objectProperty',
|
||||||
@ -656,7 +656,7 @@ describe('testing getConstraintInfo', () => {
|
|||||||
type: 'tangentialWithPrevious',
|
type: 'tangentialWithPrevious',
|
||||||
isConstrained: true,
|
isConstrained: true,
|
||||||
value: 'tangentialArcTo',
|
value: 'tangentialArcTo',
|
||||||
sourceRange: [613, 628],
|
sourceRange: [623, 638, true],
|
||||||
argPosition: undefined,
|
argPosition: undefined,
|
||||||
pathToNode: expect.any(Array),
|
pathToNode: expect.any(Array),
|
||||||
stdLibFnName: 'tangentialArcTo',
|
stdLibFnName: 'tangentialArcTo',
|
||||||
@ -665,7 +665,7 @@ describe('testing getConstraintInfo', () => {
|
|||||||
type: 'xAbsolute',
|
type: 'xAbsolute',
|
||||||
isConstrained: false,
|
isConstrained: false,
|
||||||
value: '3.14',
|
value: '3.14',
|
||||||
sourceRange: [630, 634],
|
sourceRange: [640, 644, true],
|
||||||
argPosition: { type: 'arrayItem', index: 0 },
|
argPosition: { type: 'arrayItem', index: 0 },
|
||||||
pathToNode: expect.any(Array),
|
pathToNode: expect.any(Array),
|
||||||
stdLibFnName: 'tangentialArcTo',
|
stdLibFnName: 'tangentialArcTo',
|
||||||
@ -674,7 +674,7 @@ describe('testing getConstraintInfo', () => {
|
|||||||
type: 'yAbsolute',
|
type: 'yAbsolute',
|
||||||
isConstrained: false,
|
isConstrained: false,
|
||||||
value: '13.14',
|
value: '13.14',
|
||||||
sourceRange: [636, 641],
|
sourceRange: [646, 651, true],
|
||||||
argPosition: { type: 'arrayItem', index: 1 },
|
argPosition: { type: 'arrayItem', index: 1 },
|
||||||
pathToNode: expect.any(Array),
|
pathToNode: expect.any(Array),
|
||||||
stdLibFnName: 'tangentialArcTo',
|
stdLibFnName: 'tangentialArcTo',
|
||||||
@ -685,6 +685,7 @@ describe('testing getConstraintInfo', () => {
|
|||||||
const sourceRange: SourceRange = [
|
const sourceRange: SourceRange = [
|
||||||
code.indexOf(functionName),
|
code.indexOf(functionName),
|
||||||
code.indexOf(functionName) + functionName.length,
|
code.indexOf(functionName) + functionName.length,
|
||||||
|
true,
|
||||||
]
|
]
|
||||||
if (err(ast)) return ast
|
if (err(ast)) return ast
|
||||||
const pathToNode = getNodePathFromSourceRange(ast, sourceRange)
|
const pathToNode = getNodePathFromSourceRange(ast, sourceRange)
|
||||||
@ -706,7 +707,7 @@ describe('testing getConstraintInfo', () => {
|
|||||||
|> lineTo([6.14, 3.14], %)
|
|> lineTo([6.14, 3.14], %)
|
||||||
|> xLineTo(8, %)
|
|> xLineTo(8, %)
|
||||||
|> yLineTo(5, %)
|
|> yLineTo(5, %)
|
||||||
|> yLine(3.14, %, 'a')
|
|> yLine(3.14, %, $a)
|
||||||
|> xLine(3.14, %)
|
|> xLine(3.14, %)
|
||||||
|> angledLineOfXLength([3.14, 3.14], %)
|
|> angledLineOfXLength([3.14, 3.14], %)
|
||||||
|> angledLineOfYLength([30, 3], %)
|
|> angledLineOfYLength([30, 3], %)
|
||||||
@ -714,11 +715,11 @@ describe('testing getConstraintInfo', () => {
|
|||||||
|> angledLineToY([30, 10], %)
|
|> angledLineToY([30, 10], %)
|
||||||
|> angledLineThatIntersects({
|
|> angledLineThatIntersects({
|
||||||
angle = 3.14,
|
angle = 3.14,
|
||||||
intersectTag = 'a',
|
intersectTag = a,
|
||||||
offset = 0
|
offset = 0
|
||||||
}, %)
|
}, %)
|
||||||
|> tangentialArcTo([3.14, 13.14], %)`
|
|> tangentialArcTo([3.14, 13.14], %)`
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
test.each([
|
test.each([
|
||||||
[
|
[
|
||||||
`angledLine(`,
|
`angledLine(`,
|
||||||
@ -727,7 +728,7 @@ describe('testing getConstraintInfo', () => {
|
|||||||
type: 'angle',
|
type: 'angle',
|
||||||
isConstrained: false,
|
isConstrained: false,
|
||||||
value: '3.14',
|
value: '3.14',
|
||||||
sourceRange: [112, 116],
|
sourceRange: [112, 116, true],
|
||||||
argPosition: { type: 'arrayItem', index: 0 },
|
argPosition: { type: 'arrayItem', index: 0 },
|
||||||
pathToNode: expect.any(Array),
|
pathToNode: expect.any(Array),
|
||||||
stdLibFnName: 'angledLine',
|
stdLibFnName: 'angledLine',
|
||||||
@ -736,7 +737,7 @@ describe('testing getConstraintInfo', () => {
|
|||||||
type: 'length',
|
type: 'length',
|
||||||
isConstrained: false,
|
isConstrained: false,
|
||||||
value: '3.14',
|
value: '3.14',
|
||||||
sourceRange: [118, 122],
|
sourceRange: [118, 122, true],
|
||||||
argPosition: { type: 'arrayItem', index: 1 },
|
argPosition: { type: 'arrayItem', index: 1 },
|
||||||
pathToNode: expect.any(Array),
|
pathToNode: expect.any(Array),
|
||||||
stdLibFnName: 'angledLine',
|
stdLibFnName: 'angledLine',
|
||||||
@ -750,7 +751,7 @@ describe('testing getConstraintInfo', () => {
|
|||||||
type: 'angle',
|
type: 'angle',
|
||||||
isConstrained: false,
|
isConstrained: false,
|
||||||
value: '3.14',
|
value: '3.14',
|
||||||
sourceRange: [278, 282],
|
sourceRange: [277, 281, true],
|
||||||
argPosition: { type: 'arrayItem', index: 0 },
|
argPosition: { type: 'arrayItem', index: 0 },
|
||||||
pathToNode: expect.any(Array),
|
pathToNode: expect.any(Array),
|
||||||
stdLibFnName: 'angledLineOfXLength',
|
stdLibFnName: 'angledLineOfXLength',
|
||||||
@ -759,7 +760,7 @@ describe('testing getConstraintInfo', () => {
|
|||||||
type: 'xRelative',
|
type: 'xRelative',
|
||||||
isConstrained: false,
|
isConstrained: false,
|
||||||
value: '3.14',
|
value: '3.14',
|
||||||
sourceRange: [284, 288],
|
sourceRange: [283, 287, true],
|
||||||
argPosition: { type: 'arrayItem', index: 1 },
|
argPosition: { type: 'arrayItem', index: 1 },
|
||||||
pathToNode: expect.any(Array),
|
pathToNode: expect.any(Array),
|
||||||
stdLibFnName: 'angledLineOfXLength',
|
stdLibFnName: 'angledLineOfXLength',
|
||||||
@ -773,7 +774,7 @@ describe('testing getConstraintInfo', () => {
|
|||||||
type: 'angle',
|
type: 'angle',
|
||||||
isConstrained: false,
|
isConstrained: false,
|
||||||
value: '30',
|
value: '30',
|
||||||
sourceRange: [322, 324],
|
sourceRange: [321, 323, true],
|
||||||
argPosition: { type: 'arrayItem', index: 0 },
|
argPosition: { type: 'arrayItem', index: 0 },
|
||||||
pathToNode: expect.any(Array),
|
pathToNode: expect.any(Array),
|
||||||
stdLibFnName: 'angledLineOfYLength',
|
stdLibFnName: 'angledLineOfYLength',
|
||||||
@ -782,7 +783,7 @@ describe('testing getConstraintInfo', () => {
|
|||||||
type: 'yRelative',
|
type: 'yRelative',
|
||||||
isConstrained: false,
|
isConstrained: false,
|
||||||
value: '3',
|
value: '3',
|
||||||
sourceRange: [326, 327],
|
sourceRange: [325, 326, true],
|
||||||
argPosition: { type: 'arrayItem', index: 1 },
|
argPosition: { type: 'arrayItem', index: 1 },
|
||||||
pathToNode: expect.any(Array),
|
pathToNode: expect.any(Array),
|
||||||
stdLibFnName: 'angledLineOfYLength',
|
stdLibFnName: 'angledLineOfYLength',
|
||||||
@ -796,7 +797,7 @@ describe('testing getConstraintInfo', () => {
|
|||||||
type: 'angle',
|
type: 'angle',
|
||||||
isConstrained: false,
|
isConstrained: false,
|
||||||
value: '12',
|
value: '12',
|
||||||
sourceRange: [355, 357],
|
sourceRange: [354, 356, true],
|
||||||
argPosition: { type: 'arrayItem', index: 0 },
|
argPosition: { type: 'arrayItem', index: 0 },
|
||||||
pathToNode: expect.any(Array),
|
pathToNode: expect.any(Array),
|
||||||
stdLibFnName: 'angledLineToX',
|
stdLibFnName: 'angledLineToX',
|
||||||
@ -805,7 +806,7 @@ describe('testing getConstraintInfo', () => {
|
|||||||
type: 'xAbsolute',
|
type: 'xAbsolute',
|
||||||
isConstrained: false,
|
isConstrained: false,
|
||||||
value: '12',
|
value: '12',
|
||||||
sourceRange: [359, 361],
|
sourceRange: [358, 360, true],
|
||||||
argPosition: { type: 'arrayItem', index: 1 },
|
argPosition: { type: 'arrayItem', index: 1 },
|
||||||
pathToNode: expect.any(Array),
|
pathToNode: expect.any(Array),
|
||||||
stdLibFnName: 'angledLineToX',
|
stdLibFnName: 'angledLineToX',
|
||||||
@ -819,7 +820,7 @@ describe('testing getConstraintInfo', () => {
|
|||||||
type: 'angle',
|
type: 'angle',
|
||||||
isConstrained: false,
|
isConstrained: false,
|
||||||
value: '30',
|
value: '30',
|
||||||
sourceRange: [389, 391],
|
sourceRange: [388, 390, true],
|
||||||
argPosition: { type: 'arrayItem', index: 0 },
|
argPosition: { type: 'arrayItem', index: 0 },
|
||||||
pathToNode: expect.any(Array),
|
pathToNode: expect.any(Array),
|
||||||
stdLibFnName: 'angledLineToY',
|
stdLibFnName: 'angledLineToY',
|
||||||
@ -828,7 +829,7 @@ describe('testing getConstraintInfo', () => {
|
|||||||
type: 'yAbsolute',
|
type: 'yAbsolute',
|
||||||
isConstrained: false,
|
isConstrained: false,
|
||||||
value: '10',
|
value: '10',
|
||||||
sourceRange: [393, 395],
|
sourceRange: [392, 394, true],
|
||||||
argPosition: { type: 'arrayItem', index: 1 },
|
argPosition: { type: 'arrayItem', index: 1 },
|
||||||
pathToNode: expect.any(Array),
|
pathToNode: expect.any(Array),
|
||||||
stdLibFnName: 'angledLineToY',
|
stdLibFnName: 'angledLineToY',
|
||||||
@ -839,6 +840,7 @@ describe('testing getConstraintInfo', () => {
|
|||||||
const sourceRange: SourceRange = [
|
const sourceRange: SourceRange = [
|
||||||
code.indexOf(functionName),
|
code.indexOf(functionName),
|
||||||
code.indexOf(functionName) + functionName.length,
|
code.indexOf(functionName) + functionName.length,
|
||||||
|
true,
|
||||||
]
|
]
|
||||||
if (err(ast)) return ast
|
if (err(ast)) return ast
|
||||||
const pathToNode = getNodePathFromSourceRange(ast, sourceRange)
|
const pathToNode = getNodePathFromSourceRange(ast, sourceRange)
|
||||||
@ -860,7 +862,7 @@ describe('testing getConstraintInfo', () => {
|
|||||||
|> lineTo([6.14 + 0, 3.14 + 0], %)
|
|> lineTo([6.14 + 0, 3.14 + 0], %)
|
||||||
|> xLineTo(8 + 0, %)
|
|> xLineTo(8 + 0, %)
|
||||||
|> yLineTo(5 + 0, %)
|
|> yLineTo(5 + 0, %)
|
||||||
|> yLine(3.14 + 0, %, 'a')
|
|> yLine(3.14 + 0, %, $a)
|
||||||
|> xLine(3.14 + 0, %)
|
|> xLine(3.14 + 0, %)
|
||||||
|> angledLineOfXLength({ angle = 3.14 + 0, length = 3.14 + 0 }, %)
|
|> angledLineOfXLength({ angle = 3.14 + 0, length = 3.14 + 0 }, %)
|
||||||
|> angledLineOfYLength({ angle = 30 + 0, length = 3 + 0 }, %)
|
|> angledLineOfYLength({ angle = 30 + 0, length = 3 + 0 }, %)
|
||||||
@ -868,11 +870,11 @@ describe('testing getConstraintInfo', () => {
|
|||||||
|> angledLineToY({ angle = 30 + 0, to = 10.14 + 0 }, %)
|
|> angledLineToY({ angle = 30 + 0, to = 10.14 + 0 }, %)
|
||||||
|> angledLineThatIntersects({
|
|> angledLineThatIntersects({
|
||||||
angle = 3.14 + 0,
|
angle = 3.14 + 0,
|
||||||
intersectTag = 'a',
|
intersectTag = a,
|
||||||
offset = 0 + 0
|
offset = 0 + 0
|
||||||
}, %)
|
}, %)
|
||||||
|> tangentialArcTo([3.14 + 0, 13.14 + 0], %)`
|
|> tangentialArcTo([3.14 + 0, 13.14 + 0], %)`
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
test.each([
|
test.each([
|
||||||
[
|
[
|
||||||
'line',
|
'line',
|
||||||
@ -881,7 +883,7 @@ describe('testing getConstraintInfo', () => {
|
|||||||
type: 'xRelative',
|
type: 'xRelative',
|
||||||
isConstrained: true,
|
isConstrained: true,
|
||||||
value: '3 + 0',
|
value: '3 + 0',
|
||||||
sourceRange: [83, 88],
|
sourceRange: [83, 88, true],
|
||||||
argPosition: { type: 'arrayItem', index: 0 },
|
argPosition: { type: 'arrayItem', index: 0 },
|
||||||
pathToNode: expect.any(Array),
|
pathToNode: expect.any(Array),
|
||||||
stdLibFnName: 'line',
|
stdLibFnName: 'line',
|
||||||
@ -890,7 +892,7 @@ describe('testing getConstraintInfo', () => {
|
|||||||
type: 'yRelative',
|
type: 'yRelative',
|
||||||
isConstrained: true,
|
isConstrained: true,
|
||||||
value: '4 + 0',
|
value: '4 + 0',
|
||||||
sourceRange: [90, 95],
|
sourceRange: [90, 95, true],
|
||||||
argPosition: { type: 'arrayItem', index: 1 },
|
argPosition: { type: 'arrayItem', index: 1 },
|
||||||
pathToNode: expect.any(Array),
|
pathToNode: expect.any(Array),
|
||||||
stdLibFnName: 'line',
|
stdLibFnName: 'line',
|
||||||
@ -904,7 +906,7 @@ describe('testing getConstraintInfo', () => {
|
|||||||
type: 'angle',
|
type: 'angle',
|
||||||
isConstrained: true,
|
isConstrained: true,
|
||||||
value: '3.14 + 0',
|
value: '3.14 + 0',
|
||||||
sourceRange: [128, 136],
|
sourceRange: [129, 137, true],
|
||||||
argPosition: { type: 'objectProperty', key: 'angle' },
|
argPosition: { type: 'objectProperty', key: 'angle' },
|
||||||
pathToNode: expect.any(Array),
|
pathToNode: expect.any(Array),
|
||||||
stdLibFnName: 'angledLine',
|
stdLibFnName: 'angledLine',
|
||||||
@ -913,7 +915,7 @@ describe('testing getConstraintInfo', () => {
|
|||||||
type: 'length',
|
type: 'length',
|
||||||
isConstrained: true,
|
isConstrained: true,
|
||||||
value: '3.14 + 0',
|
value: '3.14 + 0',
|
||||||
sourceRange: [146, 154],
|
sourceRange: [148, 156, true],
|
||||||
argPosition: { type: 'objectProperty', key: 'length' },
|
argPosition: { type: 'objectProperty', key: 'length' },
|
||||||
pathToNode: expect.any(Array),
|
pathToNode: expect.any(Array),
|
||||||
stdLibFnName: 'angledLine',
|
stdLibFnName: 'angledLine',
|
||||||
@ -927,7 +929,7 @@ describe('testing getConstraintInfo', () => {
|
|||||||
type: 'xAbsolute',
|
type: 'xAbsolute',
|
||||||
isConstrained: true,
|
isConstrained: true,
|
||||||
value: '6.14 + 0',
|
value: '6.14 + 0',
|
||||||
sourceRange: [176, 184],
|
sourceRange: [178, 186, true],
|
||||||
argPosition: { type: 'arrayItem', index: 0 },
|
argPosition: { type: 'arrayItem', index: 0 },
|
||||||
pathToNode: expect.any(Array),
|
pathToNode: expect.any(Array),
|
||||||
stdLibFnName: 'lineTo',
|
stdLibFnName: 'lineTo',
|
||||||
@ -936,7 +938,7 @@ describe('testing getConstraintInfo', () => {
|
|||||||
type: 'yAbsolute',
|
type: 'yAbsolute',
|
||||||
isConstrained: true,
|
isConstrained: true,
|
||||||
value: '3.14 + 0',
|
value: '3.14 + 0',
|
||||||
sourceRange: [186, 194],
|
sourceRange: [188, 196, true],
|
||||||
argPosition: { type: 'arrayItem', index: 1 },
|
argPosition: { type: 'arrayItem', index: 1 },
|
||||||
pathToNode: expect.any(Array),
|
pathToNode: expect.any(Array),
|
||||||
stdLibFnName: 'lineTo',
|
stdLibFnName: 'lineTo',
|
||||||
@ -950,7 +952,7 @@ describe('testing getConstraintInfo', () => {
|
|||||||
type: 'horizontal',
|
type: 'horizontal',
|
||||||
isConstrained: true,
|
isConstrained: true,
|
||||||
value: 'xLineTo',
|
value: 'xLineTo',
|
||||||
sourceRange: [207, 214],
|
sourceRange: [209, 216, true],
|
||||||
argPosition: undefined,
|
argPosition: undefined,
|
||||||
pathToNode: expect.any(Array),
|
pathToNode: expect.any(Array),
|
||||||
stdLibFnName: 'xLineTo',
|
stdLibFnName: 'xLineTo',
|
||||||
@ -959,7 +961,7 @@ describe('testing getConstraintInfo', () => {
|
|||||||
type: 'xAbsolute',
|
type: 'xAbsolute',
|
||||||
isConstrained: true,
|
isConstrained: true,
|
||||||
value: '8 + 0',
|
value: '8 + 0',
|
||||||
sourceRange: [215, 220],
|
sourceRange: [217, 222, true],
|
||||||
argPosition: { type: 'singleValue' },
|
argPosition: { type: 'singleValue' },
|
||||||
pathToNode: expect.any(Array),
|
pathToNode: expect.any(Array),
|
||||||
stdLibFnName: 'xLineTo',
|
stdLibFnName: 'xLineTo',
|
||||||
@ -973,7 +975,7 @@ describe('testing getConstraintInfo', () => {
|
|||||||
type: 'vertical',
|
type: 'vertical',
|
||||||
isConstrained: true,
|
isConstrained: true,
|
||||||
value: 'yLineTo',
|
value: 'yLineTo',
|
||||||
sourceRange: [232, 239],
|
sourceRange: [234, 241, true],
|
||||||
argPosition: undefined,
|
argPosition: undefined,
|
||||||
pathToNode: expect.any(Array),
|
pathToNode: expect.any(Array),
|
||||||
stdLibFnName: 'yLineTo',
|
stdLibFnName: 'yLineTo',
|
||||||
@ -982,7 +984,7 @@ describe('testing getConstraintInfo', () => {
|
|||||||
type: 'yAbsolute',
|
type: 'yAbsolute',
|
||||||
isConstrained: true,
|
isConstrained: true,
|
||||||
value: '5 + 0',
|
value: '5 + 0',
|
||||||
sourceRange: [240, 245],
|
sourceRange: [242, 247, true],
|
||||||
argPosition: { type: 'singleValue' },
|
argPosition: { type: 'singleValue' },
|
||||||
pathToNode: expect.any(Array),
|
pathToNode: expect.any(Array),
|
||||||
stdLibFnName: 'yLineTo',
|
stdLibFnName: 'yLineTo',
|
||||||
@ -996,7 +998,7 @@ describe('testing getConstraintInfo', () => {
|
|||||||
type: 'vertical',
|
type: 'vertical',
|
||||||
isConstrained: true,
|
isConstrained: true,
|
||||||
value: 'yLine',
|
value: 'yLine',
|
||||||
sourceRange: [257, 262],
|
sourceRange: [259, 264, true],
|
||||||
argPosition: undefined,
|
argPosition: undefined,
|
||||||
pathToNode: expect.any(Array),
|
pathToNode: expect.any(Array),
|
||||||
stdLibFnName: 'yLine',
|
stdLibFnName: 'yLine',
|
||||||
@ -1005,7 +1007,7 @@ describe('testing getConstraintInfo', () => {
|
|||||||
type: 'yRelative',
|
type: 'yRelative',
|
||||||
isConstrained: true,
|
isConstrained: true,
|
||||||
value: '3.14 + 0',
|
value: '3.14 + 0',
|
||||||
sourceRange: [263, 271],
|
sourceRange: [265, 273, true],
|
||||||
argPosition: { type: 'singleValue' },
|
argPosition: { type: 'singleValue' },
|
||||||
pathToNode: expect.any(Array),
|
pathToNode: expect.any(Array),
|
||||||
stdLibFnName: 'yLine',
|
stdLibFnName: 'yLine',
|
||||||
@ -1019,7 +1021,7 @@ describe('testing getConstraintInfo', () => {
|
|||||||
type: 'horizontal',
|
type: 'horizontal',
|
||||||
isConstrained: true,
|
isConstrained: true,
|
||||||
value: 'xLine',
|
value: 'xLine',
|
||||||
sourceRange: [288, 293],
|
sourceRange: [289, 294, true],
|
||||||
argPosition: undefined,
|
argPosition: undefined,
|
||||||
pathToNode: expect.any(Array),
|
pathToNode: expect.any(Array),
|
||||||
stdLibFnName: 'xLine',
|
stdLibFnName: 'xLine',
|
||||||
@ -1028,7 +1030,7 @@ describe('testing getConstraintInfo', () => {
|
|||||||
type: 'xRelative',
|
type: 'xRelative',
|
||||||
isConstrained: true,
|
isConstrained: true,
|
||||||
value: '3.14 + 0',
|
value: '3.14 + 0',
|
||||||
sourceRange: [294, 302],
|
sourceRange: [295, 303, true],
|
||||||
argPosition: { type: 'singleValue' },
|
argPosition: { type: 'singleValue' },
|
||||||
pathToNode: expect.any(Array),
|
pathToNode: expect.any(Array),
|
||||||
stdLibFnName: 'xLine',
|
stdLibFnName: 'xLine',
|
||||||
@ -1042,7 +1044,7 @@ describe('testing getConstraintInfo', () => {
|
|||||||
type: 'angle',
|
type: 'angle',
|
||||||
isConstrained: true,
|
isConstrained: true,
|
||||||
value: '3.14 + 0',
|
value: '3.14 + 0',
|
||||||
sourceRange: [343, 351],
|
sourceRange: [345, 353, true],
|
||||||
argPosition: { type: 'objectProperty', key: 'angle' },
|
argPosition: { type: 'objectProperty', key: 'angle' },
|
||||||
pathToNode: expect.any(Array),
|
pathToNode: expect.any(Array),
|
||||||
stdLibFnName: 'angledLineOfXLength',
|
stdLibFnName: 'angledLineOfXLength',
|
||||||
@ -1051,7 +1053,7 @@ describe('testing getConstraintInfo', () => {
|
|||||||
type: 'xRelative',
|
type: 'xRelative',
|
||||||
isConstrained: true,
|
isConstrained: true,
|
||||||
value: '3.14 + 0',
|
value: '3.14 + 0',
|
||||||
sourceRange: [361, 369],
|
sourceRange: [364, 372, true],
|
||||||
argPosition: { type: 'objectProperty', key: 'length' },
|
argPosition: { type: 'objectProperty', key: 'length' },
|
||||||
pathToNode: expect.any(Array),
|
pathToNode: expect.any(Array),
|
||||||
stdLibFnName: 'angledLineOfXLength',
|
stdLibFnName: 'angledLineOfXLength',
|
||||||
@ -1065,7 +1067,7 @@ describe('testing getConstraintInfo', () => {
|
|||||||
type: 'angle',
|
type: 'angle',
|
||||||
isConstrained: true,
|
isConstrained: true,
|
||||||
value: '30 + 0',
|
value: '30 + 0',
|
||||||
sourceRange: [412, 418],
|
sourceRange: [416, 422, true],
|
||||||
argPosition: { type: 'objectProperty', key: 'angle' },
|
argPosition: { type: 'objectProperty', key: 'angle' },
|
||||||
pathToNode: expect.any(Array),
|
pathToNode: expect.any(Array),
|
||||||
stdLibFnName: 'angledLineOfYLength',
|
stdLibFnName: 'angledLineOfYLength',
|
||||||
@ -1074,7 +1076,7 @@ describe('testing getConstraintInfo', () => {
|
|||||||
type: 'yRelative',
|
type: 'yRelative',
|
||||||
isConstrained: true,
|
isConstrained: true,
|
||||||
value: '3 + 0',
|
value: '3 + 0',
|
||||||
sourceRange: [428, 433],
|
sourceRange: [433, 438, true],
|
||||||
argPosition: { type: 'objectProperty', key: 'length' },
|
argPosition: { type: 'objectProperty', key: 'length' },
|
||||||
pathToNode: expect.any(Array),
|
pathToNode: expect.any(Array),
|
||||||
stdLibFnName: 'angledLineOfYLength',
|
stdLibFnName: 'angledLineOfYLength',
|
||||||
@ -1088,7 +1090,7 @@ describe('testing getConstraintInfo', () => {
|
|||||||
type: 'angle',
|
type: 'angle',
|
||||||
isConstrained: true,
|
isConstrained: true,
|
||||||
value: '12.14 + 0',
|
value: '12.14 + 0',
|
||||||
sourceRange: [470, 479],
|
sourceRange: [476, 485, true],
|
||||||
argPosition: { type: 'objectProperty', key: 'angle' },
|
argPosition: { type: 'objectProperty', key: 'angle' },
|
||||||
pathToNode: expect.any(Array),
|
pathToNode: expect.any(Array),
|
||||||
stdLibFnName: 'angledLineToX',
|
stdLibFnName: 'angledLineToX',
|
||||||
@ -1097,7 +1099,7 @@ describe('testing getConstraintInfo', () => {
|
|||||||
type: 'xAbsolute',
|
type: 'xAbsolute',
|
||||||
isConstrained: true,
|
isConstrained: true,
|
||||||
value: '12 + 0',
|
value: '12 + 0',
|
||||||
sourceRange: [485, 491],
|
sourceRange: [492, 498, true],
|
||||||
argPosition: { type: 'objectProperty', key: 'to' },
|
argPosition: { type: 'objectProperty', key: 'to' },
|
||||||
pathToNode: expect.any(Array),
|
pathToNode: expect.any(Array),
|
||||||
stdLibFnName: 'angledLineToX',
|
stdLibFnName: 'angledLineToX',
|
||||||
@ -1111,7 +1113,7 @@ describe('testing getConstraintInfo', () => {
|
|||||||
type: 'angle',
|
type: 'angle',
|
||||||
isConstrained: true,
|
isConstrained: true,
|
||||||
value: '30 + 0',
|
value: '30 + 0',
|
||||||
sourceRange: [528, 534],
|
sourceRange: [536, 542, true],
|
||||||
argPosition: { type: 'objectProperty', key: 'angle' },
|
argPosition: { type: 'objectProperty', key: 'angle' },
|
||||||
pathToNode: expect.any(Array),
|
pathToNode: expect.any(Array),
|
||||||
stdLibFnName: 'angledLineToY',
|
stdLibFnName: 'angledLineToY',
|
||||||
@ -1120,7 +1122,7 @@ describe('testing getConstraintInfo', () => {
|
|||||||
type: 'yAbsolute',
|
type: 'yAbsolute',
|
||||||
isConstrained: true,
|
isConstrained: true,
|
||||||
value: '10.14 + 0',
|
value: '10.14 + 0',
|
||||||
sourceRange: [540, 549],
|
sourceRange: [549, 558, true],
|
||||||
argPosition: { type: 'objectProperty', key: 'to' },
|
argPosition: { type: 'objectProperty', key: 'to' },
|
||||||
pathToNode: expect.any(Array),
|
pathToNode: expect.any(Array),
|
||||||
stdLibFnName: 'angledLineToY',
|
stdLibFnName: 'angledLineToY',
|
||||||
@ -1134,7 +1136,7 @@ describe('testing getConstraintInfo', () => {
|
|||||||
type: 'angle',
|
type: 'angle',
|
||||||
isConstrained: true,
|
isConstrained: true,
|
||||||
value: '3.14 + 0',
|
value: '3.14 + 0',
|
||||||
sourceRange: [606, 614],
|
sourceRange: [616, 624, true],
|
||||||
argPosition: { type: 'objectProperty', key: 'angle' },
|
argPosition: { type: 'objectProperty', key: 'angle' },
|
||||||
pathToNode: expect.any(Array),
|
pathToNode: expect.any(Array),
|
||||||
stdLibFnName: 'angledLineThatIntersects',
|
stdLibFnName: 'angledLineThatIntersects',
|
||||||
@ -1143,7 +1145,7 @@ describe('testing getConstraintInfo', () => {
|
|||||||
type: 'intersectionOffset',
|
type: 'intersectionOffset',
|
||||||
isConstrained: true,
|
isConstrained: true,
|
||||||
value: '0 + 0',
|
value: '0 + 0',
|
||||||
sourceRange: [661, 666],
|
sourceRange: [671, 676, true],
|
||||||
argPosition: { type: 'objectProperty', key: 'offset' },
|
argPosition: { type: 'objectProperty', key: 'offset' },
|
||||||
pathToNode: expect.any(Array),
|
pathToNode: expect.any(Array),
|
||||||
stdLibFnName: 'angledLineThatIntersects',
|
stdLibFnName: 'angledLineThatIntersects',
|
||||||
@ -1151,8 +1153,8 @@ describe('testing getConstraintInfo', () => {
|
|||||||
{
|
{
|
||||||
type: 'intersectionTag',
|
type: 'intersectionTag',
|
||||||
isConstrained: false,
|
isConstrained: false,
|
||||||
value: "'a'",
|
value: 'a',
|
||||||
sourceRange: [639, 642],
|
sourceRange: [650, 651, true],
|
||||||
argPosition: { key: 'intersectTag', type: 'objectProperty' },
|
argPosition: { key: 'intersectTag', type: 'objectProperty' },
|
||||||
pathToNode: expect.any(Array),
|
pathToNode: expect.any(Array),
|
||||||
stdLibFnName: 'angledLineThatIntersects',
|
stdLibFnName: 'angledLineThatIntersects',
|
||||||
@ -1166,7 +1168,7 @@ describe('testing getConstraintInfo', () => {
|
|||||||
type: 'tangentialWithPrevious',
|
type: 'tangentialWithPrevious',
|
||||||
isConstrained: true,
|
isConstrained: true,
|
||||||
value: 'tangentialArcTo',
|
value: 'tangentialArcTo',
|
||||||
sourceRange: [687, 702],
|
sourceRange: [697, 712, true],
|
||||||
argPosition: undefined,
|
argPosition: undefined,
|
||||||
pathToNode: expect.any(Array),
|
pathToNode: expect.any(Array),
|
||||||
stdLibFnName: 'tangentialArcTo',
|
stdLibFnName: 'tangentialArcTo',
|
||||||
@ -1175,7 +1177,7 @@ describe('testing getConstraintInfo', () => {
|
|||||||
type: 'xAbsolute',
|
type: 'xAbsolute',
|
||||||
isConstrained: true,
|
isConstrained: true,
|
||||||
value: '3.14 + 0',
|
value: '3.14 + 0',
|
||||||
sourceRange: [704, 712],
|
sourceRange: [714, 722, true],
|
||||||
argPosition: { type: 'arrayItem', index: 0 },
|
argPosition: { type: 'arrayItem', index: 0 },
|
||||||
pathToNode: expect.any(Array),
|
pathToNode: expect.any(Array),
|
||||||
stdLibFnName: 'tangentialArcTo',
|
stdLibFnName: 'tangentialArcTo',
|
||||||
@ -1184,7 +1186,7 @@ describe('testing getConstraintInfo', () => {
|
|||||||
type: 'yAbsolute',
|
type: 'yAbsolute',
|
||||||
isConstrained: true,
|
isConstrained: true,
|
||||||
value: '13.14 + 0',
|
value: '13.14 + 0',
|
||||||
sourceRange: [714, 723],
|
sourceRange: [724, 733, true],
|
||||||
argPosition: { type: 'arrayItem', index: 1 },
|
argPosition: { type: 'arrayItem', index: 1 },
|
||||||
pathToNode: expect.any(Array),
|
pathToNode: expect.any(Array),
|
||||||
stdLibFnName: 'tangentialArcTo',
|
stdLibFnName: 'tangentialArcTo',
|
||||||
@ -1195,6 +1197,7 @@ describe('testing getConstraintInfo', () => {
|
|||||||
const sourceRange: SourceRange = [
|
const sourceRange: SourceRange = [
|
||||||
code.indexOf(functionName),
|
code.indexOf(functionName),
|
||||||
code.indexOf(functionName) + functionName.length,
|
code.indexOf(functionName) + functionName.length,
|
||||||
|
true,
|
||||||
]
|
]
|
||||||
if (err(ast)) return ast
|
if (err(ast)) return ast
|
||||||
const pathToNode = getNodePathFromSourceRange(ast, sourceRange)
|
const pathToNode = getNodePathFromSourceRange(ast, sourceRange)
|
||||||
|
@ -222,7 +222,7 @@ const commonConstraintInfoHelper = (
|
|||||||
code.slice(input1.start, input1.end),
|
code.slice(input1.start, input1.end),
|
||||||
stdLibFnName,
|
stdLibFnName,
|
||||||
isArr ? abbreviatedInputs[0].arrayInput : abbreviatedInputs[0].objInput,
|
isArr ? abbreviatedInputs[0].arrayInput : abbreviatedInputs[0].objInput,
|
||||||
[input1.start, input1.end],
|
[input1.start, input1.end, true],
|
||||||
pathToFirstArg
|
pathToFirstArg
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -234,7 +234,7 @@ const commonConstraintInfoHelper = (
|
|||||||
code.slice(input2.start, input2.end),
|
code.slice(input2.start, input2.end),
|
||||||
stdLibFnName,
|
stdLibFnName,
|
||||||
isArr ? abbreviatedInputs[1].arrayInput : abbreviatedInputs[1].objInput,
|
isArr ? abbreviatedInputs[1].arrayInput : abbreviatedInputs[1].objInput,
|
||||||
[input2.start, input2.end],
|
[input2.start, input2.end, true],
|
||||||
pathToSecondArg
|
pathToSecondArg
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -266,7 +266,7 @@ const horzVertConstraintInfoHelper = (
|
|||||||
callee.name,
|
callee.name,
|
||||||
stdLibFnName,
|
stdLibFnName,
|
||||||
undefined,
|
undefined,
|
||||||
[callee.start, callee.end],
|
[callee.start, callee.end, true],
|
||||||
pathToCallee
|
pathToCallee
|
||||||
),
|
),
|
||||||
constrainInfo(
|
constrainInfo(
|
||||||
@ -275,7 +275,7 @@ const horzVertConstraintInfoHelper = (
|
|||||||
code.slice(firstArg.start, firstArg.end),
|
code.slice(firstArg.start, firstArg.end),
|
||||||
stdLibFnName,
|
stdLibFnName,
|
||||||
abbreviatedInput,
|
abbreviatedInput,
|
||||||
[firstArg.start, firstArg.end],
|
[firstArg.start, firstArg.end, true],
|
||||||
pathToFirstArg
|
pathToFirstArg
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
@ -905,7 +905,7 @@ export const tangentialArcTo: SketchLineHelper = {
|
|||||||
callee.name,
|
callee.name,
|
||||||
'tangentialArcTo',
|
'tangentialArcTo',
|
||||||
undefined,
|
undefined,
|
||||||
[callee.start, callee.end],
|
[callee.start, callee.end, true],
|
||||||
pathToCallee
|
pathToCallee
|
||||||
),
|
),
|
||||||
constrainInfo(
|
constrainInfo(
|
||||||
@ -914,7 +914,7 @@ export const tangentialArcTo: SketchLineHelper = {
|
|||||||
code.slice(firstArg.elements[0].start, firstArg.elements[0].end),
|
code.slice(firstArg.elements[0].start, firstArg.elements[0].end),
|
||||||
'tangentialArcTo',
|
'tangentialArcTo',
|
||||||
0,
|
0,
|
||||||
[firstArg.elements[0].start, firstArg.elements[0].end],
|
[firstArg.elements[0].start, firstArg.elements[0].end, true],
|
||||||
pathToFirstArg
|
pathToFirstArg
|
||||||
),
|
),
|
||||||
constrainInfo(
|
constrainInfo(
|
||||||
@ -923,7 +923,7 @@ export const tangentialArcTo: SketchLineHelper = {
|
|||||||
code.slice(firstArg.elements[1].start, firstArg.elements[1].end),
|
code.slice(firstArg.elements[1].start, firstArg.elements[1].end),
|
||||||
'tangentialArcTo',
|
'tangentialArcTo',
|
||||||
1,
|
1,
|
||||||
[firstArg.elements[1].start, firstArg.elements[1].end],
|
[firstArg.elements[1].start, firstArg.elements[1].end, true],
|
||||||
pathToSecondArg
|
pathToSecondArg
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
@ -1052,7 +1052,7 @@ export const circle: SketchLineHelper = {
|
|||||||
code.slice(radiusDetails.expr.start, radiusDetails.expr.end),
|
code.slice(radiusDetails.expr.start, radiusDetails.expr.end),
|
||||||
'circle',
|
'circle',
|
||||||
'radius',
|
'radius',
|
||||||
[radiusDetails.expr.start, radiusDetails.expr.end],
|
[radiusDetails.expr.start, radiusDetails.expr.end, true],
|
||||||
pathToRadiusLiteral
|
pathToRadiusLiteral
|
||||||
),
|
),
|
||||||
{
|
{
|
||||||
@ -1064,6 +1064,7 @@ export const circle: SketchLineHelper = {
|
|||||||
sourceRange: [
|
sourceRange: [
|
||||||
centerDetails.expr.elements[0].start,
|
centerDetails.expr.elements[0].start,
|
||||||
centerDetails.expr.elements[0].end,
|
centerDetails.expr.elements[0].end,
|
||||||
|
true,
|
||||||
],
|
],
|
||||||
pathToNode: pathToXArg,
|
pathToNode: pathToXArg,
|
||||||
value: code.slice(
|
value: code.slice(
|
||||||
@ -1085,6 +1086,7 @@ export const circle: SketchLineHelper = {
|
|||||||
sourceRange: [
|
sourceRange: [
|
||||||
centerDetails.expr.elements[1].start,
|
centerDetails.expr.elements[1].start,
|
||||||
centerDetails.expr.elements[1].end,
|
centerDetails.expr.elements[1].end,
|
||||||
|
true,
|
||||||
],
|
],
|
||||||
pathToNode: pathToYArg,
|
pathToNode: pathToYArg,
|
||||||
value: code.slice(
|
value: code.slice(
|
||||||
@ -1761,7 +1763,7 @@ export const angledLineThatIntersects: SketchLineHelper = {
|
|||||||
code.slice(angle.start, angle.end),
|
code.slice(angle.start, angle.end),
|
||||||
'angledLineThatIntersects',
|
'angledLineThatIntersects',
|
||||||
'angle',
|
'angle',
|
||||||
[angle.start, angle.end],
|
[angle.start, angle.end, true],
|
||||||
pathToAngleProp
|
pathToAngleProp
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -1780,7 +1782,7 @@ export const angledLineThatIntersects: SketchLineHelper = {
|
|||||||
code.slice(offset.start, offset.end),
|
code.slice(offset.start, offset.end),
|
||||||
'angledLineThatIntersects',
|
'angledLineThatIntersects',
|
||||||
'offset',
|
'offset',
|
||||||
[offset.start, offset.end],
|
[offset.start, offset.end, true],
|
||||||
pathToOffsetProp
|
pathToOffsetProp
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -1799,7 +1801,7 @@ export const angledLineThatIntersects: SketchLineHelper = {
|
|||||||
code.slice(tag.start, tag.end),
|
code.slice(tag.start, tag.end),
|
||||||
'angledLineThatIntersects',
|
'angledLineThatIntersects',
|
||||||
'intersectTag',
|
'intersectTag',
|
||||||
[tag.start, tag.end],
|
[tag.start, tag.end, true],
|
||||||
pathToTagProp
|
pathToTagProp
|
||||||
)
|
)
|
||||||
returnVal.push(info)
|
returnVal.push(info)
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import {
|
import {
|
||||||
parse,
|
assertParse,
|
||||||
Sketch,
|
Sketch,
|
||||||
recast,
|
recast,
|
||||||
initPromise,
|
initPromise,
|
||||||
@ -31,12 +31,11 @@ async function testingSwapSketchFnCall({
|
|||||||
constraintType: ConstraintType
|
constraintType: ConstraintType
|
||||||
}): Promise<{
|
}): Promise<{
|
||||||
newCode: string
|
newCode: string
|
||||||
originalRange: [number, number]
|
originalRange: [number, number, boolean]
|
||||||
}> {
|
}> {
|
||||||
const startIndex = inputCode.indexOf(callToSwap)
|
const startIndex = inputCode.indexOf(callToSwap)
|
||||||
const range: SourceRange = [startIndex, startIndex + callToSwap.length]
|
const range: SourceRange = [startIndex, startIndex + callToSwap.length, true]
|
||||||
const ast = parse(inputCode)
|
const ast = assertParse(inputCode)
|
||||||
if (err(ast)) return Promise.reject(ast)
|
|
||||||
|
|
||||||
const execState = await enginelessExecutor(ast)
|
const execState = await enginelessExecutor(ast)
|
||||||
const selections = {
|
const selections = {
|
||||||
@ -370,13 +369,13 @@ part001 = startSketchOn('XY')
|
|||||||
|> line([2.14, 1.35], %) // normal-segment
|
|> line([2.14, 1.35], %) // normal-segment
|
||||||
|> xLine(3.54, %)`
|
|> xLine(3.54, %)`
|
||||||
it('normal case works', async () => {
|
it('normal case works', async () => {
|
||||||
const execState = await enginelessExecutor(parse(code))
|
const execState = await enginelessExecutor(assertParse(code))
|
||||||
const index = code.indexOf('// normal-segment') - 7
|
const index = code.indexOf('// normal-segment') - 7
|
||||||
const sg = sketchFromKclValue(
|
const sg = sketchFromKclValue(
|
||||||
execState.memory.get('part001'),
|
execState.memory.get('part001'),
|
||||||
'part001'
|
'part001'
|
||||||
) as Sketch
|
) as Sketch
|
||||||
const _segment = getSketchSegmentFromSourceRange(sg, [index, index])
|
const _segment = getSketchSegmentFromSourceRange(sg, [index, index, true])
|
||||||
if (err(_segment)) throw _segment
|
if (err(_segment)) throw _segment
|
||||||
const { __geoMeta, ...segment } = _segment.segment
|
const { __geoMeta, ...segment } = _segment.segment
|
||||||
expect(segment).toEqual({
|
expect(segment).toEqual({
|
||||||
@ -387,11 +386,11 @@ part001 = startSketchOn('XY')
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
it('verify it works when the segment is in the `start` property', async () => {
|
it('verify it works when the segment is in the `start` property', async () => {
|
||||||
const execState = await enginelessExecutor(parse(code))
|
const execState = await enginelessExecutor(assertParse(code))
|
||||||
const index = code.indexOf('// segment-in-start') - 7
|
const index = code.indexOf('// segment-in-start') - 7
|
||||||
const _segment = getSketchSegmentFromSourceRange(
|
const _segment = getSketchSegmentFromSourceRange(
|
||||||
sketchFromKclValue(execState.memory.get('part001'), 'part001') as Sketch,
|
sketchFromKclValue(execState.memory.get('part001'), 'part001') as Sketch,
|
||||||
[index, index]
|
[index, index, true]
|
||||||
)
|
)
|
||||||
if (err(_segment)) throw _segment
|
if (err(_segment)) throw _segment
|
||||||
const { __geoMeta, ...segment } = _segment.segment
|
const { __geoMeta, ...segment } = _segment.segment
|
||||||
|
@ -31,7 +31,7 @@ export function getSketchSegmentFromPathToNode(
|
|||||||
const node = nodeMeta.node
|
const node = nodeMeta.node
|
||||||
if (!node || typeof node.start !== 'number' || !node.end)
|
if (!node || typeof node.start !== 'number' || !node.end)
|
||||||
return new Error('no node found')
|
return new Error('no node found')
|
||||||
const sourceRange: SourceRange = [node.start, node.end]
|
const sourceRange: SourceRange = [node.start, node.end, true]
|
||||||
return getSketchSegmentFromSourceRange(sketch, sourceRange)
|
return getSketchSegmentFromSourceRange(sketch, sourceRange)
|
||||||
}
|
}
|
||||||
export function getSketchSegmentFromSourceRange(
|
export function getSketchSegmentFromSourceRange(
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { parse, Expr, recast, initPromise, Program } from '../wasm'
|
import { assertParse, Expr, recast, initPromise, Program } from '../wasm'
|
||||||
import {
|
import {
|
||||||
getConstraintType,
|
getConstraintType,
|
||||||
getTransformInfos,
|
getTransformInfos,
|
||||||
@ -66,8 +66,7 @@ describe('testing getConstraintType', () => {
|
|||||||
function getConstraintTypeFromSourceHelper(
|
function getConstraintTypeFromSourceHelper(
|
||||||
code: string
|
code: string
|
||||||
): ReturnType<typeof getConstraintType> | Error {
|
): ReturnType<typeof getConstraintType> | Error {
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(ast)) return ast
|
|
||||||
|
|
||||||
const args = (ast.body[0] as any).expression.arguments[0].elements as [
|
const args = (ast.body[0] as any).expression.arguments[0].elements as [
|
||||||
Expr,
|
Expr,
|
||||||
@ -79,8 +78,7 @@ function getConstraintTypeFromSourceHelper(
|
|||||||
function getConstraintTypeFromSourceHelper2(
|
function getConstraintTypeFromSourceHelper2(
|
||||||
code: string
|
code: string
|
||||||
): ReturnType<typeof getConstraintType> | Error {
|
): ReturnType<typeof getConstraintType> | Error {
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
if (err(ast)) return ast
|
|
||||||
|
|
||||||
const arg = (ast.body[0] as any).expression.arguments[0] as Expr
|
const arg = (ast.body[0] as any).expression.arguments[0] as Expr
|
||||||
const fnName = (ast.body[0] as any).expression.callee.name as ToolTip
|
const fnName = (ast.body[0] as any).expression.callee.name as ToolTip
|
||||||
@ -127,7 +125,7 @@ describe('testing transformAstForSketchLines for equal length constraint', () =>
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
const start = codeBeforeLine + line.indexOf('|> ' + 5)
|
const start = codeBeforeLine + line.indexOf('|> ' + 5)
|
||||||
const range: [number, number] = [start, start]
|
const range: [number, number, boolean] = [start, start, true]
|
||||||
return {
|
return {
|
||||||
codeRef: codeRefFromRange(range, ast),
|
codeRef: codeRefFromRange(range, ast),
|
||||||
}
|
}
|
||||||
@ -137,8 +135,7 @@ describe('testing transformAstForSketchLines for equal length constraint', () =>
|
|||||||
inputCode: string,
|
inputCode: string,
|
||||||
selectionRanges: Selections['graphSelections']
|
selectionRanges: Selections['graphSelections']
|
||||||
) {
|
) {
|
||||||
const ast = parse(inputCode)
|
const ast = assertParse(inputCode)
|
||||||
if (err(ast)) return Promise.reject(ast)
|
|
||||||
const execState = await enginelessExecutor(ast)
|
const execState = await enginelessExecutor(ast)
|
||||||
const transformInfos = getTransformInfos(
|
const transformInfos = getTransformInfos(
|
||||||
makeSelections(selectionRanges.slice(1)),
|
makeSelections(selectionRanges.slice(1)),
|
||||||
@ -161,8 +158,7 @@ describe('testing transformAstForSketchLines for equal length constraint', () =>
|
|||||||
}
|
}
|
||||||
|
|
||||||
it(`Should reorder when user selects first-to-last`, async () => {
|
it(`Should reorder when user selects first-to-last`, async () => {
|
||||||
const ast = parse(inputScript)
|
const ast = assertParse(inputScript)
|
||||||
if (err(ast)) return Promise.reject(ast)
|
|
||||||
const selectionRanges: Selections['graphSelections'] = [
|
const selectionRanges: Selections['graphSelections'] = [
|
||||||
selectLine(inputScript, 3, ast),
|
selectLine(inputScript, 3, ast),
|
||||||
selectLine(inputScript, 4, ast),
|
selectLine(inputScript, 4, ast),
|
||||||
@ -173,8 +169,7 @@ describe('testing transformAstForSketchLines for equal length constraint', () =>
|
|||||||
})
|
})
|
||||||
|
|
||||||
it(`Should reorder when user selects last-to-first`, async () => {
|
it(`Should reorder when user selects last-to-first`, async () => {
|
||||||
const ast = parse(inputScript)
|
const ast = assertParse(inputScript)
|
||||||
if (err(ast)) return Promise.reject(ast)
|
|
||||||
const selectionRanges: Selections['graphSelections'] = [
|
const selectionRanges: Selections['graphSelections'] = [
|
||||||
selectLine(inputScript, 4, ast),
|
selectLine(inputScript, 4, ast),
|
||||||
selectLine(inputScript, 3, ast),
|
selectLine(inputScript, 3, ast),
|
||||||
@ -293,8 +288,7 @@ part001 = startSketchOn('XY')
|
|||||||
|> yLine(segLen(seg01), %) // ln-yLineTo-free should convert to yLine
|
|> yLine(segLen(seg01), %) // ln-yLineTo-free should convert to yLine
|
||||||
`
|
`
|
||||||
it('should transform the ast', async () => {
|
it('should transform the ast', async () => {
|
||||||
const ast = parse(inputScript)
|
const ast = assertParse(inputScript)
|
||||||
if (err(ast)) return Promise.reject(ast)
|
|
||||||
|
|
||||||
const selectionRanges: Selections['graphSelections'] = inputScript
|
const selectionRanges: Selections['graphSelections'] = inputScript
|
||||||
.split('\n')
|
.split('\n')
|
||||||
@ -303,7 +297,7 @@ part001 = startSketchOn('XY')
|
|||||||
const comment = ln.split('//')[1]
|
const comment = ln.split('//')[1]
|
||||||
const start = inputScript.indexOf('//' + comment) - 7
|
const start = inputScript.indexOf('//' + comment) - 7
|
||||||
return {
|
return {
|
||||||
codeRef: codeRefFromRange([start, start], ast),
|
codeRef: codeRefFromRange([start, start, true], ast),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -383,8 +377,7 @@ part001 = startSketchOn('XY')
|
|||||||
|> xLineTo(myVar3, %) // select for horizontal constraint 10
|
|> xLineTo(myVar3, %) // select for horizontal constraint 10
|
||||||
|> angledLineToY([301, myVar], %) // select for vertical constraint 10
|
|> angledLineToY([301, myVar], %) // select for vertical constraint 10
|
||||||
`
|
`
|
||||||
const ast = parse(inputScript)
|
const ast = assertParse(inputScript)
|
||||||
if (err(ast)) return Promise.reject(ast)
|
|
||||||
|
|
||||||
const selectionRanges: Selections['graphSelections'] = inputScript
|
const selectionRanges: Selections['graphSelections'] = inputScript
|
||||||
.split('\n')
|
.split('\n')
|
||||||
@ -393,7 +386,7 @@ part001 = startSketchOn('XY')
|
|||||||
const comment = ln.split('//')[1]
|
const comment = ln.split('//')[1]
|
||||||
const start = inputScript.indexOf('//' + comment) - 7
|
const start = inputScript.indexOf('//' + comment) - 7
|
||||||
return {
|
return {
|
||||||
codeRef: codeRefFromRange([start, start], ast),
|
codeRef: codeRefFromRange([start, start, true], ast),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -444,8 +437,7 @@ part001 = startSketchOn('XY')
|
|||||||
|> angledLineToX([333, myVar3], %) // select for horizontal constraint 10
|
|> angledLineToX([333, myVar3], %) // select for horizontal constraint 10
|
||||||
|> yLineTo(myVar, %) // select for vertical constraint 10
|
|> yLineTo(myVar, %) // select for vertical constraint 10
|
||||||
`
|
`
|
||||||
const ast = parse(inputScript)
|
const ast = assertParse(inputScript)
|
||||||
if (err(ast)) return Promise.reject(ast)
|
|
||||||
|
|
||||||
const selectionRanges: Selections['graphSelections'] = inputScript
|
const selectionRanges: Selections['graphSelections'] = inputScript
|
||||||
.split('\n')
|
.split('\n')
|
||||||
@ -454,7 +446,7 @@ part001 = startSketchOn('XY')
|
|||||||
const comment = ln.split('//')[1]
|
const comment = ln.split('//')[1]
|
||||||
const start = inputScript.indexOf('//' + comment) - 7
|
const start = inputScript.indexOf('//' + comment) - 7
|
||||||
return {
|
return {
|
||||||
codeRef: codeRefFromRange([start, start], ast),
|
codeRef: codeRefFromRange([start, start, true], ast),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -538,8 +530,7 @@ async function helperThing(
|
|||||||
linesOfInterest: string[],
|
linesOfInterest: string[],
|
||||||
constraint: ConstraintType
|
constraint: ConstraintType
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
const ast = parse(inputScript)
|
const ast = assertParse(inputScript)
|
||||||
if (err(ast)) return Promise.reject(ast)
|
|
||||||
|
|
||||||
const selectionRanges: Selections['graphSelections'] = inputScript
|
const selectionRanges: Selections['graphSelections'] = inputScript
|
||||||
.split('\n')
|
.split('\n')
|
||||||
@ -550,7 +541,7 @@ async function helperThing(
|
|||||||
const comment = ln.split('//')[1]
|
const comment = ln.split('//')[1]
|
||||||
const start = inputScript.indexOf('//' + comment) - 7
|
const start = inputScript.indexOf('//' + comment) - 7
|
||||||
return {
|
return {
|
||||||
codeRef: codeRefFromRange([start, start], ast),
|
codeRef: codeRefFromRange([start, start, true], ast),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -606,7 +597,7 @@ part001 = startSketchOn('XY')
|
|||||||
|> line([-1.49, 1.06], %) // free
|
|> line([-1.49, 1.06], %) // free
|
||||||
|> xLine(-3.43 + 0, %) // full
|
|> xLine(-3.43 + 0, %) // full
|
||||||
|> angledLineOfXLength([243 + 0, 1.2 + 0], %) // full`
|
|> angledLineOfXLength([243 + 0, 1.2 + 0], %) // full`
|
||||||
const ast = parse(code)
|
const ast = assertParse(code)
|
||||||
const constraintLevels: ConstraintLevel[] = ['full', 'partial', 'free']
|
const constraintLevels: ConstraintLevel[] = ['full', 'partial', 'free']
|
||||||
constraintLevels.forEach((constraintLevel) => {
|
constraintLevels.forEach((constraintLevel) => {
|
||||||
const recursivelySearchCommentsAndCheckConstraintLevel = (
|
const recursivelySearchCommentsAndCheckConstraintLevel = (
|
||||||
@ -619,7 +610,7 @@ part001 = startSketchOn('XY')
|
|||||||
}
|
}
|
||||||
const offsetIndex = index - 7
|
const offsetIndex = index - 7
|
||||||
const expectedConstraintLevel = getConstraintLevelFromSourceRange(
|
const expectedConstraintLevel = getConstraintLevelFromSourceRange(
|
||||||
[offsetIndex, offsetIndex],
|
[offsetIndex, offsetIndex, true],
|
||||||
ast
|
ast
|
||||||
)
|
)
|
||||||
if (err(expectedConstraintLevel)) {
|
if (err(expectedConstraintLevel)) {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { parse, initPromise } from '../wasm'
|
import { assertParse, initPromise } from '../wasm'
|
||||||
import { enginelessExecutor } from '../../lib/testHelpers'
|
import { enginelessExecutor } from '../../lib/testHelpers'
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
@ -17,9 +17,9 @@ describe('testing angledLineThatIntersects', () => {
|
|||||||
offset: ${offset},
|
offset: ${offset},
|
||||||
}, %, $yo2)
|
}, %, $yo2)
|
||||||
intersect = segEndX(yo2)`
|
intersect = segEndX(yo2)`
|
||||||
const execState = await enginelessExecutor(parse(code('-1')))
|
const execState = await enginelessExecutor(assertParse(code('-1')))
|
||||||
expect(execState.memory.get('intersect')?.value).toBe(1 + Math.sqrt(2))
|
expect(execState.memory.get('intersect')?.value).toBe(1 + Math.sqrt(2))
|
||||||
const noOffset = await enginelessExecutor(parse(code('0')))
|
const noOffset = await enginelessExecutor(assertParse(code('0')))
|
||||||
expect(noOffset.memory.get('intersect')?.value).toBeCloseTo(1)
|
expect(noOffset.memory.get('intersect')?.value).toBeCloseTo(1)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -1,13 +1,18 @@
|
|||||||
import { err } from 'lib/trap'
|
import { err } from 'lib/trap'
|
||||||
import { parse } from './wasm'
|
import { parse, ParseResult } from './wasm'
|
||||||
import { enginelessExecutor } from 'lib/testHelpers'
|
import { enginelessExecutor } from 'lib/testHelpers'
|
||||||
|
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||||
|
import { Program } from '../wasm-lib/kcl/bindings/Program'
|
||||||
|
|
||||||
it('can execute parsed AST', async () => {
|
it('can execute parsed AST', async () => {
|
||||||
const code = `x = 1
|
const code = `x = 1
|
||||||
// A comment.`
|
// A comment.`
|
||||||
const ast = parse(code)
|
const result = parse(code)
|
||||||
expect(err(ast)).toEqual(false)
|
expect(err(result)).toEqual(false)
|
||||||
const execState = await enginelessExecutor(ast)
|
const pResult = result as ParseResult
|
||||||
expect(err(ast)).toEqual(false)
|
expect(pResult.errors.length).toEqual(0)
|
||||||
|
expect(pResult.program).not.toEqual(null)
|
||||||
|
const execState = await enginelessExecutor(pResult.program as Node<Program>)
|
||||||
|
expect(err(execState)).toEqual(false)
|
||||||
expect(execState.memory.get('x')?.value).toEqual(1)
|
expect(execState.memory.get('x')?.value).toEqual(1)
|
||||||
})
|
})
|
||||||
|
138
src/lang/wasm.ts
@ -35,12 +35,13 @@ import { Configuration } from 'wasm-lib/kcl/bindings/Configuration'
|
|||||||
import { DeepPartial } from 'lib/types'
|
import { DeepPartial } from 'lib/types'
|
||||||
import { ProjectConfiguration } from 'wasm-lib/kcl/bindings/ProjectConfiguration'
|
import { ProjectConfiguration } from 'wasm-lib/kcl/bindings/ProjectConfiguration'
|
||||||
import { Sketch } from '../wasm-lib/kcl/bindings/Sketch'
|
import { Sketch } from '../wasm-lib/kcl/bindings/Sketch'
|
||||||
import { IdGenerator } from 'wasm-lib/kcl/bindings/IdGenerator'
|
|
||||||
import { ExecState as RawExecState } from '../wasm-lib/kcl/bindings/ExecState'
|
import { ExecState as RawExecState } from '../wasm-lib/kcl/bindings/ExecState'
|
||||||
import { ProgramMemory as RawProgramMemory } from '../wasm-lib/kcl/bindings/ProgramMemory'
|
import { ProgramMemory as RawProgramMemory } from '../wasm-lib/kcl/bindings/ProgramMemory'
|
||||||
import { EnvironmentRef } from '../wasm-lib/kcl/bindings/EnvironmentRef'
|
import { EnvironmentRef } from '../wasm-lib/kcl/bindings/EnvironmentRef'
|
||||||
import { Environment } from '../wasm-lib/kcl/bindings/Environment'
|
import { Environment } from '../wasm-lib/kcl/bindings/Environment'
|
||||||
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||||
|
import { CompilationError } from 'wasm-lib/kcl/bindings/CompilationError'
|
||||||
|
import { SourceRange as RustSourceRange } from 'wasm-lib/kcl/bindings/SourceRange'
|
||||||
|
|
||||||
export type { Program } from '../wasm-lib/kcl/bindings/Program'
|
export type { Program } from '../wasm-lib/kcl/bindings/Program'
|
||||||
export type { Expr } from '../wasm-lib/kcl/bindings/Expr'
|
export type { Expr } from '../wasm-lib/kcl/bindings/Expr'
|
||||||
@ -84,13 +85,22 @@ export type SyntaxType =
|
|||||||
| 'NonCodeNode'
|
| 'NonCodeNode'
|
||||||
| 'UnaryExpression'
|
| 'UnaryExpression'
|
||||||
|
|
||||||
export type { SourceRange } from '../wasm-lib/kcl/bindings/SourceRange'
|
|
||||||
export type { Path } from '../wasm-lib/kcl/bindings/Path'
|
export type { Path } from '../wasm-lib/kcl/bindings/Path'
|
||||||
export type { Sketch } from '../wasm-lib/kcl/bindings/Sketch'
|
export type { Sketch } from '../wasm-lib/kcl/bindings/Sketch'
|
||||||
export type { Solid } from '../wasm-lib/kcl/bindings/Solid'
|
export type { Solid } from '../wasm-lib/kcl/bindings/Solid'
|
||||||
export type { KclValue } from '../wasm-lib/kcl/bindings/KclValue'
|
export type { KclValue } from '../wasm-lib/kcl/bindings/KclValue'
|
||||||
export type { ExtrudeSurface } from '../wasm-lib/kcl/bindings/ExtrudeSurface'
|
export type { ExtrudeSurface } from '../wasm-lib/kcl/bindings/ExtrudeSurface'
|
||||||
|
|
||||||
|
export type SourceRange = [number, number, boolean]
|
||||||
|
|
||||||
|
export function sourceRangeFromRust(s: RustSourceRange): SourceRange {
|
||||||
|
return [s[0], s[1], s[2] === 0]
|
||||||
|
}
|
||||||
|
|
||||||
|
export function defaultSourceRange(): SourceRange {
|
||||||
|
return [0, 0, true]
|
||||||
|
}
|
||||||
|
|
||||||
export const wasmUrl = () => {
|
export const wasmUrl = () => {
|
||||||
// For when we're in electron (file based) or web server (network based)
|
// For when we're in electron (file based) or web server (network based)
|
||||||
// For some reason relative paths don't work as expected. Otherwise we would
|
// For some reason relative paths don't work as expected. Otherwise we would
|
||||||
@ -120,26 +130,81 @@ const initialise = async () => {
|
|||||||
|
|
||||||
export const initPromise = initialise()
|
export const initPromise = initialise()
|
||||||
|
|
||||||
export const rangeTypeFix = (ranges: number[][]): [number, number, number][] =>
|
const splitErrors = (
|
||||||
ranges.map(([start, end, moduleId]) => [start, end, moduleId])
|
input: CompilationError[]
|
||||||
|
): { errors: CompilationError[]; warnings: CompilationError[] } => {
|
||||||
|
let errors = []
|
||||||
|
let warnings = []
|
||||||
|
for (const i of input) {
|
||||||
|
if (i.severity === 'Warning') {
|
||||||
|
warnings.push(i)
|
||||||
|
} else {
|
||||||
|
errors.push(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const parse = (code: string | Error): Node<Program> | Error => {
|
return { errors, warnings }
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ParseResult {
|
||||||
|
program: Node<Program> | null
|
||||||
|
errors: CompilationError[]
|
||||||
|
warnings: CompilationError[]
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
program: Node<Program> | null,
|
||||||
|
errors: CompilationError[],
|
||||||
|
warnings: CompilationError[]
|
||||||
|
) {
|
||||||
|
this.program = program
|
||||||
|
this.errors = errors
|
||||||
|
this.warnings = warnings
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SuccessParseResult extends ParseResult {
|
||||||
|
program: Node<Program>
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
program: Node<Program>,
|
||||||
|
errors: CompilationError[],
|
||||||
|
warnings: CompilationError[]
|
||||||
|
) {
|
||||||
|
super(program, errors, warnings)
|
||||||
|
this.program = program
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function resultIsOk(result: ParseResult): result is SuccessParseResult {
|
||||||
|
return !!result.program && result.errors.length === 0
|
||||||
|
}
|
||||||
|
|
||||||
|
export const parse = (code: string | Error): ParseResult | Error => {
|
||||||
if (err(code)) return code
|
if (err(code)) return code
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const program: Node<Program> = parse_wasm(code)
|
const parsed: [Node<Program>, CompilationError[]] = parse_wasm(code)
|
||||||
return program
|
let errs = splitErrors(parsed[1])
|
||||||
|
return new ParseResult(parsed[0], errs.errors, errs.warnings)
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
// throw e
|
// throw e
|
||||||
const parsed: RustKclError = JSON.parse(e.toString())
|
const parsed: RustKclError = JSON.parse(e.toString())
|
||||||
return new KCLError(
|
return new KCLError(
|
||||||
parsed.kind,
|
parsed.kind,
|
||||||
parsed.msg,
|
parsed.msg,
|
||||||
rangeTypeFix(parsed.sourceRanges)
|
sourceRangeFromRust(parsed.sourceRanges[0])
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parse and throw an exception if there are any errors (probably not suitable for use outside of testing).
|
||||||
|
export const assertParse = (code: string): Node<Program> => {
|
||||||
|
const result = parse(code)
|
||||||
|
// eslint-disable-next-line suggest-no-throw/suggest-no-throw
|
||||||
|
if (err(result) || !resultIsOk(result)) throw result
|
||||||
|
return result.program
|
||||||
|
}
|
||||||
|
|
||||||
export type PathToNode = [string | number, string][]
|
export type PathToNode = [string | number, string][]
|
||||||
|
|
||||||
export const isPathToNodeNumber = (
|
export const isPathToNodeNumber = (
|
||||||
@ -150,7 +215,6 @@ export const isPathToNodeNumber = (
|
|||||||
|
|
||||||
export interface ExecState {
|
export interface ExecState {
|
||||||
memory: ProgramMemory
|
memory: ProgramMemory
|
||||||
idGenerator: IdGenerator
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -160,21 +224,12 @@ export interface ExecState {
|
|||||||
export function emptyExecState(): ExecState {
|
export function emptyExecState(): ExecState {
|
||||||
return {
|
return {
|
||||||
memory: ProgramMemory.empty(),
|
memory: ProgramMemory.empty(),
|
||||||
idGenerator: defaultIdGenerator(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function execStateFromRaw(raw: RawExecState): ExecState {
|
function execStateFromRaw(raw: RawExecState): ExecState {
|
||||||
return {
|
return {
|
||||||
memory: ProgramMemory.fromRaw(raw.memory),
|
memory: ProgramMemory.fromRaw(raw.memory),
|
||||||
idGenerator: raw.idGenerator,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function defaultIdGenerator(): IdGenerator {
|
|
||||||
return {
|
|
||||||
nextId: 0,
|
|
||||||
ids: [],
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -188,6 +243,19 @@ function emptyEnvironment(): Environment {
|
|||||||
return { bindings: {}, parent: null }
|
return { bindings: {}, parent: null }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function emptyRootEnvironment(): Environment {
|
||||||
|
return {
|
||||||
|
// This is dumb this is copied from rust.
|
||||||
|
bindings: {
|
||||||
|
ZERO: { type: 'Number', value: 0.0, __meta: [] },
|
||||||
|
QUARTER_TURN: { type: 'Number', value: 90.0, __meta: [] },
|
||||||
|
HALF_TURN: { type: 'Number', value: 180.0, __meta: [] },
|
||||||
|
THREE_QUARTER_TURN: { type: 'Number', value: 270.0, __meta: [] },
|
||||||
|
},
|
||||||
|
parent: null,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This duplicates logic in Rust. The hope is to keep ProgramMemory internals
|
* This duplicates logic in Rust. The hope is to keep ProgramMemory internals
|
||||||
* isolated from the rest of the TypeScript code so that we can move it to Rust
|
* isolated from the rest of the TypeScript code so that we can move it to Rust
|
||||||
@ -210,7 +278,7 @@ export class ProgramMemory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
environments: Environment[] = [emptyEnvironment()],
|
environments: Environment[] = [emptyRootEnvironment()],
|
||||||
currentEnv: EnvironmentRef = ROOT_ENVIRONMENT_REF,
|
currentEnv: EnvironmentRef = ROOT_ENVIRONMENT_REF,
|
||||||
returnVal: KclValue | null = null
|
returnVal: KclValue | null = null
|
||||||
) {
|
) {
|
||||||
@ -397,36 +465,31 @@ export function sketchFromKclValue(
|
|||||||
|
|
||||||
export const executor = async (
|
export const executor = async (
|
||||||
node: Node<Program>,
|
node: Node<Program>,
|
||||||
programMemory: ProgramMemory | Error = ProgramMemory.empty(),
|
|
||||||
idGenerator: IdGenerator = defaultIdGenerator(),
|
|
||||||
engineCommandManager: EngineCommandManager,
|
engineCommandManager: EngineCommandManager,
|
||||||
isMock: boolean = false
|
programMemoryOverride: ProgramMemory | Error | null = null
|
||||||
): Promise<ExecState> => {
|
): Promise<ExecState> => {
|
||||||
if (err(programMemory)) return Promise.reject(programMemory)
|
if (programMemoryOverride !== null && err(programMemoryOverride))
|
||||||
|
return Promise.reject(programMemoryOverride)
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||||
engineCommandManager.startNewSession()
|
engineCommandManager.startNewSession()
|
||||||
const _programMemory = await _executor(
|
const _programMemory = await _executor(
|
||||||
node,
|
node,
|
||||||
programMemory,
|
|
||||||
idGenerator,
|
|
||||||
engineCommandManager,
|
engineCommandManager,
|
||||||
isMock
|
programMemoryOverride
|
||||||
)
|
)
|
||||||
await engineCommandManager.waitForAllCommands()
|
await engineCommandManager.waitForAllCommands()
|
||||||
|
|
||||||
engineCommandManager.endSession()
|
|
||||||
return _programMemory
|
return _programMemory
|
||||||
}
|
}
|
||||||
|
|
||||||
export const _executor = async (
|
export const _executor = async (
|
||||||
node: Node<Program>,
|
node: Node<Program>,
|
||||||
programMemory: ProgramMemory | Error = ProgramMemory.empty(),
|
|
||||||
idGenerator: IdGenerator = defaultIdGenerator(),
|
|
||||||
engineCommandManager: EngineCommandManager,
|
engineCommandManager: EngineCommandManager,
|
||||||
isMock: boolean
|
programMemoryOverride: ProgramMemory | Error | null = null
|
||||||
): Promise<ExecState> => {
|
): Promise<ExecState> => {
|
||||||
if (err(programMemory)) return Promise.reject(programMemory)
|
if (programMemoryOverride !== null && err(programMemoryOverride))
|
||||||
|
return Promise.reject(programMemoryOverride)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let baseUnit = 'mm'
|
let baseUnit = 'mm'
|
||||||
@ -439,13 +502,10 @@ export const _executor = async (
|
|||||||
}
|
}
|
||||||
const execState: RawExecState = await execute_wasm(
|
const execState: RawExecState = await execute_wasm(
|
||||||
JSON.stringify(node),
|
JSON.stringify(node),
|
||||||
JSON.stringify(programMemory.toRaw()),
|
JSON.stringify(programMemoryOverride?.toRaw() || null),
|
||||||
JSON.stringify(idGenerator),
|
|
||||||
baseUnit,
|
baseUnit,
|
||||||
engineCommandManager,
|
engineCommandManager,
|
||||||
fileSystemManager,
|
fileSystemManager
|
||||||
undefined,
|
|
||||||
isMock
|
|
||||||
)
|
)
|
||||||
return execStateFromRaw(execState)
|
return execStateFromRaw(execState)
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
@ -454,7 +514,7 @@ export const _executor = async (
|
|||||||
const kclError = new KCLError(
|
const kclError = new KCLError(
|
||||||
parsed.kind,
|
parsed.kind,
|
||||||
parsed.msg,
|
parsed.msg,
|
||||||
rangeTypeFix(parsed.sourceRanges)
|
sourceRangeFromRust(parsed.sourceRanges[0])
|
||||||
)
|
)
|
||||||
|
|
||||||
return Promise.reject(kclError)
|
return Promise.reject(kclError)
|
||||||
@ -527,7 +587,7 @@ export const modifyAstForSketch = async (
|
|||||||
const kclError = new KCLError(
|
const kclError = new KCLError(
|
||||||
parsed.kind,
|
parsed.kind,
|
||||||
parsed.msg,
|
parsed.msg,
|
||||||
rangeTypeFix(parsed.sourceRanges)
|
sourceRangeFromRust(parsed.sourceRanges[0])
|
||||||
)
|
)
|
||||||
|
|
||||||
console.log(kclError)
|
console.log(kclError)
|
||||||
@ -595,7 +655,7 @@ export function programMemoryInit(): ProgramMemory | Error {
|
|||||||
return new KCLError(
|
return new KCLError(
|
||||||
parsed.kind,
|
parsed.kind,
|
||||||
parsed.msg,
|
parsed.msg,
|
||||||
rangeTypeFix(parsed.sourceRanges)
|
sourceRangeFromRust(parsed.sourceRanges[0])
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
12
src/lib/appVersion.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { NODE_ENV } from 'env'
|
||||||
|
import { isDesktop } from './isDesktop'
|
||||||
|
import { isTestEnv } from './isTestEnv'
|
||||||
|
|
||||||
|
/** Version number of the app */
|
||||||
|
export const APP_VERSION =
|
||||||
|
isTestEnv && NODE_ENV === 'development'
|
||||||
|
? '11.22.33'
|
||||||
|
: isDesktop()
|
||||||
|
? // @ts-ignore
|
||||||
|
window.electron.packageJson.version
|
||||||
|
: 'main'
|
@ -31,6 +31,9 @@ export type ModelingCommandSchema = {
|
|||||||
// result: (typeof EXTRUSION_RESULTS)[number]
|
// result: (typeof EXTRUSION_RESULTS)[number]
|
||||||
distance: KclCommandValue
|
distance: KclCommandValue
|
||||||
}
|
}
|
||||||
|
Loft: {
|
||||||
|
selection: Selections
|
||||||
|
}
|
||||||
Revolve: {
|
Revolve: {
|
||||||
selection: Selections
|
selection: Selections
|
||||||
angle: KclCommandValue
|
angle: KclCommandValue
|
||||||
@ -260,6 +263,20 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
Loft: {
|
||||||
|
description: 'Create a 3D body by blending between two or more sketches',
|
||||||
|
icon: 'loft',
|
||||||
|
needsReview: true,
|
||||||
|
args: {
|
||||||
|
selection: {
|
||||||
|
inputType: 'selection',
|
||||||
|
selectionTypes: ['solid2D'],
|
||||||
|
multiple: true,
|
||||||
|
required: true,
|
||||||
|
skip: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
// TODO: Update this configuration, copied from extrude for MVP of revolve, specifically the args.selection
|
// TODO: Update this configuration, copied from extrude for MVP of revolve, specifically the args.selection
|
||||||
Revolve: {
|
Revolve: {
|
||||||
description: 'Create a 3D body by rotating a sketch region about an axis.',
|
description: 'Create a 3D body by rotating a sketch region about an axis.',
|
||||||
|
@ -52,6 +52,7 @@ export const ONBOARDING_PROJECT_NAME = 'Tutorial Project $nn'
|
|||||||
export const KCL_DEFAULT_CONSTANT_PREFIXES = {
|
export const KCL_DEFAULT_CONSTANT_PREFIXES = {
|
||||||
SKETCH: 'sketch',
|
SKETCH: 'sketch',
|
||||||
EXTRUDE: 'extrude',
|
EXTRUDE: 'extrude',
|
||||||
|
LOFT: 'loft',
|
||||||
SEGMENT: 'seg',
|
SEGMENT: 'seg',
|
||||||
REVOLVE: 'revolve',
|
REVOLVE: 'revolve',
|
||||||
PLANE: 'plane',
|
PLANE: 'plane',
|
||||||
|
@ -2,7 +2,7 @@ import { CommandLog, EngineCommandManager } from 'lang/std/engineConnection'
|
|||||||
import { WebrtcStats } from 'wasm-lib/kcl/bindings/WebrtcStats'
|
import { WebrtcStats } from 'wasm-lib/kcl/bindings/WebrtcStats'
|
||||||
import { OsInfo } from 'wasm-lib/kcl/bindings/OsInfo'
|
import { OsInfo } from 'wasm-lib/kcl/bindings/OsInfo'
|
||||||
import { isDesktop } from 'lib/isDesktop'
|
import { isDesktop } from 'lib/isDesktop'
|
||||||
import { APP_VERSION } from 'routes/Settings'
|
import { APP_VERSION } from 'lib/appVersion'
|
||||||
import { UAParser } from 'ua-parser-js'
|
import { UAParser } from 'ua-parser-js'
|
||||||
import screenshot from 'lib/screenshot'
|
import screenshot from 'lib/screenshot'
|
||||||
import { VITE_KC_API_BASE_URL } from 'env'
|
import { VITE_KC_API_BASE_URL } from 'env'
|
||||||
|
@ -116,8 +116,8 @@ export async function createAndOpenNewTutorialProject({
|
|||||||
) => void
|
) => void
|
||||||
navigate: (path: string) => void
|
navigate: (path: string) => void
|
||||||
}) {
|
}) {
|
||||||
// Clear the scene and end the session.
|
// Clear the scene.
|
||||||
engineCommandManager.endSession()
|
engineCommandManager.clearScene()
|
||||||
|
|
||||||
// Create a new project with the onboarding project name
|
// Create a new project with the onboarding project name
|
||||||
const configuration = await readAppSettingsFile()
|
const configuration = await readAppSettingsFile()
|
||||||
|
@ -30,27 +30,27 @@ const bracketLeg1Sketch = startSketchOn('XY')
|
|||||||
|> line([-shelfMountL + filletRadius, 0], %)
|
|> line([-shelfMountL + filletRadius, 0], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
|> hole(circle({
|
|> hole(circle({
|
||||||
center: [1, 1],
|
center = [1, 1],
|
||||||
radius: mountingHoleDiameter / 2
|
radius = mountingHoleDiameter / 2
|
||||||
}, %), %)
|
}, %), %)
|
||||||
|> hole(circle({
|
|> hole(circle({
|
||||||
center: [shelfMountL - 1.5, width - 1],
|
center = [shelfMountL - 1.5, width - 1],
|
||||||
radius: mountingHoleDiameter / 2
|
radius = mountingHoleDiameter / 2
|
||||||
}, %), %)
|
}, %), %)
|
||||||
|> hole(circle({
|
|> hole(circle({
|
||||||
center: [1, width - 1],
|
center = [1, width - 1],
|
||||||
radius: mountingHoleDiameter / 2
|
radius = mountingHoleDiameter / 2
|
||||||
}, %), %)
|
}, %), %)
|
||||||
|> hole(circle({
|
|> hole(circle({
|
||||||
center: [shelfMountL - 1.5, 1],
|
center = [shelfMountL - 1.5, 1],
|
||||||
radius: mountingHoleDiameter / 2
|
radius = mountingHoleDiameter / 2
|
||||||
}, %), %)
|
}, %), %)
|
||||||
|
|
||||||
// Extrude the leg 2 bracket sketch
|
// Extrude the leg 2 bracket sketch
|
||||||
const bracketLeg1Extrude = extrude(thickness, bracketLeg1Sketch)
|
const bracketLeg1Extrude = extrude(thickness, bracketLeg1Sketch)
|
||||||
|> fillet({
|
|> fillet({
|
||||||
radius: extFilletRadius,
|
radius = extFilletRadius,
|
||||||
tags: [
|
tags = [
|
||||||
getNextAdjacentEdge(fillet1),
|
getNextAdjacentEdge(fillet1),
|
||||||
getNextAdjacentEdge(fillet2)
|
getNextAdjacentEdge(fillet2)
|
||||||
]
|
]
|
||||||
@ -61,15 +61,15 @@ const filletSketch = startSketchOn('XZ')
|
|||||||
|> startProfileAt([0, 0], %)
|
|> startProfileAt([0, 0], %)
|
||||||
|> line([0, thickness], %)
|
|> line([0, thickness], %)
|
||||||
|> arc({
|
|> arc({
|
||||||
angleEnd: 180,
|
angleEnd = 180,
|
||||||
angleStart: 90,
|
angleStart = 90,
|
||||||
radius: filletRadius + thickness
|
radius = filletRadius + thickness
|
||||||
}, %)
|
}, %)
|
||||||
|> line([thickness, 0], %)
|
|> line([thickness, 0], %)
|
||||||
|> arc({
|
|> arc({
|
||||||
angleEnd: 90,
|
angleEnd = 90,
|
||||||
angleStart: 180,
|
angleStart = 180,
|
||||||
radius: filletRadius
|
radius = filletRadius
|
||||||
}, %)
|
}, %)
|
||||||
|
|
||||||
// Sketch the bend
|
// Sketch the bend
|
||||||
@ -77,11 +77,11 @@ const filletExtrude = extrude(-width, filletSketch)
|
|||||||
|
|
||||||
// Create a custom plane for the leg that sits on the wall
|
// Create a custom plane for the leg that sits on the wall
|
||||||
const customPlane = {
|
const customPlane = {
|
||||||
plane: {
|
plane = {
|
||||||
origin: { x: -filletRadius, y: 0, z: 0 },
|
origin = { x = -filletRadius, y = 0, z = 0 },
|
||||||
xAxis: { x: 0, y: 1, z: 0 },
|
xAxis = { x = 0, y = 1, z = 0 },
|
||||||
yAxis: { x: 0, y: 0, z: 1 },
|
yAxis = { x = 0, y = 0, z = 1 },
|
||||||
zAxis: { x: 1, y: 0, z: 0 }
|
zAxis = { x = 1, y = 0, z = 0 }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,19 +93,19 @@ const bracketLeg2Sketch = startSketchOn(customPlane)
|
|||||||
|> line([-width, 0], %, $fillet4)
|
|> line([-width, 0], %, $fillet4)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
|> hole(circle({
|
|> hole(circle({
|
||||||
center: [1, -1.5],
|
center = [1, -1.5],
|
||||||
radius: mountingHoleDiameter / 2
|
radius = mountingHoleDiameter / 2
|
||||||
}, %), %)
|
}, %), %)
|
||||||
|> hole(circle({
|
|> hole(circle({
|
||||||
center: [5, -1.5],
|
center = [5, -1.5],
|
||||||
radius: mountingHoleDiameter / 2
|
radius = mountingHoleDiameter / 2
|
||||||
}, %), %)
|
}, %), %)
|
||||||
|
|
||||||
// Extrude the second leg
|
// Extrude the second leg
|
||||||
const bracketLeg2Extrude = extrude(-thickness, bracketLeg2Sketch)
|
const bracketLeg2Extrude = extrude(-thickness, bracketLeg2Sketch)
|
||||||
|> fillet({
|
|> fillet({
|
||||||
radius: extFilletRadius,
|
radius = extFilletRadius,
|
||||||
tags: [
|
tags = [
|
||||||
getNextAdjacentEdge(fillet3),
|
getNextAdjacentEdge(fillet3),
|
||||||
getNextAdjacentEdge(fillet4)
|
getNextAdjacentEdge(fillet4)
|
||||||
]
|
]
|
||||||
|
4
src/lib/isTestEnv.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
import { IS_PLAYWRIGHT_KEY } from '../../e2e/playwright/storageStates'
|
||||||
|
|
||||||
|
export const isTestEnv =
|
||||||
|
globalThis.window?.localStorage.getItem(IS_PLAYWRIGHT_KEY) === 'true'
|
@ -5,7 +5,12 @@ import {
|
|||||||
kclManager,
|
kclManager,
|
||||||
sceneEntitiesManager,
|
sceneEntitiesManager,
|
||||||
} from 'lib/singletons'
|
} from 'lib/singletons'
|
||||||
import { CallExpression, SourceRange, Expr } from 'lang/wasm'
|
import {
|
||||||
|
CallExpression,
|
||||||
|
SourceRange,
|
||||||
|
Expr,
|
||||||
|
defaultSourceRange,
|
||||||
|
} from 'lang/wasm'
|
||||||
import { ModelingMachineEvent } from 'machines/modelingMachine'
|
import { ModelingMachineEvent } from 'machines/modelingMachine'
|
||||||
import { isNonNullable, uuidv4 } from 'lib/utils'
|
import { isNonNullable, uuidv4 } from 'lib/utils'
|
||||||
import { EditorSelection, SelectionRange } from '@codemirror/state'
|
import { EditorSelection, SelectionRange } from '@codemirror/state'
|
||||||
@ -266,7 +271,7 @@ export function getEventForSegmentSelection(
|
|||||||
selectionType: 'singleCodeCursor',
|
selectionType: 'singleCodeCursor',
|
||||||
selection: {
|
selection: {
|
||||||
codeRef: {
|
codeRef: {
|
||||||
range: [node.node.start, node.node.end],
|
range: [node.node.start, node.node.end, true],
|
||||||
pathToNode: group.userData.pathToNode,
|
pathToNode: group.userData.pathToNode,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -309,10 +314,11 @@ export function handleSelectionBatch({
|
|||||||
selectionToEngine.push({
|
selectionToEngine.push({
|
||||||
type: 'default',
|
type: 'default',
|
||||||
id: artifact?.id,
|
id: artifact?.id,
|
||||||
range: getCodeRefsByArtifactId(
|
range:
|
||||||
|
getCodeRefsByArtifactId(
|
||||||
artifact.id,
|
artifact.id,
|
||||||
engineCommandManager.artifactGraph
|
engineCommandManager.artifactGraph
|
||||||
)?.[0].range || [0, 0],
|
)?.[0].range || defaultSourceRange(),
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
const engineEvents: Models['WebSocketRequest_type'][] =
|
const engineEvents: Models['WebSocketRequest_type'][] =
|
||||||
@ -376,10 +382,10 @@ export function processCodeMirrorRanges({
|
|||||||
if (!isChange) return null
|
if (!isChange) return null
|
||||||
const codeBasedSelections: Selections['graphSelections'] =
|
const codeBasedSelections: Selections['graphSelections'] =
|
||||||
codeMirrorRanges.map(({ from, to }) => {
|
codeMirrorRanges.map(({ from, to }) => {
|
||||||
const pathToNode = getNodePathFromSourceRange(ast, [from, to])
|
const pathToNode = getNodePathFromSourceRange(ast, [from, to, true])
|
||||||
return {
|
return {
|
||||||
codeRef: {
|
codeRef: {
|
||||||
range: [from, to],
|
range: [from, to, true],
|
||||||
pathToNode,
|
pathToNode,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -442,7 +448,7 @@ function updateSceneObjectColors(codeBasedSelections: Selection[]) {
|
|||||||
if (err(nodeMeta)) return
|
if (err(nodeMeta)) return
|
||||||
const node = nodeMeta.node
|
const node = nodeMeta.node
|
||||||
const groupHasCursor = codeBasedSelections.some((selection) => {
|
const groupHasCursor = codeBasedSelections.some((selection) => {
|
||||||
return isOverlap(selection?.codeRef?.range, [node.start, node.end])
|
return isOverlap(selection?.codeRef?.range, [node.start, node.end, true])
|
||||||
})
|
})
|
||||||
|
|
||||||
const color = groupHasCursor
|
const color = groupHasCursor
|
||||||
@ -529,6 +535,10 @@ function nodeHasExtrude(node: CommonASTNode) {
|
|||||||
doesPipeHaveCallExp({
|
doesPipeHaveCallExp({
|
||||||
calleeName: 'revolve',
|
calleeName: 'revolve',
|
||||||
...node,
|
...node,
|
||||||
|
}) ||
|
||||||
|
doesPipeHaveCallExp({
|
||||||
|
calleeName: 'loft',
|
||||||
|
...node,
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -559,6 +569,22 @@ export function canSweepSelection(selection: Selections) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function canLoftSelection(selection: Selections) {
|
||||||
|
const commonNodes = selection.graphSelections.map((_, i) =>
|
||||||
|
buildCommonNodeFromSelection(selection, i)
|
||||||
|
)
|
||||||
|
return (
|
||||||
|
!!isCursorInSketchCommandRange(
|
||||||
|
engineCommandManager.artifactGraph,
|
||||||
|
selection
|
||||||
|
) &&
|
||||||
|
commonNodes.length > 1 &&
|
||||||
|
commonNodes.every((n) => !hasSketchPipeBeenExtruded(n.selection, n.ast)) &&
|
||||||
|
commonNodes.every((n) => nodeHasClose(n) || nodeHasCircle(n)) &&
|
||||||
|
commonNodes.every((n) => !nodeHasExtrude(n))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// This accounts for non-geometry selections under "other"
|
// This accounts for non-geometry selections under "other"
|
||||||
export type ResolvedSelectionType = Artifact['type'] | 'other'
|
export type ResolvedSelectionType = Artifact['type'] | 'other'
|
||||||
export type SelectionCountsByType = Map<ResolvedSelectionType, number>
|
export type SelectionCountsByType = Map<ResolvedSelectionType, number>
|
||||||
@ -905,7 +931,7 @@ export function updateSelections(
|
|||||||
return {
|
return {
|
||||||
artifact: artifact,
|
artifact: artifact,
|
||||||
codeRef: {
|
codeRef: {
|
||||||
range: [node.start, node.end],
|
range: [node.start, node.end, true],
|
||||||
pathToNode: pathToNode,
|
pathToNode: pathToNode,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -919,7 +945,7 @@ export function updateSelections(
|
|||||||
if (err(node)) return node
|
if (err(node)) return node
|
||||||
pathToNodeBasedSelections.push({
|
pathToNodeBasedSelections.push({
|
||||||
codeRef: {
|
codeRef: {
|
||||||
range: [node.node.start, node.node.end],
|
range: [node.node.start, node.node.end, true],
|
||||||
pathToNode: pathToNode,
|
pathToNode: pathToNode,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
@ -4,7 +4,6 @@ import {
|
|||||||
_executor,
|
_executor,
|
||||||
SourceRange,
|
SourceRange,
|
||||||
ExecState,
|
ExecState,
|
||||||
defaultIdGenerator,
|
|
||||||
} from '../lang/wasm'
|
} from '../lang/wasm'
|
||||||
import {
|
import {
|
||||||
EngineCommandManager,
|
EngineCommandManager,
|
||||||
@ -16,7 +15,6 @@ import { v4 as uuidv4 } from 'uuid'
|
|||||||
import { DefaultPlanes } from 'wasm-lib/kcl/bindings/DefaultPlanes'
|
import { DefaultPlanes } from 'wasm-lib/kcl/bindings/DefaultPlanes'
|
||||||
import { err, reportRejection } from 'lib/trap'
|
import { err, reportRejection } from 'lib/trap'
|
||||||
import { toSync } from './utils'
|
import { toSync } from './utils'
|
||||||
import { IdGenerator } from 'wasm-lib/kcl/bindings/IdGenerator'
|
|
||||||
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||||
|
|
||||||
type WebSocketResponse = Models['WebSocketResponse_type']
|
type WebSocketResponse = Models['WebSocketResponse_type']
|
||||||
@ -85,12 +83,10 @@ class MockEngineCommandManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function enginelessExecutor(
|
export async function enginelessExecutor(
|
||||||
ast: Node<Program> | Error,
|
ast: Node<Program>,
|
||||||
pm: ProgramMemory | Error = ProgramMemory.empty(),
|
pmo: ProgramMemory | Error = ProgramMemory.empty()
|
||||||
idGenerator: IdGenerator = defaultIdGenerator()
|
|
||||||
): Promise<ExecState> {
|
): Promise<ExecState> {
|
||||||
if (err(ast)) return Promise.reject(ast)
|
if (pmo !== null && err(pmo)) return Promise.reject(pmo)
|
||||||
if (err(pm)) return Promise.reject(pm)
|
|
||||||
|
|
||||||
const mockEngineCommandManager = new MockEngineCommandManager({
|
const mockEngineCommandManager = new MockEngineCommandManager({
|
||||||
setIsStreamReady: () => {},
|
setIsStreamReady: () => {},
|
||||||
@ -98,21 +94,14 @@ export async function enginelessExecutor(
|
|||||||
}) as any as EngineCommandManager
|
}) as any as EngineCommandManager
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||||
mockEngineCommandManager.startNewSession()
|
mockEngineCommandManager.startNewSession()
|
||||||
const execState = await _executor(
|
const execState = await _executor(ast, mockEngineCommandManager, pmo)
|
||||||
ast,
|
|
||||||
pm,
|
|
||||||
idGenerator,
|
|
||||||
mockEngineCommandManager,
|
|
||||||
true
|
|
||||||
)
|
|
||||||
await mockEngineCommandManager.waitForAllCommands()
|
await mockEngineCommandManager.waitForAllCommands()
|
||||||
return execState
|
return execState
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function executor(
|
export async function executor(
|
||||||
ast: Node<Program>,
|
ast: Node<Program>,
|
||||||
pm: ProgramMemory = ProgramMemory.empty(),
|
pmo: ProgramMemory = ProgramMemory.empty()
|
||||||
idGenerator: IdGenerator = defaultIdGenerator()
|
|
||||||
): Promise<ExecState> {
|
): Promise<ExecState> {
|
||||||
const engineCommandManager = new EngineCommandManager()
|
const engineCommandManager = new EngineCommandManager()
|
||||||
engineCommandManager.start({
|
engineCommandManager.start({
|
||||||
@ -134,13 +123,7 @@ export async function executor(
|
|||||||
toSync(async () => {
|
toSync(async () => {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||||
engineCommandManager.startNewSession()
|
engineCommandManager.startNewSession()
|
||||||
const execState = await _executor(
|
const execState = await _executor(ast, engineCommandManager, pmo)
|
||||||
ast,
|
|
||||||
pm,
|
|
||||||
idGenerator,
|
|
||||||
engineCommandManager,
|
|
||||||
false
|
|
||||||
)
|
|
||||||
await engineCommandManager.waitForAllCommands()
|
await engineCommandManager.waitForAllCommands()
|
||||||
resolve(execState)
|
resolve(execState)
|
||||||
}, reportRejection)
|
}, reportRejection)
|
||||||
|
@ -139,9 +139,14 @@ export const toolbarConfig: Record<ToolbarModeName, ToolbarMode> = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'loft',
|
id: 'loft',
|
||||||
onClick: () => console.error('Loft not yet implemented'),
|
onClick: ({ commandBarSend }) =>
|
||||||
|
commandBarSend({
|
||||||
|
type: 'Find and select command',
|
||||||
|
data: { name: 'Loft', groupId: 'modeling' },
|
||||||
|
}),
|
||||||
|
disabled: (state) => !state.can({ type: 'Loft' }),
|
||||||
icon: 'loft',
|
icon: 'loft',
|
||||||
status: 'kcl-only',
|
status: 'available',
|
||||||
title: 'Loft',
|
title: 'Loft',
|
||||||
hotkey: 'L',
|
hotkey: 'L',
|
||||||
description:
|
description:
|
||||||
|
@ -3,7 +3,7 @@ import { kclManager, engineCommandManager } from 'lib/singletons'
|
|||||||
import { useKclContext } from 'lang/KclProvider'
|
import { useKclContext } from 'lang/KclProvider'
|
||||||
import { findUniqueName } from 'lang/modifyAst'
|
import { findUniqueName } from 'lang/modifyAst'
|
||||||
import { PrevVariable, findAllPreviousVariables } from 'lang/queryAst'
|
import { PrevVariable, findAllPreviousVariables } from 'lang/queryAst'
|
||||||
import { ProgramMemory, Expr, parse } from 'lang/wasm'
|
import { ProgramMemory, Expr, parse, resultIsOk } from 'lang/wasm'
|
||||||
import { useEffect, useRef, useState } from 'react'
|
import { useEffect, useRef, useState } from 'react'
|
||||||
import { executeAst } from 'lang/langHelpers'
|
import { executeAst } from 'lang/langHelpers'
|
||||||
import { err, trap } from 'lib/trap'
|
import { err, trap } from 'lib/trap'
|
||||||
@ -87,9 +87,9 @@ export function useCalculateKclExpression({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const execAstAndSetResult = async () => {
|
const execAstAndSetResult = async () => {
|
||||||
const _code = `const __result__ = ${value}`
|
const _code = `const __result__ = ${value}`
|
||||||
const ast = parse(_code)
|
const pResult = parse(_code)
|
||||||
if (err(ast)) return
|
if (err(pResult) || !resultIsOk(pResult)) return
|
||||||
if (trap(ast, { suppress: true })) return
|
const ast = pResult.program
|
||||||
|
|
||||||
const _programMem: ProgramMemory = ProgramMemory.empty()
|
const _programMem: ProgramMemory = ProgramMemory.empty()
|
||||||
for (const { key, value } of availableVarInfo.variables) {
|
for (const { key, value } of availableVarInfo.variables) {
|
||||||
@ -103,9 +103,8 @@ export function useCalculateKclExpression({
|
|||||||
const { execState } = await executeAst({
|
const { execState } = await executeAst({
|
||||||
ast,
|
ast,
|
||||||
engineCommandManager,
|
engineCommandManager,
|
||||||
useFakeExecutor: true,
|
// We make sure to send an empty program memory to denote we mean mock mode.
|
||||||
programMemoryOverride: kclManager.programMemory.clone(),
|
programMemoryOverride: kclManager.programMemory.clone(),
|
||||||
idGenerator: kclManager.execState.idGenerator,
|
|
||||||
})
|
})
|
||||||
const resultDeclaration = ast.body.find(
|
const resultDeclaration = ast.body.find(
|
||||||
(a) =>
|
(a) =>
|
||||||
|
@ -9,13 +9,13 @@ import {
|
|||||||
import { SourceRange } from '../lang/wasm'
|
import { SourceRange } from '../lang/wasm'
|
||||||
|
|
||||||
describe('testing isOverlapping', () => {
|
describe('testing isOverlapping', () => {
|
||||||
testBothOrders([0, 3], [3, 10])
|
testBothOrders([0, 3, true], [3, 10, true])
|
||||||
testBothOrders([0, 5], [3, 4])
|
testBothOrders([0, 5, true], [3, 4, true])
|
||||||
testBothOrders([0, 5], [5, 10])
|
testBothOrders([0, 5, true], [5, 10, true])
|
||||||
testBothOrders([0, 5], [6, 10], false)
|
testBothOrders([0, 5, true], [6, 10, true], false)
|
||||||
testBothOrders([0, 5], [-1, 1])
|
testBothOrders([0, 5, true], [-1, 1, true])
|
||||||
testBothOrders([0, 5], [-1, 0])
|
testBothOrders([0, 5, true], [-1, 0, true])
|
||||||
testBothOrders([0, 5], [-2, -1], false)
|
testBothOrders([0, 5, true], [-2, -1, true], false)
|
||||||
})
|
})
|
||||||
|
|
||||||
function testBothOrders(a: SourceRange, b: SourceRange, result = true) {
|
function testBothOrders(a: SourceRange, b: SourceRange, result = true) {
|
||||||
|
22
src/lib/xStateValueToString.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import { AnyStateMachine, StateFrom } from 'xstate'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert an XState state value to a pretty string,
|
||||||
|
* with nested states separated by slashes
|
||||||
|
*/
|
||||||
|
export function xStateValueToString(
|
||||||
|
stateValue: StateFrom<AnyStateMachine>['value']
|
||||||
|
) {
|
||||||
|
const sep = ' / '
|
||||||
|
let output = ''
|
||||||
|
let remainingValues = stateValue
|
||||||
|
let isFirstStep = true
|
||||||
|
while (remainingValues instanceof Object) {
|
||||||
|
const key: keyof typeof remainingValues = Object.keys(remainingValues)[0]
|
||||||
|
output += (isFirstStep ? '' : sep) + key
|
||||||
|
remainingValues = remainingValues[key]
|
||||||
|
isFirstStep = false
|
||||||
|
}
|
||||||
|
if (typeof remainingValues === 'string' && remainingValues.trim().length)
|
||||||
|
return output + sep + remainingValues.trim()
|
||||||
|
}
|
@ -1,9 +1,11 @@
|
|||||||
import {
|
import {
|
||||||
PathToNode,
|
PathToNode,
|
||||||
|
ProgramMemory,
|
||||||
VariableDeclaration,
|
VariableDeclaration,
|
||||||
VariableDeclarator,
|
VariableDeclarator,
|
||||||
parse,
|
parse,
|
||||||
recast,
|
recast,
|
||||||
|
resultIsOk,
|
||||||
} from 'lang/wasm'
|
} from 'lang/wasm'
|
||||||
import {
|
import {
|
||||||
Axis,
|
Axis,
|
||||||
@ -44,13 +46,14 @@ import {
|
|||||||
addOffsetPlane,
|
addOffsetPlane,
|
||||||
deleteFromSelection,
|
deleteFromSelection,
|
||||||
extrudeSketch,
|
extrudeSketch,
|
||||||
|
loftSketches,
|
||||||
revolveSketch,
|
revolveSketch,
|
||||||
} from 'lang/modifyAst'
|
} from 'lang/modifyAst'
|
||||||
import {
|
import {
|
||||||
applyEdgeTreatmentToSelection,
|
applyEdgeTreatmentToSelection,
|
||||||
EdgeTreatmentType,
|
EdgeTreatmentType,
|
||||||
FilletParameters,
|
FilletParameters,
|
||||||
} from 'lang/modifyAst/addFillet'
|
} from 'lang/modifyAst/addEdgeTreatment'
|
||||||
import { getNodeFromPath } from '../lang/queryAst'
|
import { getNodeFromPath } from '../lang/queryAst'
|
||||||
import {
|
import {
|
||||||
applyConstraintEqualAngle,
|
applyConstraintEqualAngle,
|
||||||
@ -256,6 +259,7 @@ export type ModelingMachineEvent =
|
|||||||
| { type: 'Export'; data: ModelingCommandSchema['Export'] }
|
| { type: 'Export'; data: ModelingCommandSchema['Export'] }
|
||||||
| { type: 'Make'; data: ModelingCommandSchema['Make'] }
|
| { type: 'Make'; data: ModelingCommandSchema['Make'] }
|
||||||
| { type: 'Extrude'; data?: ModelingCommandSchema['Extrude'] }
|
| { type: 'Extrude'; data?: ModelingCommandSchema['Extrude'] }
|
||||||
|
| { type: 'Loft'; data?: ModelingCommandSchema['Loft'] }
|
||||||
| { type: 'Revolve'; data?: ModelingCommandSchema['Revolve'] }
|
| { type: 'Revolve'; data?: ModelingCommandSchema['Revolve'] }
|
||||||
| { type: 'Fillet'; data?: ModelingCommandSchema['Fillet'] }
|
| { type: 'Fillet'; data?: ModelingCommandSchema['Fillet'] }
|
||||||
| { type: 'Offset plane'; data: ModelingCommandSchema['Offset plane'] }
|
| { type: 'Offset plane'; data: ModelingCommandSchema['Offset plane'] }
|
||||||
@ -387,6 +391,7 @@ export const modelingMachine = setup({
|
|||||||
guards: {
|
guards: {
|
||||||
'Selection is on face': () => false,
|
'Selection is on face': () => false,
|
||||||
'has valid sweep selection': () => false,
|
'has valid sweep selection': () => false,
|
||||||
|
'has valid loft selection': () => false,
|
||||||
'has valid edge treatment selection': () => false,
|
'has valid edge treatment selection': () => false,
|
||||||
'Has exportable geometry': () => false,
|
'Has exportable geometry': () => false,
|
||||||
'has valid selection for deletion': () => false,
|
'has valid selection for deletion': () => false,
|
||||||
@ -539,8 +544,11 @@ export const modelingMachine = setup({
|
|||||||
if (event.type !== 'Convert to variable') return false
|
if (event.type !== 'Convert to variable') return false
|
||||||
if (!event.data) return false
|
if (!event.data) return false
|
||||||
const ast = parse(recast(kclManager.ast))
|
const ast = parse(recast(kclManager.ast))
|
||||||
if (err(ast)) return false
|
if (err(ast) || !ast.program || ast.errors.length > 0) return false
|
||||||
const isSafeRetVal = isNodeSafeToReplacePath(ast, event.data.pathToNode)
|
const isSafeRetVal = isNodeSafeToReplacePath(
|
||||||
|
ast.program,
|
||||||
|
event.data.pathToNode
|
||||||
|
)
|
||||||
if (err(isSafeRetVal)) return false
|
if (err(isSafeRetVal)) return false
|
||||||
return isSafeRetVal.isSafe
|
return isSafeRetVal.isSafe
|
||||||
},
|
},
|
||||||
@ -723,9 +731,9 @@ export const modelingMachine = setup({
|
|||||||
|
|
||||||
const testExecute = await executeAst({
|
const testExecute = await executeAst({
|
||||||
ast: modifiedAst,
|
ast: modifiedAst,
|
||||||
idGenerator: kclManager.execState.idGenerator,
|
|
||||||
useFakeExecutor: true,
|
|
||||||
engineCommandManager,
|
engineCommandManager,
|
||||||
|
// We make sure to send an empty program memory to denote we mean mock mode.
|
||||||
|
programMemoryOverride: ProgramMemory.empty(),
|
||||||
})
|
})
|
||||||
if (testExecute.errors.length) {
|
if (testExecute.errors.length) {
|
||||||
toast.error('Unable to delete part')
|
toast.error('Unable to delete part')
|
||||||
@ -1329,9 +1337,12 @@ export const modelingMachine = setup({
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const recastAst = parse(recast(modifiedAst))
|
||||||
|
if (err(recastAst) || !resultIsOk(recastAst)) return
|
||||||
|
|
||||||
const updatedAst = await sceneEntitiesManager.updateAstAndRejigSketch(
|
const updatedAst = await sceneEntitiesManager.updateAstAndRejigSketch(
|
||||||
sketchDetails?.sketchPathToNode || [],
|
sketchDetails?.sketchPathToNode || [],
|
||||||
parse(recast(modifiedAst)),
|
recastAst.program,
|
||||||
sketchDetails.zAxis,
|
sketchDetails.zAxis,
|
||||||
sketchDetails.yAxis,
|
sketchDetails.yAxis,
|
||||||
sketchDetails.origin
|
sketchDetails.origin
|
||||||
@ -1529,6 +1540,50 @@ export const modelingMachine = setup({
|
|||||||
updateAstResult.newAst
|
updateAstResult.newAst
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (updateAstResult?.selections) {
|
||||||
|
editorManager.selectRange(updateAstResult?.selections)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
),
|
||||||
|
loftAstMod: fromPromise(
|
||||||
|
async ({
|
||||||
|
input,
|
||||||
|
}: {
|
||||||
|
input: ModelingCommandSchema['Loft'] | undefined
|
||||||
|
}) => {
|
||||||
|
if (!input) return new Error('No input provided')
|
||||||
|
// Extract inputs
|
||||||
|
const ast = kclManager.ast
|
||||||
|
const { selection } = input
|
||||||
|
const declarators = selection.graphSelections.flatMap((s) => {
|
||||||
|
const path = getNodePathFromSourceRange(ast, s?.codeRef.range)
|
||||||
|
const nodeFromPath = getNodeFromPath<VariableDeclarator>(
|
||||||
|
ast,
|
||||||
|
path,
|
||||||
|
'VariableDeclarator'
|
||||||
|
)
|
||||||
|
return err(nodeFromPath) ? [] : nodeFromPath.node
|
||||||
|
})
|
||||||
|
|
||||||
|
// TODO: add better validation on selection
|
||||||
|
if (!(declarators && declarators.length > 1)) {
|
||||||
|
trap('Not enough sketches selected')
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform the loft
|
||||||
|
const loftSketchesRes = loftSketches(ast, declarators)
|
||||||
|
const updateAstResult = await kclManager.updateAst(
|
||||||
|
loftSketchesRes.modifiedAst,
|
||||||
|
true,
|
||||||
|
{
|
||||||
|
focusPath: [loftSketchesRes.pathToNode],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
await codeManager.updateEditorWithAstAndWriteToFile(
|
||||||
|
updateAstResult.newAst
|
||||||
|
)
|
||||||
|
|
||||||
if (updateAstResult?.selections) {
|
if (updateAstResult?.selections) {
|
||||||
editorManager.selectRange(updateAstResult?.selections)
|
editorManager.selectRange(updateAstResult?.selections)
|
||||||
}
|
}
|
||||||
@ -1570,6 +1625,11 @@ export const modelingMachine = setup({
|
|||||||
reenter: false,
|
reenter: false,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
Loft: {
|
||||||
|
target: 'Applying loft',
|
||||||
|
reenter: true,
|
||||||
|
},
|
||||||
|
|
||||||
Fillet: {
|
Fillet: {
|
||||||
target: 'idle',
|
target: 'idle',
|
||||||
guard: 'has valid edge treatment selection',
|
guard: 'has valid edge treatment selection',
|
||||||
@ -2318,6 +2378,19 @@ export const modelingMachine = setup({
|
|||||||
onError: ['idle'],
|
onError: ['idle'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
'Applying loft': {
|
||||||
|
invoke: {
|
||||||
|
src: 'loftAstMod',
|
||||||
|
id: 'loftAstMod',
|
||||||
|
input: ({ event }) => {
|
||||||
|
if (event.type !== 'Loft') return undefined
|
||||||
|
return event.data
|
||||||
|
},
|
||||||
|
onDone: ['idle'],
|
||||||
|
onError: ['idle'],
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
initial: 'idle',
|
initial: 'idle',
|
||||||
|
@ -13,18 +13,6 @@ import { AllSettingsFields } from 'components/Settings/AllSettingsFields'
|
|||||||
import { AllKeybindingsFields } from 'components/Settings/AllKeybindingsFields'
|
import { AllKeybindingsFields } from 'components/Settings/AllKeybindingsFields'
|
||||||
import { KeybindingsSectionsList } from 'components/Settings/KeybindingsSectionsList'
|
import { KeybindingsSectionsList } from 'components/Settings/KeybindingsSectionsList'
|
||||||
import { isDesktop } from 'lib/isDesktop'
|
import { isDesktop } from 'lib/isDesktop'
|
||||||
import { IS_PLAYWRIGHT_KEY } from '../../e2e/playwright/storageStates'
|
|
||||||
import { NODE_ENV } from 'env'
|
|
||||||
|
|
||||||
const isTestEnv = window?.localStorage.getItem(IS_PLAYWRIGHT_KEY) === 'true'
|
|
||||||
|
|
||||||
export const APP_VERSION =
|
|
||||||
isTestEnv && NODE_ENV === 'development'
|
|
||||||
? '11.22.33'
|
|
||||||
: isDesktop()
|
|
||||||
? // @ts-ignore
|
|
||||||
window.electron.packageJson.version
|
|
||||||
: 'main'
|
|
||||||
|
|
||||||
export const PACKAGE_NAME = isDesktop()
|
export const PACKAGE_NAME = isDesktop()
|
||||||
? window.electron.packageJson.name
|
? window.electron.packageJson.name
|
||||||
|
@ -5,11 +5,11 @@ import { Themes, getSystemTheme } from '../lib/theme'
|
|||||||
import { PATHS } from 'lib/paths'
|
import { PATHS } from 'lib/paths'
|
||||||
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
||||||
import { APP_NAME } from 'lib/constants'
|
import { APP_NAME } from 'lib/constants'
|
||||||
|
import { APP_VERSION } from 'lib/appVersion'
|
||||||
import { CSSProperties, useCallback, useState } from 'react'
|
import { CSSProperties, useCallback, useState } from 'react'
|
||||||
import { Logo } from 'components/Logo'
|
import { Logo } from 'components/Logo'
|
||||||
import { CustomIcon } from 'components/CustomIcon'
|
import { CustomIcon } from 'components/CustomIcon'
|
||||||
import { Link } from 'react-router-dom'
|
import { Link } from 'react-router-dom'
|
||||||
import { APP_VERSION } from './Settings'
|
|
||||||
import { openExternalBrowserIfDesktop } from 'lib/openWindow'
|
import { openExternalBrowserIfDesktop } from 'lib/openWindow'
|
||||||
import { toSync } from 'lib/utils'
|
import { toSync } from 'lib/utils'
|
||||||
import { reportRejection } from 'lib/trap'
|
import { reportRejection } from 'lib/trap'
|
||||||
|
82
src/wasm-lib/Cargo.lock
generated
@ -316,7 +316,7 @@ dependencies = [
|
|||||||
"bitvec",
|
"bitvec",
|
||||||
"chrono",
|
"chrono",
|
||||||
"hex",
|
"hex",
|
||||||
"indexmap 2.6.0",
|
"indexmap 2.7.0",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
@ -712,29 +712,6 @@ version = "2.6.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2"
|
checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "databake"
|
|
||||||
version = "0.1.8"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6a04fbfbecca8f0679c8c06fef907594adcc3e2052e11163a6d30535a1a5604d"
|
|
||||||
dependencies = [
|
|
||||||
"databake-derive",
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "databake-derive"
|
|
||||||
version = "0.1.8"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "4078275de501a61ceb9e759d37bdd3d7210e654dbc167ac1a3678ef4435ed57b"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 2.0.87",
|
|
||||||
"synstructure",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "deranged"
|
name = "deranged"
|
||||||
version = "0.3.11"
|
version = "0.3.11"
|
||||||
@ -746,7 +723,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "derive-docs"
|
name = "derive-docs"
|
||||||
version = "0.1.31"
|
version = "0.1.32"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"Inflector",
|
"Inflector",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
@ -1136,7 +1113,7 @@ dependencies = [
|
|||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-sink",
|
"futures-sink",
|
||||||
"http 1.1.0",
|
"http 1.1.0",
|
||||||
"indexmap 2.6.0",
|
"indexmap 2.7.0",
|
||||||
"slab",
|
"slab",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-util",
|
"tokio-util",
|
||||||
@ -1182,9 +1159,9 @@ checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.15.0"
|
version = "0.15.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb"
|
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "heck"
|
name = "heck"
|
||||||
@ -1584,12 +1561,12 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "indexmap"
|
name = "indexmap"
|
||||||
version = "2.6.0"
|
version = "2.7.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da"
|
checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"equivalent",
|
"equivalent",
|
||||||
"hashbrown 0.15.0",
|
"hashbrown 0.15.2",
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -1706,7 +1683,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kcl-lib"
|
name = "kcl-lib"
|
||||||
version = "0.2.26"
|
version = "0.2.28"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"approx 0.5.1",
|
"approx 0.5.1",
|
||||||
@ -1719,7 +1696,6 @@ dependencies = [
|
|||||||
"convert_case",
|
"convert_case",
|
||||||
"criterion",
|
"criterion",
|
||||||
"dashmap 6.1.0",
|
"dashmap 6.1.0",
|
||||||
"databake",
|
|
||||||
"derive-docs",
|
"derive-docs",
|
||||||
"dhat",
|
"dhat",
|
||||||
"expectorate",
|
"expectorate",
|
||||||
@ -1732,7 +1708,7 @@ dependencies = [
|
|||||||
"http 1.1.0",
|
"http 1.1.0",
|
||||||
"iai",
|
"iai",
|
||||||
"image",
|
"image",
|
||||||
"indexmap 2.6.0",
|
"indexmap 2.7.0",
|
||||||
"insta",
|
"insta",
|
||||||
"itertools 0.13.0",
|
"itertools 0.13.0",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
@ -1773,7 +1749,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kcl-test-server"
|
name = "kcl-test-server"
|
||||||
version = "0.1.17"
|
version = "0.1.18"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"hyper 0.14.30",
|
"hyper 0.14.30",
|
||||||
@ -1790,7 +1766,7 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"indexmap 2.6.0",
|
"indexmap 2.7.0",
|
||||||
"kcl-lib",
|
"kcl-lib",
|
||||||
"kittycad",
|
"kittycad",
|
||||||
"kittycad-modeling-cmds",
|
"kittycad-modeling-cmds",
|
||||||
@ -1800,9 +1776,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kittycad"
|
name = "kittycad"
|
||||||
version = "0.3.25"
|
version = "0.3.28"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f6359cc0a1bbccbcf78775eea17a033cf2aa89d3fe6a9784f8ce94e5f882c185"
|
checksum = "933cb5f77624386c87d296e3fd493daf50156d1cbfa03b9f333a6d4da2896369"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@ -1831,7 +1807,7 @@ dependencies = [
|
|||||||
"serde_bytes",
|
"serde_bytes",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_urlencoded",
|
"serde_urlencoded",
|
||||||
"thiserror 1.0.68",
|
"thiserror 2.0.0",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tracing",
|
"tracing",
|
||||||
"url",
|
"url",
|
||||||
@ -2921,9 +2897,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "reqwest-conditional-middleware"
|
name = "reqwest-conditional-middleware"
|
||||||
version = "0.3.0"
|
version = "0.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b1663d9d4fbb6e3900f91455d6d7833301c91ae3c7fc6e116fd7acd40e478a93"
|
checksum = "f67ad7fdf5c0a015763fcd164bee294b13fb7b6f89f1b55961d40f00c3e32d6b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"http 1.1.0",
|
"http 1.1.0",
|
||||||
@ -2933,9 +2909,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "reqwest-middleware"
|
name = "reqwest-middleware"
|
||||||
version = "0.3.3"
|
version = "0.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "562ceb5a604d3f7c885a792d42c199fd8af239d0a51b2fa6a78aafa092452b04"
|
checksum = "d1ccd3b55e711f91a9885a2fa6fbbb2e39db1776420b062efc058c6410f7e5e3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@ -2948,9 +2924,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "reqwest-retry"
|
name = "reqwest-retry"
|
||||||
version = "0.6.1"
|
version = "0.7.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a83df1aaec00176d0fabb65dea13f832d2a446ca99107afc17c5d2d4981221d0"
|
checksum = "29c73e4195a6bfbcb174b790d9b3407ab90646976c55de58a6515da25d851178"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@ -2962,6 +2938,7 @@ dependencies = [
|
|||||||
"reqwest",
|
"reqwest",
|
||||||
"reqwest-middleware",
|
"reqwest-middleware",
|
||||||
"retry-policies",
|
"retry-policies",
|
||||||
|
"thiserror 1.0.68",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tracing",
|
"tracing",
|
||||||
"wasm-timer",
|
"wasm-timer",
|
||||||
@ -2969,9 +2946,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "reqwest-tracing"
|
name = "reqwest-tracing"
|
||||||
version = "0.5.3"
|
version = "0.5.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bfdd9bfa64c72233d8dd99ab7883efcdefe9e16d46488ecb9228b71a2e2ceb45"
|
checksum = "ff82cf5730a1311fb9413b0bc2b8e743e0157cd73f010ab4ec374a923873b6a2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@ -3157,7 +3134,7 @@ dependencies = [
|
|||||||
"chrono",
|
"chrono",
|
||||||
"dyn-clone",
|
"dyn-clone",
|
||||||
"indexmap 1.9.3",
|
"indexmap 1.9.3",
|
||||||
"indexmap 2.6.0",
|
"indexmap 2.7.0",
|
||||||
"schemars_derive",
|
"schemars_derive",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
@ -3258,7 +3235,7 @@ version = "1.0.133"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377"
|
checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indexmap 2.6.0",
|
"indexmap 2.7.0",
|
||||||
"itoa",
|
"itoa",
|
||||||
"memchr",
|
"memchr",
|
||||||
"ryu",
|
"ryu",
|
||||||
@ -3846,7 +3823,7 @@ version = "0.22.22"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5"
|
checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indexmap 2.6.0",
|
"indexmap 2.7.0",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_spanned",
|
"serde_spanned",
|
||||||
"toml_datetime",
|
"toml_datetime",
|
||||||
@ -4012,7 +3989,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "3a2f31991cee3dce1ca4f929a8a04fdd11fd8801aac0f2030b0fa8a0a3fef6b9"
|
checksum = "3a2f31991cee3dce1ca4f929a8a04fdd11fd8801aac0f2030b0fa8a0a3fef6b9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"indexmap 2.6.0",
|
"indexmap 2.7.0",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"thiserror 1.0.68",
|
"thiserror 1.0.68",
|
||||||
@ -4331,6 +4308,7 @@ dependencies = [
|
|||||||
"kcl-lib",
|
"kcl-lib",
|
||||||
"kittycad",
|
"kittycad",
|
||||||
"kittycad-modeling-cmds",
|
"kittycad-modeling-cmds",
|
||||||
|
"lazy_static",
|
||||||
"pretty_assertions",
|
"pretty_assertions",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
@ -4769,7 +4747,7 @@ dependencies = [
|
|||||||
"crc32fast",
|
"crc32fast",
|
||||||
"crossbeam-utils",
|
"crossbeam-utils",
|
||||||
"displaydoc",
|
"displaydoc",
|
||||||
"indexmap 2.6.0",
|
"indexmap 2.7.0",
|
||||||
"memchr",
|
"memchr",
|
||||||
"thiserror 1.0.68",
|
"thiserror 1.0.68",
|
||||||
]
|
]
|
||||||
|
@ -15,6 +15,7 @@ data-encoding = "2.6.0"
|
|||||||
gloo-utils = "0.2.0"
|
gloo-utils = "0.2.0"
|
||||||
kcl-lib = { path = "kcl" }
|
kcl-lib = { path = "kcl" }
|
||||||
kittycad.workspace = true
|
kittycad.workspace = true
|
||||||
|
lazy_static = "1.5.0"
|
||||||
serde_json = "1.0.128"
|
serde_json = "1.0.128"
|
||||||
tokio = { version = "1.41.1", features = ["sync"] }
|
tokio = { version = "1.41.1", features = ["sync"] }
|
||||||
toml = "0.8.19"
|
toml = "0.8.19"
|
||||||
@ -74,8 +75,11 @@ members = [
|
|||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
http = "1"
|
http = "1"
|
||||||
kittycad = { version = "0.3.25", default-features = false, features = ["js", "requests"] }
|
kittycad = { version = "0.3.28", default-features = false, features = ["js", "requests"] }
|
||||||
kittycad-modeling-cmds = { version = "0.2.76", features = ["websocket"] }
|
kittycad-modeling-cmds = { version = "0.2.77", features = ["websocket"] }
|
||||||
|
|
||||||
|
[workspace.lints.clippy]
|
||||||
|
iter_over_hash_type = "warn"
|
||||||
|
|
||||||
[[test]]
|
[[test]]
|
||||||
name = "executor"
|
name = "executor"
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "derive-docs"
|
name = "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.31"
|
version = "0.1.32"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
repository = "https://github.com/KittyCAD/modeling-app"
|
repository = "https://github.com/KittyCAD/modeling-app"
|
||||||
@ -27,3 +27,6 @@ anyhow = "1.0.93"
|
|||||||
expectorate = "1.1.0"
|
expectorate = "1.1.0"
|
||||||
pretty_assertions = "1.4.1"
|
pretty_assertions = "1.4.1"
|
||||||
rustfmt-wrapper = "0.2.1"
|
rustfmt-wrapper = "0.2.1"
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
|
@ -23,17 +23,30 @@ use unbox::unbox;
|
|||||||
struct StdlibMetadata {
|
struct StdlibMetadata {
|
||||||
/// The name of the function in the API.
|
/// The name of the function in the API.
|
||||||
name: String,
|
name: String,
|
||||||
|
|
||||||
/// Tags for the function.
|
/// Tags for the function.
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
tags: Vec<String>,
|
tags: Vec<String>,
|
||||||
|
|
||||||
/// Whether the function is unpublished.
|
/// Whether the function is unpublished.
|
||||||
/// Then docs will not be generated.
|
/// Then docs will not be generated.
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
unpublished: bool,
|
unpublished: bool,
|
||||||
|
|
||||||
/// Whether the function is deprecated.
|
/// Whether the function is deprecated.
|
||||||
/// Then specific docs detailing that this is deprecated will be generated.
|
/// Then specific docs detailing that this is deprecated will be generated.
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
deprecated: bool,
|
deprecated: bool,
|
||||||
|
|
||||||
|
/// If true, expects keyword arguments.
|
||||||
|
/// If false, expects positional arguments.
|
||||||
|
#[serde(default)]
|
||||||
|
keywords: bool,
|
||||||
|
|
||||||
|
/// If true, the first argument is unlabeled.
|
||||||
|
/// If false, all arguments require labels.
|
||||||
|
#[serde(default)]
|
||||||
|
unlabeled_first: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[proc_macro_attribute]
|
#[proc_macro_attribute]
|
||||||
@ -169,9 +182,9 @@ fn do_stdlib_inner(
|
|||||||
quote! {
|
quote! {
|
||||||
let code_blocks = vec![#(#cb),*];
|
let code_blocks = vec![#(#cb),*];
|
||||||
code_blocks.iter().map(|cb| {
|
code_blocks.iter().map(|cb| {
|
||||||
let program = crate::Program::parse(cb).unwrap();
|
let program = crate::Program::parse_no_errs(cb).unwrap();
|
||||||
|
|
||||||
let mut options: crate::ast::types::FormatOptions = Default::default();
|
let mut options: crate::parsing::ast::types::FormatOptions = Default::default();
|
||||||
options.insert_final_newline = false;
|
options.insert_final_newline = false;
|
||||||
program.ast.recast(&options, 0)
|
program.ast.recast(&options, 0)
|
||||||
}).collect::<Vec<String>>()
|
}).collect::<Vec<String>>()
|
||||||
@ -225,6 +238,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
|
||||||
@ -233,7 +252,7 @@ fn do_stdlib_inner(
|
|||||||
// of the various parameters. We do this by calling dummy functions that
|
// of the various parameters. We do this by calling dummy functions that
|
||||||
// require a type that satisfies SharedExtractor or ExclusiveExtractor.
|
// require a type that satisfies SharedExtractor or ExclusiveExtractor.
|
||||||
let mut arg_types = Vec::new();
|
let mut arg_types = Vec::new();
|
||||||
for arg in ast.sig.inputs.iter() {
|
for (i, arg) in ast.sig.inputs.iter().enumerate() {
|
||||||
// Get the name of the argument.
|
// Get the name of the argument.
|
||||||
let arg_name = match arg {
|
let arg_name = match arg {
|
||||||
syn::FnArg::Receiver(pat) => {
|
syn::FnArg::Receiver(pat) => {
|
||||||
@ -263,7 +282,7 @@ 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 label_required = !(i == 0 && metadata.unlabeled_first);
|
||||||
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>()
|
||||||
@ -274,6 +293,7 @@ fn do_stdlib_inner(
|
|||||||
type_: #ty_string.to_string(),
|
type_: #ty_string.to_string(),
|
||||||
schema: #schema,
|
schema: #schema,
|
||||||
required: #required,
|
required: #required,
|
||||||
|
label_required: #label_required,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -334,6 +354,7 @@ fn do_stdlib_inner(
|
|||||||
type_: #ret_ty_string.to_string(),
|
type_: #ret_ty_string.to_string(),
|
||||||
schema,
|
schema,
|
||||||
required: true,
|
required: true,
|
||||||
|
label_required: true,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -400,6 +421,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.
|
||||||
@ -744,7 +769,7 @@ fn generate_code_block_test(fn_name: &str, code_block: &str, index: usize) -> pr
|
|||||||
quote! {
|
quote! {
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
async fn #test_name_mock() {
|
async fn #test_name_mock() {
|
||||||
let program = crate::Program::parse(#code_block).unwrap();
|
let program = crate::Program::parse_no_errs(#code_block).unwrap();
|
||||||
let ctx = crate::executor::ExecutorContext {
|
let ctx = crate::executor::ExecutorContext {
|
||||||
engine: std::sync::Arc::new(Box::new(crate::engine::conn_mock::EngineConnection::new().await.unwrap())),
|
engine: std::sync::Arc::new(Box::new(crate::engine::conn_mock::EngineConnection::new().await.unwrap())),
|
||||||
fs: std::sync::Arc::new(crate::fs::FileManager::new()),
|
fs: std::sync::Arc::new(crate::fs::FileManager::new()),
|
||||||
@ -753,7 +778,7 @@ fn generate_code_block_test(fn_name: &str, code_block: &str, index: usize) -> pr
|
|||||||
context_type: crate::executor::ContextType::Mock,
|
context_type: crate::executor::ContextType::Mock,
|
||||||
};
|
};
|
||||||
|
|
||||||
ctx.run(&program, &mut crate::ExecState::default()).await.unwrap();
|
ctx.run(program.into(), &mut crate::ExecState::default()).await.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
|
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
mod test_examples_someFn {
|
mod test_examples_someFn {
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
async fn test_mock_example_someFn0() {
|
async fn test_mock_example_someFn0() {
|
||||||
let program = crate::Program::parse("someFn()").unwrap();
|
let program = crate::Program::parse_no_errs("someFn()").unwrap();
|
||||||
let ctx = crate::executor::ExecutorContext {
|
let ctx = crate::executor::ExecutorContext {
|
||||||
engine: std::sync::Arc::new(Box::new(
|
engine: std::sync::Arc::new(Box::new(
|
||||||
crate::engine::conn_mock::EngineConnection::new()
|
crate::engine::conn_mock::EngineConnection::new()
|
||||||
@ -14,7 +14,7 @@ mod test_examples_someFn {
|
|||||||
settings: Default::default(),
|
settings: Default::default(),
|
||||||
context_type: crate::executor::ContextType::Mock,
|
context_type: crate::executor::ContextType::Mock,
|
||||||
};
|
};
|
||||||
ctx.run(&program, &mut crate::ExecState::default())
|
ctx.run(program.into(), &mut crate::ExecState::default())
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
@ -74,6 +74,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;
|
||||||
@ -83,6 +87,7 @@ impl crate::docs::StdLibFn for SomeFn {
|
|||||||
type_: "Foo".to_string(),
|
type_: "Foo".to_string(),
|
||||||
schema: generator.root_schema_for::<Foo>(),
|
schema: generator.root_schema_for::<Foo>(),
|
||||||
required: true,
|
required: true,
|
||||||
|
label_required: true,
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,6 +101,7 @@ impl crate::docs::StdLibFn for SomeFn {
|
|||||||
type_: "i32".to_string(),
|
type_: "i32".to_string(),
|
||||||
schema,
|
schema,
|
||||||
required: true,
|
required: true,
|
||||||
|
label_required: true,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,8 +118,8 @@ impl crate::docs::StdLibFn for SomeFn {
|
|||||||
code_blocks
|
code_blocks
|
||||||
.iter()
|
.iter()
|
||||||
.map(|cb| {
|
.map(|cb| {
|
||||||
let program = crate::Program::parse(cb).unwrap();
|
let program = crate::Program::parse_no_errs(cb).unwrap();
|
||||||
let mut options: crate::ast::types::FormatOptions = Default::default();
|
let mut options: crate::parsing::ast::types::FormatOptions = Default::default();
|
||||||
options.insert_final_newline = false;
|
options.insert_final_newline = false;
|
||||||
program.ast.recast(&options, 0)
|
program.ast.recast(&options, 0)
|
||||||
})
|
})
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
mod test_examples_someFn {
|
mod test_examples_someFn {
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
async fn test_mock_example_someFn0() {
|
async fn test_mock_example_someFn0() {
|
||||||
let program = crate::Program::parse("someFn()").unwrap();
|
let program = crate::Program::parse_no_errs("someFn()").unwrap();
|
||||||
let ctx = crate::executor::ExecutorContext {
|
let ctx = crate::executor::ExecutorContext {
|
||||||
engine: std::sync::Arc::new(Box::new(
|
engine: std::sync::Arc::new(Box::new(
|
||||||
crate::engine::conn_mock::EngineConnection::new()
|
crate::engine::conn_mock::EngineConnection::new()
|
||||||
@ -14,7 +14,7 @@ mod test_examples_someFn {
|
|||||||
settings: Default::default(),
|
settings: Default::default(),
|
||||||
context_type: crate::executor::ContextType::Mock,
|
context_type: crate::executor::ContextType::Mock,
|
||||||
};
|
};
|
||||||
ctx.run(&program, &mut crate::ExecState::default())
|
ctx.run(program.into(), &mut crate::ExecState::default())
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
@ -74,6 +74,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;
|
||||||
@ -83,6 +87,7 @@ impl crate::docs::StdLibFn for SomeFn {
|
|||||||
type_: "string".to_string(),
|
type_: "string".to_string(),
|
||||||
schema: generator.root_schema_for::<str>(),
|
schema: generator.root_schema_for::<str>(),
|
||||||
required: true,
|
required: true,
|
||||||
|
label_required: true,
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,6 +101,7 @@ impl crate::docs::StdLibFn for SomeFn {
|
|||||||
type_: "i32".to_string(),
|
type_: "i32".to_string(),
|
||||||
schema,
|
schema,
|
||||||
required: true,
|
required: true,
|
||||||
|
label_required: true,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,8 +118,8 @@ impl crate::docs::StdLibFn for SomeFn {
|
|||||||
code_blocks
|
code_blocks
|
||||||
.iter()
|
.iter()
|
||||||
.map(|cb| {
|
.map(|cb| {
|
||||||
let program = crate::Program::parse(cb).unwrap();
|
let program = crate::Program::parse_no_errs(cb).unwrap();
|
||||||
let mut options: crate::ast::types::FormatOptions = Default::default();
|
let mut options: crate::parsing::ast::types::FormatOptions = Default::default();
|
||||||
options.insert_final_newline = false;
|
options.insert_final_newline = false;
|
||||||
program.ast.recast(&options, 0)
|
program.ast.recast(&options, 0)
|
||||||
})
|
})
|
||||||
|
@ -3,7 +3,7 @@ mod test_examples_show {
|
|||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
async fn test_mock_example_show0() {
|
async fn test_mock_example_show0() {
|
||||||
let program =
|
let program =
|
||||||
crate::Program::parse("This is another code block.\nyes sirrr.\nshow").unwrap();
|
crate::Program::parse_no_errs("This is another code block.\nyes sirrr.\nshow").unwrap();
|
||||||
let ctx = crate::executor::ExecutorContext {
|
let ctx = crate::executor::ExecutorContext {
|
||||||
engine: std::sync::Arc::new(Box::new(
|
engine: std::sync::Arc::new(Box::new(
|
||||||
crate::engine::conn_mock::EngineConnection::new()
|
crate::engine::conn_mock::EngineConnection::new()
|
||||||
@ -15,7 +15,7 @@ mod test_examples_show {
|
|||||||
settings: Default::default(),
|
settings: Default::default(),
|
||||||
context_type: crate::executor::ContextType::Mock,
|
context_type: crate::executor::ContextType::Mock,
|
||||||
};
|
};
|
||||||
ctx.run(&program, &mut crate::ExecState::default())
|
ctx.run(program.into(), &mut crate::ExecState::default())
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
@ -36,7 +36,8 @@ mod test_examples_show {
|
|||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
async fn test_mock_example_show1() {
|
async fn test_mock_example_show1() {
|
||||||
let program = crate::Program::parse("This is code.\nIt does other shit.\nshow").unwrap();
|
let program =
|
||||||
|
crate::Program::parse_no_errs("This is code.\nIt does other shit.\nshow").unwrap();
|
||||||
let ctx = crate::executor::ExecutorContext {
|
let ctx = crate::executor::ExecutorContext {
|
||||||
engine: std::sync::Arc::new(Box::new(
|
engine: std::sync::Arc::new(Box::new(
|
||||||
crate::engine::conn_mock::EngineConnection::new()
|
crate::engine::conn_mock::EngineConnection::new()
|
||||||
@ -48,7 +49,7 @@ mod test_examples_show {
|
|||||||
settings: Default::default(),
|
settings: Default::default(),
|
||||||
context_type: crate::executor::ContextType::Mock,
|
context_type: crate::executor::ContextType::Mock,
|
||||||
};
|
};
|
||||||
ctx.run(&program, &mut crate::ExecState::default())
|
ctx.run(program.into(), &mut crate::ExecState::default())
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
@ -108,6 +109,10 @@ 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;
|
||||||
@ -117,6 +122,7 @@ impl crate::docs::StdLibFn for Show {
|
|||||||
type_: "[number]".to_string(),
|
type_: "[number]".to_string(),
|
||||||
schema: generator.root_schema_for::<[f64; 2usize]>(),
|
schema: generator.root_schema_for::<[f64; 2usize]>(),
|
||||||
required: true,
|
required: true,
|
||||||
|
label_required: true,
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -130,6 +136,7 @@ impl crate::docs::StdLibFn for Show {
|
|||||||
type_: "number".to_string(),
|
type_: "number".to_string(),
|
||||||
schema,
|
schema,
|
||||||
required: true,
|
required: true,
|
||||||
|
label_required: true,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -149,8 +156,8 @@ impl crate::docs::StdLibFn for Show {
|
|||||||
code_blocks
|
code_blocks
|
||||||
.iter()
|
.iter()
|
||||||
.map(|cb| {
|
.map(|cb| {
|
||||||
let program = crate::Program::parse(cb).unwrap();
|
let program = crate::Program::parse_no_errs(cb).unwrap();
|
||||||
let mut options: crate::ast::types::FormatOptions = Default::default();
|
let mut options: crate::parsing::ast::types::FormatOptions = Default::default();
|
||||||
options.insert_final_newline = false;
|
options.insert_final_newline = false;
|
||||||
program.ast.recast(&options, 0)
|
program.ast.recast(&options, 0)
|
||||||
})
|
})
|
||||||
|