Merge branch 'main' into pierremtb/adhoc/pnpm
This commit is contained in:
		@ -3,6 +3,7 @@ import { HomePageFixture } from './fixtures/homePageFixture'
 | 
				
			|||||||
import { getUtils } from './test-utils'
 | 
					import { getUtils } from './test-utils'
 | 
				
			||||||
import { EngineCommand } from 'lang/std/artifactGraph'
 | 
					import { EngineCommand } from 'lang/std/artifactGraph'
 | 
				
			||||||
import { uuidv4 } from 'lib/utils'
 | 
					import { uuidv4 } from 'lib/utils'
 | 
				
			||||||
 | 
					import { SceneFixture } from './fixtures/sceneFixture'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
test.describe(
 | 
					test.describe(
 | 
				
			||||||
  'Can create sketches on all planes and their back sides',
 | 
					  'Can create sketches on all planes and their back sides',
 | 
				
			||||||
@ -11,16 +12,17 @@ test.describe(
 | 
				
			|||||||
    const sketchOnPlaneAndBackSideTest = async (
 | 
					    const sketchOnPlaneAndBackSideTest = async (
 | 
				
			||||||
      page: Page,
 | 
					      page: Page,
 | 
				
			||||||
      homePage: HomePageFixture,
 | 
					      homePage: HomePageFixture,
 | 
				
			||||||
 | 
					      scene: SceneFixture,
 | 
				
			||||||
      plane: string,
 | 
					      plane: string,
 | 
				
			||||||
      clickCoords: { x: number; y: number }
 | 
					      clickCoords: { x: number; y: number }
 | 
				
			||||||
    ) => {
 | 
					    ) => {
 | 
				
			||||||
      const u = await getUtils(page)
 | 
					      const u = await getUtils(page)
 | 
				
			||||||
      const PUR = 400 / 37.5 //pixeltoUnitRatio
 | 
					 | 
				
			||||||
      await page.setBodyDimensions({ width: 1200, height: 500 })
 | 
					      await page.setBodyDimensions({ width: 1200, height: 500 })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      await homePage.goToModelingScene()
 | 
					      await homePage.goToModelingScene()
 | 
				
			||||||
      // FIXME: Cannot use scene.waitForExecutionDone() since there is no KCL code
 | 
					      const XYPlanRed: [number, number, number] = [98, 50, 51]
 | 
				
			||||||
      await page.waitForTimeout(10000)
 | 
					      await scene.expectPixelColor(XYPlanRed, { x: 700, y: 300 }, 15)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      await u.openDebugPanel()
 | 
					      await u.openDebugPanel()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const coord =
 | 
					      const coord =
 | 
				
			||||||
@ -43,7 +45,7 @@ test.describe(
 | 
				
			|||||||
        },
 | 
					        },
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const code = `sketch001 = startSketchOn('${plane}')profile001 = startProfileAt([0.9, -1.22], sketch001)`
 | 
					      const code = `sketch001 = startSketchOn('${plane}')profile001 = startProfileAt([0.91, -1.22], sketch001)`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      await u.openDebugPanel()
 | 
					      await u.openDebugPanel()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -56,17 +58,14 @@ test.describe(
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
      await u.closeDebugPanel()
 | 
					      await u.closeDebugPanel()
 | 
				
			||||||
      await page.mouse.click(clickCoords.x, clickCoords.y)
 | 
					      await page.mouse.click(clickCoords.x, clickCoords.y)
 | 
				
			||||||
      await page.waitForTimeout(300) // wait for animation
 | 
					      await page.waitForTimeout(600) // wait for animation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      await expect(
 | 
					      await expect(
 | 
				
			||||||
        page.getByRole('button', { name: 'line Line', exact: true })
 | 
					        page.getByRole('button', { name: 'line Line', exact: true })
 | 
				
			||||||
      ).toBeVisible()
 | 
					      ).toBeVisible()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      // draw a line
 | 
					 | 
				
			||||||
      const startXPx = 600
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      await u.closeDebugPanel()
 | 
					      await u.closeDebugPanel()
 | 
				
			||||||
      await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
 | 
					      await page.mouse.click(707, 393)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      await expect(page.locator('.cm-content')).toHaveText(code)
 | 
					      await expect(page.locator('.cm-content')).toHaveText(code)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -81,49 +80,50 @@ test.describe(
 | 
				
			|||||||
      await u.clearCommandLogs()
 | 
					      await u.clearCommandLogs()
 | 
				
			||||||
      await u.removeCurrentCode()
 | 
					      await u.removeCurrentCode()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    test('XY', async ({ page, homePage }) => {
 | 
					 | 
				
			||||||
      await sketchOnPlaneAndBackSideTest(
 | 
					 | 
				
			||||||
        page,
 | 
					 | 
				
			||||||
        homePage,
 | 
					 | 
				
			||||||
        'XY',
 | 
					 | 
				
			||||||
        { x: 600, y: 388 } // red plane
 | 
					 | 
				
			||||||
        // { x: 600, y: 400 }, // red plane // clicks grid helper and that causes problems, should fix so that these coords work too.
 | 
					 | 
				
			||||||
      )
 | 
					 | 
				
			||||||
    })
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    test('YZ', async ({ page, homePage }) => {
 | 
					    const planeConfigs = [
 | 
				
			||||||
      await sketchOnPlaneAndBackSideTest(page, homePage, 'YZ', {
 | 
					      {
 | 
				
			||||||
        x: 700,
 | 
					        plane: 'XY',
 | 
				
			||||||
        y: 250,
 | 
					        coords: { x: 600, y: 388 },
 | 
				
			||||||
      }) // green plane
 | 
					        description: 'red plane',
 | 
				
			||||||
    })
 | 
					      },
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        plane: 'YZ',
 | 
				
			||||||
 | 
					        coords: { x: 700, y: 250 },
 | 
				
			||||||
 | 
					        description: 'green plane',
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        plane: 'XZ',
 | 
				
			||||||
 | 
					        coords: { x: 684, y: 427 },
 | 
				
			||||||
 | 
					        description: 'blue plane',
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        plane: '-XY',
 | 
				
			||||||
 | 
					        coords: { x: 600, y: 118 },
 | 
				
			||||||
 | 
					        description: 'back of red plane',
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        plane: '-YZ',
 | 
				
			||||||
 | 
					        coords: { x: 700, y: 219 },
 | 
				
			||||||
 | 
					        description: 'back of green plane',
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        plane: '-XZ',
 | 
				
			||||||
 | 
					        coords: { x: 700, y: 80 },
 | 
				
			||||||
 | 
					        description: 'back of blue plane',
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    test('XZ', async ({ page, homePage }) => {
 | 
					    for (const config of planeConfigs) {
 | 
				
			||||||
      await sketchOnPlaneAndBackSideTest(page, homePage, '-XZ', {
 | 
					      test(config.plane, async ({ page, homePage, scene }) => {
 | 
				
			||||||
        x: 700,
 | 
					        await sketchOnPlaneAndBackSideTest(
 | 
				
			||||||
        y: 80,
 | 
					          page,
 | 
				
			||||||
      }) // blue plane
 | 
					          homePage,
 | 
				
			||||||
    })
 | 
					          scene,
 | 
				
			||||||
 | 
					          config.plane,
 | 
				
			||||||
    test('-XY', async ({ page, homePage }) => {
 | 
					          config.coords
 | 
				
			||||||
      await sketchOnPlaneAndBackSideTest(page, homePage, '-XY', {
 | 
					        )
 | 
				
			||||||
        x: 600,
 | 
					      })
 | 
				
			||||||
        y: 118,
 | 
					    }
 | 
				
			||||||
      }) // back of red plane
 | 
					 | 
				
			||||||
    })
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    test('-YZ', async ({ page, homePage }) => {
 | 
					 | 
				
			||||||
      await sketchOnPlaneAndBackSideTest(page, homePage, '-YZ', {
 | 
					 | 
				
			||||||
        x: 700,
 | 
					 | 
				
			||||||
        y: 219,
 | 
					 | 
				
			||||||
      }) // back of green plan
 | 
					 | 
				
			||||||
    })
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    test('-XZ', async ({ page, homePage }) => {
 | 
					 | 
				
			||||||
      await sketchOnPlaneAndBackSideTest(page, homePage, 'XZ', {
 | 
					 | 
				
			||||||
        x: 700,
 | 
					 | 
				
			||||||
        y: 427,
 | 
					 | 
				
			||||||
      }) // back of blue plane
 | 
					 | 
				
			||||||
    })
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
				
			|||||||
@ -629,6 +629,39 @@ extrude001 = extrude(sketch001, length = 50)
 | 
				
			|||||||
      )
 | 
					      )
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  test(`Toolbar doesn't show modeling tools during sketch plane selection animation`, async ({
 | 
				
			||||||
 | 
					    page,
 | 
				
			||||||
 | 
					    homePage,
 | 
				
			||||||
 | 
					    toolbar,
 | 
				
			||||||
 | 
					  }) => {
 | 
				
			||||||
 | 
					    await test.step('Load an empty file', async () => {
 | 
				
			||||||
 | 
					      await page.addInitScript(async () => {
 | 
				
			||||||
 | 
					        localStorage.setItem('persistCode', '')
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					      await page.setBodyDimensions({ width: 1200, height: 500 })
 | 
				
			||||||
 | 
					      await homePage.goToModelingScene()
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const toolBarMode = () =>
 | 
				
			||||||
 | 
					      page.locator('[data-currentMode]').getAttribute('data-currentMode')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    await test.step('Start sketch and select a plane', async () => {
 | 
				
			||||||
 | 
					      await expect.poll(toolBarMode).toEqual('modeling')
 | 
				
			||||||
 | 
					      // Click the start sketch button
 | 
				
			||||||
 | 
					      await toolbar.startSketchPlaneSelection()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // Click on a default plane at position [700, 200]
 | 
				
			||||||
 | 
					      await page.mouse.click(700, 200)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // Check that the modeling toolbar doesn't appear during the animation
 | 
				
			||||||
 | 
					      // The animation typically takes around 500ms, so we'll check for a second
 | 
				
			||||||
 | 
					      await expect.poll(toolBarMode, { timeout: 1000 }).not.toEqual('modeling')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // After animation completes, we should see the sketching toolbar
 | 
				
			||||||
 | 
					      await expect.poll(toolBarMode).toEqual('sketching')
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async function clickExportButton(page: Page) {
 | 
					async function clickExportButton(page: Page) {
 | 
				
			||||||
 | 
				
			|||||||
@ -29,5 +29,5 @@
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  ],
 | 
					  ],
 | 
				
			||||||
  "kcl_version": "0.2.41"
 | 
					  "kcl_version": "0.2.44"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -167,7 +167,10 @@ export function Toolbar({
 | 
				
			|||||||
  }, [currentMode, disableAllButtons, configCallbackProps])
 | 
					  }, [currentMode, disableAllButtons, configCallbackProps])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <menu className="max-w-full whitespace-nowrap rounded-b px-2 py-1 bg-chalkboard-10 dark:bg-chalkboard-90 relative border border-chalkboard-30 dark:border-chalkboard-80 border-t-0 shadow-sm">
 | 
					    <menu
 | 
				
			||||||
 | 
					      data-currentMode={currentMode}
 | 
				
			||||||
 | 
					      className="max-w-full whitespace-nowrap rounded-b px-2 py-1 bg-chalkboard-10 dark:bg-chalkboard-90 relative border border-chalkboard-30 dark:border-chalkboard-80 border-t-0 shadow-sm"
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
      <ul
 | 
					      <ul
 | 
				
			||||||
        {...props}
 | 
					        {...props}
 | 
				
			||||||
        ref={toolbarButtonsRef}
 | 
					        ref={toolbarButtonsRef}
 | 
				
			||||||
 | 
				
			|||||||
@ -72,6 +72,7 @@ import {
 | 
				
			|||||||
  addCallExpressionsToPipe,
 | 
					  addCallExpressionsToPipe,
 | 
				
			||||||
  addCloseToPipe,
 | 
					  addCloseToPipe,
 | 
				
			||||||
  addNewSketchLn,
 | 
					  addNewSketchLn,
 | 
				
			||||||
 | 
					  ARG_END_ABSOLUTE,
 | 
				
			||||||
  changeSketchArguments,
 | 
					  changeSketchArguments,
 | 
				
			||||||
  updateStartProfileAtArgs,
 | 
					  updateStartProfileAtArgs,
 | 
				
			||||||
} from 'lang/std/sketch'
 | 
					} from 'lang/std/sketch'
 | 
				
			||||||
@ -904,7 +905,7 @@ export class SceneEntities {
 | 
				
			|||||||
                    createPipeSubstitution(),
 | 
					                    createPipeSubstitution(),
 | 
				
			||||||
                  ])
 | 
					                  ])
 | 
				
			||||||
                : createCallExpressionStdLibKw('line', null, [
 | 
					                : createCallExpressionStdLibKw('line', null, [
 | 
				
			||||||
                    createLabeledArg('endAbsolute', originCoords),
 | 
					                    createLabeledArg(ARG_END_ABSOLUTE, originCoords),
 | 
				
			||||||
                  ]),
 | 
					                  ]),
 | 
				
			||||||
            ],
 | 
					            ],
 | 
				
			||||||
          })
 | 
					          })
 | 
				
			||||||
 | 
				
			|||||||
@ -159,7 +159,7 @@ export class KCLTypeError extends KCLError {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class KCLUnimplementedError extends KCLError {
 | 
					export class KCLIoError extends KCLError {
 | 
				
			||||||
  constructor(
 | 
					  constructor(
 | 
				
			||||||
    msg: string,
 | 
					    msg: string,
 | 
				
			||||||
    sourceRange: SourceRange,
 | 
					    sourceRange: SourceRange,
 | 
				
			||||||
@ -169,7 +169,7 @@ export class KCLUnimplementedError extends KCLError {
 | 
				
			|||||||
    filenames: { [x: number]: ModulePath | undefined }
 | 
					    filenames: { [x: number]: ModulePath | undefined }
 | 
				
			||||||
  ) {
 | 
					  ) {
 | 
				
			||||||
    super(
 | 
					    super(
 | 
				
			||||||
      'unimplemented',
 | 
					      'io',
 | 
				
			||||||
      msg,
 | 
					      msg,
 | 
				
			||||||
      sourceRange,
 | 
					      sourceRange,
 | 
				
			||||||
      operations,
 | 
					      operations,
 | 
				
			||||||
@ -177,7 +177,7 @@ export class KCLUnimplementedError extends KCLError {
 | 
				
			|||||||
      artifactGraph,
 | 
					      artifactGraph,
 | 
				
			||||||
      filenames
 | 
					      filenames
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
    Object.setPrototypeOf(this, KCLUnimplementedError.prototype)
 | 
					    Object.setPrototypeOf(this, KCLIoError.prototype)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -629,8 +629,8 @@ export const lineTo: SketchLineHelperKw = {
 | 
				
			|||||||
    const { node: callExpression } = nodeMeta
 | 
					    const { node: callExpression } = nodeMeta
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const toArrExp = createArrayExpression([
 | 
					    const toArrExp = createArrayExpression([
 | 
				
			||||||
      createLiteral(roundOff(to[0] - from[0], 2)),
 | 
					      createLiteral(roundOff(to[0], 2)),
 | 
				
			||||||
      createLiteral(roundOff(to[1] - from[1], 2)),
 | 
					      createLiteral(roundOff(to[1], 2)),
 | 
				
			||||||
    ])
 | 
					    ])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    mutateKwArg(ARG_END_ABSOLUTE, callExpression, toArrExp)
 | 
					    mutateKwArg(ARG_END_ABSOLUTE, callExpression, toArrExp)
 | 
				
			||||||
@ -2348,7 +2348,7 @@ export function changeSketchArguments(
 | 
				
			|||||||
  if (fnName in sketchLineHelperMapKw) {
 | 
					  if (fnName in sketchLineHelperMapKw) {
 | 
				
			||||||
    const isAbsolute =
 | 
					    const isAbsolute =
 | 
				
			||||||
      callExpression.type === 'CallExpressionKw' &&
 | 
					      callExpression.type === 'CallExpressionKw' &&
 | 
				
			||||||
      findKwArg('endAbsolute', callExpression) !== undefined
 | 
					      findKwArg(ARG_END_ABSOLUTE, callExpression) !== undefined
 | 
				
			||||||
    const correctFnName = fnName === 'line' && isAbsolute ? 'lineTo' : fnName
 | 
					    const correctFnName = fnName === 'line' && isAbsolute ? 'lineTo' : fnName
 | 
				
			||||||
    const { updateArgs } = sketchLineHelperMapKw[correctFnName]
 | 
					    const { updateArgs } = sketchLineHelperMapKw[correctFnName]
 | 
				
			||||||
    if (!updateArgs) {
 | 
					    if (!updateArgs) {
 | 
				
			||||||
@ -2391,7 +2391,7 @@ export function getConstraintInfoKw(
 | 
				
			|||||||
  const fnName = callExpression?.callee?.name || ''
 | 
					  const fnName = callExpression?.callee?.name || ''
 | 
				
			||||||
  const isAbsolute =
 | 
					  const isAbsolute =
 | 
				
			||||||
    fnName === 'circleThreePoint' ||
 | 
					    fnName === 'circleThreePoint' ||
 | 
				
			||||||
    findKwArg('endAbsolute', callExpression) !== undefined
 | 
					    findKwArg(ARG_END_ABSOLUTE, callExpression) !== undefined
 | 
				
			||||||
  if (!(fnName in sketchLineHelperMapKw)) return []
 | 
					  if (!(fnName in sketchLineHelperMapKw)) return []
 | 
				
			||||||
  const correctFnName = fnName === 'line' && isAbsolute ? 'lineTo' : fnName
 | 
					  const correctFnName = fnName === 'line' && isAbsolute ? 'lineTo' : fnName
 | 
				
			||||||
  return sketchLineHelperMapKw[correctFnName].getConstraintInfo(
 | 
					  return sketchLineHelperMapKw[correctFnName].getConstraintInfo(
 | 
				
			||||||
 | 
				
			|||||||
@ -138,7 +138,7 @@ function createCallWrapper(
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
    if (tooltip === 'lineTo') {
 | 
					    if (tooltip === 'lineTo') {
 | 
				
			||||||
      const labeledArgs = [
 | 
					      const labeledArgs = [
 | 
				
			||||||
        createLabeledArg('endAbsolute', createArrayExpression(val)),
 | 
					        createLabeledArg(ARG_END_ABSOLUTE, createArrayExpression(val)),
 | 
				
			||||||
      ]
 | 
					      ]
 | 
				
			||||||
      if (tag) {
 | 
					      if (tag) {
 | 
				
			||||||
        labeledArgs.push(createLabeledArg(ARG_TAG, tag))
 | 
					        labeledArgs.push(createLabeledArg(ARG_TAG, tag))
 | 
				
			||||||
 | 
				
			|||||||
@ -12,7 +12,6 @@ import {
 | 
				
			|||||||
  get_kcl_version,
 | 
					  get_kcl_version,
 | 
				
			||||||
  make_default_planes,
 | 
					  make_default_planes,
 | 
				
			||||||
  coredump,
 | 
					  coredump,
 | 
				
			||||||
  toml_stringify,
 | 
					 | 
				
			||||||
  default_app_settings,
 | 
					  default_app_settings,
 | 
				
			||||||
  parse_app_settings,
 | 
					  parse_app_settings,
 | 
				
			||||||
  parse_project_settings,
 | 
					  parse_project_settings,
 | 
				
			||||||
@ -21,6 +20,8 @@ import {
 | 
				
			|||||||
  clear_scene_and_bust_cache,
 | 
					  clear_scene_and_bust_cache,
 | 
				
			||||||
  kcl_settings,
 | 
					  kcl_settings,
 | 
				
			||||||
  change_kcl_settings,
 | 
					  change_kcl_settings,
 | 
				
			||||||
 | 
					  serialize_project_configuration,
 | 
				
			||||||
 | 
					  serialize_configuration,
 | 
				
			||||||
  reloadModule,
 | 
					  reloadModule,
 | 
				
			||||||
} from 'lib/wasm_lib_wrapper'
 | 
					} from 'lib/wasm_lib_wrapper'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -636,10 +637,6 @@ export async function coreDump(
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function tomlStringify(toml: any): string | Error {
 | 
					 | 
				
			||||||
  return toml_stringify(JSON.stringify(toml))
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export function defaultAppSettings(): DeepPartial<Configuration> | Error {
 | 
					export function defaultAppSettings(): DeepPartial<Configuration> | Error {
 | 
				
			||||||
  return default_app_settings()
 | 
					  return default_app_settings()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -786,3 +783,27 @@ export function unitAngToUnitAngle(input: UnitAng): UnitAngle {
 | 
				
			|||||||
export function getKclVersion(): string {
 | 
					export function getKclVersion(): string {
 | 
				
			||||||
  return get_kcl_version()
 | 
					  return get_kcl_version()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Serialize a project configuration to a TOML string.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export function serializeConfiguration(configuration: any): string | Error {
 | 
				
			||||||
 | 
					  try {
 | 
				
			||||||
 | 
					    return serialize_configuration(configuration)
 | 
				
			||||||
 | 
					  } catch (e: any) {
 | 
				
			||||||
 | 
					    return new Error(`Error serializing configuration: ${e}`)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Serialize a project configuration to a TOML string.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export function serializeProjectConfiguration(
 | 
				
			||||||
 | 
					  configuration: any
 | 
				
			||||||
 | 
					): string | Error {
 | 
				
			||||||
 | 
					  try {
 | 
				
			||||||
 | 
					    return serialize_project_configuration(configuration)
 | 
				
			||||||
 | 
					  } catch (e: any) {
 | 
				
			||||||
 | 
					    return new Error(`Error serializing project configuration: ${e}`)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -24,6 +24,7 @@ import {
 | 
				
			|||||||
  isBinaryExpression,
 | 
					  isBinaryExpression,
 | 
				
			||||||
  isLiteralValueNumber,
 | 
					  isLiteralValueNumber,
 | 
				
			||||||
} from 'lang/util'
 | 
					} from 'lang/util'
 | 
				
			||||||
 | 
					import { ARG_END_ABSOLUTE } from 'lang/std/sketch'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * It does not create the startSketchOn and it does not create the startProfileAt.
 | 
					 * It does not create the startSketchOn and it does not create the startProfileAt.
 | 
				
			||||||
@ -70,7 +71,7 @@ export const getRectangleCallExpressions = (
 | 
				
			|||||||
  ]),
 | 
					  ]),
 | 
				
			||||||
  createCallExpressionStdLibKw('line', null, [
 | 
					  createCallExpressionStdLibKw('line', null, [
 | 
				
			||||||
    createLabeledArg(
 | 
					    createLabeledArg(
 | 
				
			||||||
      'endAbsolute',
 | 
					      ARG_END_ABSOLUTE,
 | 
				
			||||||
      createArrayExpression([
 | 
					      createArrayExpression([
 | 
				
			||||||
        createCallExpressionStdLib('profileStartX', [createPipeSubstitution()]),
 | 
					        createCallExpressionStdLib('profileStartX', [createPipeSubstitution()]),
 | 
				
			||||||
        createCallExpressionStdLib('profileStartY', [createPipeSubstitution()]),
 | 
					        createCallExpressionStdLib('profileStartY', [createPipeSubstitution()]),
 | 
				
			||||||
 | 
				
			|||||||
@ -4,7 +4,8 @@ import {
 | 
				
			|||||||
  initPromise,
 | 
					  initPromise,
 | 
				
			||||||
  parseAppSettings,
 | 
					  parseAppSettings,
 | 
				
			||||||
  parseProjectSettings,
 | 
					  parseProjectSettings,
 | 
				
			||||||
  tomlStringify,
 | 
					  serializeConfiguration,
 | 
				
			||||||
 | 
					  serializeProjectConfiguration,
 | 
				
			||||||
} from 'lang/wasm'
 | 
					} from 'lang/wasm'
 | 
				
			||||||
import { mouseControlsToCameraSystem } from 'lib/cameraControls'
 | 
					import { mouseControlsToCameraSystem } from 'lib/cameraControls'
 | 
				
			||||||
import { BROWSER_PROJECT_NAME } from 'lib/constants'
 | 
					import { BROWSER_PROJECT_NAME } from 'lib/constants'
 | 
				
			||||||
@ -131,7 +132,7 @@ export function readLocalStorageAppSettingsFile():
 | 
				
			|||||||
  } catch (e) {
 | 
					  } catch (e) {
 | 
				
			||||||
    const settings = defaultAppSettings()
 | 
					    const settings = defaultAppSettings()
 | 
				
			||||||
    if (err(settings)) return settings
 | 
					    if (err(settings)) return settings
 | 
				
			||||||
    const tomlStr = tomlStringify(settings)
 | 
					    const tomlStr = serializeConfiguration(settings)
 | 
				
			||||||
    if (err(tomlStr)) return tomlStr
 | 
					    if (err(tomlStr)) return tomlStr
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    localStorage.setItem(localStorageAppSettingsPath(), tomlStr)
 | 
					    localStorage.setItem(localStorageAppSettingsPath(), tomlStr)
 | 
				
			||||||
@ -152,7 +153,7 @@ function readLocalStorageProjectSettingsFile():
 | 
				
			|||||||
  const projectSettings = parseProjectSettings(stored)
 | 
					  const projectSettings = parseProjectSettings(stored)
 | 
				
			||||||
  if (err(projectSettings)) {
 | 
					  if (err(projectSettings)) {
 | 
				
			||||||
    const settings = defaultProjectSettings()
 | 
					    const settings = defaultProjectSettings()
 | 
				
			||||||
    const tomlStr = tomlStringify(settings)
 | 
					    const tomlStr = serializeProjectConfiguration(settings)
 | 
				
			||||||
    if (err(tomlStr)) return tomlStr
 | 
					    if (err(tomlStr)) return tomlStr
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    localStorage.setItem(localStorageProjectSettingsPath(), tomlStr)
 | 
					    localStorage.setItem(localStorageProjectSettingsPath(), tomlStr)
 | 
				
			||||||
@ -229,7 +230,7 @@ export async function saveSettings(
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  // Get the user settings.
 | 
					  // Get the user settings.
 | 
				
			||||||
  const jsAppSettings = getChangedSettingsAtLevel(allSettings, 'user')
 | 
					  const jsAppSettings = getChangedSettingsAtLevel(allSettings, 'user')
 | 
				
			||||||
  const appTomlString = tomlStringify({ settings: jsAppSettings })
 | 
					  const appTomlString = serializeConfiguration({ settings: jsAppSettings })
 | 
				
			||||||
  if (err(appTomlString)) return
 | 
					  if (err(appTomlString)) return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Write the app settings.
 | 
					  // Write the app settings.
 | 
				
			||||||
@ -246,7 +247,9 @@ export async function saveSettings(
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  // Get the project settings.
 | 
					  // Get the project settings.
 | 
				
			||||||
  const jsProjectSettings = getChangedSettingsAtLevel(allSettings, 'project')
 | 
					  const jsProjectSettings = getChangedSettingsAtLevel(allSettings, 'project')
 | 
				
			||||||
  const projectTomlString = tomlStringify({ settings: jsProjectSettings })
 | 
					  const projectTomlString = serializeProjectConfiguration({
 | 
				
			||||||
 | 
					    settings: jsProjectSettings,
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
  if (err(projectTomlString)) return
 | 
					  if (err(projectTomlString)) return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Write the project settings.
 | 
					  // Write the project settings.
 | 
				
			||||||
 | 
				
			|||||||
@ -56,7 +56,12 @@ export type ToolbarItemResolved = Omit<
 | 
				
			|||||||
export const toolbarConfig: Record<ToolbarModeName, ToolbarMode> = {
 | 
					export const toolbarConfig: Record<ToolbarModeName, ToolbarMode> = {
 | 
				
			||||||
  modeling: {
 | 
					  modeling: {
 | 
				
			||||||
    check: (state) =>
 | 
					    check: (state) =>
 | 
				
			||||||
      !(state.matches('Sketch') || state.matches('Sketch no face')),
 | 
					      !(
 | 
				
			||||||
 | 
					        state.matches('Sketch') ||
 | 
				
			||||||
 | 
					        state.matches('Sketch no face') ||
 | 
				
			||||||
 | 
					        state.matches('animating to existing sketch') ||
 | 
				
			||||||
 | 
					        state.matches('animating to plane')
 | 
				
			||||||
 | 
					      ),
 | 
				
			||||||
    items: [
 | 
					    items: [
 | 
				
			||||||
      {
 | 
					      {
 | 
				
			||||||
        id: 'sketch',
 | 
					        id: 'sketch',
 | 
				
			||||||
@ -330,7 +335,10 @@ export const toolbarConfig: Record<ToolbarModeName, ToolbarMode> = {
 | 
				
			|||||||
  },
 | 
					  },
 | 
				
			||||||
  sketching: {
 | 
					  sketching: {
 | 
				
			||||||
    check: (state) =>
 | 
					    check: (state) =>
 | 
				
			||||||
      state.matches('Sketch') || state.matches('Sketch no face'),
 | 
					      state.matches('Sketch') ||
 | 
				
			||||||
 | 
					      state.matches('Sketch no face') ||
 | 
				
			||||||
 | 
					      state.matches('animating to existing sketch') ||
 | 
				
			||||||
 | 
					      state.matches('animating to plane'),
 | 
				
			||||||
    items: [
 | 
					    items: [
 | 
				
			||||||
      {
 | 
					      {
 | 
				
			||||||
        id: 'sketch-exit',
 | 
					        id: 'sketch-exit',
 | 
				
			||||||
 | 
				
			|||||||
@ -19,7 +19,6 @@ import {
 | 
				
			|||||||
  get_tangential_arc_to_info as GetTangentialArcToInfo,
 | 
					  get_tangential_arc_to_info as GetTangentialArcToInfo,
 | 
				
			||||||
  make_default_planes as MakeDefaultPlanes,
 | 
					  make_default_planes as MakeDefaultPlanes,
 | 
				
			||||||
  coredump as CoreDump,
 | 
					  coredump as CoreDump,
 | 
				
			||||||
  toml_stringify as TomlStringify,
 | 
					 | 
				
			||||||
  default_app_settings as DefaultAppSettings,
 | 
					  default_app_settings as DefaultAppSettings,
 | 
				
			||||||
  parse_app_settings as ParseAppSettings,
 | 
					  parse_app_settings as ParseAppSettings,
 | 
				
			||||||
  parse_project_settings as ParseProjectSettings,
 | 
					  parse_project_settings as ParseProjectSettings,
 | 
				
			||||||
@ -29,6 +28,8 @@ import {
 | 
				
			|||||||
  kcl_settings as KclSettings,
 | 
					  kcl_settings as KclSettings,
 | 
				
			||||||
  change_kcl_settings as ChangeKclSettings,
 | 
					  change_kcl_settings as ChangeKclSettings,
 | 
				
			||||||
  get_kcl_version as GetKclVersion,
 | 
					  get_kcl_version as GetKclVersion,
 | 
				
			||||||
 | 
					  serialize_configuration as SerializeConfiguration,
 | 
				
			||||||
 | 
					  serialize_project_configuration as SerializeProjectConfiguration,
 | 
				
			||||||
} from '../wasm-lib/pkg/wasm_lib'
 | 
					} from '../wasm-lib/pkg/wasm_lib'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type ModuleType = typeof import('../wasm-lib/pkg/wasm_lib')
 | 
					type ModuleType = typeof import('../wasm-lib/pkg/wasm_lib')
 | 
				
			||||||
@ -86,9 +87,6 @@ export const make_default_planes: typeof MakeDefaultPlanes = (...args) => {
 | 
				
			|||||||
export const coredump: typeof CoreDump = (...args) => {
 | 
					export const coredump: typeof CoreDump = (...args) => {
 | 
				
			||||||
  return getModule().coredump(...args)
 | 
					  return getModule().coredump(...args)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
export const toml_stringify: typeof TomlStringify = (...args) => {
 | 
					 | 
				
			||||||
  return getModule().toml_stringify(...args)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
export const default_app_settings: typeof DefaultAppSettings = (...args) => {
 | 
					export const default_app_settings: typeof DefaultAppSettings = (...args) => {
 | 
				
			||||||
  return getModule().default_app_settings(...args)
 | 
					  return getModule().default_app_settings(...args)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -122,3 +120,12 @@ export const change_kcl_settings: typeof ChangeKclSettings = (...args) => {
 | 
				
			|||||||
export const get_kcl_version: typeof GetKclVersion = () => {
 | 
					export const get_kcl_version: typeof GetKclVersion = () => {
 | 
				
			||||||
  return getModule().get_kcl_version()
 | 
					  return getModule().get_kcl_version()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					export const serialize_configuration: typeof SerializeConfiguration = (
 | 
				
			||||||
 | 
					  ...args
 | 
				
			||||||
 | 
					) => {
 | 
				
			||||||
 | 
					  return getModule().serialize_configuration(...args)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					export const serialize_project_configuration: typeof SerializeProjectConfiguration =
 | 
				
			||||||
 | 
					  (...args) => {
 | 
				
			||||||
 | 
					    return getModule().serialize_project_configuration(...args)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										6
									
								
								src/wasm-lib/Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										6
									
								
								src/wasm-lib/Cargo.lock
									
									
									
										generated
									
									
									
								
							@ -730,7 +730,7 @@ dependencies = [
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "derive-docs"
 | 
					name = "derive-docs"
 | 
				
			||||||
version = "0.1.40"
 | 
					version = "0.1.44"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "Inflector",
 | 
					 "Inflector",
 | 
				
			||||||
 "anyhow",
 | 
					 "anyhow",
 | 
				
			||||||
@ -1724,7 +1724,7 @@ dependencies = [
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "kcl-lib"
 | 
					name = "kcl-lib"
 | 
				
			||||||
version = "0.2.41"
 | 
					version = "0.2.44"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "anyhow",
 | 
					 "anyhow",
 | 
				
			||||||
 "approx 0.5.1",
 | 
					 "approx 0.5.1",
 | 
				
			||||||
@ -1791,7 +1791,7 @@ dependencies = [
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "kcl-test-server"
 | 
					name = "kcl-test-server"
 | 
				
			||||||
version = "0.1.40"
 | 
					version = "0.1.44"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "anyhow",
 | 
					 "anyhow",
 | 
				
			||||||
 "hyper 0.14.32",
 | 
					 "hyper 0.14.32",
 | 
				
			||||||
 | 
				
			|||||||
@ -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.40"
 | 
					version = "0.1.44"
 | 
				
			||||||
edition = "2021"
 | 
					edition = "2021"
 | 
				
			||||||
license = "MIT"
 | 
					license = "MIT"
 | 
				
			||||||
repository = "https://github.com/KittyCAD/modeling-app"
 | 
					repository = "https://github.com/KittyCAD/modeling-app"
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,7 @@
 | 
				
			|||||||
[package]
 | 
					[package]
 | 
				
			||||||
name = "kcl-test-server"
 | 
					name = "kcl-test-server"
 | 
				
			||||||
description = "A test server for KCL"
 | 
					description = "A test server for KCL"
 | 
				
			||||||
version = "0.1.40"
 | 
					version = "0.1.44"
 | 
				
			||||||
edition = "2021"
 | 
					edition = "2021"
 | 
				
			||||||
license = "MIT"
 | 
					license = "MIT"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,7 @@
 | 
				
			|||||||
[package]
 | 
					[package]
 | 
				
			||||||
name = "kcl-lib"
 | 
					name = "kcl-lib"
 | 
				
			||||||
description = "KittyCAD Language implementation and tools"
 | 
					description = "KittyCAD Language implementation and tools"
 | 
				
			||||||
version = "0.2.41"
 | 
					version = "0.2.44"
 | 
				
			||||||
edition = "2021"
 | 
					edition = "2021"
 | 
				
			||||||
license = "MIT"
 | 
					license = "MIT"
 | 
				
			||||||
repository = "https://github.com/KittyCAD/modeling-app"
 | 
					repository = "https://github.com/KittyCAD/modeling-app"
 | 
				
			||||||
@ -22,7 +22,7 @@ clap = { version = "4.5.27", default-features = false, optional = true, features
 | 
				
			|||||||
] }
 | 
					] }
 | 
				
			||||||
convert_case = "0.6.0"
 | 
					convert_case = "0.6.0"
 | 
				
			||||||
dashmap = "6.1.0"
 | 
					dashmap = "6.1.0"
 | 
				
			||||||
derive-docs = { version = "0.1.40", path = "../derive-docs" }
 | 
					derive-docs = { version = "0.1.44", path = "../derive-docs" }
 | 
				
			||||||
dhat = { version = "0.3", optional = true }
 | 
					dhat = { version = "0.3", optional = true }
 | 
				
			||||||
fnv = "1.0.7"
 | 
					fnv = "1.0.7"
 | 
				
			||||||
form_urlencoded = "1.2.1"
 | 
					form_urlencoded = "1.2.1"
 | 
				
			||||||
 | 
				
			|||||||
@ -87,8 +87,8 @@ pub enum KclError {
 | 
				
			|||||||
    ImportCycle(KclErrorDetails),
 | 
					    ImportCycle(KclErrorDetails),
 | 
				
			||||||
    #[error("type: {0:?}")]
 | 
					    #[error("type: {0:?}")]
 | 
				
			||||||
    Type(KclErrorDetails),
 | 
					    Type(KclErrorDetails),
 | 
				
			||||||
    #[error("unimplemented: {0:?}")]
 | 
					    #[error("i/o: {0:?}")]
 | 
				
			||||||
    Unimplemented(KclErrorDetails),
 | 
					    Io(KclErrorDetails),
 | 
				
			||||||
    #[error("unexpected: {0:?}")]
 | 
					    #[error("unexpected: {0:?}")]
 | 
				
			||||||
    Unexpected(KclErrorDetails),
 | 
					    Unexpected(KclErrorDetails),
 | 
				
			||||||
    #[error("value already defined: {0:?}")]
 | 
					    #[error("value already defined: {0:?}")]
 | 
				
			||||||
@ -150,7 +150,7 @@ impl KclErrorWithOutputs {
 | 
				
			|||||||
            source_files: Default::default(),
 | 
					            source_files: Default::default(),
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    pub fn into_miette_report_with_outputs(self, code: &str) -> anyhow::Result<ReportWithOutputs> {
 | 
					    pub fn into_miette_report_with_outputs(self) -> anyhow::Result<ReportWithOutputs> {
 | 
				
			||||||
        let mut source_ranges = self.error.source_ranges();
 | 
					        let mut source_ranges = self.error.source_ranges();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Pop off the first source range to get the filename.
 | 
					        // Pop off the first source range to get the filename.
 | 
				
			||||||
@ -162,33 +162,23 @@ impl KclErrorWithOutputs {
 | 
				
			|||||||
            .source_files
 | 
					            .source_files
 | 
				
			||||||
            .get(&first_source_range.module_id())
 | 
					            .get(&first_source_range.module_id())
 | 
				
			||||||
            .cloned()
 | 
					            .cloned()
 | 
				
			||||||
            .unwrap_or(ModuleSource {
 | 
					            .ok_or_else(|| {
 | 
				
			||||||
                source: code.to_string(),
 | 
					                anyhow::anyhow!(
 | 
				
			||||||
                path: self
 | 
					                    "Could not find source file for module id: {:?}",
 | 
				
			||||||
                    .filenames
 | 
					                    first_source_range.module_id()
 | 
				
			||||||
                    .get(&first_source_range.module_id())
 | 
					                )
 | 
				
			||||||
                    .ok_or_else(|| {
 | 
					            })?;
 | 
				
			||||||
                        anyhow::anyhow!(
 | 
					 | 
				
			||||||
                            "Could not find filename for module id: {:?}",
 | 
					 | 
				
			||||||
                            first_source_range.module_id()
 | 
					 | 
				
			||||||
                        )
 | 
					 | 
				
			||||||
                    })?
 | 
					 | 
				
			||||||
                    .clone(),
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
        let filename = source.path.to_string();
 | 
					        let filename = source.path.to_string();
 | 
				
			||||||
        let kcl_source = source.source.to_string();
 | 
					        let kcl_source = source.source.to_string();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let mut related = Vec::new();
 | 
					        let mut related = Vec::new();
 | 
				
			||||||
        for source_range in source_ranges {
 | 
					        for source_range in source_ranges {
 | 
				
			||||||
            let module_id = source_range.module_id();
 | 
					            let module_id = source_range.module_id();
 | 
				
			||||||
            let source = self.source_files.get(&module_id).cloned().unwrap_or(ModuleSource {
 | 
					            let source = self
 | 
				
			||||||
                source: code.to_string(),
 | 
					                .source_files
 | 
				
			||||||
                path: self
 | 
					                .get(&module_id)
 | 
				
			||||||
                    .filenames
 | 
					                .cloned()
 | 
				
			||||||
                    .get(&module_id)
 | 
					                .ok_or_else(|| anyhow::anyhow!("Could not find source file for module id: {:?}", module_id))?;
 | 
				
			||||||
                    .ok_or_else(|| anyhow::anyhow!("Could not find filename for module id: {:?}", module_id))?
 | 
					 | 
				
			||||||
                    .clone(),
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
            let error = self.error.override_source_ranges(vec![source_range]);
 | 
					            let error = self.error.override_source_ranges(vec![source_range]);
 | 
				
			||||||
            let report = Report {
 | 
					            let report = Report {
 | 
				
			||||||
                error,
 | 
					                error,
 | 
				
			||||||
@ -278,7 +268,7 @@ impl miette::Diagnostic for ReportWithOutputs {
 | 
				
			|||||||
            KclError::Semantic(_) => "Semantic",
 | 
					            KclError::Semantic(_) => "Semantic",
 | 
				
			||||||
            KclError::ImportCycle(_) => "ImportCycle",
 | 
					            KclError::ImportCycle(_) => "ImportCycle",
 | 
				
			||||||
            KclError::Type(_) => "Type",
 | 
					            KclError::Type(_) => "Type",
 | 
				
			||||||
            KclError::Unimplemented(_) => "Unimplemented",
 | 
					            KclError::Io(_) => "I/O",
 | 
				
			||||||
            KclError::Unexpected(_) => "Unexpected",
 | 
					            KclError::Unexpected(_) => "Unexpected",
 | 
				
			||||||
            KclError::ValueAlreadyDefined(_) => "ValueAlreadyDefined",
 | 
					            KclError::ValueAlreadyDefined(_) => "ValueAlreadyDefined",
 | 
				
			||||||
            KclError::UndefinedValue(_) => "UndefinedValue",
 | 
					            KclError::UndefinedValue(_) => "UndefinedValue",
 | 
				
			||||||
@ -328,7 +318,7 @@ impl miette::Diagnostic for Report {
 | 
				
			|||||||
            KclError::Semantic(_) => "Semantic",
 | 
					            KclError::Semantic(_) => "Semantic",
 | 
				
			||||||
            KclError::ImportCycle(_) => "ImportCycle",
 | 
					            KclError::ImportCycle(_) => "ImportCycle",
 | 
				
			||||||
            KclError::Type(_) => "Type",
 | 
					            KclError::Type(_) => "Type",
 | 
				
			||||||
            KclError::Unimplemented(_) => "Unimplemented",
 | 
					            KclError::Io(_) => "I/O",
 | 
				
			||||||
            KclError::Unexpected(_) => "Unexpected",
 | 
					            KclError::Unexpected(_) => "Unexpected",
 | 
				
			||||||
            KclError::ValueAlreadyDefined(_) => "ValueAlreadyDefined",
 | 
					            KclError::ValueAlreadyDefined(_) => "ValueAlreadyDefined",
 | 
				
			||||||
            KclError::UndefinedValue(_) => "UndefinedValue",
 | 
					            KclError::UndefinedValue(_) => "UndefinedValue",
 | 
				
			||||||
@ -387,7 +377,7 @@ impl KclError {
 | 
				
			|||||||
            KclError::Semantic(_) => "semantic",
 | 
					            KclError::Semantic(_) => "semantic",
 | 
				
			||||||
            KclError::ImportCycle(_) => "import cycle",
 | 
					            KclError::ImportCycle(_) => "import cycle",
 | 
				
			||||||
            KclError::Type(_) => "type",
 | 
					            KclError::Type(_) => "type",
 | 
				
			||||||
            KclError::Unimplemented(_) => "unimplemented",
 | 
					            KclError::Io(_) => "i/o",
 | 
				
			||||||
            KclError::Unexpected(_) => "unexpected",
 | 
					            KclError::Unexpected(_) => "unexpected",
 | 
				
			||||||
            KclError::ValueAlreadyDefined(_) => "value already defined",
 | 
					            KclError::ValueAlreadyDefined(_) => "value already defined",
 | 
				
			||||||
            KclError::UndefinedValue(_) => "undefined value",
 | 
					            KclError::UndefinedValue(_) => "undefined value",
 | 
				
			||||||
@ -404,7 +394,7 @@ impl KclError {
 | 
				
			|||||||
            KclError::Semantic(e) => e.source_ranges.clone(),
 | 
					            KclError::Semantic(e) => e.source_ranges.clone(),
 | 
				
			||||||
            KclError::ImportCycle(e) => e.source_ranges.clone(),
 | 
					            KclError::ImportCycle(e) => e.source_ranges.clone(),
 | 
				
			||||||
            KclError::Type(e) => e.source_ranges.clone(),
 | 
					            KclError::Type(e) => e.source_ranges.clone(),
 | 
				
			||||||
            KclError::Unimplemented(e) => e.source_ranges.clone(),
 | 
					            KclError::Io(e) => e.source_ranges.clone(),
 | 
				
			||||||
            KclError::Unexpected(e) => e.source_ranges.clone(),
 | 
					            KclError::Unexpected(e) => e.source_ranges.clone(),
 | 
				
			||||||
            KclError::ValueAlreadyDefined(e) => e.source_ranges.clone(),
 | 
					            KclError::ValueAlreadyDefined(e) => e.source_ranges.clone(),
 | 
				
			||||||
            KclError::UndefinedValue(e) => e.source_ranges.clone(),
 | 
					            KclError::UndefinedValue(e) => e.source_ranges.clone(),
 | 
				
			||||||
@ -422,7 +412,7 @@ impl KclError {
 | 
				
			|||||||
            KclError::Semantic(e) => &e.message,
 | 
					            KclError::Semantic(e) => &e.message,
 | 
				
			||||||
            KclError::ImportCycle(e) => &e.message,
 | 
					            KclError::ImportCycle(e) => &e.message,
 | 
				
			||||||
            KclError::Type(e) => &e.message,
 | 
					            KclError::Type(e) => &e.message,
 | 
				
			||||||
            KclError::Unimplemented(e) => &e.message,
 | 
					            KclError::Io(e) => &e.message,
 | 
				
			||||||
            KclError::Unexpected(e) => &e.message,
 | 
					            KclError::Unexpected(e) => &e.message,
 | 
				
			||||||
            KclError::ValueAlreadyDefined(e) => &e.message,
 | 
					            KclError::ValueAlreadyDefined(e) => &e.message,
 | 
				
			||||||
            KclError::UndefinedValue(e) => &e.message,
 | 
					            KclError::UndefinedValue(e) => &e.message,
 | 
				
			||||||
@ -440,7 +430,7 @@ impl KclError {
 | 
				
			|||||||
            KclError::Semantic(e) => e.source_ranges = source_ranges,
 | 
					            KclError::Semantic(e) => e.source_ranges = source_ranges,
 | 
				
			||||||
            KclError::ImportCycle(e) => e.source_ranges = source_ranges,
 | 
					            KclError::ImportCycle(e) => e.source_ranges = source_ranges,
 | 
				
			||||||
            KclError::Type(e) => e.source_ranges = source_ranges,
 | 
					            KclError::Type(e) => e.source_ranges = source_ranges,
 | 
				
			||||||
            KclError::Unimplemented(e) => e.source_ranges = source_ranges,
 | 
					            KclError::Io(e) => e.source_ranges = source_ranges,
 | 
				
			||||||
            KclError::Unexpected(e) => e.source_ranges = source_ranges,
 | 
					            KclError::Unexpected(e) => e.source_ranges = source_ranges,
 | 
				
			||||||
            KclError::ValueAlreadyDefined(e) => e.source_ranges = source_ranges,
 | 
					            KclError::ValueAlreadyDefined(e) => e.source_ranges = source_ranges,
 | 
				
			||||||
            KclError::UndefinedValue(e) => e.source_ranges = source_ranges,
 | 
					            KclError::UndefinedValue(e) => e.source_ranges = source_ranges,
 | 
				
			||||||
@ -460,7 +450,7 @@ impl KclError {
 | 
				
			|||||||
            KclError::Semantic(e) => e.source_ranges.extend(source_ranges),
 | 
					            KclError::Semantic(e) => e.source_ranges.extend(source_ranges),
 | 
				
			||||||
            KclError::ImportCycle(e) => e.source_ranges.extend(source_ranges),
 | 
					            KclError::ImportCycle(e) => e.source_ranges.extend(source_ranges),
 | 
				
			||||||
            KclError::Type(e) => e.source_ranges.extend(source_ranges),
 | 
					            KclError::Type(e) => e.source_ranges.extend(source_ranges),
 | 
				
			||||||
            KclError::Unimplemented(e) => e.source_ranges.extend(source_ranges),
 | 
					            KclError::Io(e) => e.source_ranges.extend(source_ranges),
 | 
				
			||||||
            KclError::Unexpected(e) => e.source_ranges.extend(source_ranges),
 | 
					            KclError::Unexpected(e) => e.source_ranges.extend(source_ranges),
 | 
				
			||||||
            KclError::ValueAlreadyDefined(e) => e.source_ranges.extend(source_ranges),
 | 
					            KclError::ValueAlreadyDefined(e) => e.source_ranges.extend(source_ranges),
 | 
				
			||||||
            KclError::UndefinedValue(e) => e.source_ranges.extend(source_ranges),
 | 
					            KclError::UndefinedValue(e) => e.source_ranges.extend(source_ranges),
 | 
				
			||||||
 | 
				
			|||||||
@ -34,7 +34,7 @@ use crate::{
 | 
				
			|||||||
    },
 | 
					    },
 | 
				
			||||||
    fs::FileManager,
 | 
					    fs::FileManager,
 | 
				
			||||||
    modules::{ModuleId, ModulePath},
 | 
					    modules::{ModuleId, ModulePath},
 | 
				
			||||||
    parsing::ast::types::{Expr, ImportPath, Node, NodeRef, Program},
 | 
					    parsing::ast::types::{Expr, ImportPath, NodeRef},
 | 
				
			||||||
    settings::types::UnitLength,
 | 
					    settings::types::UnitLength,
 | 
				
			||||||
    source_range::SourceRange,
 | 
					    source_range::SourceRange,
 | 
				
			||||||
    std::StdLib,
 | 
					    std::StdLib,
 | 
				
			||||||
@ -532,7 +532,7 @@ impl ExecutorContext {
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let result = self.inner_run(&program.ast, &mut exec_state, true).await?;
 | 
					        let result = self.inner_run(&program, &mut exec_state, true).await?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Restore any temporary variables, then save any newly created variables back to
 | 
					        // Restore any temporary variables, then save any newly created variables back to
 | 
				
			||||||
        // memory in case another run wants to use them. Note this is just saved to the preserved
 | 
					        // memory in case another run wants to use them. Note this is just saved to the preserved
 | 
				
			||||||
@ -585,9 +585,15 @@ impl ExecutorContext {
 | 
				
			|||||||
                            .await
 | 
					                            .await
 | 
				
			||||||
                            .is_err()
 | 
					                            .is_err()
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        (true, program.ast)
 | 
					                        (true, program)
 | 
				
			||||||
                    } else {
 | 
					                    } else {
 | 
				
			||||||
                        (clear_scene, changed_program)
 | 
					                        (
 | 
				
			||||||
 | 
					                            clear_scene,
 | 
				
			||||||
 | 
					                            crate::Program {
 | 
				
			||||||
 | 
					                                ast: changed_program,
 | 
				
			||||||
 | 
					                                original_file_contents: program.original_file_contents,
 | 
				
			||||||
 | 
					                            },
 | 
				
			||||||
 | 
					                        )
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                CacheResult::NoAction(true) => {
 | 
					                CacheResult::NoAction(true) => {
 | 
				
			||||||
@ -608,7 +614,7 @@ impl ExecutorContext {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                        return Ok(old_state.to_wasm_outcome(result_env));
 | 
					                        return Ok(old_state.to_wasm_outcome(result_env));
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    (true, program.ast)
 | 
					                    (true, program)
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                CacheResult::NoAction(false) => return Ok(old_state.to_wasm_outcome(result_env)),
 | 
					                CacheResult::NoAction(false) => return Ok(old_state.to_wasm_outcome(result_env)),
 | 
				
			||||||
            };
 | 
					            };
 | 
				
			||||||
@ -636,7 +642,7 @@ impl ExecutorContext {
 | 
				
			|||||||
            self.send_clear_scene(&mut exec_state, Default::default())
 | 
					            self.send_clear_scene(&mut exec_state, Default::default())
 | 
				
			||||||
                .await
 | 
					                .await
 | 
				
			||||||
                .map_err(KclErrorWithOutputs::no_outputs)?;
 | 
					                .map_err(KclErrorWithOutputs::no_outputs)?;
 | 
				
			||||||
            (program.ast, exec_state, false)
 | 
					            (program, exec_state, false)
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let result = self.inner_run(&program, &mut exec_state, preserve_mem).await;
 | 
					        let result = self.inner_run(&program, &mut exec_state, preserve_mem).await;
 | 
				
			||||||
@ -650,7 +656,7 @@ impl ExecutorContext {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        // Save this as the last successful execution to the cache.
 | 
					        // Save this as the last successful execution to the cache.
 | 
				
			||||||
        cache::write_old_ast(OldAstState {
 | 
					        cache::write_old_ast(OldAstState {
 | 
				
			||||||
            ast: program,
 | 
					            ast: program.ast,
 | 
				
			||||||
            exec_state: exec_state.clone(),
 | 
					            exec_state: exec_state.clone(),
 | 
				
			||||||
            settings: self.settings.clone(),
 | 
					            settings: self.settings.clone(),
 | 
				
			||||||
            result_env: result.0,
 | 
					            result_env: result.0,
 | 
				
			||||||
@ -691,18 +697,19 @@ impl ExecutorContext {
 | 
				
			|||||||
        self.send_clear_scene(exec_state, Default::default())
 | 
					        self.send_clear_scene(exec_state, Default::default())
 | 
				
			||||||
            .await
 | 
					            .await
 | 
				
			||||||
            .map_err(KclErrorWithOutputs::no_outputs)?;
 | 
					            .map_err(KclErrorWithOutputs::no_outputs)?;
 | 
				
			||||||
        self.inner_run(&program.ast, exec_state, false).await
 | 
					        self.inner_run(program, exec_state, false).await
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Perform the execution of a program.  Accept all possible parameters and
 | 
					    /// Perform the execution of a program.  Accept all possible parameters and
 | 
				
			||||||
    /// output everything.
 | 
					    /// output everything.
 | 
				
			||||||
    async fn inner_run(
 | 
					    async fn inner_run(
 | 
				
			||||||
        &self,
 | 
					        &self,
 | 
				
			||||||
        program: &Node<Program>,
 | 
					        program: &crate::Program,
 | 
				
			||||||
        exec_state: &mut ExecState,
 | 
					        exec_state: &mut ExecState,
 | 
				
			||||||
        preserve_mem: bool,
 | 
					        preserve_mem: bool,
 | 
				
			||||||
    ) -> Result<(EnvironmentRef, Option<ModelingSessionData>), KclErrorWithOutputs> {
 | 
					    ) -> Result<(EnvironmentRef, Option<ModelingSessionData>), KclErrorWithOutputs> {
 | 
				
			||||||
        let _stats = crate::log::LogPerfStats::new("Interpretation");
 | 
					        let _stats = crate::log::LogPerfStats::new("Interpretation");
 | 
				
			||||||
 | 
					        exec_state.add_root_module_contents(program);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Re-apply the settings, in case the cache was busted.
 | 
					        // Re-apply the settings, in case the cache was busted.
 | 
				
			||||||
        self.engine
 | 
					        self.engine
 | 
				
			||||||
@ -711,7 +718,7 @@ impl ExecutorContext {
 | 
				
			|||||||
            .map_err(KclErrorWithOutputs::no_outputs)?;
 | 
					            .map_err(KclErrorWithOutputs::no_outputs)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let env_ref = self
 | 
					        let env_ref = self
 | 
				
			||||||
            .execute_and_build_graph(program, exec_state, preserve_mem)
 | 
					            .execute_and_build_graph(&program.ast, exec_state, preserve_mem)
 | 
				
			||||||
            .await
 | 
					            .await
 | 
				
			||||||
            .map_err(|e| {
 | 
					            .map_err(|e| {
 | 
				
			||||||
                let module_id_to_module_path: IndexMap<ModuleId, ModulePath> = exec_state
 | 
					                let module_id_to_module_path: IndexMap<ModuleId, ModulePath> = exec_state
 | 
				
			||||||
 | 
				
			|||||||
@ -182,8 +182,27 @@ impl ExecState {
 | 
				
			|||||||
        self.global.path_to_source_id.insert(path.clone(), id);
 | 
					        self.global.path_to_source_id.insert(path.clone(), id);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub(crate) fn add_root_module_contents(&mut self, program: &crate::Program) {
 | 
				
			||||||
 | 
					        let root_id = ModuleId::default();
 | 
				
			||||||
 | 
					        // Get the path for the root module.
 | 
				
			||||||
 | 
					        let path = self
 | 
				
			||||||
 | 
					            .global
 | 
				
			||||||
 | 
					            .path_to_source_id
 | 
				
			||||||
 | 
					            .iter()
 | 
				
			||||||
 | 
					            .find(|(_, v)| **v == root_id)
 | 
				
			||||||
 | 
					            .unwrap()
 | 
				
			||||||
 | 
					            .0
 | 
				
			||||||
 | 
					            .clone();
 | 
				
			||||||
 | 
					        self.add_id_to_source(
 | 
				
			||||||
 | 
					            root_id,
 | 
				
			||||||
 | 
					            ModuleSource {
 | 
				
			||||||
 | 
					                path,
 | 
				
			||||||
 | 
					                source: program.original_file_contents.to_string(),
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub(super) fn add_id_to_source(&mut self, id: ModuleId, source: ModuleSource) {
 | 
					    pub(super) fn add_id_to_source(&mut self, id: ModuleId, source: ModuleSource) {
 | 
				
			||||||
        debug_assert!(!self.global.id_to_source.contains_key(&id));
 | 
					 | 
				
			||||||
        self.global.id_to_source.insert(id, source.clone());
 | 
					        self.global.id_to_source.insert(id, source.clone());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -251,8 +270,6 @@ impl GlobalState {
 | 
				
			|||||||
        global
 | 
					        global
 | 
				
			||||||
            .path_to_source_id
 | 
					            .path_to_source_id
 | 
				
			||||||
            .insert(ModulePath::Local { value: root_path }, root_id);
 | 
					            .insert(ModulePath::Local { value: root_path }, root_id);
 | 
				
			||||||
        // Ideally we'd have a way to set the root module's source here, but
 | 
					 | 
				
			||||||
        // we don't have a way to get the source from the executor settings.
 | 
					 | 
				
			||||||
        global
 | 
					        global
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -31,7 +31,7 @@ impl FileSystem for FileManager {
 | 
				
			|||||||
        source_range: SourceRange,
 | 
					        source_range: SourceRange,
 | 
				
			||||||
    ) -> Result<Vec<u8>, KclError> {
 | 
					    ) -> Result<Vec<u8>, KclError> {
 | 
				
			||||||
        tokio::fs::read(&path).await.map_err(|e| {
 | 
					        tokio::fs::read(&path).await.map_err(|e| {
 | 
				
			||||||
            KclError::Engine(KclErrorDetails {
 | 
					            KclError::Io(KclErrorDetails {
 | 
				
			||||||
                message: format!("Failed to read file `{}`: {}", path.as_ref().display(), e),
 | 
					                message: format!("Failed to read file `{}`: {}", path.as_ref().display(), e),
 | 
				
			||||||
                source_ranges: vec![source_range],
 | 
					                source_ranges: vec![source_range],
 | 
				
			||||||
            })
 | 
					            })
 | 
				
			||||||
@ -44,7 +44,7 @@ impl FileSystem for FileManager {
 | 
				
			|||||||
        source_range: SourceRange,
 | 
					        source_range: SourceRange,
 | 
				
			||||||
    ) -> Result<String, KclError> {
 | 
					    ) -> Result<String, KclError> {
 | 
				
			||||||
        tokio::fs::read_to_string(&path).await.map_err(|e| {
 | 
					        tokio::fs::read_to_string(&path).await.map_err(|e| {
 | 
				
			||||||
            KclError::Engine(KclErrorDetails {
 | 
					            KclError::Io(KclErrorDetails {
 | 
				
			||||||
                message: format!("Failed to read file `{}`: {}", path.as_ref().display(), e),
 | 
					                message: format!("Failed to read file `{}`: {}", path.as_ref().display(), e),
 | 
				
			||||||
                source_ranges: vec![source_range],
 | 
					                source_ranges: vec![source_range],
 | 
				
			||||||
            })
 | 
					            })
 | 
				
			||||||
@ -60,7 +60,7 @@ impl FileSystem for FileManager {
 | 
				
			|||||||
            if e.kind() == std::io::ErrorKind::NotFound {
 | 
					            if e.kind() == std::io::ErrorKind::NotFound {
 | 
				
			||||||
                Ok(false)
 | 
					                Ok(false)
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                Err(KclError::Engine(KclErrorDetails {
 | 
					                Err(KclError::Io(KclErrorDetails {
 | 
				
			||||||
                    message: format!("Failed to check if file `{}` exists: {}", path.as_ref().display(), e),
 | 
					                    message: format!("Failed to check if file `{}` exists: {}", path.as_ref().display(), e),
 | 
				
			||||||
                    source_ranges: vec![source_range],
 | 
					                    source_ranges: vec![source_range],
 | 
				
			||||||
                }))
 | 
					                }))
 | 
				
			||||||
@ -82,7 +82,7 @@ impl FileSystem for FileManager {
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            let mut read_dir = tokio::fs::read_dir(&path).await.map_err(|e| {
 | 
					            let mut read_dir = tokio::fs::read_dir(&path).await.map_err(|e| {
 | 
				
			||||||
                KclError::Engine(KclErrorDetails {
 | 
					                KclError::Io(KclErrorDetails {
 | 
				
			||||||
                    message: format!("Failed to read directory `{}`: {}", path.display(), e),
 | 
					                    message: format!("Failed to read directory `{}`: {}", path.display(), e),
 | 
				
			||||||
                    source_ranges: vec![source_range],
 | 
					                    source_ranges: vec![source_range],
 | 
				
			||||||
                })
 | 
					                })
 | 
				
			||||||
 | 
				
			|||||||
@ -134,6 +134,11 @@ use crate::log::{log, logln};
 | 
				
			|||||||
pub struct Program {
 | 
					pub struct Program {
 | 
				
			||||||
    #[serde(flatten)]
 | 
					    #[serde(flatten)]
 | 
				
			||||||
    pub ast: parsing::ast::types::Node<parsing::ast::types::Program>,
 | 
					    pub ast: parsing::ast::types::Node<parsing::ast::types::Program>,
 | 
				
			||||||
 | 
					    // The ui doesn't need to know about this.
 | 
				
			||||||
 | 
					    // It's purely used for saving the contents of the original file, so we can use it for errors.
 | 
				
			||||||
 | 
					    // Because in the case of the root file, we don't want to read the file from disk again.
 | 
				
			||||||
 | 
					    #[serde(skip)]
 | 
				
			||||||
 | 
					    pub original_file_contents: String,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[cfg(any(test, feature = "lsp-test-util"))]
 | 
					#[cfg(any(test, feature = "lsp-test-util"))]
 | 
				
			||||||
@ -147,7 +152,13 @@ impl Program {
 | 
				
			|||||||
        let tokens = parsing::token::lex(input, module_id)?;
 | 
					        let tokens = parsing::token::lex(input, module_id)?;
 | 
				
			||||||
        let (ast, errs) = parsing::parse_tokens(tokens).0?;
 | 
					        let (ast, errs) = parsing::parse_tokens(tokens).0?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Ok((ast.map(|ast| Program { ast }), errs))
 | 
					        Ok((
 | 
				
			||||||
 | 
					            ast.map(|ast| Program {
 | 
				
			||||||
 | 
					                ast,
 | 
				
			||||||
 | 
					                original_file_contents: input.to_string(),
 | 
				
			||||||
 | 
					            }),
 | 
				
			||||||
 | 
					            errs,
 | 
				
			||||||
 | 
					        ))
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn parse_no_errs(input: &str) -> Result<Program, KclError> {
 | 
					    pub fn parse_no_errs(input: &str) -> Result<Program, KclError> {
 | 
				
			||||||
@ -155,7 +166,10 @@ impl Program {
 | 
				
			|||||||
        let tokens = parsing::token::lex(input, module_id)?;
 | 
					        let tokens = parsing::token::lex(input, module_id)?;
 | 
				
			||||||
        let ast = parsing::parse_tokens(tokens).parse_errs_as_err()?;
 | 
					        let ast = parsing::parse_tokens(tokens).parse_errs_as_err()?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Ok(Program { ast })
 | 
					        Ok(Program {
 | 
				
			||||||
 | 
					            ast,
 | 
				
			||||||
 | 
					            original_file_contents: input.to_string(),
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn compute_digest(&mut self) -> parsing::ast::digest::Digest {
 | 
					    pub fn compute_digest(&mut self) -> parsing::ast::digest::Digest {
 | 
				
			||||||
@ -168,9 +182,10 @@ impl Program {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Change the meta settings for the kcl file.
 | 
					    /// Change the meta settings for the kcl file.
 | 
				
			||||||
    pub fn change_meta_settings(&mut self, settings: crate::MetaSettings) -> Result<Self, KclError> {
 | 
					    pub fn change_meta_settings(&self, settings: crate::MetaSettings) -> Result<Self, KclError> {
 | 
				
			||||||
        Ok(Self {
 | 
					        Ok(Self {
 | 
				
			||||||
            ast: self.ast.change_meta_settings(settings)?,
 | 
					            ast: self.ast.change_meta_settings(settings)?,
 | 
				
			||||||
 | 
					            original_file_contents: self.original_file_contents.clone(),
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -192,12 +207,6 @@ impl Program {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl From<parsing::ast::types::Node<parsing::ast::types::Program>> for Program {
 | 
					 | 
				
			||||||
    fn from(ast: parsing::ast::types::Node<parsing::ast::types::Program>) -> Program {
 | 
					 | 
				
			||||||
        Self { ast }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#[inline]
 | 
					#[inline]
 | 
				
			||||||
fn try_f64_to_usize(f: f64) -> Option<usize> {
 | 
					fn try_f64_to_usize(f: f64) -> Option<usize> {
 | 
				
			||||||
    let i = f as usize;
 | 
					    let i = f as usize;
 | 
				
			||||||
 | 
				
			|||||||
@ -46,7 +46,7 @@ use crate::{
 | 
				
			|||||||
    errors::Suggestion,
 | 
					    errors::Suggestion,
 | 
				
			||||||
    lsp::{backend::Backend as _, util::IntoDiagnostic},
 | 
					    lsp::{backend::Backend as _, util::IntoDiagnostic},
 | 
				
			||||||
    parsing::{
 | 
					    parsing::{
 | 
				
			||||||
        ast::types::{Expr, Node, VariableKind},
 | 
					        ast::types::{Expr, VariableKind},
 | 
				
			||||||
        token::TokenStream,
 | 
					        token::TokenStream,
 | 
				
			||||||
        PIPE_OPERATOR,
 | 
					        PIPE_OPERATOR,
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
@ -102,7 +102,7 @@ pub struct Backend {
 | 
				
			|||||||
    /// Token maps.
 | 
					    /// Token maps.
 | 
				
			||||||
    pub(super) token_map: DashMap<String, TokenStream>,
 | 
					    pub(super) token_map: DashMap<String, TokenStream>,
 | 
				
			||||||
    /// AST maps.
 | 
					    /// AST maps.
 | 
				
			||||||
    pub ast_map: DashMap<String, Node<crate::parsing::ast::types::Program>>,
 | 
					    pub ast_map: DashMap<String, crate::Program>,
 | 
				
			||||||
    /// Current code.
 | 
					    /// Current code.
 | 
				
			||||||
    pub code_map: DashMap<String, Vec<u8>>,
 | 
					    pub code_map: DashMap<String, Vec<u8>>,
 | 
				
			||||||
    /// Diagnostics.
 | 
					    /// Diagnostics.
 | 
				
			||||||
@ -327,11 +327,17 @@ impl crate::lsp::backend::Backend for Backend {
 | 
				
			|||||||
        // this if it backfires and only hork the LSP.
 | 
					        // this if it backfires and only hork the LSP.
 | 
				
			||||||
        ast.compute_digest();
 | 
					        ast.compute_digest();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Save it as a program.
 | 
				
			||||||
 | 
					        let ast = crate::Program {
 | 
				
			||||||
 | 
					            ast,
 | 
				
			||||||
 | 
					            original_file_contents: params.text.clone(),
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Check if the ast changed.
 | 
					        // Check if the ast changed.
 | 
				
			||||||
        let ast_changed = match self.ast_map.get(&filename) {
 | 
					        let ast_changed = match self.ast_map.get(&filename) {
 | 
				
			||||||
            Some(old_ast) => {
 | 
					            Some(old_ast) => {
 | 
				
			||||||
                // Check if the ast changed.
 | 
					                // Check if the ast changed.
 | 
				
			||||||
                *old_ast != ast
 | 
					                *old_ast.ast != *ast.ast
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            None => true,
 | 
					            None => true,
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
@ -346,7 +352,7 @@ impl crate::lsp::backend::Backend for Backend {
 | 
				
			|||||||
            // Update the symbols map.
 | 
					            // Update the symbols map.
 | 
				
			||||||
            self.symbols_map.insert(
 | 
					            self.symbols_map.insert(
 | 
				
			||||||
                params.uri.to_string(),
 | 
					                params.uri.to_string(),
 | 
				
			||||||
                ast.get_lsp_symbols(¶ms.text).unwrap_or_default(),
 | 
					                ast.ast.get_lsp_symbols(¶ms.text).unwrap_or_default(),
 | 
				
			||||||
            );
 | 
					            );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // Update our semantic tokens.
 | 
					            // Update our semantic tokens.
 | 
				
			||||||
@ -361,14 +367,14 @@ impl crate::lsp::backend::Backend for Backend {
 | 
				
			|||||||
            // Only send the notification if we can execute.
 | 
					            // Only send the notification if we can execute.
 | 
				
			||||||
            // Otherwise it confuses the client.
 | 
					            // Otherwise it confuses the client.
 | 
				
			||||||
            self.client
 | 
					            self.client
 | 
				
			||||||
                .send_notification::<custom_notifications::AstUpdated>(ast.clone())
 | 
					                .send_notification::<custom_notifications::AstUpdated>(ast.ast.clone())
 | 
				
			||||||
                .await;
 | 
					                .await;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Execute the code if we have an executor context.
 | 
					        // Execute the code if we have an executor context.
 | 
				
			||||||
        // This function automatically executes if we should & updates the diagnostics if we got
 | 
					        // This function automatically executes if we should & updates the diagnostics if we got
 | 
				
			||||||
        // errors.
 | 
					        // errors.
 | 
				
			||||||
        if self.execute(¶ms, &ast.into()).await.is_err() {
 | 
					        if self.execute(¶ms, &ast).await.is_err() {
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -421,7 +427,7 @@ impl Backend {
 | 
				
			|||||||
            let token_modifiers_bitset = if let Some(ast) = self.ast_map.get(params.uri.as_str()) {
 | 
					            let token_modifiers_bitset = if let Some(ast) = self.ast_map.get(params.uri.as_str()) {
 | 
				
			||||||
                let token_index = Arc::new(Mutex::new(token_type_index));
 | 
					                let token_index = Arc::new(Mutex::new(token_type_index));
 | 
				
			||||||
                let modifier_index: Arc<Mutex<u32>> = Arc::new(Mutex::new(0));
 | 
					                let modifier_index: Arc<Mutex<u32>> = Arc::new(Mutex::new(0));
 | 
				
			||||||
                crate::walk::walk(&ast, |node: crate::walk::Node| {
 | 
					                crate::walk::walk(&ast.ast, |node: crate::walk::Node| {
 | 
				
			||||||
                    let Ok(node_range): Result<SourceRange, _> = (&node).try_into() else {
 | 
					                    let Ok(node_range): Result<SourceRange, _> = (&node).try_into() else {
 | 
				
			||||||
                        return Ok(true);
 | 
					                        return Ok(true);
 | 
				
			||||||
                    };
 | 
					                    };
 | 
				
			||||||
@ -1021,7 +1027,7 @@ impl LanguageServer for Backend {
 | 
				
			|||||||
            return Ok(None);
 | 
					            return Ok(None);
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let Some(hover) = ast.get_hover_value_for_position(pos, current_code) else {
 | 
					        let Some(hover) = ast.ast.get_hover_value_for_position(pos, current_code) else {
 | 
				
			||||||
            return Ok(None);
 | 
					            return Ok(None);
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -1150,13 +1156,13 @@ impl LanguageServer for Backend {
 | 
				
			|||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let position = position_to_char_index(params.text_document_position.position, current_code);
 | 
					        let position = position_to_char_index(params.text_document_position.position, current_code);
 | 
				
			||||||
        if ast.get_non_code_meta_for_position(position).is_some() {
 | 
					        if ast.ast.get_non_code_meta_for_position(position).is_some() {
 | 
				
			||||||
            // If we are in a code comment we don't want to show completions.
 | 
					            // If we are in a code comment we don't want to show completions.
 | 
				
			||||||
            return Ok(None);
 | 
					            return Ok(None);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Get the completion items for the ast.
 | 
					        // Get the completion items for the ast.
 | 
				
			||||||
        let Ok(variables) = ast.completion_items() else {
 | 
					        let Ok(variables) = ast.ast.completion_items() else {
 | 
				
			||||||
            return Ok(Some(CompletionResponse::Array(completions)));
 | 
					            return Ok(Some(CompletionResponse::Array(completions)));
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -1211,7 +1217,7 @@ impl LanguageServer for Backend {
 | 
				
			|||||||
            return Ok(None);
 | 
					            return Ok(None);
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let Some(value) = ast.get_expr_for_position(pos) else {
 | 
					        let Some(value) = ast.ast.get_expr_for_position(pos) else {
 | 
				
			||||||
            return Ok(None);
 | 
					            return Ok(None);
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -1360,7 +1366,7 @@ impl LanguageServer for Backend {
 | 
				
			|||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Get the folding ranges.
 | 
					        // Get the folding ranges.
 | 
				
			||||||
        let folding_ranges = ast.get_lsp_folding_ranges();
 | 
					        let folding_ranges = ast.ast.get_lsp_folding_ranges();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if folding_ranges.is_empty() {
 | 
					        if folding_ranges.is_empty() {
 | 
				
			||||||
            return Ok(None);
 | 
					            return Ok(None);
 | 
				
			||||||
 | 
				
			|||||||
@ -1138,7 +1138,7 @@ fn myFn = (param1) => {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // Get the ast.
 | 
					    // Get the ast.
 | 
				
			||||||
    let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
 | 
					    let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
 | 
				
			||||||
    assert!(ast != Node::<Program>::default());
 | 
					    assert!(ast.ast != Node::<Program>::default());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Send semantic tokens request.
 | 
					    // Send semantic tokens request.
 | 
				
			||||||
    let semantic_tokens = server
 | 
					    let semantic_tokens = server
 | 
				
			||||||
@ -2251,7 +2251,7 @@ part001 = cube([0,0], 20)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // Get the ast.
 | 
					    // Get the ast.
 | 
				
			||||||
    let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
 | 
					    let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
 | 
				
			||||||
    assert_eq!(ast.body.len(), 2);
 | 
					    assert_eq!(ast.ast.body.len(), 2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Send change file.
 | 
					    // Send change file.
 | 
				
			||||||
    server
 | 
					    server
 | 
				
			||||||
@ -2428,7 +2428,7 @@ async fn kcl_test_kcl_lsp_full_to_empty_file_updates_ast_and_memory() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // Get the ast.
 | 
					    // Get the ast.
 | 
				
			||||||
    let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
 | 
					    let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
 | 
				
			||||||
    assert!(ast != Node::<Program>::default());
 | 
					    assert!(ast.ast != Node::<Program>::default());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Send change file.
 | 
					    // Send change file.
 | 
				
			||||||
    server
 | 
					    server
 | 
				
			||||||
@ -2450,7 +2450,7 @@ async fn kcl_test_kcl_lsp_full_to_empty_file_updates_ast_and_memory() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // Get the ast.
 | 
					    // Get the ast.
 | 
				
			||||||
    let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
 | 
					    let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
 | 
				
			||||||
    assert_eq!(ast, default_hashed);
 | 
					    assert_eq!(ast.ast, default_hashed);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[tokio::test(flavor = "multi_thread")]
 | 
					#[tokio::test(flavor = "multi_thread")]
 | 
				
			||||||
@ -2479,7 +2479,7 @@ async fn kcl_test_kcl_lsp_code_unchanged_but_has_diagnostics_reexecute() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // Get the ast.
 | 
					    // Get the ast.
 | 
				
			||||||
    let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
 | 
					    let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
 | 
				
			||||||
    assert!(ast != Node::<Program>::default());
 | 
					    assert!(ast.ast != Node::<Program>::default());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Assure we have no diagnostics.
 | 
					    // Assure we have no diagnostics.
 | 
				
			||||||
    assert_diagnostic_count(server.diagnostics_map.get("file:///test.kcl").as_deref(), 0);
 | 
					    assert_diagnostic_count(server.diagnostics_map.get("file:///test.kcl").as_deref(), 0);
 | 
				
			||||||
@ -2506,11 +2506,15 @@ async fn kcl_test_kcl_lsp_code_unchanged_but_has_diagnostics_reexecute() {
 | 
				
			|||||||
    assert_diagnostic_count(server.diagnostics_map.get("file:///test.kcl").as_deref(), 1);
 | 
					    assert_diagnostic_count(server.diagnostics_map.get("file:///test.kcl").as_deref(), 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Clear the ast and memory.
 | 
					    // Clear the ast and memory.
 | 
				
			||||||
    server
 | 
					    server.ast_map.insert(
 | 
				
			||||||
        .ast_map
 | 
					        "file:///test.kcl".to_string(),
 | 
				
			||||||
        .insert("file:///test.kcl".to_string(), Node::<Program>::default());
 | 
					        crate::Program {
 | 
				
			||||||
 | 
					            ast: Default::default(),
 | 
				
			||||||
 | 
					            original_file_contents: Default::default(),
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
    let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
 | 
					    let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
 | 
				
			||||||
    assert_eq!(ast, Node::<Program>::default());
 | 
					    assert_eq!(ast.ast, Node::<Program>::default());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Send change file, but the code is the same.
 | 
					    // Send change file, but the code is the same.
 | 
				
			||||||
    server
 | 
					    server
 | 
				
			||||||
@ -2529,7 +2533,7 @@ async fn kcl_test_kcl_lsp_code_unchanged_but_has_diagnostics_reexecute() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // Get the ast.
 | 
					    // Get the ast.
 | 
				
			||||||
    let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
 | 
					    let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
 | 
				
			||||||
    assert!(ast != Node::<Program>::default());
 | 
					    assert!(ast.ast != Node::<Program>::default());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Assure we have no diagnostics.
 | 
					    // Assure we have no diagnostics.
 | 
				
			||||||
    assert_diagnostic_count(server.diagnostics_map.get("file:///test.kcl").as_deref(), 0);
 | 
					    assert_diagnostic_count(server.diagnostics_map.get("file:///test.kcl").as_deref(), 0);
 | 
				
			||||||
@ -2561,7 +2565,7 @@ async fn kcl_test_kcl_lsp_code_and_ast_unchanged_but_has_diagnostics_reexecute()
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // Get the ast.
 | 
					    // Get the ast.
 | 
				
			||||||
    let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
 | 
					    let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
 | 
				
			||||||
    assert!(ast != Node::<Program>::default());
 | 
					    assert!(ast.ast != Node::<Program>::default());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Assure we have no diagnostics.
 | 
					    // Assure we have no diagnostics.
 | 
				
			||||||
    assert_diagnostic_count(server.diagnostics_map.get("file:///test.kcl").as_deref(), 0);
 | 
					    assert_diagnostic_count(server.diagnostics_map.get("file:///test.kcl").as_deref(), 0);
 | 
				
			||||||
@ -2604,7 +2608,7 @@ async fn kcl_test_kcl_lsp_code_and_ast_unchanged_but_has_diagnostics_reexecute()
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // Get the ast.
 | 
					    // Get the ast.
 | 
				
			||||||
    let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
 | 
					    let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
 | 
				
			||||||
    assert!(ast != Node::<Program>::default());
 | 
					    assert!(ast.ast != Node::<Program>::default());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Assure we have no diagnostics.
 | 
					    // Assure we have no diagnostics.
 | 
				
			||||||
    assert_diagnostic_count(server.diagnostics_map.get("file:///test.kcl").as_deref(), 0);
 | 
					    assert_diagnostic_count(server.diagnostics_map.get("file:///test.kcl").as_deref(), 0);
 | 
				
			||||||
@ -2636,7 +2640,7 @@ async fn kcl_test_kcl_lsp_code_and_ast_units_unchanged_but_has_diagnostics_reexe
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // Get the ast.
 | 
					    // Get the ast.
 | 
				
			||||||
    let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
 | 
					    let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
 | 
				
			||||||
    assert!(ast != Node::<Program>::default());
 | 
					    assert!(ast.ast != Node::<Program>::default());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Assure we have no diagnostics.
 | 
					    // Assure we have no diagnostics.
 | 
				
			||||||
    assert_diagnostic_count(server.diagnostics_map.get("file:///test.kcl").as_deref(), 0);
 | 
					    assert_diagnostic_count(server.diagnostics_map.get("file:///test.kcl").as_deref(), 0);
 | 
				
			||||||
@ -2682,7 +2686,7 @@ async fn kcl_test_kcl_lsp_code_and_ast_units_unchanged_but_has_diagnostics_reexe
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // Get the ast.
 | 
					    // Get the ast.
 | 
				
			||||||
    let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
 | 
					    let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
 | 
				
			||||||
    assert!(ast != Node::<Program>::default());
 | 
					    assert!(ast.ast != Node::<Program>::default());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Assure we have no diagnostics.
 | 
					    // Assure we have no diagnostics.
 | 
				
			||||||
    assert_diagnostic_count(server.diagnostics_map.get("file:///test.kcl").as_deref(), 0);
 | 
					    assert_diagnostic_count(server.diagnostics_map.get("file:///test.kcl").as_deref(), 0);
 | 
				
			||||||
@ -2714,7 +2718,7 @@ async fn kcl_test_kcl_lsp_code_and_ast_units_unchanged_but_has_memory_reexecute_
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // Get the ast.
 | 
					    // Get the ast.
 | 
				
			||||||
    let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
 | 
					    let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
 | 
				
			||||||
    assert!(ast != Node::<Program>::default());
 | 
					    assert!(ast.ast != Node::<Program>::default());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Assure we have no diagnostics.
 | 
					    // Assure we have no diagnostics.
 | 
				
			||||||
    assert_diagnostic_count(server.diagnostics_map.get("file:///test.kcl").as_deref(), 0);
 | 
					    assert_diagnostic_count(server.diagnostics_map.get("file:///test.kcl").as_deref(), 0);
 | 
				
			||||||
@ -2739,7 +2743,7 @@ async fn kcl_test_kcl_lsp_code_and_ast_units_unchanged_but_has_memory_reexecute_
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // Get the ast.
 | 
					    // Get the ast.
 | 
				
			||||||
    let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
 | 
					    let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
 | 
				
			||||||
    assert!(ast != Node::<Program>::default());
 | 
					    assert!(ast.ast != Node::<Program>::default());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Assure we have no diagnostics.
 | 
					    // Assure we have no diagnostics.
 | 
				
			||||||
    assert_diagnostic_count(server.diagnostics_map.get("file:///test.kcl").as_deref(), 0);
 | 
					    assert_diagnostic_count(server.diagnostics_map.get("file:///test.kcl").as_deref(), 0);
 | 
				
			||||||
@ -2771,7 +2775,7 @@ async fn kcl_test_kcl_lsp_cant_execute_set() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // Get the ast.
 | 
					    // Get the ast.
 | 
				
			||||||
    let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
 | 
					    let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
 | 
				
			||||||
    assert!(ast != Node::<Program>::default());
 | 
					    assert!(ast.ast != Node::<Program>::default());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Assure we have no diagnostics.
 | 
					    // Assure we have no diagnostics.
 | 
				
			||||||
    assert_diagnostic_count(server.diagnostics_map.get("file:///test.kcl").as_deref(), 0);
 | 
					    assert_diagnostic_count(server.diagnostics_map.get("file:///test.kcl").as_deref(), 0);
 | 
				
			||||||
@ -2795,7 +2799,7 @@ async fn kcl_test_kcl_lsp_cant_execute_set() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // Get the ast.
 | 
					    // Get the ast.
 | 
				
			||||||
    let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
 | 
					    let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
 | 
				
			||||||
    assert!(ast != Node::<Program>::default());
 | 
					    assert!(ast.ast != Node::<Program>::default());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Assure we have no diagnostics.
 | 
					    // Assure we have no diagnostics.
 | 
				
			||||||
    assert_diagnostic_count(server.diagnostics_map.get("file:///test.kcl").as_deref(), 0);
 | 
					    assert_diagnostic_count(server.diagnostics_map.get("file:///test.kcl").as_deref(), 0);
 | 
				
			||||||
@ -2831,7 +2835,7 @@ async fn kcl_test_kcl_lsp_cant_execute_set() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // Get the ast.
 | 
					    // Get the ast.
 | 
				
			||||||
    let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
 | 
					    let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
 | 
				
			||||||
    assert!(ast != default_hashed);
 | 
					    assert!(ast.ast != default_hashed);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Assure we have no diagnostics.
 | 
					    // Assure we have no diagnostics.
 | 
				
			||||||
    assert_diagnostic_count(server.diagnostics_map.get("file:///test.kcl").as_deref(), 0);
 | 
					    assert_diagnostic_count(server.diagnostics_map.get("file:///test.kcl").as_deref(), 0);
 | 
				
			||||||
@ -2862,7 +2866,7 @@ async fn kcl_test_kcl_lsp_cant_execute_set() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // Get the ast.
 | 
					    // Get the ast.
 | 
				
			||||||
    let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
 | 
					    let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
 | 
				
			||||||
    assert!(ast != Node::<Program>::default());
 | 
					    assert!(ast.ast != Node::<Program>::default());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Assure we have no diagnostics.
 | 
					    // Assure we have no diagnostics.
 | 
				
			||||||
    assert_diagnostic_count(server.diagnostics_map.get("file:///test.kcl").as_deref(), 0);
 | 
					    assert_diagnostic_count(server.diagnostics_map.get("file:///test.kcl").as_deref(), 0);
 | 
				
			||||||
@ -2995,7 +2999,7 @@ part001 = startSketchOn('XY')
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // Get the ast.
 | 
					    // Get the ast.
 | 
				
			||||||
    let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
 | 
					    let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
 | 
				
			||||||
    assert!(ast != Node::<Program>::default());
 | 
					    assert!(ast.ast != Node::<Program>::default());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Assure we have one diagnostics.
 | 
					    // Assure we have one diagnostics.
 | 
				
			||||||
    assert_diagnostic_count(server.diagnostics_map.get("file:///test.kcl").as_deref(), 1);
 | 
					    assert_diagnostic_count(server.diagnostics_map.get("file:///test.kcl").as_deref(), 1);
 | 
				
			||||||
@ -3016,7 +3020,7 @@ part001 = startSketchOn('XY')
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // Get the ast.
 | 
					    // Get the ast.
 | 
				
			||||||
    let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
 | 
					    let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
 | 
				
			||||||
    assert!(ast != Node::<Program>::default());
 | 
					    assert!(ast.ast != Node::<Program>::default());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Assure we have one diagnostics.
 | 
					    // Assure we have one diagnostics.
 | 
				
			||||||
    assert_diagnostic_count(server.diagnostics_map.get("file:///test.kcl").as_deref(), 1);
 | 
					    assert_diagnostic_count(server.diagnostics_map.get("file:///test.kcl").as_deref(), 1);
 | 
				
			||||||
@ -3109,7 +3113,7 @@ part001 = startSketchOn('XY')
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // Get the ast.
 | 
					    // Get the ast.
 | 
				
			||||||
    let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
 | 
					    let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
 | 
				
			||||||
    assert!(ast != Node::<Program>::default());
 | 
					    assert!(ast.ast != Node::<Program>::default());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Send change file, but the code is the same.
 | 
					    // Send change file, but the code is the same.
 | 
				
			||||||
    server
 | 
					    server
 | 
				
			||||||
@ -3128,7 +3132,7 @@ part001 = startSketchOn('XY')
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // Get the ast.
 | 
					    // Get the ast.
 | 
				
			||||||
    let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
 | 
					    let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
 | 
				
			||||||
    assert!(ast != Node::<Program>::default());
 | 
					    assert!(ast.ast != Node::<Program>::default());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Assure we have diagnostics.
 | 
					    // Assure we have diagnostics.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -3168,7 +3172,7 @@ part001 = startSketchOn('XY')
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // Get the ast.
 | 
					    // Get the ast.
 | 
				
			||||||
    let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
 | 
					    let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
 | 
				
			||||||
    assert!(ast != Node::<Program>::default());
 | 
					    assert!(ast.ast != Node::<Program>::default());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Send change file, but the code is the same.
 | 
					    // Send change file, but the code is the same.
 | 
				
			||||||
    server
 | 
					    server
 | 
				
			||||||
@ -3195,7 +3199,7 @@ NEW_LINT = 1"#
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // Get the ast.
 | 
					    // Get the ast.
 | 
				
			||||||
    let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
 | 
					    let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
 | 
				
			||||||
    assert!(ast != Node::<Program>::default());
 | 
					    assert!(ast.ast != Node::<Program>::default());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Assure we have diagnostics.
 | 
					    // Assure we have diagnostics.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -3302,7 +3306,7 @@ part001 = startSketchOn('XY')
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // Get the ast.
 | 
					    // Get the ast.
 | 
				
			||||||
    let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
 | 
					    let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
 | 
				
			||||||
    assert!(ast != Node::<Program>::default());
 | 
					    assert!(ast.ast != Node::<Program>::default());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Get the symbols map.
 | 
					    // Get the symbols map.
 | 
				
			||||||
    let symbols_map = server.symbols_map.get("file:///test.kcl").unwrap().clone();
 | 
					    let symbols_map = server.symbols_map.get("file:///test.kcl").unwrap().clone();
 | 
				
			||||||
@ -3389,7 +3393,7 @@ part001 = startSketchOn('XY')
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // Get the ast.
 | 
					    // Get the ast.
 | 
				
			||||||
    let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
 | 
					    let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
 | 
				
			||||||
    assert!(ast != Node::<Program>::default());
 | 
					    assert!(ast.ast != Node::<Program>::default());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Get the symbols map.
 | 
					    // Get the symbols map.
 | 
				
			||||||
    let symbols_map = server.symbols_map.get("file:///test.kcl").unwrap().clone();
 | 
					    let symbols_map = server.symbols_map.get("file:///test.kcl").unwrap().clone();
 | 
				
			||||||
@ -3428,7 +3432,7 @@ part001 = startSketchOn('XY')
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // Get the ast.
 | 
					    // Get the ast.
 | 
				
			||||||
    let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
 | 
					    let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
 | 
				
			||||||
    assert!(ast != Node::<Program>::default());
 | 
					    assert!(ast.ast != Node::<Program>::default());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Get the symbols map.
 | 
					    // Get the symbols map.
 | 
				
			||||||
    let symbols_map = server.symbols_map.get("file:///test.kcl").unwrap().clone();
 | 
					    let symbols_map = server.symbols_map.get("file:///test.kcl").unwrap().clone();
 | 
				
			||||||
 | 
				
			|||||||
@ -182,9 +182,7 @@ pub async fn modify_ast_for_sketch(
 | 
				
			|||||||
    let recasted = program.ast.recast(&FormatOptions::default(), 0);
 | 
					    let recasted = program.ast.recast(&FormatOptions::default(), 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Re-parse the ast so we get the correct source ranges.
 | 
					    // Re-parse the ast so we get the correct source ranges.
 | 
				
			||||||
    *program = crate::parsing::parse_str(&recasted, module_id)
 | 
					    program.ast = crate::parsing::parse_str(&recasted, module_id).parse_errs_as_err()?;
 | 
				
			||||||
        .parse_errs_as_err()?
 | 
					 | 
				
			||||||
        .into();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Ok(recasted)
 | 
					    Ok(recasted)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -275,7 +275,7 @@ impl Node<Program> {
 | 
				
			|||||||
        Ok(None)
 | 
					        Ok(None)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn change_meta_settings(&mut self, settings: crate::execution::MetaSettings) -> Result<Self, KclError> {
 | 
					    pub fn change_meta_settings(&self, settings: crate::execution::MetaSettings) -> Result<Self, KclError> {
 | 
				
			||||||
        let mut new_program = self.clone();
 | 
					        let mut new_program = self.clone();
 | 
				
			||||||
        let mut found = false;
 | 
					        let mut found = false;
 | 
				
			||||||
        for node in &mut new_program.inner_attrs {
 | 
					        for node in &mut new_program.inner_attrs {
 | 
				
			||||||
@ -4035,7 +4035,7 @@ startSketchOn('XY')"#;
 | 
				
			|||||||
        let some_program_string = r#"@settings(defaultLengthUnit = inch)
 | 
					        let some_program_string = r#"@settings(defaultLengthUnit = inch)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
startSketchOn('XY')"#;
 | 
					startSketchOn('XY')"#;
 | 
				
			||||||
        let mut program = crate::parsing::top_level_parse(some_program_string).unwrap();
 | 
					        let program = crate::parsing::top_level_parse(some_program_string).unwrap();
 | 
				
			||||||
        let result = program.meta_settings().unwrap();
 | 
					        let result = program.meta_settings().unwrap();
 | 
				
			||||||
        assert!(result.is_some());
 | 
					        assert!(result.is_some());
 | 
				
			||||||
        let meta_settings = result.unwrap();
 | 
					        let meta_settings = result.unwrap();
 | 
				
			||||||
@ -4077,7 +4077,7 @@ startSketchOn('XY')
 | 
				
			|||||||
    #[tokio::test(flavor = "multi_thread")]
 | 
					    #[tokio::test(flavor = "multi_thread")]
 | 
				
			||||||
    async fn test_parse_get_meta_settings_nothing_to_mm() {
 | 
					    async fn test_parse_get_meta_settings_nothing_to_mm() {
 | 
				
			||||||
        let some_program_string = r#"startSketchOn('XY')"#;
 | 
					        let some_program_string = r#"startSketchOn('XY')"#;
 | 
				
			||||||
        let mut program = crate::parsing::top_level_parse(some_program_string).unwrap();
 | 
					        let program = crate::parsing::top_level_parse(some_program_string).unwrap();
 | 
				
			||||||
        let result = program.meta_settings().unwrap();
 | 
					        let result = program.meta_settings().unwrap();
 | 
				
			||||||
        assert!(result.is_none());
 | 
					        assert!(result.is_none());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -84,10 +84,14 @@ async fn execute(test_name: &str, render_to_png: bool) {
 | 
				
			|||||||
    let Ok(ast) = ast_res else {
 | 
					    let Ok(ast) = ast_res else {
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					    let ast = crate::Program {
 | 
				
			||||||
 | 
					        ast,
 | 
				
			||||||
 | 
					        original_file_contents: read("input.kcl", test_name),
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Run the program.
 | 
					    // Run the program.
 | 
				
			||||||
    let exec_res = crate::test_server::execute_and_snapshot_ast(
 | 
					    let exec_res = crate::test_server::execute_and_snapshot_ast(
 | 
				
			||||||
        ast.into(),
 | 
					        ast,
 | 
				
			||||||
        crate::settings::types::UnitLength::Mm,
 | 
					        crate::settings::types::UnitLength::Mm,
 | 
				
			||||||
        Some(Path::new("tests").join(test_name).join("input.kcl").to_owned()),
 | 
					        Some(Path::new("tests").join(test_name).join("input.kcl").to_owned()),
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
@ -132,10 +136,7 @@ async fn execute(test_name: &str, render_to_png: bool) {
 | 
				
			|||||||
                        Box::new(miette::MietteHandlerOpts::new().show_related_errors_as_nested().build())
 | 
					                        Box::new(miette::MietteHandlerOpts::new().show_related_errors_as_nested().build())
 | 
				
			||||||
                    }))
 | 
					                    }))
 | 
				
			||||||
                    .unwrap();
 | 
					                    .unwrap();
 | 
				
			||||||
                    let report = error
 | 
					                    let report = error.clone().into_miette_report_with_outputs().unwrap();
 | 
				
			||||||
                        .clone()
 | 
					 | 
				
			||||||
                        .into_miette_report_with_outputs(&read("input.kcl", test_name))
 | 
					 | 
				
			||||||
                        .unwrap();
 | 
					 | 
				
			||||||
                    let report = miette::Report::new(report);
 | 
					                    let report = miette::Report::new(report);
 | 
				
			||||||
                    if previously_passed {
 | 
					                    if previously_passed {
 | 
				
			||||||
                        eprintln!("This test case failed, but it previously passed. If this is intended, and the test should actually be failing now, please delete kcl/{ok_path_str} and other associated passing artifacts");
 | 
					                        eprintln!("This test case failed, but it previously passed. If this is intended, and the test should actually be failing now, please delete kcl/{ok_path_str} and other associated passing artifacts");
 | 
				
			||||||
 | 
				
			|||||||
@ -2,9 +2,9 @@
 | 
				
			|||||||
source: kcl/src/simulation_tests.rs
 | 
					source: kcl/src/simulation_tests.rs
 | 
				
			||||||
description: Error from executing import_file_not_exist_error.kcl
 | 
					description: Error from executing import_file_not_exist_error.kcl
 | 
				
			||||||
---
 | 
					---
 | 
				
			||||||
KCL Engine error
 | 
					KCL I/O error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  × engine: Failed to read file `tests/import_file_not_exist_error/not-
 | 
					  × i/o: Failed to read file `tests/import_file_not_exist_error/not-
 | 
				
			||||||
  │ exist.kcl`: No such file or directory (os error 2)
 | 
					  │ exist.kcl`: No such file or directory (os error 2)
 | 
				
			||||||
   ╭────
 | 
					   ╭────
 | 
				
			||||||
 1 │ import hotdog from "not-exist.kcl"
 | 
					 1 │ import hotdog from "not-exist.kcl"
 | 
				
			||||||
 | 
				
			|||||||
@ -1,11 +1,7 @@
 | 
				
			|||||||
//! Wasm bindings for `kcl`.
 | 
					//! Wasm bindings for `kcl`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[cfg(target_arch = "wasm32")]
 | 
					 | 
				
			||||||
mod toml;
 | 
					 | 
				
			||||||
#[cfg(target_arch = "wasm32")]
 | 
					#[cfg(target_arch = "wasm32")]
 | 
				
			||||||
mod wasm;
 | 
					mod wasm;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[cfg(target_arch = "wasm32")]
 | 
					 | 
				
			||||||
pub use toml::*;
 | 
					 | 
				
			||||||
#[cfg(target_arch = "wasm32")]
 | 
					#[cfg(target_arch = "wasm32")]
 | 
				
			||||||
pub use wasm::*;
 | 
					pub use wasm::*;
 | 
				
			||||||
 | 
				
			|||||||
@ -1,13 +0,0 @@
 | 
				
			|||||||
//! Functions for interacting with TOML files.
 | 
					 | 
				
			||||||
//! We do this in rust because the Javascript TOML libraries are actual trash.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
use wasm_bindgen::prelude::wasm_bindgen;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#[wasm_bindgen]
 | 
					 | 
				
			||||||
pub fn toml_stringify(json: &str) -> Result<String, String> {
 | 
					 | 
				
			||||||
    console_error_panic_hook::set_once();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    let value: serde_json::Value = serde_json::from_str(json).map_err(|e| e.to_string())?;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    toml::to_string_pretty(&value).map_err(|e| e.to_string())
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -483,9 +483,9 @@ pub fn parse_project_settings(toml_str: &str) -> Result<JsValue, String> {
 | 
				
			|||||||
    JsValue::from_serde(&settings).map_err(|e| e.to_string())
 | 
					    JsValue::from_serde(&settings).map_err(|e| e.to_string())
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Serialize the project settings.
 | 
					/// Serialize the configuration settings.
 | 
				
			||||||
#[wasm_bindgen]
 | 
					#[wasm_bindgen]
 | 
				
			||||||
pub fn serialize_project_settings(val: JsValue) -> Result<JsValue, String> {
 | 
					pub fn serialize_configuration(val: JsValue) -> Result<JsValue, String> {
 | 
				
			||||||
    console_error_panic_hook::set_once();
 | 
					    console_error_panic_hook::set_once();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let config: kcl_lib::Configuration = val.into_serde().map_err(|e| e.to_string())?;
 | 
					    let config: kcl_lib::Configuration = val.into_serde().map_err(|e| e.to_string())?;
 | 
				
			||||||
@ -497,6 +497,20 @@ pub fn serialize_project_settings(val: JsValue) -> Result<JsValue, String> {
 | 
				
			|||||||
    Ok(JsValue::from_str(&toml_str))
 | 
					    Ok(JsValue::from_str(&toml_str))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Serialize the project configuration settings.
 | 
				
			||||||
 | 
					#[wasm_bindgen]
 | 
				
			||||||
 | 
					pub fn serialize_project_configuration(val: JsValue) -> Result<JsValue, String> {
 | 
				
			||||||
 | 
					    console_error_panic_hook::set_once();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let config: kcl_lib::ProjectConfiguration = val.into_serde().map_err(|e| e.to_string())?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let toml_str = toml::to_string_pretty(&config).map_err(|e| e.to_string())?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // The serde-wasm-bindgen does not work here because of weird HashMap issues so we use the
 | 
				
			||||||
 | 
					    // gloo-serialize crate instead.
 | 
				
			||||||
 | 
					    Ok(JsValue::from_str(&toml_str))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static ALLOWED_DECODING_FORMATS: &[data_encoding::Encoding] = &[
 | 
					static ALLOWED_DECODING_FORMATS: &[data_encoding::Encoding] = &[
 | 
				
			||||||
    data_encoding::BASE64,
 | 
					    data_encoding::BASE64,
 | 
				
			||||||
    data_encoding::BASE64URL,
 | 
					    data_encoding::BASE64URL,
 | 
				
			||||||
@ -561,7 +575,7 @@ pub fn change_kcl_settings(code: &str, settings_str: &str) -> Result<String, Str
 | 
				
			|||||||
    console_error_panic_hook::set_once();
 | 
					    console_error_panic_hook::set_once();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let settings: kcl_lib::MetaSettings = serde_json::from_str(settings_str).map_err(|e| e.to_string())?;
 | 
					    let settings: kcl_lib::MetaSettings = serde_json::from_str(settings_str).map_err(|e| e.to_string())?;
 | 
				
			||||||
    let mut program = Program::parse_no_errs(code).map_err(|e| e.to_string())?;
 | 
					    let program = Program::parse_no_errs(code).map_err(|e| e.to_string())?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let new_program = program.change_meta_settings(settings).map_err(|e| e.to_string())?;
 | 
					    let new_program = program.change_meta_settings(settings).map_err(|e| e.to_string())?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user