Compare commits
12 Commits
pierremtb/
...
v0.30.0
Author | SHA1 | Date | |
---|---|---|---|
00e97257ae | |||
aeb656d176 | |||
ac49ebd6e0 | |||
b40f03ad25 | |||
a8ad86e645 | |||
87f50cd5e9 | |||
0400e6228e | |||
26f150fd6c | |||
3049f405f5 | |||
53d40301dc | |||
671c01e36f | |||
e80151979b |
3
.github/workflows/build-apps.yml
vendored
@ -165,7 +165,6 @@ jobs:
|
||||
- name: Build the app (release)
|
||||
if: ${{ env.IS_RELEASE == 'true' || env.IS_NIGHTLY == 'true' }}
|
||||
env:
|
||||
PUBLISH_FOR_PULL_REQUEST: true
|
||||
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||||
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
|
||||
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
|
||||
@ -173,7 +172,6 @@ jobs:
|
||||
CSC_LINK: ${{ secrets.APPLE_CERTIFICATE }}
|
||||
CSC_KEY_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
|
||||
CSC_KEYCHAIN: ${{ secrets.APPLE_SIGNING_IDENTITY }}
|
||||
CSC_FOR_PULL_REQUEST: true
|
||||
WINDOWS_CERTIFICATE_THUMBPRINT: ${{ secrets.WINDOWS_CERTIFICATE_THUMBPRINT }}
|
||||
run: yarn electron-builder --config --publish always
|
||||
|
||||
@ -229,7 +227,6 @@ jobs:
|
||||
CSC_LINK: ${{ secrets.APPLE_CERTIFICATE }}
|
||||
CSC_KEY_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
|
||||
CSC_KEYCHAIN: ${{ secrets.APPLE_SIGNING_IDENTITY }}
|
||||
CSC_FOR_PULL_REQUEST: true
|
||||
WINDOWS_CERTIFICATE_THUMBPRINT: ${{ secrets.WINDOWS_CERTIFICATE_THUMBPRINT }}
|
||||
run: yarn electron-builder --config --publish always
|
||||
|
||||
|
@ -22,3 +22,5 @@ once fixed in engine will just start working here with no language changes.
|
||||
|
||||
- **Chamfers**: Chamfers cannot intersect, you will get an error. Only simple
|
||||
chamfer cases work currently.
|
||||
|
||||
- **Appearance**: Changing the appearance on a loft does not work.
|
||||
|
210
docs/kcl/appearance.md
Normal file
@ -19,6 +19,7 @@ layout: manual
|
||||
* [`angledLineThatIntersects`](kcl/angledLineThatIntersects)
|
||||
* [`angledLineToX`](kcl/angledLineToX)
|
||||
* [`angledLineToY`](kcl/angledLineToY)
|
||||
* [`appearance`](kcl/appearance)
|
||||
* [`arc`](kcl/arc)
|
||||
* [`arcTo`](kcl/arcTo)
|
||||
* [`asin`](kcl/asin)
|
||||
|
2883
docs/kcl/std.json
23
docs/kcl/types/AppearanceData.md
Normal file
@ -0,0 +1,23 @@
|
||||
---
|
||||
title: "AppearanceData"
|
||||
excerpt: "Data for appearance."
|
||||
layout: manual
|
||||
---
|
||||
|
||||
Data for appearance.
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `color` |`string`| Color of the new material, a hex string like "#ff0000". | No |
|
||||
| `metalness` |`number` (**maximum:** 100.0)| Metalness of the new material, a percentage like 95.7. | No |
|
||||
| `roughness` |`number` (**maximum:** 100.0)| Roughness of the new material, a percentage like 95.7. | No |
|
||||
|
||||
|
@ -950,7 +950,75 @@ test(
|
||||
|
||||
test.describe('Grid visibility', { tag: '@snapshot' }, () => {
|
||||
// FIXME: Skip on macos its being weird.
|
||||
test.skip(process.platform === 'darwin', 'Skip on macos')
|
||||
// test.skip(process.platform === 'darwin', 'Skip on macos')
|
||||
|
||||
test('Grid turned off to on via command bar', async ({ page }) => {
|
||||
const u = await getUtils(page)
|
||||
const stream = page.getByTestId('stream')
|
||||
const mask = [
|
||||
page.locator('#app-header'),
|
||||
page.locator('#sidebar-top-ribbon'),
|
||||
page.locator('#sidebar-bottom-ribbon'),
|
||||
]
|
||||
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.goto('/')
|
||||
await u.waitForAuthSkipAppStart()
|
||||
|
||||
await u.openDebugPanel()
|
||||
// wait for execution done
|
||||
await expect(
|
||||
page.locator('[data-message-type="execution-done"]')
|
||||
).toHaveCount(1)
|
||||
await u.closeDebugPanel()
|
||||
await u.closeKclCodePanel()
|
||||
// TODO: Find a way to truly know that the objects have finished
|
||||
// rendering, because an execution-done message is not sufficient.
|
||||
await page.waitForTimeout(1000)
|
||||
|
||||
// Open the command bar.
|
||||
await page
|
||||
.getByRole('button', { name: 'Commands', exact: false })
|
||||
.or(page.getByRole('button', { name: '⌘K' }))
|
||||
.click()
|
||||
const commandName = 'show scale grid'
|
||||
const commandOption = page.getByRole('option', {
|
||||
name: commandName,
|
||||
exact: false,
|
||||
})
|
||||
const cmdSearchBar = page.getByPlaceholder('Search commands')
|
||||
// This selector changes after we set the setting
|
||||
await cmdSearchBar.fill(commandName)
|
||||
await expect(commandOption).toBeVisible()
|
||||
await commandOption.click()
|
||||
|
||||
const toggleInput = page.getByPlaceholder('Off')
|
||||
await expect(toggleInput).toBeVisible()
|
||||
await expect(toggleInput).toBeFocused()
|
||||
|
||||
// Select On
|
||||
await page.keyboard.press('ArrowDown')
|
||||
await expect(page.getByRole('option', { name: 'Off' })).toHaveAttribute(
|
||||
'data-headlessui-state',
|
||||
'active selected'
|
||||
)
|
||||
await page.keyboard.press('ArrowUp')
|
||||
await expect(page.getByRole('option', { name: 'On' })).toHaveAttribute(
|
||||
'data-headlessui-state',
|
||||
'active'
|
||||
)
|
||||
await page.keyboard.press('Enter')
|
||||
|
||||
// Check the toast appeared
|
||||
await expect(
|
||||
page.getByText(`Set show scale grid to "true" as a user default`)
|
||||
).toBeVisible()
|
||||
|
||||
await expect(stream).toHaveScreenshot({
|
||||
maxDiffPixels: 100,
|
||||
mask,
|
||||
})
|
||||
})
|
||||
|
||||
test('Grid turned off', async ({ page }) => {
|
||||
const u = await getUtils(page)
|
||||
|
After Width: | Height: | Size: 52 KiB |
After Width: | Height: | Size: 54 KiB |
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 40 KiB |
@ -1,20 +1,9 @@
|
||||
import type { ForgeConfig } from '@electron-forge/shared-types'
|
||||
import { MakerSquirrel } from '@electron-forge/maker-squirrel'
|
||||
import { MakerZIP } from '@electron-forge/maker-zip'
|
||||
import { MakerDeb } from '@electron-forge/maker-deb'
|
||||
import { MakerRpm } from '@electron-forge/maker-rpm'
|
||||
import { VitePlugin } from '@electron-forge/plugin-vite'
|
||||
import { MakerWix, MakerWixConfig } from '@electron-forge/maker-wix'
|
||||
import { FusesPlugin } from '@electron-forge/plugin-fuses'
|
||||
import { FuseV1Options, FuseVersion } from '@electron/fuses'
|
||||
import path from 'path'
|
||||
|
||||
interface ExtendedMakerWixConfig extends MakerWixConfig {
|
||||
// see https://github.com/electron/forge/issues/3673
|
||||
// this is an undocumented property of electron-wix-msi
|
||||
associateExtensions?: string
|
||||
}
|
||||
|
||||
const rootDir = process.cwd()
|
||||
|
||||
const config: ForgeConfig = {
|
||||
@ -39,26 +28,7 @@ const config: ForgeConfig = {
|
||||
extendInfo: 'Info.plist', // Information for file associations.
|
||||
},
|
||||
rebuildConfig: {},
|
||||
makers: [
|
||||
new MakerSquirrel({
|
||||
setupIcon: path.resolve(rootDir, 'assets', 'icon.ico'),
|
||||
}),
|
||||
new MakerWix({
|
||||
icon: path.resolve(rootDir, 'assets', 'icon.ico'),
|
||||
associateExtensions: 'kcl',
|
||||
} as ExtendedMakerWixConfig),
|
||||
new MakerZIP({}, ['darwin']),
|
||||
new MakerRpm({
|
||||
options: {
|
||||
icon: path.resolve(rootDir, 'assets', 'icon.png'),
|
||||
},
|
||||
}),
|
||||
new MakerDeb({
|
||||
options: {
|
||||
icon: path.resolve(rootDir, 'assets', 'icon.png'),
|
||||
},
|
||||
}),
|
||||
],
|
||||
makers: [],
|
||||
plugins: [
|
||||
new VitePlugin({
|
||||
// `build` can specify multiple entry builds, which can be Main process, Preload scripts, Worker process, etc.
|
||||
|
26
package.json
@ -39,7 +39,6 @@
|
||||
"chokidar": "^4.0.1",
|
||||
"codemirror": "^6.0.1",
|
||||
"decamelize": "^6.0.0",
|
||||
"electron-squirrel-startup": "^1.0.1",
|
||||
"electron-updater": "6.3.0",
|
||||
"fuse.js": "^7.0.0",
|
||||
"html2canvas-pro": "^1.5.8",
|
||||
@ -69,7 +68,7 @@
|
||||
"yargs": "^17.7.2"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "vite",
|
||||
"start": "vite --port=3000 --host=0.0.0.0",
|
||||
"start:prod": "vite preview --port=3000",
|
||||
"serve": "vite serve --port=3000",
|
||||
"build": "curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y && source \"$HOME/.cargo/env\" && curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh -s -- -y && yarn build:wasm && vite build",
|
||||
@ -104,8 +103,6 @@
|
||||
"generate:machine-api": "npx openapi-typescript ./openapi/machine-api.json -o src/lib/machine-api.d.ts",
|
||||
"tron:start": "electron-forge start",
|
||||
"tron:package": "electron-forge package",
|
||||
"tron:make": "electron-forge make",
|
||||
"tron:publish": "electron-forge publish",
|
||||
"tron:test": "NODE_ENV=development yarn playwright test --config=playwright.electron.config.ts --grep=@electron",
|
||||
"tronb:vite": "vite build -c vite.main.config.ts && vite build -c vite.preload.config.ts && vite build -c vite.renderer.config.ts",
|
||||
"tronb:package": "electron-builder --config electron-builder.yml",
|
||||
@ -148,17 +145,10 @@
|
||||
"devDependencies": {
|
||||
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
|
||||
"@babel/preset-env": "^7.25.4",
|
||||
"@electron-forge/cli": "^7.4.0",
|
||||
"@electron-forge/maker-deb": "^7.4.0",
|
||||
"@electron-forge/maker-rpm": "^7.4.0",
|
||||
"@electron-forge/maker-squirrel": "^7.4.0",
|
||||
"@electron-forge/maker-wix": "^7.5.0",
|
||||
"@electron-forge/maker-zip": "^7.5.0",
|
||||
"@electron-forge/plugin-auto-unpack-natives": "^7.4.0",
|
||||
"@electron-forge/plugin-fuses": "^7.4.0",
|
||||
"@electron-forge/plugin-vite": "^7.4.0",
|
||||
"@electron/fuses": "^1.8.0",
|
||||
"@electron/rebuild": "^3.6.0",
|
||||
"@electron-forge/cli": "7.4.0",
|
||||
"@electron-forge/plugin-fuses": "7.4.0",
|
||||
"@electron-forge/plugin-vite": "7.4.0",
|
||||
"@electron/fuses": "1.8.0",
|
||||
"@iarna/toml": "^2.2.5",
|
||||
"@lezer/generator": "^1.7.1",
|
||||
"@nabla/vite-plugin-eslint": "^2.0.5",
|
||||
@ -188,9 +178,9 @@
|
||||
"@xstate/cli": "^0.5.17",
|
||||
"autoprefixer": "^10.4.19",
|
||||
"d3-force": "^3.0.0",
|
||||
"electron": "^32.1.2",
|
||||
"electron-builder": "^24.13.3",
|
||||
"electron-notarize": "^1.2.2",
|
||||
"electron": "32.1.2",
|
||||
"electron-builder": "24.13.3",
|
||||
"electron-notarize": "1.2.2",
|
||||
"eslint": "^8.0.1",
|
||||
"eslint-config-react-app": "^7.0.1",
|
||||
"eslint-plugin-css-modules": "^2.12.0",
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { APP_VERSION, RELEASE_URL } from 'routes/Settings'
|
||||
import { APP_VERSION, getReleaseUrl } from 'routes/Settings'
|
||||
import { CustomIcon } from 'components/CustomIcon'
|
||||
import Tooltip from 'components/Tooltip'
|
||||
import { PATHS } from 'lib/paths'
|
||||
@ -72,8 +72,8 @@ export function LowerRightControls({
|
||||
<menu className="flex items-center justify-end gap-3 pointer-events-auto">
|
||||
{!location.pathname.startsWith(PATHS.HOME) && <ModelStateIndicator />}
|
||||
<a
|
||||
onClick={openExternalBrowserIfDesktop(RELEASE_URL)}
|
||||
href={RELEASE_URL}
|
||||
onClick={openExternalBrowserIfDesktop(getReleaseUrl())}
|
||||
href={getReleaseUrl()}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className={'!no-underline font-mono text-xs ' + linkOverrideClassName}
|
||||
|
@ -69,14 +69,7 @@ export const LspProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
const [isKclLspReady, setIsKclLspReady] = useState(false)
|
||||
const [isCopilotLspReady, setIsCopilotLspReady] = useState(false)
|
||||
|
||||
const {
|
||||
auth,
|
||||
settings: {
|
||||
context: {
|
||||
modeling: { defaultUnit },
|
||||
},
|
||||
},
|
||||
} = useSettingsAuthContext()
|
||||
const { auth } = useSettingsAuthContext()
|
||||
const token = auth?.context.token
|
||||
const navigate = useNavigate()
|
||||
|
||||
@ -92,7 +85,6 @@ export const LspProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
const initEvent: KclWorkerOptions = {
|
||||
wasmUrl: wasmUrl(),
|
||||
token: token,
|
||||
baseUnit: defaultUnit.current,
|
||||
apiBaseUrl: VITE_KC_API_BASE_URL,
|
||||
}
|
||||
lspWorker.postMessage({
|
||||
|
@ -13,7 +13,7 @@ import { isDesktop } from 'lib/isDesktop'
|
||||
import { ActionButton } from 'components/ActionButton'
|
||||
import { SettingsFieldInput } from './SettingsFieldInput'
|
||||
import toast from 'react-hot-toast'
|
||||
import { APP_VERSION, IS_NIGHTLY, RELEASE_URL } from 'routes/Settings'
|
||||
import { APP_VERSION, IS_NIGHTLY, getReleaseUrl } from 'routes/Settings'
|
||||
import { PATHS } from 'lib/paths'
|
||||
import {
|
||||
createAndOpenNewTutorialProject,
|
||||
@ -246,8 +246,8 @@ export const AllSettingsFields = forwardRef(
|
||||
to inject the version from package.json */}
|
||||
App version {APP_VERSION}.{' '}
|
||||
<a
|
||||
onClick={openExternalBrowserIfDesktop(RELEASE_URL)}
|
||||
href={RELEASE_URL}
|
||||
onClick={openExternalBrowserIfDesktop(getReleaseUrl())}
|
||||
href={getReleaseUrl()}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { trap } from 'lib/trap'
|
||||
import { useMachine } from '@xstate/react'
|
||||
import { useMachine, useSelector } from '@xstate/react'
|
||||
import { useNavigate, useRouteLoaderData, useLocation } from 'react-router-dom'
|
||||
import { PATHS, BROWSER_PATH } from 'lib/paths'
|
||||
import { authMachine, TOKEN_PERSIST_KEY } from '../machines/authMachine'
|
||||
@ -23,7 +23,6 @@ import {
|
||||
engineCommandManager,
|
||||
sceneEntitiesManager,
|
||||
} from 'lib/singletons'
|
||||
import { uuidv4 } from 'lib/utils'
|
||||
import { IndexLoaderData } from 'lib/types'
|
||||
import { settings } from 'lib/settings/initialSettings'
|
||||
import {
|
||||
@ -55,11 +54,15 @@ type SettingsAuthContextType = {
|
||||
settings: MachineContext<typeof settingsMachine>
|
||||
}
|
||||
|
||||
// a little hacky for sure, open to changing it
|
||||
// this implies that we should only even have one instance of this provider mounted at any one time
|
||||
// but I think that's a safe assumption
|
||||
let settingsStateRef: ContextFrom<typeof settingsMachine> | undefined
|
||||
export const getSettingsState = () => settingsStateRef
|
||||
/**
|
||||
* This variable is used to store the last snapshot of the settings context
|
||||
* for use outside of React, such as in `wasm.ts`. It is updated every time
|
||||
* the settings machine changes with `useSelector`.
|
||||
* TODO: when we decouple XState from React, we can just subscribe to the actor directly from `wasm.ts`
|
||||
*/
|
||||
export let lastSettingsContextSnapshot:
|
||||
| ContextFrom<typeof settingsMachine>
|
||||
| undefined
|
||||
|
||||
export const SettingsAuthContext = createContext({} as SettingsAuthContextType)
|
||||
|
||||
@ -129,27 +132,11 @@ export const SettingsAuthProviderBase = ({
|
||||
.setTheme(context.app.theme.current)
|
||||
.catch(reportRejection)
|
||||
},
|
||||
setEngineScaleGridVisibility: ({ context }) => {
|
||||
engineCommandManager.setScaleGridVisibility(
|
||||
context.modeling.showScaleGrid.current
|
||||
)
|
||||
},
|
||||
setClientTheme: ({ context }) => {
|
||||
const opposingTheme = getOppositeTheme(context.app.theme.current)
|
||||
sceneInfra.theme = opposingTheme
|
||||
sceneEntitiesManager.updateSegmentBaseColor(opposingTheme)
|
||||
},
|
||||
setEngineEdges: ({ context }) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
engineCommandManager.sendSceneCommand({
|
||||
cmd_id: uuidv4(),
|
||||
type: 'modeling_cmd_req',
|
||||
cmd: {
|
||||
type: 'edge_lines_visible' as any, // TODO update kittycad.ts to get this new command type
|
||||
hidden: !context.modeling.highlightEdges.current,
|
||||
},
|
||||
})
|
||||
},
|
||||
toastSuccess: ({ event }) => {
|
||||
if (!('data' in event)) return
|
||||
const eventParts = event.type.replace(/^set./, '').split('.') as [
|
||||
@ -175,17 +162,27 @@ export const SettingsAuthProviderBase = ({
|
||||
},
|
||||
'Execute AST': ({ context, event }) => {
|
||||
try {
|
||||
const relevantSetting = (s: typeof settings) => {
|
||||
return (
|
||||
s.modeling?.defaultUnit?.current !==
|
||||
context.modeling.defaultUnit.current ||
|
||||
s.modeling.showScaleGrid.current !==
|
||||
context.modeling.showScaleGrid.current ||
|
||||
s.modeling?.highlightEdges.current !==
|
||||
context.modeling.highlightEdges.current
|
||||
)
|
||||
}
|
||||
|
||||
const allSettingsIncludesUnitChange =
|
||||
event.type === 'Set all settings' &&
|
||||
event.settings?.modeling?.defaultUnit?.current !==
|
||||
context.modeling.defaultUnit.current
|
||||
relevantSetting(event.settings)
|
||||
const resetSettingsIncludesUnitChange =
|
||||
event.type === 'Reset settings' &&
|
||||
context.modeling.defaultUnit.current !==
|
||||
settings?.modeling?.defaultUnit?.default
|
||||
event.type === 'Reset settings' && relevantSetting(settings)
|
||||
|
||||
if (
|
||||
event.type === 'set.modeling.defaultUnit' ||
|
||||
event.type === 'set.modeling.showScaleGrid' ||
|
||||
event.type === 'set.modeling.highlightEdges' ||
|
||||
allSettingsIncludesUnitChange ||
|
||||
resetSettingsIncludesUnitChange
|
||||
) {
|
||||
@ -214,7 +211,10 @@ export const SettingsAuthProviderBase = ({
|
||||
}),
|
||||
{ input: loadedSettings }
|
||||
)
|
||||
settingsStateRef = settingsState.context
|
||||
// Any time the actor changes, update the settings state for external use
|
||||
useSelector(settingsActor, (s) => {
|
||||
lastSettingsContextSnapshot = s.context
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
if (!isDesktop()) return
|
||||
|
@ -2,6 +2,7 @@ import toast from 'react-hot-toast'
|
||||
import { ActionButton } from './ActionButton'
|
||||
import { openExternalBrowserIfDesktop } from 'lib/openWindow'
|
||||
import { Marked } from '@ts-stack/markdown'
|
||||
import { getReleaseUrl } from 'routes/Settings'
|
||||
|
||||
export function ToastUpdate({
|
||||
version,
|
||||
@ -32,10 +33,8 @@ export function ToastUpdate({
|
||||
A new update has downloaded and will be available next time you
|
||||
start the app. You can view the release notes{' '}
|
||||
<a
|
||||
onClick={openExternalBrowserIfDesktop(
|
||||
`https://github.com/KittyCAD/modeling-app/releases/tag/v${version}`
|
||||
)}
|
||||
href={`https://github.com/KittyCAD/modeling-app/releases/tag/v${version}`}
|
||||
onClick={openExternalBrowserIfDesktop(getReleaseUrl(version))}
|
||||
href={getReleaseUrl(version)}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
|
@ -41,7 +41,10 @@ export function UnitsMenu() {
|
||||
close()
|
||||
}}
|
||||
>
|
||||
{baseUnitLabels[unit]}
|
||||
<span className="flex-1">{baseUnitLabels[unit]}</span>
|
||||
{unit === settings.context.modeling.defaultUnit.current && (
|
||||
<span className="text-chalkboard-60">current</span>
|
||||
)}
|
||||
</button>
|
||||
</li>
|
||||
))}
|
||||
|
@ -1,7 +1,5 @@
|
||||
import { LspWorkerEventType } from '@kittycad/codemirror-lsp-client'
|
||||
|
||||
import { UnitLength } from 'wasm-lib/kcl/bindings/UnitLength'
|
||||
|
||||
export enum LspWorker {
|
||||
Kcl = 'kcl',
|
||||
Copilot = 'copilot',
|
||||
@ -9,7 +7,6 @@ export enum LspWorker {
|
||||
export interface KclWorkerOptions {
|
||||
wasmUrl: string
|
||||
token: string
|
||||
baseUnit: UnitLength
|
||||
apiBaseUrl: string
|
||||
}
|
||||
|
||||
|
@ -17,7 +17,6 @@ import {
|
||||
KclWorkerOptions,
|
||||
CopilotWorkerOptions,
|
||||
} from 'editor/plugins/lsp/types'
|
||||
import { EngineCommandManager } from 'lang/std/engineConnection'
|
||||
import { err, reportRejection } from 'lib/trap'
|
||||
|
||||
const intoServer: IntoServer = new IntoServer()
|
||||
@ -46,14 +45,12 @@ export async function copilotLspRun(
|
||||
|
||||
export async function kclLspRun(
|
||||
config: ServerConfig,
|
||||
engineCommandManager: EngineCommandManager | null,
|
||||
token: string,
|
||||
baseUnit: string,
|
||||
baseUrl: string
|
||||
) {
|
||||
try {
|
||||
console.log('start kcl lsp')
|
||||
await kcl_lsp_run(config, engineCommandManager, baseUnit, token, baseUrl)
|
||||
await kcl_lsp_run(config, null, undefined, token, baseUrl)
|
||||
} catch (e: any) {
|
||||
console.log('kcl lsp failed', e)
|
||||
// We can't restart here because a moved value, we should do this another way.
|
||||
@ -82,13 +79,7 @@ onmessage = function (event: MessageEvent) {
|
||||
switch (worker) {
|
||||
case LspWorker.Kcl:
|
||||
const kclData = eventData as KclWorkerOptions
|
||||
await kclLspRun(
|
||||
config,
|
||||
null,
|
||||
kclData.token,
|
||||
kclData.baseUnit,
|
||||
kclData.apiBaseUrl
|
||||
)
|
||||
await kclLspRun(config, kclData.token, kclData.apiBaseUrl)
|
||||
break
|
||||
case LspWorker.Copilot:
|
||||
let copilotData = eventData as CopilotWorkerOptions
|
||||
|
@ -2,7 +2,7 @@ import { useLayoutEffect, useEffect, useRef } from 'react'
|
||||
import { engineCommandManager, kclManager } from 'lib/singletons'
|
||||
import { deferExecution } from 'lib/utils'
|
||||
import { Themes } from 'lib/theme'
|
||||
import { makeDefaultPlanes, modifyGrid } from 'lang/wasm'
|
||||
import { makeDefaultPlanes } from 'lang/wasm'
|
||||
import { useModelingContext } from './useModelingContext'
|
||||
import { useNetworkContext } from 'hooks/useNetworkContext'
|
||||
import { useAppState, useAppStream } from 'AppState'
|
||||
@ -56,9 +56,6 @@ export function useSetupEngineManager(
|
||||
makeDefaultPlanes: () => {
|
||||
return makeDefaultPlanes(kclManager.engineCommandManager)
|
||||
},
|
||||
modifyGrid: (hidden: boolean) => {
|
||||
return modifyGrid(kclManager.engineCommandManager, hidden)
|
||||
},
|
||||
})
|
||||
hasSetNonZeroDimensions.current = true
|
||||
}
|
||||
|
@ -317,3 +317,8 @@ code {
|
||||
#code-mirror-override .cm-editor {
|
||||
height: 100% !important;
|
||||
}
|
||||
|
||||
/* Can't use #code-mirror-override here as we're outside of this div */
|
||||
.body-bg .cm-diagnosticAction {
|
||||
@apply bg-primary;
|
||||
}
|
||||
|
@ -40,7 +40,6 @@ beforeAll(async () => {
|
||||
makeDefaultPlanes: () => makeDefaultPlanes(engineCommandManager),
|
||||
setMediaStream: () => {},
|
||||
setIsStreamReady: () => {},
|
||||
modifyGrid: async () => {},
|
||||
callbackOnEngineLiteConnect: () => {
|
||||
resolve(true)
|
||||
},
|
||||
|
@ -139,7 +139,6 @@ beforeAll(async () => {
|
||||
makeDefaultPlanes: () => makeDefaultPlanes(engineCommandManager),
|
||||
setMediaStream: () => {},
|
||||
setIsStreamReady: () => {},
|
||||
modifyGrid: async () => {},
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||
callbackOnEngineLiteConnect: async () => {
|
||||
const cacheEntries = Object.entries(codeToWriteCacheFor) as [
|
||||
|
@ -1399,7 +1399,6 @@ export class EngineCommandManager extends EventTarget {
|
||||
}
|
||||
|
||||
private makeDefaultPlanes: () => Promise<DefaultPlanes> | null = () => null
|
||||
private modifyGrid: (hidden: boolean) => Promise<void> | null = () => null
|
||||
|
||||
private onEngineConnectionOpened = () => {}
|
||||
private onEngineConnectionClosed = () => {}
|
||||
@ -1432,7 +1431,6 @@ export class EngineCommandManager extends EventTarget {
|
||||
height,
|
||||
token,
|
||||
makeDefaultPlanes,
|
||||
modifyGrid,
|
||||
settings = {
|
||||
pool: null,
|
||||
theme: Themes.Dark,
|
||||
@ -1452,14 +1450,12 @@ export class EngineCommandManager extends EventTarget {
|
||||
height: number
|
||||
token?: string
|
||||
makeDefaultPlanes: () => Promise<DefaultPlanes>
|
||||
modifyGrid: (hidden: boolean) => Promise<void>
|
||||
settings?: SettingsViaQueryString
|
||||
}) {
|
||||
if (settings) {
|
||||
this.settings = settings
|
||||
}
|
||||
this.makeDefaultPlanes = makeDefaultPlanes
|
||||
this.modifyGrid = modifyGrid
|
||||
if (width === 0 || height === 0) {
|
||||
return
|
||||
}
|
||||
@ -1539,21 +1535,15 @@ export class EngineCommandManager extends EventTarget {
|
||||
type: 'default_camera_get_settings',
|
||||
},
|
||||
})
|
||||
// We want modify the grid first because we don't want it to flash.
|
||||
// Ideally these would already be default hidden in engine (TODO do
|
||||
// that) https://github.com/KittyCAD/engine/issues/2282
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.modifyGrid(!this.settings.showScaleGrid)?.then(async () => {
|
||||
await this.initPlanes()
|
||||
setIsStreamReady(true)
|
||||
await this.initPlanes()
|
||||
setIsStreamReady(true)
|
||||
|
||||
// Other parts of the application should use this to react on scene ready.
|
||||
this.dispatchEvent(
|
||||
new CustomEvent(EngineCommandManagerEvents.SceneReady, {
|
||||
detail: this.engineConnection,
|
||||
})
|
||||
)
|
||||
})
|
||||
// Other parts of the application should use this to react on scene ready.
|
||||
this.dispatchEvent(
|
||||
new CustomEvent(EngineCommandManagerEvents.SceneReady, {
|
||||
detail: this.engineConnection,
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
this.engineConnection.addEventListener(
|
||||
@ -2212,15 +2202,6 @@ export class EngineCommandManager extends EventTarget {
|
||||
}).catch(reportRejection)
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the visibility of the scale grid in the engine scene.
|
||||
* @param visible - whether to show or hide the scale grid
|
||||
*/
|
||||
setScaleGridVisibility(visible: boolean) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.modifyGrid(!visible)
|
||||
}
|
||||
|
||||
// Some "objects" have the same source range, such as sketch_mode_start and start_path.
|
||||
// So when passing a range, we need to also specify the command type
|
||||
mapRangeToObjectId(
|
||||
|
@ -1,14 +1,13 @@
|
||||
import init, {
|
||||
parse_wasm,
|
||||
recast_wasm,
|
||||
execute_wasm,
|
||||
execute,
|
||||
kcl_lint,
|
||||
modify_ast_for_sketch_wasm,
|
||||
is_points_ccw,
|
||||
get_tangential_arc_to_info,
|
||||
program_memory_init,
|
||||
make_default_planes,
|
||||
modify_grid,
|
||||
coredump,
|
||||
toml_stringify,
|
||||
default_app_settings,
|
||||
@ -43,7 +42,9 @@ import { Environment } from '../wasm-lib/kcl/bindings/Environment'
|
||||
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||
import { CompilationError } from 'wasm-lib/kcl/bindings/CompilationError'
|
||||
import { SourceRange as RustSourceRange } from 'wasm-lib/kcl/bindings/SourceRange'
|
||||
import { getAllCurrentSettings } from 'lib/settings/settingsUtils'
|
||||
|
||||
export type { Configuration } from 'wasm-lib/kcl/bindings/Configuration'
|
||||
export type { Program } from '../wasm-lib/kcl/bindings/Program'
|
||||
export type { Expr } from '../wasm-lib/kcl/bindings/Expr'
|
||||
export type { ObjectExpression } from '../wasm-lib/kcl/bindings/ObjectExpression'
|
||||
@ -92,12 +93,26 @@ export type { Solid } from '../wasm-lib/kcl/bindings/Solid'
|
||||
export type { KclValue } from '../wasm-lib/kcl/bindings/KclValue'
|
||||
export type { ExtrudeSurface } from '../wasm-lib/kcl/bindings/ExtrudeSurface'
|
||||
|
||||
/**
|
||||
* The first two items are the start and end points (byte offsets from the start of the file).
|
||||
* The third item is whether the source range belongs to the 'main' file, i.e., the file currently
|
||||
* being rendered/displayed in the editor (TODO we need to handle modules better in the frontend).
|
||||
*/
|
||||
export type SourceRange = [number, number, boolean]
|
||||
|
||||
/**
|
||||
* Convert a SourceRange as used inside the KCL interpreter into the above one for use in the
|
||||
* frontend (essentially we're eagerly checking whether the frontend should care about the SourceRange
|
||||
* so as not to expose details of the interpreter's current representation of module ids throughout
|
||||
* the frontend).
|
||||
*/
|
||||
export function sourceRangeFromRust(s: RustSourceRange): SourceRange {
|
||||
return [s[0], s[1], s[2] === 0]
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a default SourceRange for testing or as a placeholder.
|
||||
*/
|
||||
export function defaultSourceRange(): SourceRange {
|
||||
return [0, 0, true]
|
||||
}
|
||||
@ -122,7 +137,7 @@ const initialise = async () => {
|
||||
const fullUrl = wasmUrl()
|
||||
const input = await fetch(fullUrl)
|
||||
const buffer = await input.arrayBuffer()
|
||||
return await init(buffer)
|
||||
return await init({ module_or_path: buffer })
|
||||
} catch (e) {
|
||||
console.log('Error initialising WASM', e)
|
||||
return Promise.reject(e)
|
||||
@ -163,6 +178,10 @@ export class ParseResult {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parsing was successful. There is guaranteed to be an AST and no fatal errors. There may or may
|
||||
* not be warnings or non-fatal errors.
|
||||
*/
|
||||
class SuccessParseResult extends ParseResult {
|
||||
program: Node<Program>
|
||||
|
||||
@ -493,18 +512,19 @@ export const _executor = async (
|
||||
return Promise.reject(programMemoryOverride)
|
||||
|
||||
try {
|
||||
let baseUnit = 'mm'
|
||||
let jsAppSettings = default_app_settings()
|
||||
if (!TEST) {
|
||||
const getSettingsState = import('components/SettingsAuthProvider').then(
|
||||
(module) => module.getSettingsState
|
||||
)
|
||||
baseUnit =
|
||||
(await getSettingsState)()?.modeling.defaultUnit.current || 'mm'
|
||||
const lastSettingsSnapshot = await import(
|
||||
'components/SettingsAuthProvider'
|
||||
).then((module) => module.lastSettingsContextSnapshot)
|
||||
if (lastSettingsSnapshot) {
|
||||
jsAppSettings = getAllCurrentSettings(lastSettingsSnapshot)
|
||||
}
|
||||
}
|
||||
const execState: RawExecState = await execute_wasm(
|
||||
const execState: RawExecState = await execute(
|
||||
JSON.stringify(node),
|
||||
JSON.stringify(programMemoryOverride?.toRaw() || null),
|
||||
baseUnit,
|
||||
JSON.stringify({ settings: jsAppSettings }),
|
||||
engineCommandManager,
|
||||
fileSystemManager
|
||||
)
|
||||
@ -552,20 +572,6 @@ export const makeDefaultPlanes = async (
|
||||
}
|
||||
}
|
||||
|
||||
export const modifyGrid = async (
|
||||
engineCommandManager: EngineCommandManager,
|
||||
hidden: boolean
|
||||
): Promise<void> => {
|
||||
try {
|
||||
await modify_grid(engineCommandManager, hidden)
|
||||
return
|
||||
} catch (e) {
|
||||
// TODO: do something real with the error.
|
||||
console.log('modify grid error', e)
|
||||
return Promise.reject(e)
|
||||
}
|
||||
}
|
||||
|
||||
export const modifyAstForSketch = async (
|
||||
engineCommandManager: EngineCommandManager,
|
||||
ast: Node<Program>,
|
||||
|
@ -2,6 +2,7 @@ import { DeepPartial } from 'lib/types'
|
||||
import { Configuration } from 'wasm-lib/kcl/bindings/Configuration'
|
||||
import {
|
||||
configurationToSettingsPayload,
|
||||
getAllCurrentSettings,
|
||||
projectConfigurationToSettingsPayload,
|
||||
setSettingsAtLevel,
|
||||
} from './settingsUtils'
|
||||
@ -65,3 +66,48 @@ describe(`testing settings initialization`, () => {
|
||||
expect(settings.app.themeColor.current).toBe('200')
|
||||
})
|
||||
})
|
||||
|
||||
describe(`testing getAllCurrentSettings`, () => {
|
||||
it(`returns the correct settings`, () => {
|
||||
// Set up the settings
|
||||
let settings = createSettings()
|
||||
const appConfiguration: DeepPartial<Configuration> = {
|
||||
settings: {
|
||||
app: {
|
||||
appearance: {
|
||||
theme: 'dark',
|
||||
color: 190,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
const projectConfiguration: DeepPartial<Configuration> = {
|
||||
settings: {
|
||||
app: {
|
||||
appearance: {
|
||||
theme: 'light',
|
||||
color: 200,
|
||||
},
|
||||
},
|
||||
modeling: {
|
||||
base_unit: 'ft',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const appSettingsPayload = configurationToSettingsPayload(appConfiguration)
|
||||
const projectSettingsPayload =
|
||||
projectConfigurationToSettingsPayload(projectConfiguration)
|
||||
|
||||
setSettingsAtLevel(settings, 'user', appSettingsPayload)
|
||||
setSettingsAtLevel(settings, 'project', projectSettingsPayload)
|
||||
|
||||
// Now the test: get all the settings' current resolved values
|
||||
const allCurrentSettings = getAllCurrentSettings(settings)
|
||||
// This one gets the 'user'-level theme because it's ignored at the project level
|
||||
// (see the test "doesn't read theme from project settings")
|
||||
expect(allCurrentSettings.app.theme).toBe('dark')
|
||||
expect(allCurrentSettings.app.themeColor).toBe('200')
|
||||
expect(allCurrentSettings.modeling.defaultUnit).toBe('ft')
|
||||
})
|
||||
})
|
||||
|
@ -286,6 +286,27 @@ export function getChangedSettingsAtLevel(
|
||||
return changedSettings
|
||||
}
|
||||
|
||||
export function getAllCurrentSettings(
|
||||
allSettings: typeof settings
|
||||
): SaveSettingsPayload {
|
||||
const currentSettings = {} as SaveSettingsPayload
|
||||
Object.entries(allSettings).forEach(([category, settingsCategory]) => {
|
||||
const categoryKey = category as keyof typeof settings
|
||||
Object.entries(settingsCategory).forEach(
|
||||
([setting, settingValue]: [string, Setting]) => {
|
||||
const settingKey =
|
||||
setting as keyof (typeof settings)[typeof categoryKey]
|
||||
currentSettings[categoryKey] = {
|
||||
...currentSettings[categoryKey],
|
||||
[settingKey]: settingValue.current,
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
return currentSettings
|
||||
}
|
||||
|
||||
export function setSettingsAtLevel(
|
||||
allSettings: typeof settings,
|
||||
level: SettingsLevel,
|
||||
|
@ -112,9 +112,6 @@ export async function executor(
|
||||
makeDefaultPlanes: () => {
|
||||
return new Promise((resolve) => resolve(defaultPlanes))
|
||||
},
|
||||
modifyGrid: (hidden: boolean) => {
|
||||
return new Promise((resolve) => resolve())
|
||||
},
|
||||
})
|
||||
|
||||
return new Promise((resolve) => {
|
||||
|
@ -42,8 +42,6 @@ export const settingsMachine = setup({
|
||||
setClientTheme: () => {},
|
||||
'Execute AST': () => {},
|
||||
toastSuccess: () => {},
|
||||
setEngineEdges: () => {},
|
||||
setEngineScaleGridVisibility: () => {},
|
||||
setClientSideSceneUnits: () => {},
|
||||
persistSettings: () => {},
|
||||
resetSettings: assign(({ context, event }) => {
|
||||
@ -172,7 +170,7 @@ export const settingsMachine = setup({
|
||||
'set.modeling.highlightEdges': {
|
||||
target: 'persisting settings',
|
||||
|
||||
actions: ['setSettingAtLevel', 'toastSuccess', 'setEngineEdges'],
|
||||
actions: ['setSettingAtLevel', 'toastSuccess', 'Execute AST'],
|
||||
},
|
||||
|
||||
'Reset settings': {
|
||||
@ -201,11 +199,7 @@ export const settingsMachine = setup({
|
||||
|
||||
'set.modeling.showScaleGrid': {
|
||||
target: 'persisting settings',
|
||||
actions: [
|
||||
'setSettingAtLevel',
|
||||
'toastSuccess',
|
||||
'setEngineScaleGridVisibility',
|
||||
],
|
||||
actions: ['setSettingAtLevel', 'toastSuccess', 'Execute AST'],
|
||||
},
|
||||
},
|
||||
},
|
||||
|
41
src/main.ts
@ -23,15 +23,6 @@ import argvFromYargs from './commandLineArgs'
|
||||
|
||||
let mainWindow: BrowserWindow | null = null
|
||||
|
||||
// Supporting multiple instances instead of multiple applications
|
||||
let cmdQPressed = false
|
||||
const instances: BrowserWindow[] = []
|
||||
const gotTheLock = app.requestSingleInstanceLock()
|
||||
if (!gotTheLock) {
|
||||
app.quit()
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
// Check the command line arguments for a project path
|
||||
const args = parseCLIArgs()
|
||||
|
||||
@ -53,11 +44,6 @@ process.env.VITE_KC_SITE_BASE_URL ??= 'https://zoo.dev'
|
||||
process.env.VITE_KC_SKIP_AUTH ??= 'false'
|
||||
process.env.VITE_KC_CONNECTION_TIMEOUT_MS ??= '15000'
|
||||
|
||||
// Handle creating/removing shortcuts on Windows when installing/uninstalling.
|
||||
if (require('electron-squirrel-startup')) {
|
||||
app.quit()
|
||||
}
|
||||
|
||||
const ZOO_STUDIO_PROTOCOL = 'zoo-studio'
|
||||
|
||||
/// Register our application to handle all "electron-fiddle://" protocols.
|
||||
@ -126,34 +112,16 @@ const createWindow = (filePath?: string): BrowserWindow => {
|
||||
|
||||
newWindow.show()
|
||||
|
||||
instances.push(newWindow)
|
||||
return newWindow
|
||||
}
|
||||
|
||||
// before-quit with multiple instances
|
||||
if (process.platform === 'darwin') {
|
||||
// Quit from the dock context menu should quit the application directly
|
||||
app.on('before-quit', () => {
|
||||
cmdQPressed = true
|
||||
})
|
||||
}
|
||||
|
||||
// Quit when all windows are closed, even on macOS. There, it's common
|
||||
// for applications and their menu bar to stay active until the user quits
|
||||
// explicitly with Cmd + Q, but it is a really weird behavior with our app.
|
||||
// app.on('window-all-closed', () => {
|
||||
// app.quit()
|
||||
// })
|
||||
app.on('window-all-closed', () => {
|
||||
if (cmdQPressed || process.platform !== 'darwin') {
|
||||
app.quit()
|
||||
}
|
||||
app.quit()
|
||||
})
|
||||
|
||||
// Various actions can trigger this event, such as launching the application for the first time,
|
||||
// attempting to re-launch the application when it's already running, or clicking on the application's dock or taskbar icon.
|
||||
app.on('activate', () => createWindow())
|
||||
|
||||
// This method will be called when Electron has finished
|
||||
// initialization and is ready to create browser windows.
|
||||
// Some APIs can only be used after this event occurs.
|
||||
@ -162,10 +130,6 @@ app.on('ready', (event, data) => {
|
||||
mainWindow = createWindow()
|
||||
})
|
||||
|
||||
// This event will be emitted inside the primary instance of your application when a second instance
|
||||
// has been executed and calls app.requestSingleInstanceLock().
|
||||
app.on('second-instance', (event, argv, workingDirectory) => createWindow())
|
||||
|
||||
// For now there is no good reason to separate these out to another file(s)
|
||||
// There is just not enough code to warrant it and further abstracts everything
|
||||
// which is already quite abstracted
|
||||
@ -287,6 +251,9 @@ export function getAutoUpdater(): AppUpdater {
|
||||
|
||||
app.on('ready', () => {
|
||||
const autoUpdater = getAutoUpdater()
|
||||
// TODO: we're getting `Error: Response ends without calling any handlers` with our setup,
|
||||
// so at the moment this isn't worth enabling
|
||||
autoUpdater.disableDifferentialDownload = true
|
||||
setTimeout(() => {
|
||||
autoUpdater.checkForUpdates().catch(reportRejection)
|
||||
}, 1000)
|
||||
|
@ -32,9 +32,11 @@ export const PACKAGE_NAME = isDesktop()
|
||||
|
||||
export const IS_NIGHTLY = PACKAGE_NAME.indexOf('-nightly') > -1
|
||||
|
||||
export const RELEASE_URL = `https://github.com/KittyCAD/modeling-app/releases/tag/${
|
||||
IS_NIGHTLY ? 'nightly-' : ''
|
||||
}v${APP_VERSION}`
|
||||
export function getReleaseUrl(version: string = APP_VERSION) {
|
||||
return `https://github.com/KittyCAD/modeling-app/releases/tag/${
|
||||
IS_NIGHTLY ? 'nightly-' : ''
|
||||
}v${version}`
|
||||
}
|
||||
|
||||
export const Settings = () => {
|
||||
const navigate = useNavigate()
|
||||
|
8
src/wasm-lib/Cargo.lock
generated
@ -1721,7 +1721,9 @@ dependencies = [
|
||||
"parse-display 0.9.1",
|
||||
"pretty_assertions",
|
||||
"pyo3",
|
||||
"regex",
|
||||
"reqwest",
|
||||
"rgba_simple",
|
||||
"ropey",
|
||||
"schemars",
|
||||
"serde",
|
||||
@ -2971,6 +2973,12 @@ dependencies = [
|
||||
"rand 0.8.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rgba_simple"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6cd655523701785087f69900df39892fb7b9b0721aa67682f571c38c32ac58a"
|
||||
|
||||
[[package]]
|
||||
name = "ring"
|
||||
version = "0.17.8"
|
||||
|
@ -40,10 +40,12 @@ miette = "7.2.0"
|
||||
mime_guess = "2.0.5"
|
||||
parse-display = "0.9.1"
|
||||
pyo3 = { version = "0.22.6", optional = true }
|
||||
regex = "1.11.1"
|
||||
reqwest = { version = "0.12", default-features = false, features = [
|
||||
"stream",
|
||||
"rustls-tls",
|
||||
] }
|
||||
rgba_simple = "0.10.0"
|
||||
ropey = "1.6.1"
|
||||
schemars = { version = "0.8.17", features = [
|
||||
"impl_json_schema",
|
||||
|
@ -489,6 +489,12 @@ fn get_autocomplete_snippet_from_schema(
|
||||
continue;
|
||||
}
|
||||
|
||||
if prop_name == "color" {
|
||||
fn_docs.push_str(&format!("\t{}: ${{{}:\"#ff0000\"}},\n", prop_name, i));
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some((new_index, snippet)) = get_autocomplete_snippet_from_schema(prop, i)? {
|
||||
fn_docs.push_str(&format!("\t{}: {},\n", prop_name, snippet));
|
||||
i = new_index + 1;
|
||||
@ -946,6 +952,21 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_autocomplete_snippet_appearance() {
|
||||
let appearance_fn: Box<dyn StdLibFn> = Box::new(crate::std::appearance::Appearance);
|
||||
let snippet = appearance_fn.to_autocomplete_snippet().unwrap();
|
||||
assert_eq!(
|
||||
snippet,
|
||||
r#"appearance({
|
||||
color: ${0:"#
|
||||
.to_owned()
|
||||
+ "\"#"
|
||||
+ r#"ff0000"},
|
||||
}, ${1:%})${}"#
|
||||
);
|
||||
}
|
||||
|
||||
// We want to test the snippets we compile at lsp start.
|
||||
#[test]
|
||||
fn get_all_stdlib_autocomplete_snippets() {
|
||||
|
@ -120,6 +120,61 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Set the visibility of edges.
|
||||
async fn set_edge_visibility(
|
||||
&self,
|
||||
visible: bool,
|
||||
source_range: SourceRange,
|
||||
) -> Result<(), crate::errors::KclError> {
|
||||
self.batch_modeling_cmd(
|
||||
uuid::Uuid::new_v4(),
|
||||
source_range,
|
||||
&ModelingCmd::from(mcmd::EdgeLinesVisible { hidden: !visible }),
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn set_units(
|
||||
&self,
|
||||
units: crate::UnitLength,
|
||||
source_range: SourceRange,
|
||||
) -> Result<(), crate::errors::KclError> {
|
||||
// Before we even start executing the program, set the units.
|
||||
self.batch_modeling_cmd(
|
||||
uuid::Uuid::new_v4(),
|
||||
source_range,
|
||||
&ModelingCmd::from(mcmd::SetSceneUnits { unit: units.into() }),
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Re-run the command to apply the settings.
|
||||
async fn reapply_settings(
|
||||
&self,
|
||||
settings: &crate::ExecutorSettings,
|
||||
source_range: SourceRange,
|
||||
) -> Result<(), crate::errors::KclError> {
|
||||
// Set the edge visibility.
|
||||
self.set_edge_visibility(settings.highlight_edges, source_range).await?;
|
||||
|
||||
// Change the units.
|
||||
self.set_units(settings.units, source_range).await?;
|
||||
|
||||
// Send the command to show the grid.
|
||||
self.modify_grid(!settings.show_grid, source_range).await?;
|
||||
|
||||
// We do not have commands for changing ssao on the fly.
|
||||
|
||||
// Flush the batch queue, so the settings are applied right away.
|
||||
self.flush_batch(false, source_range).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Add a modeling command to the batch but don't fire it right away.
|
||||
async fn batch_modeling_cmd(
|
||||
&self,
|
||||
@ -504,11 +559,11 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
||||
}))
|
||||
}
|
||||
|
||||
async fn modify_grid(&self, hidden: bool) -> Result<(), KclError> {
|
||||
async fn modify_grid(&self, hidden: bool, source_range: SourceRange) -> Result<(), KclError> {
|
||||
// Hide/show the grid.
|
||||
self.batch_modeling_cmd(
|
||||
uuid::Uuid::new_v4(),
|
||||
Default::default(),
|
||||
source_range,
|
||||
&ModelingCmd::from(mcmd::ObjectVisible {
|
||||
hidden,
|
||||
object_id: *GRID_OBJECT_ID,
|
||||
@ -519,7 +574,7 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
||||
// Hide/show the grid scale text.
|
||||
self.batch_modeling_cmd(
|
||||
uuid::Uuid::new_v4(),
|
||||
Default::default(),
|
||||
source_range,
|
||||
&ModelingCmd::from(mcmd::ObjectVisible {
|
||||
hidden,
|
||||
object_id: *GRID_SCALE_TEXT_OBJECT_ID,
|
||||
@ -527,8 +582,6 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
||||
)
|
||||
.await?;
|
||||
|
||||
self.flush_batch(false, Default::default()).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
50
src/wasm-lib/kcl/src/execution/cache.rs
Normal file
@ -0,0 +1,50 @@
|
||||
//! Functions for helping with caching an ast and finding the parts the changed.
|
||||
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
execution::ExecState,
|
||||
parsing::ast::types::{Node, Program},
|
||||
};
|
||||
|
||||
/// Information for the caching an AST and smartly re-executing it if we can.
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
pub struct CacheInformation {
|
||||
/// The old information.
|
||||
pub old: Option<OldAstState>,
|
||||
/// The new ast to executed.
|
||||
pub new_ast: Node<Program>,
|
||||
}
|
||||
|
||||
/// The old ast and program memory.
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
pub struct OldAstState {
|
||||
/// The ast.
|
||||
pub ast: Node<Program>,
|
||||
/// The exec state.
|
||||
pub exec_state: ExecState,
|
||||
/// The last settings used for execution.
|
||||
pub settings: crate::execution::ExecutorSettings,
|
||||
}
|
||||
|
||||
impl From<crate::Program> for CacheInformation {
|
||||
fn from(program: crate::Program) -> Self {
|
||||
CacheInformation {
|
||||
old: None,
|
||||
new_ast: program.ast,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The result of a cache check.
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
pub struct CacheResult {
|
||||
/// Should we clear the scene and start over?
|
||||
pub clear_scene: bool,
|
||||
/// The program that needs to be executed.
|
||||
pub program: Node<Program>,
|
||||
}
|
@ -326,29 +326,12 @@ async fn inner_execute_pipe_body(
|
||||
ctx: &ExecutorContext,
|
||||
) -> Result<KclValue, KclError> {
|
||||
for expression in body {
|
||||
match expression {
|
||||
Expr::TagDeclarator(_) => {
|
||||
return Err(KclError::Semantic(KclErrorDetails {
|
||||
message: format!("This cannot be in a PipeExpression: {:?}", expression),
|
||||
source_ranges: vec![expression.into()],
|
||||
}));
|
||||
}
|
||||
Expr::Literal(_)
|
||||
| Expr::Identifier(_)
|
||||
| Expr::BinaryExpression(_)
|
||||
| Expr::FunctionExpression(_)
|
||||
| Expr::CallExpression(_)
|
||||
| Expr::CallExpressionKw(_)
|
||||
| Expr::PipeExpression(_)
|
||||
| Expr::PipeSubstitution(_)
|
||||
| Expr::ArrayExpression(_)
|
||||
| Expr::ArrayRangeExpression(_)
|
||||
| Expr::ObjectExpression(_)
|
||||
| Expr::MemberExpression(_)
|
||||
| Expr::UnaryExpression(_)
|
||||
| Expr::IfExpression(_)
|
||||
| Expr::None(_) => {}
|
||||
};
|
||||
if let Expr::TagDeclarator(_) = expression {
|
||||
return Err(KclError::Semantic(KclErrorDetails {
|
||||
message: format!("This cannot be in a PipeExpression: {:?}", expression),
|
||||
source_ranges: vec![expression.into()],
|
||||
}));
|
||||
}
|
||||
let metadata = Metadata {
|
||||
source_range: SourceRange::from(expression),
|
||||
};
|
||||
|
@ -23,15 +23,18 @@ type Point3D = kcmc::shared::Point3d<f64>;
|
||||
pub use function_param::FunctionParam;
|
||||
pub use kcl_value::{KclObjectFields, KclValue};
|
||||
|
||||
pub(crate) mod cache;
|
||||
mod exec_ast;
|
||||
mod function_param;
|
||||
mod kcl_value;
|
||||
|
||||
use crate::{
|
||||
engine::{EngineManager, ExecutionKind},
|
||||
errors::{KclError, KclErrorDetails},
|
||||
execution::cache::{CacheInformation, CacheResult},
|
||||
fs::{FileManager, FileSystem},
|
||||
parsing::ast::{
|
||||
cache::{get_changed_program, CacheInformation},
|
||||
types::{
|
||||
BodyItem, Expr, FunctionExpression, ImportSelector, ItemVisibility, Node, NodeRef, TagDeclarator, TagNode,
|
||||
},
|
||||
parsing::ast::types::{
|
||||
BodyItem, Expr, FunctionExpression, ImportSelector, ItemVisibility, Node, NodeRef, TagDeclarator, TagNode,
|
||||
},
|
||||
settings::types::UnitLength,
|
||||
source_range::{ModuleId, SourceRange},
|
||||
@ -39,10 +42,6 @@ use crate::{
|
||||
ExecError, Program,
|
||||
};
|
||||
|
||||
mod exec_ast;
|
||||
mod function_param;
|
||||
mod kcl_value;
|
||||
|
||||
/// State for executing a program.
|
||||
#[derive(Debug, Default, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
@ -1660,17 +1659,6 @@ impl ExecutorContext {
|
||||
let engine: Arc<Box<dyn EngineManager>> =
|
||||
Arc::new(Box::new(crate::engine::conn::EngineConnection::new(ws).await?));
|
||||
|
||||
// Set the edge visibility.
|
||||
engine
|
||||
.batch_modeling_cmd(
|
||||
uuid::Uuid::new_v4(),
|
||||
SourceRange::default(),
|
||||
&ModelingCmd::from(mcmd::EdgeLinesVisible {
|
||||
hidden: !settings.highlight_edges,
|
||||
}),
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(Self {
|
||||
engine,
|
||||
fs: Arc::new(FileManager::new()),
|
||||
@ -1697,7 +1685,7 @@ impl ExecutorContext {
|
||||
pub async fn new(
|
||||
engine_manager: crate::engine::conn_wasm::EngineCommandManager,
|
||||
fs_manager: crate::fs::wasm::FileSystemManager,
|
||||
units: UnitLength,
|
||||
settings: ExecutorSettings,
|
||||
) -> Result<Self, String> {
|
||||
Ok(ExecutorContext {
|
||||
engine: Arc::new(Box::new(
|
||||
@ -1707,16 +1695,16 @@ impl ExecutorContext {
|
||||
)),
|
||||
fs: Arc::new(FileManager::new(fs_manager)),
|
||||
stdlib: Arc::new(StdLib::new()),
|
||||
settings: ExecutorSettings {
|
||||
units,
|
||||
..Default::default()
|
||||
},
|
||||
settings,
|
||||
context_type: ContextType::Live,
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub async fn new_mock(fs_manager: crate::fs::wasm::FileSystemManager, units: UnitLength) -> Result<Self, String> {
|
||||
pub async fn new_mock(
|
||||
fs_manager: crate::fs::wasm::FileSystemManager,
|
||||
settings: ExecutorSettings,
|
||||
) -> Result<Self, String> {
|
||||
Ok(ExecutorContext {
|
||||
engine: Arc::new(Box::new(
|
||||
crate::engine::conn_mock::EngineConnection::new()
|
||||
@ -1725,10 +1713,7 @@ impl ExecutorContext {
|
||||
)),
|
||||
fs: Arc::new(FileManager::new(fs_manager)),
|
||||
stdlib: Arc::new(StdLib::new()),
|
||||
settings: ExecutorSettings {
|
||||
units,
|
||||
..Default::default()
|
||||
},
|
||||
settings,
|
||||
context_type: ContextType::Mock,
|
||||
})
|
||||
}
|
||||
@ -1817,6 +1802,71 @@ impl ExecutorContext {
|
||||
// AND if we aren't in wasm it doesn't really matter.
|
||||
Ok(())
|
||||
}
|
||||
// Given an old ast, old program memory and new ast, find the parts of the code that need to be
|
||||
// re-executed.
|
||||
// This function should never error, because in the case of any internal error, we should just pop
|
||||
// the cache.
|
||||
pub async fn get_changed_program(&self, info: CacheInformation) -> Option<CacheResult> {
|
||||
let Some(old) = info.old else {
|
||||
// We have no old info, we need to re-execute the whole thing.
|
||||
return Some(CacheResult {
|
||||
clear_scene: true,
|
||||
program: info.new_ast,
|
||||
});
|
||||
};
|
||||
|
||||
// If the settings are different we might need to bust the cache.
|
||||
// We specifically do this before checking if they are the exact same.
|
||||
if old.settings != self.settings {
|
||||
// If the units are different we need to re-execute the whole thing.
|
||||
if old.settings.units != self.settings.units {
|
||||
return Some(CacheResult {
|
||||
clear_scene: true,
|
||||
program: info.new_ast,
|
||||
});
|
||||
}
|
||||
|
||||
// If anything else is different we do not need to re-execute, but rather just
|
||||
// run the settings again.
|
||||
|
||||
if self
|
||||
.engine
|
||||
.reapply_settings(&self.settings, Default::default())
|
||||
.await
|
||||
.is_err()
|
||||
{
|
||||
// Bust the cache, we errored.
|
||||
return Some(CacheResult {
|
||||
clear_scene: true,
|
||||
program: info.new_ast,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// If the ASTs are the EXACT same we return None.
|
||||
// We don't even need to waste time computing the digests.
|
||||
if old.ast == info.new_ast {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut old_ast = old.ast.inner;
|
||||
old_ast.compute_digest();
|
||||
let mut new_ast = info.new_ast.inner.clone();
|
||||
new_ast.compute_digest();
|
||||
|
||||
// Check if the digest is the same.
|
||||
if old_ast.digest == new_ast.digest {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Check if the changes were only to Non-code areas, like comments or whitespace.
|
||||
|
||||
// For any unhandled cases just re-execute the whole thing.
|
||||
Some(CacheResult {
|
||||
clear_scene: true,
|
||||
program: info.new_ast,
|
||||
})
|
||||
}
|
||||
|
||||
/// Perform the execution of a program.
|
||||
/// You can optionally pass in some initialization memory.
|
||||
@ -1837,7 +1887,7 @@ impl ExecutorContext {
|
||||
let _stats = crate::log::LogPerfStats::new("Interpretation");
|
||||
|
||||
// Get the program that actually changed from the old and new information.
|
||||
let cache_result = get_changed_program(cache_info.clone(), &self.settings);
|
||||
let cache_result = self.get_changed_program(cache_info.clone()).await;
|
||||
|
||||
// Check if we don't need to re-execute.
|
||||
let Some(cache_result) = cache_result else {
|
||||
@ -1854,23 +1904,9 @@ impl ExecutorContext {
|
||||
|
||||
// TODO: Use the top-level file's path.
|
||||
exec_state.add_module(std::path::PathBuf::from(""));
|
||||
// Before we even start executing the program, set the units.
|
||||
self.engine
|
||||
.batch_modeling_cmd(
|
||||
exec_state.id_generator.next_uuid(),
|
||||
SourceRange::default(),
|
||||
&ModelingCmd::from(mcmd::SetSceneUnits {
|
||||
unit: match self.settings.units {
|
||||
UnitLength::Cm => kcmc::units::UnitLength::Centimeters,
|
||||
UnitLength::Ft => kcmc::units::UnitLength::Feet,
|
||||
UnitLength::In => kcmc::units::UnitLength::Inches,
|
||||
UnitLength::M => kcmc::units::UnitLength::Meters,
|
||||
UnitLength::Mm => kcmc::units::UnitLength::Millimeters,
|
||||
UnitLength::Yd => kcmc::units::UnitLength::Yards,
|
||||
},
|
||||
}),
|
||||
)
|
||||
.await?;
|
||||
|
||||
// Re-apply the settings, in case the cache was busted.
|
||||
self.engine.reapply_settings(&self.settings, Default::default()).await?;
|
||||
|
||||
self.inner_execute(&cache_result.program, exec_state, crate::execution::BodyType::Root)
|
||||
.await?;
|
||||
@ -2081,7 +2117,8 @@ impl ExecutorContext {
|
||||
Ok((module_memory, module_exports))
|
||||
}
|
||||
|
||||
pub async fn execute_expr<'a>(
|
||||
#[async_recursion]
|
||||
pub async fn execute_expr<'a: 'async_recursion>(
|
||||
&self,
|
||||
init: &Expr,
|
||||
exec_state: &mut ExecState,
|
||||
@ -2138,6 +2175,14 @@ impl ExecutorContext {
|
||||
Expr::MemberExpression(member_expression) => member_expression.get_result(exec_state)?,
|
||||
Expr::UnaryExpression(unary_expression) => unary_expression.get_result(exec_state, self).await?,
|
||||
Expr::IfExpression(expr) => expr.get_result(exec_state, self).await?,
|
||||
Expr::LabelledExpression(expr) => {
|
||||
let result = self
|
||||
.execute_expr(&expr.expr, exec_state, metadata, statement_kind)
|
||||
.await?;
|
||||
exec_state.memory.add(&expr.label.name, result.clone(), init.into())?;
|
||||
// TODO this lets us use the label as a variable name, but not as a tag in most cases
|
||||
result
|
||||
}
|
||||
};
|
||||
Ok(item)
|
||||
}
|
||||
@ -2147,23 +2192,8 @@ impl ExecutorContext {
|
||||
self.settings.units = units;
|
||||
}
|
||||
|
||||
/// Execute the program, then get a PNG screenshot.
|
||||
pub async fn execute_and_prepare_snapshot(
|
||||
&self,
|
||||
program: &Program,
|
||||
exec_state: &mut ExecState,
|
||||
) -> std::result::Result<TakeSnapshot, ExecError> {
|
||||
self.execute_and_prepare(program, exec_state).await
|
||||
}
|
||||
|
||||
/// Execute the program, return the interpreter and outputs.
|
||||
pub async fn execute_and_prepare(
|
||||
&self,
|
||||
program: &Program,
|
||||
exec_state: &mut ExecState,
|
||||
) -> std::result::Result<TakeSnapshot, ExecError> {
|
||||
self.run(program.clone().into(), exec_state).await?;
|
||||
|
||||
/// Get a snapshot of the current scene.
|
||||
pub async fn prepare_snapshot(&self) -> std::result::Result<TakeSnapshot, ExecError> {
|
||||
// Zoom to fit.
|
||||
self.engine
|
||||
.send_modeling_cmd(
|
||||
@ -2199,6 +2229,17 @@ impl ExecutorContext {
|
||||
};
|
||||
Ok(contents)
|
||||
}
|
||||
|
||||
/// Execute the program, then get a PNG screenshot.
|
||||
pub async fn execute_and_prepare_snapshot(
|
||||
&self,
|
||||
program: &Program,
|
||||
exec_state: &mut ExecState,
|
||||
) -> std::result::Result<TakeSnapshot, ExecError> {
|
||||
self.run(program.clone().into(), exec_state).await?;
|
||||
|
||||
self.prepare_snapshot().await
|
||||
}
|
||||
}
|
||||
|
||||
/// For each argument given,
|
||||
@ -2378,9 +2419,12 @@ mod tests {
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
use super::*;
|
||||
use crate::parsing::ast::types::{DefaultParamVal, Identifier, Node, Parameter};
|
||||
use crate::{
|
||||
parsing::ast::types::{DefaultParamVal, Identifier, Node, Parameter},
|
||||
OldAstState,
|
||||
};
|
||||
|
||||
pub async fn parse_execute(code: &str) -> Result<ProgramMemory> {
|
||||
pub async fn parse_execute(code: &str) -> Result<(Program, ExecutorContext, ExecState)> {
|
||||
let program = Program::parse_no_errs(code)?;
|
||||
|
||||
let ctx = ExecutorContext {
|
||||
@ -2391,9 +2435,9 @@ mod tests {
|
||||
context_type: ContextType::Mock,
|
||||
};
|
||||
let mut exec_state = ExecState::default();
|
||||
ctx.run(program.into(), &mut exec_state).await?;
|
||||
ctx.run(program.clone().into(), &mut exec_state).await?;
|
||||
|
||||
Ok(exec_state.memory)
|
||||
Ok((program, ctx, exec_state))
|
||||
}
|
||||
|
||||
/// Convenience function to get a JSON value from memory and unwrap.
|
||||
@ -2804,36 +2848,39 @@ let shape = layer() |> patternTransform(10, transform, %)
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_math_execute_with_functions() {
|
||||
let ast = r#"const myVar = 2 + min(100, -1 + legLen(5, 3))"#;
|
||||
let memory = parse_execute(ast).await.unwrap();
|
||||
assert_eq!(5.0, mem_get_json(&memory, "myVar").as_f64().unwrap());
|
||||
let (_, _, exec_state) = parse_execute(ast).await.unwrap();
|
||||
assert_eq!(5.0, mem_get_json(&exec_state.memory, "myVar").as_f64().unwrap());
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_math_execute() {
|
||||
let ast = r#"const myVar = 1 + 2 * (3 - 4) / -5 + 6"#;
|
||||
let memory = parse_execute(ast).await.unwrap();
|
||||
assert_eq!(7.4, mem_get_json(&memory, "myVar").as_f64().unwrap());
|
||||
let (_, _, exec_state) = parse_execute(ast).await.unwrap();
|
||||
assert_eq!(7.4, mem_get_json(&exec_state.memory, "myVar").as_f64().unwrap());
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_math_execute_start_negative() {
|
||||
let ast = r#"const myVar = -5 + 6"#;
|
||||
let memory = parse_execute(ast).await.unwrap();
|
||||
assert_eq!(1.0, mem_get_json(&memory, "myVar").as_f64().unwrap());
|
||||
let (_, _, exec_state) = parse_execute(ast).await.unwrap();
|
||||
assert_eq!(1.0, mem_get_json(&exec_state.memory, "myVar").as_f64().unwrap());
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_math_execute_with_pi() {
|
||||
let ast = r#"const myVar = pi() * 2"#;
|
||||
let memory = parse_execute(ast).await.unwrap();
|
||||
assert_eq!(std::f64::consts::TAU, mem_get_json(&memory, "myVar").as_f64().unwrap());
|
||||
let (_, _, exec_state) = parse_execute(ast).await.unwrap();
|
||||
assert_eq!(
|
||||
std::f64::consts::TAU,
|
||||
mem_get_json(&exec_state.memory, "myVar").as_f64().unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_math_define_decimal_without_leading_zero() {
|
||||
let ast = r#"let thing = .4 + 7"#;
|
||||
let memory = parse_execute(ast).await.unwrap();
|
||||
assert_eq!(7.4, mem_get_json(&memory, "thing").as_f64().unwrap());
|
||||
let (_, _, exec_state) = parse_execute(ast).await.unwrap();
|
||||
assert_eq!(7.4, mem_get_json(&exec_state.memory, "thing").as_f64().unwrap());
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
@ -2872,11 +2919,11 @@ fn check = (x) => {
|
||||
}
|
||||
check(false)
|
||||
"#;
|
||||
let mem = parse_execute(ast).await.unwrap();
|
||||
assert_eq!(false, mem_get_json(&mem, "notTrue").as_bool().unwrap());
|
||||
assert_eq!(true, mem_get_json(&mem, "notFalse").as_bool().unwrap());
|
||||
assert_eq!(true, mem_get_json(&mem, "c").as_bool().unwrap());
|
||||
assert_eq!(false, mem_get_json(&mem, "d").as_bool().unwrap());
|
||||
let (_, _, exec_state) = parse_execute(ast).await.unwrap();
|
||||
assert_eq!(false, mem_get_json(&exec_state.memory, "notTrue").as_bool().unwrap());
|
||||
assert_eq!(true, mem_get_json(&exec_state.memory, "notFalse").as_bool().unwrap());
|
||||
assert_eq!(true, mem_get_json(&exec_state.memory, "c").as_bool().unwrap());
|
||||
assert_eq!(false, mem_get_json(&exec_state.memory, "d").as_bool().unwrap());
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
@ -3258,4 +3305,310 @@ let w = f() + f()
|
||||
let json = serde_json::to_string(&mem).unwrap();
|
||||
assert_eq!(json, r#"{"type":"Solids","value":[]}"#);
|
||||
}
|
||||
|
||||
// Easy case where we have no old ast and memory.
|
||||
// We need to re-execute everything.
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_get_changed_program_no_old_information() {
|
||||
let new = r#"// Remove the end face for the extrusion.
|
||||
firstSketch = startSketchOn('XY')
|
||||
|> startProfileAt([-12, 12], %)
|
||||
|> line([24, 0], %)
|
||||
|> line([0, -24], %)
|
||||
|> line([-24, 0], %)
|
||||
|> close(%)
|
||||
|> extrude(6, %)
|
||||
|
||||
// Remove the end face for the extrusion.
|
||||
shell({ faces = ['end'], thickness = 0.25 }, firstSketch)"#;
|
||||
let (program, ctx, _) = parse_execute(new).await.unwrap();
|
||||
|
||||
let result = ctx
|
||||
.get_changed_program(CacheInformation {
|
||||
old: None,
|
||||
new_ast: program.ast.clone(),
|
||||
})
|
||||
.await;
|
||||
|
||||
assert!(result.is_some());
|
||||
|
||||
let result = result.unwrap();
|
||||
|
||||
assert_eq!(result.program, program.ast);
|
||||
assert!(result.clear_scene);
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_get_changed_program_same_code() {
|
||||
let new = r#"// Remove the end face for the extrusion.
|
||||
firstSketch = startSketchOn('XY')
|
||||
|> startProfileAt([-12, 12], %)
|
||||
|> line([24, 0], %)
|
||||
|> line([0, -24], %)
|
||||
|> line([-24, 0], %)
|
||||
|> close(%)
|
||||
|> extrude(6, %)
|
||||
|
||||
// Remove the end face for the extrusion.
|
||||
shell({ faces = ['end'], thickness = 0.25 }, firstSketch)"#;
|
||||
|
||||
let (program, ctx, exec_state) = parse_execute(new).await.unwrap();
|
||||
|
||||
let result = ctx
|
||||
.get_changed_program(CacheInformation {
|
||||
old: Some(OldAstState {
|
||||
ast: program.ast.clone(),
|
||||
exec_state,
|
||||
settings: Default::default(),
|
||||
}),
|
||||
new_ast: program.ast.clone(),
|
||||
})
|
||||
.await;
|
||||
|
||||
assert_eq!(result, None);
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_get_changed_program_same_code_changed_whitespace() {
|
||||
let old = r#" // Remove the end face for the extrusion.
|
||||
firstSketch = startSketchOn('XY')
|
||||
|> startProfileAt([-12, 12], %)
|
||||
|> line([24, 0], %)
|
||||
|> line([0, -24], %)
|
||||
|> line([-24, 0], %)
|
||||
|> close(%)
|
||||
|> extrude(6, %)
|
||||
|
||||
// Remove the end face for the extrusion.
|
||||
shell({ faces = ['end'], thickness = 0.25 }, firstSketch) "#;
|
||||
|
||||
let new = r#"// Remove the end face for the extrusion.
|
||||
firstSketch = startSketchOn('XY')
|
||||
|> startProfileAt([-12, 12], %)
|
||||
|> line([24, 0], %)
|
||||
|> line([0, -24], %)
|
||||
|> line([-24, 0], %)
|
||||
|> close(%)
|
||||
|> extrude(6, %)
|
||||
|
||||
// Remove the end face for the extrusion.
|
||||
shell({ faces = ['end'], thickness = 0.25 }, firstSketch)"#;
|
||||
|
||||
let (program_old, ctx, exec_state) = parse_execute(old).await.unwrap();
|
||||
|
||||
let program_new = crate::Program::parse_no_errs(new).unwrap();
|
||||
|
||||
let result = ctx
|
||||
.get_changed_program(CacheInformation {
|
||||
old: Some(OldAstState {
|
||||
ast: program_old.ast.clone(),
|
||||
exec_state,
|
||||
settings: Default::default(),
|
||||
}),
|
||||
new_ast: program_new.ast.clone(),
|
||||
})
|
||||
.await;
|
||||
|
||||
assert_eq!(result, None);
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_get_changed_program_same_code_changed_code_comment_start_of_program() {
|
||||
let old = r#" // Removed the end face for the extrusion.
|
||||
firstSketch = startSketchOn('XY')
|
||||
|> startProfileAt([-12, 12], %)
|
||||
|> line([24, 0], %)
|
||||
|> line([0, -24], %)
|
||||
|> line([-24, 0], %)
|
||||
|> close(%)
|
||||
|> extrude(6, %)
|
||||
|
||||
// Remove the end face for the extrusion.
|
||||
shell({ faces = ['end'], thickness = 0.25 }, firstSketch) "#;
|
||||
|
||||
let new = r#"// Remove the end face for the extrusion.
|
||||
firstSketch = startSketchOn('XY')
|
||||
|> startProfileAt([-12, 12], %)
|
||||
|> line([24, 0], %)
|
||||
|> line([0, -24], %)
|
||||
|> line([-24, 0], %)
|
||||
|> close(%)
|
||||
|> extrude(6, %)
|
||||
|
||||
// Remove the end face for the extrusion.
|
||||
shell({ faces = ['end'], thickness = 0.25 }, firstSketch)"#;
|
||||
|
||||
let (program, ctx, exec_state) = parse_execute(old).await.unwrap();
|
||||
|
||||
let program_new = crate::Program::parse_no_errs(new).unwrap();
|
||||
|
||||
let result = ctx
|
||||
.get_changed_program(CacheInformation {
|
||||
old: Some(OldAstState {
|
||||
ast: program.ast.clone(),
|
||||
exec_state,
|
||||
settings: Default::default(),
|
||||
}),
|
||||
new_ast: program_new.ast.clone(),
|
||||
})
|
||||
.await;
|
||||
|
||||
assert_eq!(result, None);
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_get_changed_program_same_code_changed_code_comments() {
|
||||
let old = r#" // Removed the end face for the extrusion.
|
||||
firstSketch = startSketchOn('XY')
|
||||
|> startProfileAt([-12, 12], %)
|
||||
|> line([24, 0], %)
|
||||
|> line([0, -24], %)
|
||||
|> line([-24, 0], %) // my thing
|
||||
|> close(%)
|
||||
|> extrude(6, %)
|
||||
|
||||
// Remove the end face for the extrusion.
|
||||
shell({ faces = ['end'], thickness = 0.25 }, firstSketch) "#;
|
||||
|
||||
let new = r#"// Remove the end face for the extrusion.
|
||||
firstSketch = startSketchOn('XY')
|
||||
|> startProfileAt([-12, 12], %)
|
||||
|> line([24, 0], %)
|
||||
|> line([0, -24], %)
|
||||
|> line([-24, 0], %)
|
||||
|> close(%)
|
||||
|> extrude(6, %)
|
||||
|
||||
// Remove the end face for the extrusion.
|
||||
shell({ faces = ['end'], thickness = 0.25 }, firstSketch)"#;
|
||||
|
||||
let (program, ctx, exec_state) = parse_execute(old).await.unwrap();
|
||||
|
||||
let program_new = crate::Program::parse_no_errs(new).unwrap();
|
||||
|
||||
let result = ctx
|
||||
.get_changed_program(CacheInformation {
|
||||
old: Some(OldAstState {
|
||||
ast: program.ast.clone(),
|
||||
exec_state,
|
||||
settings: Default::default(),
|
||||
}),
|
||||
new_ast: program_new.ast.clone(),
|
||||
})
|
||||
.await;
|
||||
|
||||
assert!(result.is_some());
|
||||
|
||||
let result = result.unwrap();
|
||||
|
||||
assert_eq!(result.program, program_new.ast);
|
||||
assert!(result.clear_scene);
|
||||
}
|
||||
|
||||
// Changing the units with the exact same file should bust the cache.
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_get_changed_program_same_code_but_different_units() {
|
||||
let new = r#"// Remove the end face for the extrusion.
|
||||
firstSketch = startSketchOn('XY')
|
||||
|> startProfileAt([-12, 12], %)
|
||||
|> line([24, 0], %)
|
||||
|> line([0, -24], %)
|
||||
|> line([-24, 0], %)
|
||||
|> close(%)
|
||||
|> extrude(6, %)
|
||||
|
||||
// Remove the end face for the extrusion.
|
||||
shell({ faces = ['end'], thickness = 0.25 }, firstSketch)"#;
|
||||
|
||||
let (program, mut ctx, exec_state) = parse_execute(new).await.unwrap();
|
||||
|
||||
// Change the settings to cm.
|
||||
ctx.settings.units = crate::UnitLength::Cm;
|
||||
|
||||
let result = ctx
|
||||
.get_changed_program(CacheInformation {
|
||||
old: Some(OldAstState {
|
||||
ast: program.ast.clone(),
|
||||
exec_state,
|
||||
settings: Default::default(),
|
||||
}),
|
||||
new_ast: program.ast.clone(),
|
||||
})
|
||||
.await;
|
||||
|
||||
assert!(result.is_some());
|
||||
|
||||
let result = result.unwrap();
|
||||
|
||||
assert_eq!(result.program, program.ast);
|
||||
assert!(result.clear_scene);
|
||||
}
|
||||
|
||||
// Changing the grid settings with the exact same file should NOT bust the cache.
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_get_changed_program_same_code_but_different_grid_setting() {
|
||||
let new = r#"// Remove the end face for the extrusion.
|
||||
firstSketch = startSketchOn('XY')
|
||||
|> startProfileAt([-12, 12], %)
|
||||
|> line([24, 0], %)
|
||||
|> line([0, -24], %)
|
||||
|> line([-24, 0], %)
|
||||
|> close(%)
|
||||
|> extrude(6, %)
|
||||
|
||||
// Remove the end face for the extrusion.
|
||||
shell({ faces = ['end'], thickness = 0.25 }, firstSketch)"#;
|
||||
|
||||
let (program, mut ctx, exec_state) = parse_execute(new).await.unwrap();
|
||||
|
||||
// Change the settings.
|
||||
ctx.settings.show_grid = !ctx.settings.show_grid;
|
||||
|
||||
let result = ctx
|
||||
.get_changed_program(CacheInformation {
|
||||
old: Some(OldAstState {
|
||||
ast: program.ast.clone(),
|
||||
exec_state,
|
||||
settings: Default::default(),
|
||||
}),
|
||||
new_ast: program.ast.clone(),
|
||||
})
|
||||
.await;
|
||||
|
||||
assert_eq!(result, None);
|
||||
}
|
||||
|
||||
// Changing the edge visibility settings with the exact same file should NOT bust the cache.
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_get_changed_program_same_code_but_different_edge_visiblity_setting() {
|
||||
let new = r#"// Remove the end face for the extrusion.
|
||||
firstSketch = startSketchOn('XY')
|
||||
|> startProfileAt([-12, 12], %)
|
||||
|> line([24, 0], %)
|
||||
|> line([0, -24], %)
|
||||
|> line([-24, 0], %)
|
||||
|> close(%)
|
||||
|> extrude(6, %)
|
||||
|
||||
// Remove the end face for the extrusion.
|
||||
shell({ faces = ['end'], thickness = 0.25 }, firstSketch)"#;
|
||||
|
||||
let (program, mut ctx, exec_state) = parse_execute(new).await.unwrap();
|
||||
|
||||
// Change the settings.
|
||||
ctx.settings.highlight_edges = !ctx.settings.highlight_edges;
|
||||
|
||||
let result = ctx
|
||||
.get_changed_program(CacheInformation {
|
||||
old: Some(OldAstState {
|
||||
ast: program.ast.clone(),
|
||||
exec_state,
|
||||
settings: Default::default(),
|
||||
}),
|
||||
new_ast: program.ast.clone(),
|
||||
})
|
||||
.await;
|
||||
|
||||
assert_eq!(result, None);
|
||||
}
|
||||
}
|
||||
|
@ -82,16 +82,15 @@ mod wasm;
|
||||
pub use coredump::CoreDump;
|
||||
pub use engine::{EngineManager, ExecutionKind};
|
||||
pub use errors::{CompilationError, ConnectionError, ExecError, KclError};
|
||||
pub use execution::{ExecState, ExecutorContext, ExecutorSettings};
|
||||
pub use execution::{
|
||||
cache::{CacheInformation, OldAstState},
|
||||
ExecState, ExecutorContext, ExecutorSettings,
|
||||
};
|
||||
pub use lsp::{
|
||||
copilot::Backend as CopilotLspBackend,
|
||||
kcl::{Backend as KclLspBackend, Server as KclLspServerSubCommand},
|
||||
};
|
||||
pub use parsing::ast::{
|
||||
cache::{CacheInformation, OldAstState},
|
||||
modify::modify_ast_for_sketch,
|
||||
types::FormatOptions,
|
||||
};
|
||||
pub use parsing::ast::{modify::modify_ast_for_sketch, types::FormatOptions};
|
||||
pub use settings::types::{project::ProjectConfiguration, Configuration, UnitLength};
|
||||
pub use source_range::{ModuleId, SourceRange};
|
||||
|
||||
|
@ -45,14 +45,11 @@ use crate::{
|
||||
errors::Suggestion,
|
||||
lsp::{backend::Backend as _, util::IntoDiagnostic},
|
||||
parsing::{
|
||||
ast::{
|
||||
cache::{CacheInformation, OldAstState},
|
||||
types::{Expr, Node, VariableKind},
|
||||
},
|
||||
ast::types::{Expr, Node, VariableKind},
|
||||
token::TokenStream,
|
||||
PIPE_OPERATOR,
|
||||
},
|
||||
ModuleId, Program, SourceRange,
|
||||
CacheInformation, ModuleId, OldAstState, Program, SourceRange,
|
||||
};
|
||||
const SEMANTIC_TOKEN_TYPES: [SemanticTokenType; 10] = [
|
||||
SemanticTokenType::NUMBER,
|
||||
|
@ -1,373 +0,0 @@
|
||||
//! Functions for helping with caching an ast and finding the parts the changed.
|
||||
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
execution::ExecState,
|
||||
parsing::ast::types::{Node, Program},
|
||||
};
|
||||
|
||||
/// Information for the caching an AST and smartly re-executing it if we can.
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
pub struct CacheInformation {
|
||||
/// The old information.
|
||||
pub old: Option<OldAstState>,
|
||||
/// The new ast to executed.
|
||||
pub new_ast: Node<Program>,
|
||||
}
|
||||
|
||||
/// The old ast and program memory.
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
pub struct OldAstState {
|
||||
/// The ast.
|
||||
pub ast: Node<Program>,
|
||||
/// The exec state.
|
||||
pub exec_state: ExecState,
|
||||
/// The last settings used for execution.
|
||||
pub settings: crate::execution::ExecutorSettings,
|
||||
}
|
||||
|
||||
impl From<crate::Program> for CacheInformation {
|
||||
fn from(program: crate::Program) -> Self {
|
||||
CacheInformation {
|
||||
old: None,
|
||||
new_ast: program.ast,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The result of a cache check.
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
pub struct CacheResult {
|
||||
/// Should we clear the scene and start over?
|
||||
pub clear_scene: bool,
|
||||
/// The program that needs to be executed.
|
||||
pub program: Node<Program>,
|
||||
}
|
||||
|
||||
// Given an old ast, old program memory and new ast, find the parts of the code that need to be
|
||||
// re-executed.
|
||||
// This function should never error, because in the case of any internal error, we should just pop
|
||||
// the cache.
|
||||
pub fn get_changed_program(
|
||||
info: CacheInformation,
|
||||
new_settings: &crate::execution::ExecutorSettings,
|
||||
) -> Option<CacheResult> {
|
||||
let Some(old) = info.old else {
|
||||
// We have no old info, we need to re-execute the whole thing.
|
||||
return Some(CacheResult {
|
||||
clear_scene: true,
|
||||
program: info.new_ast,
|
||||
});
|
||||
};
|
||||
|
||||
// If the settings are different we need to bust the cache.
|
||||
// We specifically do this before checking if they are the exact same.
|
||||
if old.settings != *new_settings {
|
||||
return Some(CacheResult {
|
||||
clear_scene: true,
|
||||
program: info.new_ast,
|
||||
});
|
||||
}
|
||||
|
||||
// If the ASTs are the EXACT same we return None.
|
||||
// We don't even need to waste time computing the digests.
|
||||
if old.ast == info.new_ast {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut old_ast = old.ast.inner;
|
||||
old_ast.compute_digest();
|
||||
let mut new_ast = info.new_ast.inner.clone();
|
||||
new_ast.compute_digest();
|
||||
|
||||
// Check if the digest is the same.
|
||||
if old_ast.digest == new_ast.digest {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Check if the changes were only to Non-code areas, like comments or whitespace.
|
||||
|
||||
// For any unhandled cases just re-execute the whole thing.
|
||||
Some(CacheResult {
|
||||
clear_scene: true,
|
||||
program: info.new_ast,
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::sync::Arc;
|
||||
|
||||
use anyhow::Result;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
use super::*;
|
||||
|
||||
async fn execute(program: &crate::Program) -> Result<ExecState> {
|
||||
let ctx = crate::execution::ExecutorContext {
|
||||
engine: Arc::new(Box::new(crate::engine::conn_mock::EngineConnection::new().await?)),
|
||||
fs: Arc::new(crate::fs::FileManager::new()),
|
||||
stdlib: Arc::new(crate::std::StdLib::new()),
|
||||
settings: Default::default(),
|
||||
context_type: crate::execution::ContextType::Mock,
|
||||
};
|
||||
let mut exec_state = crate::execution::ExecState::default();
|
||||
ctx.run(program.clone().into(), &mut exec_state).await?;
|
||||
|
||||
Ok(exec_state)
|
||||
}
|
||||
|
||||
// Easy case where we have no old ast and memory.
|
||||
// We need to re-execute everything.
|
||||
#[test]
|
||||
fn test_get_changed_program_no_old_information() {
|
||||
let new = r#"// Remove the end face for the extrusion.
|
||||
firstSketch = startSketchOn('XY')
|
||||
|> startProfileAt([-12, 12], %)
|
||||
|> line([24, 0], %)
|
||||
|> line([0, -24], %)
|
||||
|> line([-24, 0], %)
|
||||
|> close(%)
|
||||
|> extrude(6, %)
|
||||
|
||||
// Remove the end face for the extrusion.
|
||||
shell({ faces = ['end'], thickness = 0.25 }, firstSketch)"#;
|
||||
let program = crate::Program::parse_no_errs(new).unwrap().ast;
|
||||
|
||||
let result = get_changed_program(
|
||||
CacheInformation {
|
||||
old: None,
|
||||
new_ast: program.clone(),
|
||||
},
|
||||
&Default::default(),
|
||||
);
|
||||
|
||||
assert!(result.is_some());
|
||||
|
||||
let result = result.unwrap();
|
||||
|
||||
assert_eq!(result.program, program);
|
||||
assert!(result.clear_scene);
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_get_changed_program_same_code() {
|
||||
let new = r#"// Remove the end face for the extrusion.
|
||||
firstSketch = startSketchOn('XY')
|
||||
|> startProfileAt([-12, 12], %)
|
||||
|> line([24, 0], %)
|
||||
|> line([0, -24], %)
|
||||
|> line([-24, 0], %)
|
||||
|> close(%)
|
||||
|> extrude(6, %)
|
||||
|
||||
// Remove the end face for the extrusion.
|
||||
shell({ faces = ['end'], thickness = 0.25 }, firstSketch)"#;
|
||||
let program = crate::Program::parse_no_errs(new).unwrap();
|
||||
|
||||
let executed = execute(&program).await.unwrap();
|
||||
|
||||
let result = get_changed_program(
|
||||
CacheInformation {
|
||||
old: Some(OldAstState {
|
||||
ast: program.ast.clone(),
|
||||
exec_state: executed,
|
||||
settings: Default::default(),
|
||||
}),
|
||||
new_ast: program.ast.clone(),
|
||||
},
|
||||
&Default::default(),
|
||||
);
|
||||
|
||||
assert_eq!(result, None);
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_get_changed_program_same_code_changed_whitespace() {
|
||||
let old = r#" // Remove the end face for the extrusion.
|
||||
firstSketch = startSketchOn('XY')
|
||||
|> startProfileAt([-12, 12], %)
|
||||
|> line([24, 0], %)
|
||||
|> line([0, -24], %)
|
||||
|> line([-24, 0], %)
|
||||
|> close(%)
|
||||
|> extrude(6, %)
|
||||
|
||||
// Remove the end face for the extrusion.
|
||||
shell({ faces = ['end'], thickness = 0.25 }, firstSketch) "#;
|
||||
|
||||
let new = r#"// Remove the end face for the extrusion.
|
||||
firstSketch = startSketchOn('XY')
|
||||
|> startProfileAt([-12, 12], %)
|
||||
|> line([24, 0], %)
|
||||
|> line([0, -24], %)
|
||||
|> line([-24, 0], %)
|
||||
|> close(%)
|
||||
|> extrude(6, %)
|
||||
|
||||
// Remove the end face for the extrusion.
|
||||
shell({ faces = ['end'], thickness = 0.25 }, firstSketch)"#;
|
||||
let program_old = crate::Program::parse_no_errs(old).unwrap();
|
||||
|
||||
let executed = execute(&program_old).await.unwrap();
|
||||
|
||||
let program_new = crate::Program::parse_no_errs(new).unwrap();
|
||||
|
||||
let result = get_changed_program(
|
||||
CacheInformation {
|
||||
old: Some(OldAstState {
|
||||
ast: program_old.ast.clone(),
|
||||
exec_state: executed,
|
||||
settings: Default::default(),
|
||||
}),
|
||||
new_ast: program_new.ast.clone(),
|
||||
},
|
||||
&Default::default(),
|
||||
);
|
||||
|
||||
assert_eq!(result, None);
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_get_changed_program_same_code_changed_code_comment_start_of_program() {
|
||||
let old = r#" // Removed the end face for the extrusion.
|
||||
firstSketch = startSketchOn('XY')
|
||||
|> startProfileAt([-12, 12], %)
|
||||
|> line([24, 0], %)
|
||||
|> line([0, -24], %)
|
||||
|> line([-24, 0], %)
|
||||
|> close(%)
|
||||
|> extrude(6, %)
|
||||
|
||||
// Remove the end face for the extrusion.
|
||||
shell({ faces = ['end'], thickness = 0.25 }, firstSketch) "#;
|
||||
|
||||
let new = r#"// Remove the end face for the extrusion.
|
||||
firstSketch = startSketchOn('XY')
|
||||
|> startProfileAt([-12, 12], %)
|
||||
|> line([24, 0], %)
|
||||
|> line([0, -24], %)
|
||||
|> line([-24, 0], %)
|
||||
|> close(%)
|
||||
|> extrude(6, %)
|
||||
|
||||
// Remove the end face for the extrusion.
|
||||
shell({ faces = ['end'], thickness = 0.25 }, firstSketch)"#;
|
||||
let program_old = crate::Program::parse_no_errs(old).unwrap();
|
||||
|
||||
let executed = execute(&program_old).await.unwrap();
|
||||
|
||||
let program_new = crate::Program::parse_no_errs(new).unwrap();
|
||||
|
||||
let result = get_changed_program(
|
||||
CacheInformation {
|
||||
old: Some(OldAstState {
|
||||
ast: program_old.ast.clone(),
|
||||
exec_state: executed,
|
||||
settings: Default::default(),
|
||||
}),
|
||||
new_ast: program_new.ast.clone(),
|
||||
},
|
||||
&Default::default(),
|
||||
);
|
||||
|
||||
assert_eq!(result, None);
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_get_changed_program_same_code_changed_code_comments() {
|
||||
let old = r#" // Removed the end face for the extrusion.
|
||||
firstSketch = startSketchOn('XY')
|
||||
|> startProfileAt([-12, 12], %)
|
||||
|> line([24, 0], %)
|
||||
|> line([0, -24], %)
|
||||
|> line([-24, 0], %) // my thing
|
||||
|> close(%)
|
||||
|> extrude(6, %)
|
||||
|
||||
// Remove the end face for the extrusion.
|
||||
shell({ faces = ['end'], thickness = 0.25 }, firstSketch) "#;
|
||||
|
||||
let new = r#"// Remove the end face for the extrusion.
|
||||
firstSketch = startSketchOn('XY')
|
||||
|> startProfileAt([-12, 12], %)
|
||||
|> line([24, 0], %)
|
||||
|> line([0, -24], %)
|
||||
|> line([-24, 0], %)
|
||||
|> close(%)
|
||||
|> extrude(6, %)
|
||||
|
||||
// Remove the end face for the extrusion.
|
||||
shell({ faces = ['end'], thickness = 0.25 }, firstSketch)"#;
|
||||
let program_old = crate::Program::parse_no_errs(old).unwrap();
|
||||
|
||||
let executed = execute(&program_old).await.unwrap();
|
||||
|
||||
let program_new = crate::Program::parse_no_errs(new).unwrap();
|
||||
|
||||
let result = get_changed_program(
|
||||
CacheInformation {
|
||||
old: Some(OldAstState {
|
||||
ast: program_old.ast.clone(),
|
||||
exec_state: executed,
|
||||
settings: Default::default(),
|
||||
}),
|
||||
new_ast: program_new.ast.clone(),
|
||||
},
|
||||
&Default::default(),
|
||||
);
|
||||
|
||||
assert!(result.is_some());
|
||||
|
||||
let result = result.unwrap();
|
||||
|
||||
assert_eq!(result.program, program_new.ast);
|
||||
assert!(result.clear_scene);
|
||||
}
|
||||
|
||||
// Changing the units with the exact same file should bust the cache.
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_get_changed_program_same_code_but_different_units() {
|
||||
let new = r#"// Remove the end face for the extrusion.
|
||||
firstSketch = startSketchOn('XY')
|
||||
|> startProfileAt([-12, 12], %)
|
||||
|> line([24, 0], %)
|
||||
|> line([0, -24], %)
|
||||
|> line([-24, 0], %)
|
||||
|> close(%)
|
||||
|> extrude(6, %)
|
||||
|
||||
// Remove the end face for the extrusion.
|
||||
shell({ faces = ['end'], thickness = 0.25 }, firstSketch)"#;
|
||||
let program = crate::Program::parse_no_errs(new).unwrap();
|
||||
|
||||
let executed = execute(&program).await.unwrap();
|
||||
|
||||
let result = get_changed_program(
|
||||
CacheInformation {
|
||||
old: Some(OldAstState {
|
||||
ast: program.ast.clone(),
|
||||
exec_state: executed,
|
||||
settings: Default::default(),
|
||||
}),
|
||||
new_ast: program.ast.clone(),
|
||||
},
|
||||
&crate::ExecutorSettings {
|
||||
units: crate::UnitLength::Cm,
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
assert!(result.is_some());
|
||||
|
||||
let result = result.unwrap();
|
||||
|
||||
assert_eq!(result.program, program.ast);
|
||||
assert!(result.clear_scene);
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
use sha2::{Digest as DigestTrait, Sha256};
|
||||
|
||||
use super::types::{DefaultParamVal, ItemVisibility, VariableKind};
|
||||
use super::types::{DefaultParamVal, ItemVisibility, LabelledExpression, VariableKind};
|
||||
use crate::parsing::ast::types::{
|
||||
ArrayExpression, ArrayRangeExpression, BinaryExpression, BinaryPart, BodyItem, CallExpression, CallExpressionKw,
|
||||
CommentStyle, ElseIf, Expr, ExpressionStatement, FnArgType, FunctionExpression, Identifier, IfExpression,
|
||||
@ -115,6 +115,7 @@ impl Expr {
|
||||
Expr::MemberExpression(me) => me.compute_digest(),
|
||||
Expr::UnaryExpression(ue) => ue.compute_digest(),
|
||||
Expr::IfExpression(e) => e.compute_digest(),
|
||||
Expr::LabelledExpression(e) => e.compute_digest(),
|
||||
Expr::None(_) => {
|
||||
let mut hasher = Sha256::new();
|
||||
hasher.update(b"Value::None");
|
||||
@ -396,6 +397,13 @@ impl UnaryExpression {
|
||||
});
|
||||
}
|
||||
|
||||
impl LabelledExpression {
|
||||
compute_digest!(|slf, hasher| {
|
||||
hasher.update(slf.expr.compute_digest());
|
||||
hasher.update(slf.label.compute_digest());
|
||||
});
|
||||
}
|
||||
|
||||
impl PipeExpression {
|
||||
compute_digest!(|slf, hasher| {
|
||||
hasher.update(slf.body.len().to_ne_bytes());
|
||||
|
@ -1,4 +1,3 @@
|
||||
pub(crate) mod cache;
|
||||
pub(crate) mod digest;
|
||||
pub mod modify;
|
||||
pub mod types;
|
||||
@ -37,6 +36,7 @@ impl Expr {
|
||||
Expr::MemberExpression(member_expression) => member_expression.module_id,
|
||||
Expr::UnaryExpression(unary_expression) => unary_expression.module_id,
|
||||
Expr::IfExpression(expr) => expr.module_id,
|
||||
Expr::LabelledExpression(expr) => expr.expr.module_id(),
|
||||
Expr::None(none) => none.module_id,
|
||||
}
|
||||
}
|
||||
|
@ -598,6 +598,7 @@ pub enum Expr {
|
||||
MemberExpression(BoxNode<MemberExpression>),
|
||||
UnaryExpression(BoxNode<UnaryExpression>),
|
||||
IfExpression(BoxNode<IfExpression>),
|
||||
LabelledExpression(BoxNode<LabelledExpression>),
|
||||
None(Node<KclNone>),
|
||||
}
|
||||
|
||||
@ -640,6 +641,7 @@ impl Expr {
|
||||
Expr::UnaryExpression(_unary_exp) => None,
|
||||
Expr::PipeSubstitution(_pipe_substitution) => None,
|
||||
Expr::IfExpression(_) => None,
|
||||
Expr::LabelledExpression(expr) => expr.expr.get_non_code_meta(),
|
||||
Expr::None(_none) => None,
|
||||
}
|
||||
}
|
||||
@ -666,6 +668,7 @@ impl Expr {
|
||||
Expr::UnaryExpression(ref mut unary_exp) => unary_exp.replace_value(source_range, new_value),
|
||||
Expr::IfExpression(_) => {}
|
||||
Expr::PipeSubstitution(_) => {}
|
||||
Expr::LabelledExpression(expr) => expr.expr.replace_value(source_range, new_value),
|
||||
Expr::None(_) => {}
|
||||
}
|
||||
}
|
||||
@ -687,6 +690,7 @@ impl Expr {
|
||||
Expr::MemberExpression(member_expression) => member_expression.start,
|
||||
Expr::UnaryExpression(unary_expression) => unary_expression.start,
|
||||
Expr::IfExpression(expr) => expr.start,
|
||||
Expr::LabelledExpression(expr) => expr.start,
|
||||
Expr::None(none) => none.start,
|
||||
}
|
||||
}
|
||||
@ -708,6 +712,7 @@ impl Expr {
|
||||
Expr::MemberExpression(member_expression) => member_expression.end,
|
||||
Expr::UnaryExpression(unary_expression) => unary_expression.end,
|
||||
Expr::IfExpression(expr) => expr.end,
|
||||
Expr::LabelledExpression(expr) => expr.end,
|
||||
Expr::None(none) => none.end,
|
||||
}
|
||||
}
|
||||
@ -734,6 +739,8 @@ impl Expr {
|
||||
Expr::Literal(_) => None,
|
||||
Expr::Identifier(_) => None,
|
||||
Expr::TagDeclarator(_) => None,
|
||||
// TODO LSP hover info for tag
|
||||
Expr::LabelledExpression(expr) => expr.expr.get_hover_value_for_position(pos, code),
|
||||
// TODO: LSP hover information for symbols. https://github.com/KittyCAD/modeling-app/issues/1127
|
||||
Expr::PipeSubstitution(_) => None,
|
||||
}
|
||||
@ -763,6 +770,7 @@ impl Expr {
|
||||
}
|
||||
Expr::UnaryExpression(ref mut unary_expression) => unary_expression.rename_identifiers(old_name, new_name),
|
||||
Expr::IfExpression(ref mut expr) => expr.rename_identifiers(old_name, new_name),
|
||||
Expr::LabelledExpression(expr) => expr.expr.rename_identifiers(old_name, new_name),
|
||||
Expr::None(_) => {}
|
||||
}
|
||||
}
|
||||
@ -788,9 +796,19 @@ impl Expr {
|
||||
Expr::MemberExpression(member_expression) => member_expression.get_constraint_level(),
|
||||
Expr::UnaryExpression(unary_expression) => unary_expression.get_constraint_level(),
|
||||
Expr::IfExpression(expr) => expr.get_constraint_level(),
|
||||
Expr::LabelledExpression(expr) => expr.expr.get_constraint_level(),
|
||||
Expr::None(none) => none.get_constraint_level(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn has_substitution_arg(&self) -> bool {
|
||||
match self {
|
||||
Expr::CallExpression(call_expression) => call_expression.has_substitution_arg(),
|
||||
Expr::CallExpressionKw(call_expression) => call_expression.has_substitution_arg(),
|
||||
Expr::LabelledExpression(expr) => expr.expr.has_substitution_arg(),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Expr> for SourceRange {
|
||||
@ -805,6 +823,36 @@ impl From<&Expr> for SourceRange {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
#[serde(tag = "type")]
|
||||
pub struct LabelledExpression {
|
||||
pub expr: Expr,
|
||||
pub label: Node<Identifier>,
|
||||
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[ts(optional)]
|
||||
pub digest: Option<Digest>,
|
||||
}
|
||||
|
||||
impl LabelledExpression {
|
||||
pub(crate) fn new(expr: Expr, label: Node<Identifier>) -> Node<LabelledExpression> {
|
||||
let start = expr.start();
|
||||
let end = label.end;
|
||||
let module_id = expr.module_id();
|
||||
Node::new(
|
||||
LabelledExpression {
|
||||
expr,
|
||||
label,
|
||||
digest: None,
|
||||
},
|
||||
start,
|
||||
end,
|
||||
module_id,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
#[serde(tag = "type")]
|
||||
|
@ -33,6 +33,8 @@ use crate::{
|
||||
SourceRange,
|
||||
};
|
||||
|
||||
use super::ast::types::LabelledExpression;
|
||||
|
||||
thread_local! {
|
||||
/// The current `ParseContext`. `None` if parsing is not currently happening on this thread.
|
||||
static CTXT: RefCell<Option<ParseContext>> = const { RefCell::new(None) };
|
||||
@ -337,7 +339,7 @@ fn pipe_expression(i: &mut TokenSlice) -> PResult<Node<PipeExpression>> {
|
||||
let mut values = vec![head];
|
||||
let value_surrounded_by_comments = (
|
||||
repeat(0.., preceded(opt(whitespace), non_code_node)), // Before the expression.
|
||||
preceded(opt(whitespace), fn_call), // The expression.
|
||||
preceded(opt(whitespace), labelled_fn_call), // The expression.
|
||||
repeat(0.., noncode_just_after_code), // After the expression.
|
||||
);
|
||||
let tail: Vec<(Vec<_>, _, Vec<_>)> = repeat(
|
||||
@ -353,7 +355,7 @@ fn pipe_expression(i: &mut TokenSlice) -> PResult<Node<PipeExpression>> {
|
||||
// First, ensure they all have a % in their args.
|
||||
let calls_without_substitution = tail.iter().find_map(|(_nc, call_expr, _nc2)| {
|
||||
if !call_expr.has_substitution_arg() {
|
||||
Some(call_expr.as_source_range())
|
||||
Some(call_expr.into())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@ -373,7 +375,7 @@ fn pipe_expression(i: &mut TokenSlice) -> PResult<Node<PipeExpression>> {
|
||||
max_noncode_end = nc.end.max(max_noncode_end);
|
||||
non_code_meta.insert(code_count, nc);
|
||||
}
|
||||
values.push(Expr::CallExpression(Box::new(code)));
|
||||
values.push(code);
|
||||
code_count += 1;
|
||||
for nc in noncode_after {
|
||||
max_noncode_end = nc.end.max(max_noncode_end);
|
||||
@ -527,7 +529,8 @@ fn operand(i: &mut TokenSlice) -> PResult<BinaryPart> {
|
||||
| Expr::PipeSubstitution(_)
|
||||
| Expr::ArrayExpression(_)
|
||||
| Expr::ArrayRangeExpression(_)
|
||||
| Expr::ObjectExpression(_) => return Err(CompilationError::fatal(source_range, TODO_783)),
|
||||
| Expr::ObjectExpression(_)
|
||||
| Expr::LabelledExpression(..) => return Err(CompilationError::fatal(source_range, TODO_783)),
|
||||
Expr::None(_) => {
|
||||
return Err(CompilationError::fatal(
|
||||
source_range,
|
||||
@ -1628,13 +1631,34 @@ fn expression(i: &mut TokenSlice) -> PResult<Expr> {
|
||||
}
|
||||
|
||||
fn expression_but_not_pipe(i: &mut TokenSlice) -> PResult<Expr> {
|
||||
alt((
|
||||
let expr = alt((
|
||||
binary_expression.map(Box::new).map(Expr::BinaryExpression),
|
||||
unary_expression.map(Box::new).map(Expr::UnaryExpression),
|
||||
expr_allowed_in_pipe_expr,
|
||||
))
|
||||
.context(expected("a KCL value"))
|
||||
.parse_next(i)
|
||||
.parse_next(i)?;
|
||||
|
||||
let label = opt(label).parse_next(i)?;
|
||||
match label {
|
||||
Some(label) => Ok(Expr::LabelledExpression(Box::new(LabelledExpression::new(expr, label)))),
|
||||
None => Ok(expr),
|
||||
}
|
||||
}
|
||||
|
||||
fn label(i: &mut TokenSlice) -> PResult<Node<Identifier>> {
|
||||
let result = preceded(
|
||||
(whitespace, import_as_keyword, whitespace),
|
||||
identifier.context(expected("an identifier")),
|
||||
)
|
||||
.parse_next(i)?;
|
||||
|
||||
ParseContext::warn(CompilationError::err(
|
||||
SourceRange::new(result.start, result.end, result.module_id),
|
||||
"Using `as` for tagging expressions is experimental, likely to be buggy, and likely to change",
|
||||
));
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn unnecessarily_bracketed(i: &mut TokenSlice) -> PResult<Expr> {
|
||||
@ -2450,6 +2474,17 @@ fn typecheck(spec_arg: &crate::docs::StdLibFnArg, arg: &&Expr) -> PResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn labelled_fn_call(i: &mut TokenSlice) -> PResult<Expr> {
|
||||
let call = fn_call.parse_next(i)?;
|
||||
let expr = Expr::CallExpression(Box::new(call));
|
||||
|
||||
let label = opt(label).parse_next(i)?;
|
||||
match label {
|
||||
Some(label) => Ok(Expr::LabelledExpression(Box::new(LabelledExpression::new(expr, label)))),
|
||||
None => Ok(expr),
|
||||
}
|
||||
}
|
||||
|
||||
fn fn_call(i: &mut TokenSlice) -> PResult<Node<CallExpression>> {
|
||||
let fn_name = identifier(i)?;
|
||||
opt(whitespace).parse_next(i)?;
|
||||
|
@ -5,6 +5,7 @@ use std::{fmt, iter::Enumerate, num::NonZeroUsize};
|
||||
|
||||
use anyhow::Result;
|
||||
use parse_display::Display;
|
||||
use tokeniser::Input;
|
||||
use tower_lsp::lsp_types::SemanticTokenType;
|
||||
use winnow::{
|
||||
self,
|
||||
@ -17,7 +18,6 @@ use crate::{
|
||||
parsing::ast::types::{ItemVisibility, VariableKind},
|
||||
source_range::{ModuleId, SourceRange},
|
||||
};
|
||||
use tokeniser::Input;
|
||||
|
||||
mod tokeniser;
|
||||
|
||||
|
@ -10,13 +10,12 @@ use winnow::{
|
||||
Located, Stateful,
|
||||
};
|
||||
|
||||
use super::TokenStream;
|
||||
use crate::{
|
||||
parsing::token::{Token, TokenType},
|
||||
source_range::ModuleId,
|
||||
};
|
||||
|
||||
use super::TokenStream;
|
||||
|
||||
lazy_static! {
|
||||
pub(crate) static ref RESERVED_WORDS: FnvHashMap<&'static str, TokenType> = {
|
||||
let mut set = FnvHashMap::default();
|
||||
@ -365,9 +364,8 @@ fn keyword_type_or_word(i: &mut Input<'_>) -> PResult<Token> {
|
||||
mod tests {
|
||||
use winnow::Located;
|
||||
|
||||
use crate::parsing::token::TokenSlice;
|
||||
|
||||
use super::*;
|
||||
use crate::parsing::token::TokenSlice;
|
||||
fn assert_parse_err<'i, P, O, E>(mut p: P, s: &'i str)
|
||||
where
|
||||
O: std::fmt::Debug,
|
||||
|
263
src/wasm-lib/kcl/src/std/appearance.rs
Normal file
@ -0,0 +1,263 @@
|
||||
//! Standard library appearance.
|
||||
|
||||
use anyhow::Result;
|
||||
use derive_docs::stdlib;
|
||||
use kcmc::{each_cmd as mcmd, ModelingCmd};
|
||||
use kittycad_modeling_cmds::{self as kcmc, shared::Color};
|
||||
use regex::Regex;
|
||||
use rgba_simple::Hex;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use validator::Validate;
|
||||
|
||||
use crate::{
|
||||
errors::{KclError, KclErrorDetails},
|
||||
execution::{ExecState, KclValue, Solid, SolidSet},
|
||||
std::Args,
|
||||
};
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
static ref HEX_REGEX: Regex = Regex::new(r"^#[0-9a-fA-F]{6}$").unwrap();
|
||||
}
|
||||
|
||||
/// Data for appearance.
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, Validate)]
|
||||
#[ts(export)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct AppearanceData {
|
||||
/// Color of the new material, a hex string like "#ff0000".
|
||||
#[schemars(regex(pattern = "#[0-9a-fA-F]{6}"))]
|
||||
pub color: String,
|
||||
/// Metalness of the new material, a percentage like 95.7.
|
||||
#[validate(range(min = 0.0, max = 100.0))]
|
||||
pub metalness: Option<f64>,
|
||||
/// Roughness of the new material, a percentage like 95.7.
|
||||
#[validate(range(min = 0.0, max = 100.0))]
|
||||
pub roughness: Option<f64>,
|
||||
// TODO(jess): we can also ambient occlusion here I just don't know what it is.
|
||||
}
|
||||
|
||||
/// Set the appearance of a solid. This only works on solids, not sketches or individual paths.
|
||||
pub async fn appearance(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let (data, solid_set): (AppearanceData, SolidSet) = args.get_data_and_solid_set()?;
|
||||
|
||||
// Validate the data.
|
||||
data.validate().map_err(|err| {
|
||||
KclError::Semantic(KclErrorDetails {
|
||||
message: format!("Invalid appearance data: {}", err),
|
||||
source_ranges: vec![args.source_range],
|
||||
})
|
||||
})?;
|
||||
|
||||
// Make sure the color if set is valid.
|
||||
if !HEX_REGEX.is_match(&data.color) {
|
||||
return Err(KclError::Semantic(KclErrorDetails {
|
||||
message: format!("Invalid hex color (`{}`), try something like `#fff000`", data.color),
|
||||
source_ranges: vec![args.source_range],
|
||||
}));
|
||||
}
|
||||
|
||||
let result = inner_appearance(data, solid_set, args).await?;
|
||||
Ok(result.into())
|
||||
}
|
||||
|
||||
/// Set the appearance of a solid. This only works on solids, not sketches or individual paths.
|
||||
///
|
||||
/// This will work on any solid, including extruded solids, revolved solids, and shelled solids.
|
||||
/// ```no_run
|
||||
/// /// Add color to an extruded solid.
|
||||
/// exampleSketch = startSketchOn("XZ")
|
||||
/// |> startProfileAt([0, 0], %)
|
||||
/// |> lineTo([10, 0], %)
|
||||
/// |> lineTo([0, 10], %)
|
||||
/// |> lineTo([-10, 0], %)
|
||||
/// |> close(%)
|
||||
///
|
||||
/// example = extrude(5, exampleSketch)
|
||||
/// |> appearance({color= '#ff0000', metalness= 50, roughness= 50}, %)
|
||||
/// ```
|
||||
///
|
||||
/// ```no_run
|
||||
/// /// Add color to a revolved solid.
|
||||
/// sketch001 = startSketchOn('XY')
|
||||
/// |> circle({ center = [15, 0], radius = 5 }, %)
|
||||
/// |> revolve({ angle = 360, axis = 'y' }, %)
|
||||
/// |> appearance({
|
||||
/// color = '#ff0000',
|
||||
/// metalness = 90,
|
||||
/// roughness = 90
|
||||
/// }, %)
|
||||
/// ```
|
||||
///
|
||||
/// ```no_run
|
||||
/// /// Add color to different solids.
|
||||
/// fn cube(center) {
|
||||
/// return startSketchOn('XY')
|
||||
/// |> startProfileAt([center[0] - 10, center[1] - 10], %)
|
||||
/// |> lineTo([center[0] + 10, center[1] - 10], %)
|
||||
/// |> lineTo([center[0] + 10, center[1] + 10], %)
|
||||
/// |> lineTo([center[0] - 10, center[1] + 10], %)
|
||||
/// |> close(%)
|
||||
/// |> extrude(10, %)
|
||||
/// }
|
||||
///
|
||||
/// example0 = cube([0, 0])
|
||||
/// example1 = cube([20, 0])
|
||||
/// example2 = cube([40, 0])
|
||||
///
|
||||
/// appearance({color= '#ff0000', metalness= 50, roughness= 50}, [example0, example1])
|
||||
/// appearance({color= '#00ff00', metalness= 50, roughness= 50}, example2)
|
||||
/// ```
|
||||
///
|
||||
/// ```no_run
|
||||
/// /// You can set the appearance before or after you shell it will yield the same result.
|
||||
/// /// This example shows setting the appearance _after_ the shell.
|
||||
/// firstSketch = startSketchOn('XY')
|
||||
/// |> startProfileAt([-12, 12], %)
|
||||
/// |> line([24, 0], %)
|
||||
/// |> line([0, -24], %)
|
||||
/// |> line([-24, 0], %)
|
||||
/// |> close(%)
|
||||
/// |> extrude(6, %)
|
||||
///
|
||||
/// shell({
|
||||
/// faces = ['end'],
|
||||
/// thickness = 0.25,
|
||||
/// }, firstSketch)
|
||||
/// |> appearance({
|
||||
/// color = '#ff0000',
|
||||
/// metalness = 90,
|
||||
/// roughness = 90
|
||||
/// }, %)
|
||||
/// ```
|
||||
///
|
||||
/// ```no_run
|
||||
/// /// You can set the appearance before or after you shell it will yield the same result.
|
||||
/// /// This example shows setting the appearance _before_ the shell.
|
||||
/// firstSketch = startSketchOn('XY')
|
||||
/// |> startProfileAt([-12, 12], %)
|
||||
/// |> line([24, 0], %)
|
||||
/// |> line([0, -24], %)
|
||||
/// |> line([-24, 0], %)
|
||||
/// |> close(%)
|
||||
/// |> extrude(6, %)
|
||||
/// |> appearance({
|
||||
/// color = '#ff0000',
|
||||
/// metalness = 90,
|
||||
/// roughness = 90
|
||||
/// }, %)
|
||||
///
|
||||
/// shell({
|
||||
/// faces = ['end'],
|
||||
/// thickness = 0.25,
|
||||
/// }, firstSketch)
|
||||
/// ```
|
||||
///
|
||||
/// ```no_run
|
||||
/// /// Setting the appearance of a 3D pattern can be done _before_ or _after_ the pattern.
|
||||
/// /// This example shows _before_ the pattern.
|
||||
/// exampleSketch = startSketchOn('XZ')
|
||||
/// |> startProfileAt([0, 0], %)
|
||||
/// |> line([0, 2], %)
|
||||
/// |> line([3, 1], %)
|
||||
/// |> line([0, -4], %)
|
||||
/// |> close(%)
|
||||
///
|
||||
/// example = extrude(1, exampleSketch)
|
||||
/// |> appearance({
|
||||
/// color = '#ff0000',
|
||||
/// metalness = 90,
|
||||
/// roughness = 90
|
||||
/// }, %)
|
||||
/// |> patternLinear3d({
|
||||
/// axis = [1, 0, 1],
|
||||
/// instances = 7,
|
||||
/// distance = 6
|
||||
/// }, %)
|
||||
/// ```
|
||||
///
|
||||
/// ```no_run
|
||||
/// /// Setting the appearance of a 3D pattern can be done _before_ or _after_ the pattern.
|
||||
/// /// This example shows _after_ the pattern.
|
||||
/// exampleSketch = startSketchOn('XZ')
|
||||
/// |> startProfileAt([0, 0], %)
|
||||
/// |> line([0, 2], %)
|
||||
/// |> line([3, 1], %)
|
||||
/// |> line([0, -4], %)
|
||||
/// |> close(%)
|
||||
///
|
||||
/// example = extrude(1, exampleSketch)
|
||||
/// |> patternLinear3d({
|
||||
/// axis = [1, 0, 1],
|
||||
/// instances = 7,
|
||||
/// distance = 6
|
||||
/// }, %)
|
||||
/// |> appearance({
|
||||
/// color = '#ff0000',
|
||||
/// metalness = 90,
|
||||
/// roughness = 90
|
||||
/// }, %)
|
||||
/// ```
|
||||
///
|
||||
/// ```no_run
|
||||
/// /// Color the result of a 2D pattern that was extruded.
|
||||
/// exampleSketch = startSketchOn('XZ')
|
||||
/// |> startProfileAt([.5, 25], %)
|
||||
/// |> line([0, 5], %)
|
||||
/// |> line([-1, 0], %)
|
||||
/// |> line([0, -5], %)
|
||||
/// |> close(%)
|
||||
/// |> patternCircular2d({
|
||||
/// center = [0, 0],
|
||||
/// instances = 13,
|
||||
/// arcDegrees = 360,
|
||||
/// rotateDuplicates = true
|
||||
/// }, %)
|
||||
///
|
||||
/// example = extrude(1, exampleSketch)
|
||||
/// |> appearance({
|
||||
/// color = '#ff0000',
|
||||
/// metalness = 90,
|
||||
/// roughness = 90
|
||||
/// }, %)
|
||||
/// ```
|
||||
#[stdlib {
|
||||
name = "appearance",
|
||||
}]
|
||||
async fn inner_appearance(data: AppearanceData, solid_set: SolidSet, args: Args) -> Result<SolidSet, KclError> {
|
||||
let solids: Vec<Box<Solid>> = solid_set.into();
|
||||
|
||||
for solid in &solids {
|
||||
// Set the material properties.
|
||||
let rgb = rgba_simple::RGB::<f32>::from_hex(&data.color).map_err(|err| {
|
||||
KclError::Semantic(KclErrorDetails {
|
||||
message: format!("Invalid hex color (`{}`): {}", data.color, err),
|
||||
source_ranges: vec![args.source_range],
|
||||
})
|
||||
})?;
|
||||
|
||||
let color = Color {
|
||||
r: rgb.red,
|
||||
g: rgb.green,
|
||||
b: rgb.blue,
|
||||
a: 100.0,
|
||||
};
|
||||
|
||||
args.batch_modeling_cmd(
|
||||
uuid::Uuid::new_v4(),
|
||||
ModelingCmd::from(mcmd::ObjectSetMaterialParamsPbr {
|
||||
object_id: solid.id,
|
||||
color,
|
||||
metalness: data.metalness.unwrap_or_default() as f32 / 100.0,
|
||||
roughness: data.roughness.unwrap_or_default() as f32 / 100.0,
|
||||
ambient_occlusion: 0.0,
|
||||
}),
|
||||
)
|
||||
.await?;
|
||||
|
||||
// Idk if we want to actually modify the memory for the colors, but I'm not right now since
|
||||
// I can't think of a use case for it.
|
||||
}
|
||||
|
||||
Ok(SolidSet::from(solids))
|
||||
}
|
@ -1096,6 +1096,20 @@ impl<'a> FromKclValue<'a> for super::fillet::FilletData {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromKclValue<'a> for super::appearance::AppearanceData {
|
||||
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
||||
let obj = arg.as_object()?;
|
||||
let_field_of!(obj, color);
|
||||
let_field_of!(obj, metalness?);
|
||||
let_field_of!(obj, roughness?);
|
||||
Some(Self {
|
||||
color,
|
||||
metalness,
|
||||
roughness,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromKclValue<'a> for super::helix::HelixData {
|
||||
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
||||
let obj = arg.as_object()?;
|
||||
|
@ -1,5 +1,6 @@
|
||||
//! Functions implemented for language execution.
|
||||
|
||||
pub mod appearance;
|
||||
pub mod args;
|
||||
pub mod array;
|
||||
pub mod assert;
|
||||
@ -50,6 +51,7 @@ lazy_static! {
|
||||
Box::new(LegLen),
|
||||
Box::new(LegAngX),
|
||||
Box::new(LegAngY),
|
||||
Box::new(crate::std::appearance::Appearance),
|
||||
Box::new(crate::std::convert::Int),
|
||||
Box::new(crate::std::extrude::Extrude),
|
||||
Box::new(crate::std::segment::SegEnd),
|
||||
|
@ -55,7 +55,11 @@ async fn do_execute_and_snapshot(
|
||||
program: Program,
|
||||
) -> Result<(crate::execution::ExecState, image::DynamicImage), ExecError> {
|
||||
let mut exec_state = Default::default();
|
||||
let snapshot_png_bytes = ctx.execute_and_prepare(&program, &mut exec_state).await?.contents.0;
|
||||
let snapshot_png_bytes = ctx
|
||||
.execute_and_prepare_snapshot(&program, &mut exec_state)
|
||||
.await?
|
||||
.contents
|
||||
.0;
|
||||
|
||||
// Decode the snapshot, return it.
|
||||
let img = image::ImageReader::new(std::io::Cursor::new(snapshot_png_bytes))
|
||||
|
@ -194,6 +194,12 @@ impl Expr {
|
||||
Expr::UnaryExpression(unary_exp) => unary_exp.recast(options),
|
||||
Expr::IfExpression(e) => e.recast(options, indentation_level, ctxt),
|
||||
Expr::PipeSubstitution(_) => crate::parsing::PIPE_SUBSTITUTION_OPERATOR.to_string(),
|
||||
Expr::LabelledExpression(e) => {
|
||||
let mut result = e.expr.recast(options, indentation_level, ctxt);
|
||||
result += " as ";
|
||||
result += &e.label.name;
|
||||
result
|
||||
}
|
||||
Expr::None(_) => {
|
||||
unimplemented!("there is no literal None, see https://github.com/KittyCAD/modeling-app/issues/1115")
|
||||
}
|
||||
@ -407,7 +413,8 @@ fn expr_is_trivial(expr: &Expr) -> bool {
|
||||
| Expr::ObjectExpression(_)
|
||||
| Expr::MemberExpression(_)
|
||||
| Expr::UnaryExpression(_)
|
||||
| Expr::IfExpression(_) => false,
|
||||
| Expr::IfExpression(_)
|
||||
| Expr::LabelledExpression(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
@ -1519,6 +1526,28 @@ tabs_l = startSketchOn({
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_as() {
|
||||
let some_program_string = r#"fn cube(pos, scale) {
|
||||
x = dfsfs + dfsfsd as y
|
||||
|
||||
sg = startSketchOn('XY')
|
||||
|> startProfileAt(pos, %) as foo
|
||||
|> line([0, scale], %)
|
||||
|> line([scale, 0], %) as bar
|
||||
|> line([0 as baz, -scale] as qux, %)
|
||||
|> close(%)
|
||||
|> extrude(scale, %)
|
||||
}
|
||||
|
||||
cube(0, 0) as cub
|
||||
"#;
|
||||
let program = crate::parsing::top_level_parse(some_program_string).unwrap();
|
||||
|
||||
let recasted = program.recast(&Default::default(), 0);
|
||||
assert_eq!(recasted, some_program_string,);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_recast_with_bad_indentation() {
|
||||
let some_program_string = r#"part001 = startSketchOn('XY')
|
||||
|
@ -32,6 +32,7 @@ pub enum Node<'a> {
|
||||
UnaryExpression(NodeRef<'a, types::UnaryExpression>),
|
||||
IfExpression(NodeRef<'a, types::IfExpression>),
|
||||
ElseIf(&'a types::ElseIf),
|
||||
LabelledExpression(NodeRef<'a, types::LabelledExpression>),
|
||||
|
||||
Parameter(&'a types::Parameter),
|
||||
|
||||
@ -79,6 +80,7 @@ impl TryFrom<&Node<'_>> for SourceRange {
|
||||
Node::ObjectProperty(n) => SourceRange::from(*n),
|
||||
Node::MemberObject(m) => SourceRange::new(m.start(), m.end(), m.module_id()),
|
||||
Node::IfExpression(n) => SourceRange::from(*n),
|
||||
Node::LabelledExpression(n) => SourceRange::from(*n),
|
||||
Node::LiteralIdentifier(l) => SourceRange::new(l.start(), l.end(), l.module_id()),
|
||||
|
||||
// This is broken too
|
||||
@ -120,6 +122,7 @@ impl<'tree> From<&'tree types::Expr> for Node<'tree> {
|
||||
types::Expr::MemberExpression(me) => me.as_ref().into(),
|
||||
types::Expr::UnaryExpression(ue) => ue.as_ref().into(),
|
||||
types::Expr::IfExpression(e) => e.as_ref().into(),
|
||||
types::Expr::LabelledExpression(e) => e.as_ref().into(),
|
||||
types::Expr::None(n) => n.into(),
|
||||
}
|
||||
}
|
||||
@ -185,5 +188,6 @@ impl_from_ref!(Node, Parameter);
|
||||
impl_from_ref!(Node, MemberObject);
|
||||
impl_from!(Node, IfExpression);
|
||||
impl_from!(Node, ElseIf);
|
||||
impl_from!(Node, LabelledExpression);
|
||||
impl_from_ref!(Node, LiteralIdentifier);
|
||||
impl_from!(Node, KclNone);
|
||||
|
@ -127,6 +127,9 @@ impl<'tree> Visitable<'tree> for Node<'tree> {
|
||||
Node::ElseIf(n) => {
|
||||
vec![(&n.cond).into(), n.then_val.as_ref().into()]
|
||||
}
|
||||
Node::LabelledExpression(e) => {
|
||||
vec![(&e.expr).into(), (&e.label).into()]
|
||||
}
|
||||
Node::PipeSubstitution(_)
|
||||
| Node::TagDeclarator(_)
|
||||
| Node::Identifier(_)
|
||||
@ -141,9 +144,10 @@ impl<'tree> Visitable<'tree> for Node<'tree> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::sync::Mutex;
|
||||
|
||||
use super::*;
|
||||
|
||||
macro_rules! kcl {
|
||||
( $kcl:expr ) => {{
|
||||
$crate::parsing::top_level_parse($kcl).unwrap()
|
||||
|
After Width: | Height: | Size: 61 KiB |
After Width: | Height: | Size: 110 KiB |
After Width: | Height: | Size: 61 KiB |
After Width: | Height: | Size: 62 KiB |
After Width: | Height: | Size: 62 KiB |
After Width: | Height: | Size: 41 KiB |
After Width: | Height: | Size: 41 KiB |
After Width: | Height: | Size: 40 KiB |
@ -296,7 +296,7 @@ description: Result of parsing sketch_on_face_start.kcl
|
||||
},
|
||||
{
|
||||
"declaration": {
|
||||
"end": 236,
|
||||
"end": 243,
|
||||
"id": {
|
||||
"end": 183,
|
||||
"name": "part001",
|
||||
@ -372,35 +372,47 @@ description: Result of parsing sketch_on_face_start.kcl
|
||||
"type": "CallExpression"
|
||||
},
|
||||
{
|
||||
"arguments": [
|
||||
{
|
||||
"end": 232,
|
||||
"raw": "20",
|
||||
"start": 230,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": 20.0
|
||||
"end": 243,
|
||||
"expr": {
|
||||
"arguments": [
|
||||
{
|
||||
"end": 232,
|
||||
"raw": "20",
|
||||
"start": 230,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": 20.0
|
||||
},
|
||||
{
|
||||
"end": 235,
|
||||
"start": 234,
|
||||
"type": "PipeSubstitution",
|
||||
"type": "PipeSubstitution"
|
||||
}
|
||||
],
|
||||
"callee": {
|
||||
"end": 229,
|
||||
"name": "extrude",
|
||||
"start": 222,
|
||||
"type": "Identifier"
|
||||
},
|
||||
{
|
||||
"end": 235,
|
||||
"start": 234,
|
||||
"type": "PipeSubstitution",
|
||||
"type": "PipeSubstitution"
|
||||
}
|
||||
],
|
||||
"callee": {
|
||||
"end": 229,
|
||||
"name": "extrude",
|
||||
"end": 236,
|
||||
"start": 222,
|
||||
"type": "CallExpression",
|
||||
"type": "CallExpression"
|
||||
},
|
||||
"label": {
|
||||
"end": 243,
|
||||
"name": "foo",
|
||||
"start": 240,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"end": 236,
|
||||
"start": 222,
|
||||
"type": "CallExpression",
|
||||
"type": "CallExpression"
|
||||
"type": "LabelledExpression",
|
||||
"type": "LabelledExpression"
|
||||
}
|
||||
],
|
||||
"end": 236,
|
||||
"end": 243,
|
||||
"start": 186,
|
||||
"type": "PipeExpression",
|
||||
"type": "PipeExpression"
|
||||
@ -408,7 +420,7 @@ description: Result of parsing sketch_on_face_start.kcl
|
||||
"start": 176,
|
||||
"type": "VariableDeclarator"
|
||||
},
|
||||
"end": 236,
|
||||
"end": 243,
|
||||
"kind": "const",
|
||||
"start": 176,
|
||||
"type": "VariableDeclaration",
|
||||
@ -416,11 +428,11 @@ description: Result of parsing sketch_on_face_start.kcl
|
||||
},
|
||||
{
|
||||
"declaration": {
|
||||
"end": 410,
|
||||
"end": 413,
|
||||
"id": {
|
||||
"end": 245,
|
||||
"end": 252,
|
||||
"name": "part002",
|
||||
"start": 238,
|
||||
"start": 245,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"init": {
|
||||
@ -428,29 +440,29 @@ description: Result of parsing sketch_on_face_start.kcl
|
||||
{
|
||||
"arguments": [
|
||||
{
|
||||
"end": 269,
|
||||
"name": "part001",
|
||||
"start": 262,
|
||||
"end": 272,
|
||||
"name": "foo",
|
||||
"start": 269,
|
||||
"type": "Identifier",
|
||||
"type": "Identifier"
|
||||
},
|
||||
{
|
||||
"end": 278,
|
||||
"end": 281,
|
||||
"raw": "\"start\"",
|
||||
"start": 271,
|
||||
"start": 274,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": "start"
|
||||
}
|
||||
],
|
||||
"callee": {
|
||||
"end": 261,
|
||||
"end": 268,
|
||||
"name": "startSketchOn",
|
||||
"start": 248,
|
||||
"start": 255,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"end": 279,
|
||||
"start": 248,
|
||||
"end": 282,
|
||||
"start": 255,
|
||||
"type": "CallExpression",
|
||||
"type": "CallExpression"
|
||||
},
|
||||
@ -458,14 +470,6 @@ description: Result of parsing sketch_on_face_start.kcl
|
||||
"arguments": [
|
||||
{
|
||||
"elements": [
|
||||
{
|
||||
"end": 302,
|
||||
"raw": "0",
|
||||
"start": 301,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": 0.0
|
||||
},
|
||||
{
|
||||
"end": 305,
|
||||
"raw": "0",
|
||||
@ -473,28 +477,36 @@ description: Result of parsing sketch_on_face_start.kcl
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": 0.0
|
||||
},
|
||||
{
|
||||
"end": 308,
|
||||
"raw": "0",
|
||||
"start": 307,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": 0.0
|
||||
}
|
||||
],
|
||||
"end": 306,
|
||||
"start": 300,
|
||||
"end": 309,
|
||||
"start": 303,
|
||||
"type": "ArrayExpression",
|
||||
"type": "ArrayExpression"
|
||||
},
|
||||
{
|
||||
"end": 309,
|
||||
"start": 308,
|
||||
"end": 312,
|
||||
"start": 311,
|
||||
"type": "PipeSubstitution",
|
||||
"type": "PipeSubstitution"
|
||||
}
|
||||
],
|
||||
"callee": {
|
||||
"end": 299,
|
||||
"end": 302,
|
||||
"name": "startProfileAt",
|
||||
"start": 285,
|
||||
"start": 288,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"end": 310,
|
||||
"start": 285,
|
||||
"end": 313,
|
||||
"start": 288,
|
||||
"type": "CallExpression",
|
||||
"type": "CallExpression"
|
||||
},
|
||||
@ -503,42 +515,42 @@ description: Result of parsing sketch_on_face_start.kcl
|
||||
{
|
||||
"elements": [
|
||||
{
|
||||
"end": 323,
|
||||
"end": 326,
|
||||
"raw": "0",
|
||||
"start": 322,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": 0.0
|
||||
},
|
||||
{
|
||||
"end": 327,
|
||||
"raw": "10",
|
||||
"start": 325,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": 0.0
|
||||
},
|
||||
{
|
||||
"end": 330,
|
||||
"raw": "10",
|
||||
"start": 328,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": 10.0
|
||||
}
|
||||
],
|
||||
"end": 328,
|
||||
"start": 321,
|
||||
"end": 331,
|
||||
"start": 324,
|
||||
"type": "ArrayExpression",
|
||||
"type": "ArrayExpression"
|
||||
},
|
||||
{
|
||||
"end": 331,
|
||||
"start": 330,
|
||||
"end": 334,
|
||||
"start": 333,
|
||||
"type": "PipeSubstitution",
|
||||
"type": "PipeSubstitution"
|
||||
}
|
||||
],
|
||||
"callee": {
|
||||
"end": 320,
|
||||
"end": 323,
|
||||
"name": "line",
|
||||
"start": 316,
|
||||
"start": 319,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"end": 332,
|
||||
"start": 316,
|
||||
"end": 335,
|
||||
"start": 319,
|
||||
"type": "CallExpression",
|
||||
"type": "CallExpression"
|
||||
},
|
||||
@ -547,42 +559,42 @@ description: Result of parsing sketch_on_face_start.kcl
|
||||
{
|
||||
"elements": [
|
||||
{
|
||||
"end": 346,
|
||||
"end": 349,
|
||||
"raw": "10",
|
||||
"start": 344,
|
||||
"start": 347,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": 10.0
|
||||
},
|
||||
{
|
||||
"end": 349,
|
||||
"end": 352,
|
||||
"raw": "0",
|
||||
"start": 348,
|
||||
"start": 351,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": 0.0
|
||||
}
|
||||
],
|
||||
"end": 350,
|
||||
"start": 343,
|
||||
"end": 353,
|
||||
"start": 346,
|
||||
"type": "ArrayExpression",
|
||||
"type": "ArrayExpression"
|
||||
},
|
||||
{
|
||||
"end": 353,
|
||||
"start": 352,
|
||||
"end": 356,
|
||||
"start": 355,
|
||||
"type": "PipeSubstitution",
|
||||
"type": "PipeSubstitution"
|
||||
}
|
||||
],
|
||||
"callee": {
|
||||
"end": 342,
|
||||
"end": 345,
|
||||
"name": "line",
|
||||
"start": 338,
|
||||
"start": 341,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"end": 354,
|
||||
"start": 338,
|
||||
"end": 357,
|
||||
"start": 341,
|
||||
"type": "CallExpression",
|
||||
"type": "CallExpression"
|
||||
},
|
||||
@ -591,123 +603,123 @@ description: Result of parsing sketch_on_face_start.kcl
|
||||
{
|
||||
"elements": [
|
||||
{
|
||||
"end": 367,
|
||||
"end": 370,
|
||||
"raw": "0",
|
||||
"start": 366,
|
||||
"start": 369,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": 0.0
|
||||
},
|
||||
{
|
||||
"argument": {
|
||||
"end": 372,
|
||||
"end": 375,
|
||||
"raw": "10",
|
||||
"start": 370,
|
||||
"start": 373,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": 10.0
|
||||
},
|
||||
"end": 372,
|
||||
"end": 375,
|
||||
"operator": "-",
|
||||
"start": 369,
|
||||
"start": 372,
|
||||
"type": "UnaryExpression",
|
||||
"type": "UnaryExpression"
|
||||
}
|
||||
],
|
||||
"end": 373,
|
||||
"start": 365,
|
||||
"end": 376,
|
||||
"start": 368,
|
||||
"type": "ArrayExpression",
|
||||
"type": "ArrayExpression"
|
||||
},
|
||||
{
|
||||
"end": 376,
|
||||
"start": 375,
|
||||
"end": 379,
|
||||
"start": 378,
|
||||
"type": "PipeSubstitution",
|
||||
"type": "PipeSubstitution"
|
||||
}
|
||||
],
|
||||
"callee": {
|
||||
"end": 364,
|
||||
"end": 367,
|
||||
"name": "line",
|
||||
"start": 360,
|
||||
"start": 363,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"end": 377,
|
||||
"start": 360,
|
||||
"end": 380,
|
||||
"start": 363,
|
||||
"type": "CallExpression",
|
||||
"type": "CallExpression"
|
||||
},
|
||||
{
|
||||
"arguments": [
|
||||
{
|
||||
"end": 390,
|
||||
"start": 389,
|
||||
"end": 393,
|
||||
"start": 392,
|
||||
"type": "PipeSubstitution",
|
||||
"type": "PipeSubstitution"
|
||||
}
|
||||
],
|
||||
"callee": {
|
||||
"end": 388,
|
||||
"end": 391,
|
||||
"name": "close",
|
||||
"start": 383,
|
||||
"start": 386,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"end": 391,
|
||||
"start": 383,
|
||||
"end": 394,
|
||||
"start": 386,
|
||||
"type": "CallExpression",
|
||||
"type": "CallExpression"
|
||||
},
|
||||
{
|
||||
"arguments": [
|
||||
{
|
||||
"end": 406,
|
||||
"end": 409,
|
||||
"raw": "5",
|
||||
"start": 405,
|
||||
"start": 408,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": 5.0
|
||||
},
|
||||
{
|
||||
"end": 409,
|
||||
"start": 408,
|
||||
"end": 412,
|
||||
"start": 411,
|
||||
"type": "PipeSubstitution",
|
||||
"type": "PipeSubstitution"
|
||||
}
|
||||
],
|
||||
"callee": {
|
||||
"end": 404,
|
||||
"end": 407,
|
||||
"name": "extrude",
|
||||
"start": 397,
|
||||
"start": 400,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"end": 410,
|
||||
"start": 397,
|
||||
"end": 413,
|
||||
"start": 400,
|
||||
"type": "CallExpression",
|
||||
"type": "CallExpression"
|
||||
}
|
||||
],
|
||||
"end": 410,
|
||||
"start": 248,
|
||||
"end": 413,
|
||||
"start": 255,
|
||||
"type": "PipeExpression",
|
||||
"type": "PipeExpression"
|
||||
},
|
||||
"start": 238,
|
||||
"start": 245,
|
||||
"type": "VariableDeclarator"
|
||||
},
|
||||
"end": 410,
|
||||
"end": 413,
|
||||
"kind": "const",
|
||||
"start": 238,
|
||||
"start": 245,
|
||||
"type": "VariableDeclaration",
|
||||
"type": "VariableDeclaration"
|
||||
}
|
||||
],
|
||||
"end": 411,
|
||||
"end": 414,
|
||||
"nonCodeMeta": {
|
||||
"nonCodeNodes": {
|
||||
"1": [
|
||||
{
|
||||
"end": 238,
|
||||
"start": 236,
|
||||
"end": 245,
|
||||
"start": 243,
|
||||
"type": "NonCodeNode",
|
||||
"value": {
|
||||
"type": "newLine"
|
||||
|
@ -9,9 +9,9 @@ fn cube(pos, scale) {
|
||||
}
|
||||
part001 = cube([0, 0], 20)
|
||||
|> close(%)
|
||||
|> extrude(20, %)
|
||||
|> extrude(20, %) as foo
|
||||
|
||||
part002 = startSketchOn(part001, "start")
|
||||
part002 = startSketchOn(foo, "start")
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> line([0, 10], %)
|
||||
|> line([10, 0], %)
|
||||
|
@ -339,6 +339,209 @@ description: Program memory after executing sketch_on_face_start.kcl
|
||||
}
|
||||
]
|
||||
},
|
||||
"foo": {
|
||||
"type": "Solid",
|
||||
"type": "Solid",
|
||||
"id": "[uuid]",
|
||||
"value": [
|
||||
{
|
||||
"faceId": "[uuid]",
|
||||
"id": "[uuid]",
|
||||
"sourceRange": [
|
||||
86,
|
||||
105,
|
||||
0
|
||||
],
|
||||
"tag": null,
|
||||
"type": "extrudePlane"
|
||||
},
|
||||
{
|
||||
"faceId": "[uuid]",
|
||||
"id": "[uuid]",
|
||||
"sourceRange": [
|
||||
113,
|
||||
132,
|
||||
0
|
||||
],
|
||||
"tag": null,
|
||||
"type": "extrudePlane"
|
||||
},
|
||||
{
|
||||
"faceId": "[uuid]",
|
||||
"id": "[uuid]",
|
||||
"sourceRange": [
|
||||
140,
|
||||
160,
|
||||
0
|
||||
],
|
||||
"tag": null,
|
||||
"type": "extrudePlane"
|
||||
},
|
||||
{
|
||||
"faceId": "[uuid]",
|
||||
"id": "[uuid]",
|
||||
"sourceRange": [
|
||||
208,
|
||||
216,
|
||||
0
|
||||
],
|
||||
"tag": null,
|
||||
"type": "extrudePlane"
|
||||
}
|
||||
],
|
||||
"sketch": {
|
||||
"type": "Sketch",
|
||||
"id": "[uuid]",
|
||||
"paths": [
|
||||
{
|
||||
"__geoMeta": {
|
||||
"id": "[uuid]",
|
||||
"sourceRange": [
|
||||
86,
|
||||
105,
|
||||
0
|
||||
]
|
||||
},
|
||||
"from": [
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"tag": null,
|
||||
"to": [
|
||||
0.0,
|
||||
20.0
|
||||
],
|
||||
"type": "ToPoint"
|
||||
},
|
||||
{
|
||||
"__geoMeta": {
|
||||
"id": "[uuid]",
|
||||
"sourceRange": [
|
||||
113,
|
||||
132,
|
||||
0
|
||||
]
|
||||
},
|
||||
"from": [
|
||||
0.0,
|
||||
20.0
|
||||
],
|
||||
"tag": null,
|
||||
"to": [
|
||||
20.0,
|
||||
20.0
|
||||
],
|
||||
"type": "ToPoint"
|
||||
},
|
||||
{
|
||||
"__geoMeta": {
|
||||
"id": "[uuid]",
|
||||
"sourceRange": [
|
||||
140,
|
||||
160,
|
||||
0
|
||||
]
|
||||
},
|
||||
"from": [
|
||||
20.0,
|
||||
20.0
|
||||
],
|
||||
"tag": null,
|
||||
"to": [
|
||||
20.0,
|
||||
0.0
|
||||
],
|
||||
"type": "ToPoint"
|
||||
},
|
||||
{
|
||||
"__geoMeta": {
|
||||
"id": "[uuid]",
|
||||
"sourceRange": [
|
||||
208,
|
||||
216,
|
||||
0
|
||||
]
|
||||
},
|
||||
"from": [
|
||||
20.0,
|
||||
0.0
|
||||
],
|
||||
"tag": null,
|
||||
"to": [
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"type": "ToPoint"
|
||||
}
|
||||
],
|
||||
"on": {
|
||||
"type": "plane",
|
||||
"id": "[uuid]",
|
||||
"value": "XY",
|
||||
"origin": {
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"xAxis": {
|
||||
"x": 1.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"yAxis": {
|
||||
"x": 0.0,
|
||||
"y": 1.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"zAxis": {
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 1.0
|
||||
},
|
||||
"__meta": []
|
||||
},
|
||||
"start": {
|
||||
"from": [
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"to": [
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"tag": null,
|
||||
"__geoMeta": {
|
||||
"id": "[uuid]",
|
||||
"sourceRange": [
|
||||
56,
|
||||
78,
|
||||
0
|
||||
]
|
||||
}
|
||||
},
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
56,
|
||||
78,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"height": 20.0,
|
||||
"startCapId": "[uuid]",
|
||||
"endCapId": "[uuid]",
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
56,
|
||||
78,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"part001": {
|
||||
"type": "Solid",
|
||||
"type": "Solid",
|
||||
@ -551,8 +754,8 @@ description: Program memory after executing sketch_on_face_start.kcl
|
||||
"faceId": "[uuid]",
|
||||
"id": "[uuid]",
|
||||
"sourceRange": [
|
||||
316,
|
||||
332,
|
||||
319,
|
||||
335,
|
||||
0
|
||||
],
|
||||
"tag": null,
|
||||
@ -562,8 +765,8 @@ description: Program memory after executing sketch_on_face_start.kcl
|
||||
"faceId": "[uuid]",
|
||||
"id": "[uuid]",
|
||||
"sourceRange": [
|
||||
338,
|
||||
354,
|
||||
341,
|
||||
357,
|
||||
0
|
||||
],
|
||||
"tag": null,
|
||||
@ -573,8 +776,8 @@ description: Program memory after executing sketch_on_face_start.kcl
|
||||
"faceId": "[uuid]",
|
||||
"id": "[uuid]",
|
||||
"sourceRange": [
|
||||
360,
|
||||
377,
|
||||
363,
|
||||
380,
|
||||
0
|
||||
],
|
||||
"tag": null,
|
||||
@ -584,8 +787,8 @@ description: Program memory after executing sketch_on_face_start.kcl
|
||||
"faceId": "[uuid]",
|
||||
"id": "[uuid]",
|
||||
"sourceRange": [
|
||||
383,
|
||||
391,
|
||||
386,
|
||||
394,
|
||||
0
|
||||
],
|
||||
"tag": null,
|
||||
@ -600,8 +803,8 @@ description: Program memory after executing sketch_on_face_start.kcl
|
||||
"__geoMeta": {
|
||||
"id": "[uuid]",
|
||||
"sourceRange": [
|
||||
316,
|
||||
332,
|
||||
319,
|
||||
335,
|
||||
0
|
||||
]
|
||||
},
|
||||
@ -620,8 +823,8 @@ description: Program memory after executing sketch_on_face_start.kcl
|
||||
"__geoMeta": {
|
||||
"id": "[uuid]",
|
||||
"sourceRange": [
|
||||
338,
|
||||
354,
|
||||
341,
|
||||
357,
|
||||
0
|
||||
]
|
||||
},
|
||||
@ -640,8 +843,8 @@ description: Program memory after executing sketch_on_face_start.kcl
|
||||
"__geoMeta": {
|
||||
"id": "[uuid]",
|
||||
"sourceRange": [
|
||||
360,
|
||||
377,
|
||||
363,
|
||||
380,
|
||||
0
|
||||
]
|
||||
},
|
||||
@ -660,8 +863,8 @@ description: Program memory after executing sketch_on_face_start.kcl
|
||||
"__geoMeta": {
|
||||
"id": "[uuid]",
|
||||
"sourceRange": [
|
||||
383,
|
||||
391,
|
||||
386,
|
||||
394,
|
||||
0
|
||||
]
|
||||
},
|
||||
@ -901,8 +1104,8 @@ description: Program memory after executing sketch_on_face_start.kcl
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
248,
|
||||
279,
|
||||
255,
|
||||
282,
|
||||
0
|
||||
]
|
||||
}
|
||||
@ -921,8 +1124,8 @@ description: Program memory after executing sketch_on_face_start.kcl
|
||||
"__geoMeta": {
|
||||
"id": "[uuid]",
|
||||
"sourceRange": [
|
||||
285,
|
||||
310,
|
||||
288,
|
||||
313,
|
||||
0
|
||||
]
|
||||
}
|
||||
@ -930,8 +1133,8 @@ description: Program memory after executing sketch_on_face_start.kcl
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
285,
|
||||
310,
|
||||
288,
|
||||
313,
|
||||
0
|
||||
]
|
||||
}
|
||||
@ -943,8 +1146,8 @@ description: Program memory after executing sketch_on_face_start.kcl
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
285,
|
||||
310,
|
||||
288,
|
||||
313,
|
||||
0
|
||||
]
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
//! Wasm bindings for `kcl`.
|
||||
|
||||
use std::{str::FromStr, sync::Arc};
|
||||
use std::sync::Arc;
|
||||
|
||||
use futures::stream::TryStreamExt;
|
||||
use gloo_utils::format::JsValueSerdeExt;
|
||||
@ -56,10 +56,10 @@ pub async fn clear_scene_and_bust_cache(
|
||||
|
||||
// wasm_bindgen wrapper for execute
|
||||
#[wasm_bindgen]
|
||||
pub async fn execute_wasm(
|
||||
pub async fn execute(
|
||||
program_ast_json: &str,
|
||||
program_memory_override_str: &str,
|
||||
units: &str,
|
||||
settings: &str,
|
||||
engine_manager: kcl_lib::wasm_engine::EngineCommandManager,
|
||||
fs_manager: kcl_lib::wasm_engine::FileSystemManager,
|
||||
) -> Result<JsValue, String> {
|
||||
@ -73,11 +73,11 @@ pub async fn execute_wasm(
|
||||
// You cannot override the memory in non-mock mode.
|
||||
let is_mock = program_memory_override.is_some();
|
||||
|
||||
let units = kcl_lib::UnitLength::from_str(units).map_err(|e| e.to_string())?;
|
||||
let settings: kcl_lib::Configuration = serde_json::from_str(settings).map_err(|e| e.to_string())?;
|
||||
let ctx = if is_mock {
|
||||
kcl_lib::ExecutorContext::new_mock(fs_manager, units).await?
|
||||
kcl_lib::ExecutorContext::new_mock(fs_manager, settings.into()).await?
|
||||
} else {
|
||||
kcl_lib::ExecutorContext::new(engine_manager, fs_manager, units).await?
|
||||
kcl_lib::ExecutorContext::new(engine_manager, fs_manager, settings.into()).await?
|
||||
};
|
||||
|
||||
let mut exec_state = ExecState::default();
|
||||
@ -168,23 +168,6 @@ pub async fn make_default_planes(
|
||||
JsValue::from_serde(&default_planes).map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
// wasm_bindgen wrapper for modifying the grid
|
||||
#[wasm_bindgen]
|
||||
pub async fn modify_grid(
|
||||
engine_manager: kcl_lib::wasm_engine::EngineCommandManager,
|
||||
hidden: bool,
|
||||
) -> Result<(), String> {
|
||||
console_error_panic_hook::set_once();
|
||||
// deserialize the ast from a stringified json
|
||||
|
||||
let engine = kcl_lib::wasm_engine::EngineConnection::new(engine_manager)
|
||||
.await
|
||||
.map_err(|e| format!("{:?}", e))?;
|
||||
engine.modify_grid(hidden).await.map_err(String::from)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// wasm_bindgen wrapper for execute
|
||||
#[wasm_bindgen]
|
||||
pub async fn modify_ast_for_sketch_wasm(
|
||||
@ -296,7 +279,7 @@ impl ServerConfig {
|
||||
pub async fn kcl_lsp_run(
|
||||
config: ServerConfig,
|
||||
engine_manager: Option<kcl_lib::wasm_engine::EngineCommandManager>,
|
||||
units: &str,
|
||||
settings: Option<String>,
|
||||
token: String,
|
||||
baseurl: String,
|
||||
) -> Result<(), JsValue> {
|
||||
@ -309,8 +292,12 @@ pub async fn kcl_lsp_run(
|
||||
} = config;
|
||||
|
||||
let executor_ctx = if let Some(engine_manager) = engine_manager {
|
||||
let units = kcl_lib::UnitLength::from_str(units).map_err(|e| e.to_string())?;
|
||||
Some(kcl_lib::ExecutorContext::new(engine_manager, fs.clone(), units).await?)
|
||||
let settings: kcl_lib::Configuration = if let Some(settings) = settings {
|
||||
serde_json::from_str(&settings).map_err(|e| e.to_string())?
|
||||
} else {
|
||||
Default::default()
|
||||
};
|
||||
Some(kcl_lib::ExecutorContext::new(engine_manager, fs.clone(), settings.into()).await?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
216
src/wasm-lib/tests/executor/cache.rs
Normal file
@ -0,0 +1,216 @@
|
||||
//! Cache testing framework.
|
||||
|
||||
use anyhow::Result;
|
||||
use kcl_lib::ExecError;
|
||||
|
||||
struct Variation<'a> {
|
||||
code: &'a str,
|
||||
settings: &'a kcl_lib::ExecutorSettings,
|
||||
}
|
||||
|
||||
async fn cache_test(test_name: &str, variations: Vec<Variation<'_>>) -> Result<Vec<(String, image::DynamicImage)>> {
|
||||
let first = variations
|
||||
.first()
|
||||
.ok_or_else(|| anyhow::anyhow!("No variations provided for test '{}'", test_name))?;
|
||||
|
||||
let mut ctx = kcl_lib::ExecutorContext::new_with_client(first.settings.clone(), None, None).await?;
|
||||
let mut exec_state = kcl_lib::ExecState::default();
|
||||
|
||||
let mut old_ast_state = None;
|
||||
let mut img_results = Vec::new();
|
||||
for (index, variation) in variations.iter().enumerate() {
|
||||
let program = kcl_lib::Program::parse_no_errs(variation.code)?;
|
||||
|
||||
// set the new settings.
|
||||
ctx.settings = variation.settings.clone();
|
||||
|
||||
ctx.run(
|
||||
kcl_lib::CacheInformation {
|
||||
old: old_ast_state,
|
||||
new_ast: program.ast.clone(),
|
||||
},
|
||||
&mut exec_state,
|
||||
)
|
||||
.await?;
|
||||
let snapshot_png_bytes = ctx.prepare_snapshot().await?.contents.0;
|
||||
|
||||
// Decode the snapshot, return it.
|
||||
let img = image::ImageReader::new(std::io::Cursor::new(snapshot_png_bytes))
|
||||
.with_guessed_format()
|
||||
.map_err(|e| ExecError::BadPng(e.to_string()))
|
||||
.and_then(|x| x.decode().map_err(|e| ExecError::BadPng(e.to_string())))?;
|
||||
// Save the snapshot.
|
||||
let path = crate::assert_out(&format!("cache_{}_{}", test_name, index), &img);
|
||||
|
||||
img_results.push((path, img));
|
||||
|
||||
// Prepare the last state.
|
||||
old_ast_state = Some(kcl_lib::OldAstState {
|
||||
ast: program.ast,
|
||||
exec_state: exec_state.clone(),
|
||||
settings: variation.settings.clone(),
|
||||
});
|
||||
}
|
||||
|
||||
Ok(img_results)
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn kcl_test_cache_change_units_changes_output() {
|
||||
let code = r#"part001 = startSketchOn('XY')
|
||||
|> startProfileAt([5.5229, 5.25217], %)
|
||||
|> line([10.50433, -1.19122], %)
|
||||
|> line([8.01362, -5.48731], %)
|
||||
|> line([-1.02877, -6.76825], %)
|
||||
|> line([-11.53311, 2.81559], %)
|
||||
|> close(%)
|
||||
|> extrude(4, %)
|
||||
"#;
|
||||
|
||||
let result = cache_test(
|
||||
"change_units_changes_output",
|
||||
vec![
|
||||
Variation {
|
||||
code,
|
||||
settings: &kcl_lib::ExecutorSettings {
|
||||
units: kcl_lib::UnitLength::In,
|
||||
..Default::default()
|
||||
},
|
||||
},
|
||||
Variation {
|
||||
code,
|
||||
settings: &kcl_lib::ExecutorSettings {
|
||||
units: kcl_lib::UnitLength::Mm,
|
||||
..Default::default()
|
||||
},
|
||||
},
|
||||
],
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let first = result.first().unwrap();
|
||||
let second = result.last().unwrap();
|
||||
|
||||
assert!(first.1 != second.1);
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn kcl_test_cache_change_grid_visualizes_grid_off_to_on() {
|
||||
let code = r#"part001 = startSketchOn('XY')
|
||||
|> startProfileAt([5.5229, 5.25217], %)
|
||||
|> line([10.50433, -1.19122], %)
|
||||
|> line([8.01362, -5.48731], %)
|
||||
|> line([-1.02877, -6.76825], %)
|
||||
|> line([-11.53311, 2.81559], %)
|
||||
|> close(%)
|
||||
|> extrude(4, %)
|
||||
"#;
|
||||
|
||||
let result = cache_test(
|
||||
"change_grid_visualizes_grid_off_to_on",
|
||||
vec![
|
||||
Variation {
|
||||
code,
|
||||
settings: &kcl_lib::ExecutorSettings {
|
||||
show_grid: false,
|
||||
..Default::default()
|
||||
},
|
||||
},
|
||||
Variation {
|
||||
code,
|
||||
settings: &kcl_lib::ExecutorSettings {
|
||||
show_grid: true,
|
||||
..Default::default()
|
||||
},
|
||||
},
|
||||
],
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let first = result.first().unwrap();
|
||||
let second = result.last().unwrap();
|
||||
|
||||
assert!(first.1 != second.1);
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn kcl_test_cache_change_grid_visualizes_grid_on_to_off() {
|
||||
let code = r#"part001 = startSketchOn('XY')
|
||||
|> startProfileAt([5.5229, 5.25217], %)
|
||||
|> line([10.50433, -1.19122], %)
|
||||
|> line([8.01362, -5.48731], %)
|
||||
|> line([-1.02877, -6.76825], %)
|
||||
|> line([-11.53311, 2.81559], %)
|
||||
|> close(%)
|
||||
|> extrude(4, %)
|
||||
"#;
|
||||
|
||||
let result = cache_test(
|
||||
"change_grid_visualizes_grid_on_to_off",
|
||||
vec![
|
||||
Variation {
|
||||
code,
|
||||
settings: &kcl_lib::ExecutorSettings {
|
||||
show_grid: true,
|
||||
..Default::default()
|
||||
},
|
||||
},
|
||||
Variation {
|
||||
code,
|
||||
settings: &kcl_lib::ExecutorSettings {
|
||||
show_grid: false,
|
||||
..Default::default()
|
||||
},
|
||||
},
|
||||
],
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let first = result.first().unwrap();
|
||||
let second = result.last().unwrap();
|
||||
|
||||
assert!(first.1 != second.1);
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn kcl_test_cache_change_highlight_edges_changes_visual() {
|
||||
let code = r#"part001 = startSketchOn('XY')
|
||||
|> startProfileAt([5.5229, 5.25217], %)
|
||||
|> line([10.50433, -1.19122], %)
|
||||
|> line([8.01362, -5.48731], %)
|
||||
|> line([-1.02877, -6.76825], %)
|
||||
|> line([-11.53311, 2.81559], %)
|
||||
|> close(%)
|
||||
|> extrude(4, %)
|
||||
"#;
|
||||
|
||||
let result = cache_test(
|
||||
"change_highlight_edges_changes_visual",
|
||||
vec![
|
||||
Variation {
|
||||
code,
|
||||
settings: &kcl_lib::ExecutorSettings {
|
||||
highlight_edges: true,
|
||||
..Default::default()
|
||||
},
|
||||
},
|
||||
Variation {
|
||||
code,
|
||||
settings: &kcl_lib::ExecutorSettings {
|
||||
highlight_edges: false,
|
||||
..Default::default()
|
||||
},
|
||||
},
|
||||
],
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let first = result.first().unwrap();
|
||||
let second = result.last().unwrap();
|
||||
|
||||
assert!(first.1 != second.1);
|
||||
}
|
@ -1,3 +1,5 @@
|
||||
mod cache;
|
||||
|
||||
use kcl_lib::{
|
||||
test_server::{execute_and_snapshot, execute_and_snapshot_no_auth},
|
||||
UnitLength,
|
||||
@ -5,7 +7,7 @@ use kcl_lib::{
|
||||
|
||||
/// The minimum permissible difference between asserted twenty-twenty images.
|
||||
/// i.e. how different the current model snapshot can be from the previous saved one.
|
||||
const MIN_DIFF: f64 = 0.99;
|
||||
pub(crate) const MIN_DIFF: f64 = 0.99;
|
||||
|
||||
macro_rules! kcl_input {
|
||||
($file:literal) => {
|
||||
@ -13,8 +15,11 @@ macro_rules! kcl_input {
|
||||
};
|
||||
}
|
||||
|
||||
fn assert_out(test_name: &str, result: &image::DynamicImage) {
|
||||
twenty_twenty::assert_image(format!("tests/executor/outputs/{test_name}.png"), result, MIN_DIFF);
|
||||
pub(crate) fn assert_out(test_name: &str, result: &image::DynamicImage) -> String {
|
||||
let path = format!("tests/executor/outputs/{test_name}.png");
|
||||
twenty_twenty::assert_image(&path, result, MIN_DIFF);
|
||||
|
||||
path
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
|
After Width: | Height: | Size: 47 KiB |
After Width: | Height: | Size: 114 KiB |
After Width: | Height: | Size: 114 KiB |
After Width: | Height: | Size: 47 KiB |
After Width: | Height: | Size: 47 KiB |
After Width: | Height: | Size: 35 KiB |
After Width: | Height: | Size: 53 KiB |
After Width: | Height: | Size: 47 KiB |
374
yarn.lock
@ -1204,11 +1204,6 @@
|
||||
"@babel/helper-validator-identifier" "^7.24.7"
|
||||
to-fast-properties "^2.0.0"
|
||||
|
||||
"@bitdisaster/exe-icon-extractor@^1.0.10":
|
||||
version "1.0.10"
|
||||
resolved "https://registry.yarnpkg.com/@bitdisaster/exe-icon-extractor/-/exe-icon-extractor-1.0.10.tgz#3f5107864254c351db1db5c5922452d9d4154e8f"
|
||||
integrity sha512-iTZ8cVGZ5dglNRyFdSj8U60mHIrC8XNIuOHN/NkM5/dQP4nsmpyqeQTAADLLQgoFCNJD+DiwQCv8dR2cCeWP4g==
|
||||
|
||||
"@codemirror/autocomplete@^6.0.0", "@codemirror/autocomplete@^6.17.0":
|
||||
version "6.17.0"
|
||||
resolved "https://registry.yarnpkg.com/@codemirror/autocomplete/-/autocomplete-6.17.0.tgz#24ff5fc37fd91f6439df6f4ff9c8e910cde1b053"
|
||||
@ -1349,7 +1344,7 @@
|
||||
ajv "^6.12.0"
|
||||
ajv-keywords "^3.4.1"
|
||||
|
||||
"@electron-forge/cli@^7.4.0":
|
||||
"@electron-forge/cli@7.4.0":
|
||||
version "7.4.0"
|
||||
resolved "https://registry.yarnpkg.com/@electron-forge/cli/-/cli-7.4.0.tgz#db16f4bc678d1f6cec8890cdf86041e9c8336350"
|
||||
integrity sha512-a+zZv3ja/IxkJzNyx4sOHSZv6DPV85S0PEVF6pcRjUpbDL5r+DxjRFsNc0Nq4UIWyFm1nw7RWoPdd9uDst4Tvg==
|
||||
@ -1430,77 +1425,6 @@
|
||||
fs-extra "^10.0.0"
|
||||
which "^2.0.2"
|
||||
|
||||
"@electron-forge/maker-base@7.5.0":
|
||||
version "7.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@electron-forge/maker-base/-/maker-base-7.5.0.tgz#6626f33d5f1616b5f31eca72c6818806e3550909"
|
||||
integrity sha512-+jluKW2UPxaI1+qQQ8fqaUVVbZohRjOSF0Iti7STRFbgJKJitzPB24Cjji9qJCKIx5klMeEiwp0YPAE/d9Xt8g==
|
||||
dependencies:
|
||||
"@electron-forge/shared-types" "7.5.0"
|
||||
fs-extra "^10.0.0"
|
||||
which "^2.0.2"
|
||||
|
||||
"@electron-forge/maker-deb@^7.4.0":
|
||||
version "7.4.0"
|
||||
resolved "https://registry.yarnpkg.com/@electron-forge/maker-deb/-/maker-deb-7.4.0.tgz#7787f525ab8c7ddcc3e9665e60d704179a92848a"
|
||||
integrity sha512-npWea3IpGeu96xNqJpsCOYX6V4E+HY6u/okeTUzUOMX96UteT14MecdUefMam158glRTX84k2ryh7WcBoOa4mg==
|
||||
dependencies:
|
||||
"@electron-forge/maker-base" "7.4.0"
|
||||
"@electron-forge/shared-types" "7.4.0"
|
||||
optionalDependencies:
|
||||
electron-installer-debian "^3.2.0"
|
||||
|
||||
"@electron-forge/maker-rpm@^7.4.0":
|
||||
version "7.4.0"
|
||||
resolved "https://registry.yarnpkg.com/@electron-forge/maker-rpm/-/maker-rpm-7.4.0.tgz#135b7c4b621048ce565b3c92a7577915b43ab371"
|
||||
integrity sha512-N64Yh/K/91GzIk28T1jKsCGgYaquDuhXcEJW+TkVyP5tPZ9aTz9SjXLBxAg8WhcroArAZEsVyPOFKthmFzAUuA==
|
||||
dependencies:
|
||||
"@electron-forge/maker-base" "7.4.0"
|
||||
"@electron-forge/shared-types" "7.4.0"
|
||||
optionalDependencies:
|
||||
electron-installer-redhat "^3.2.0"
|
||||
|
||||
"@electron-forge/maker-squirrel@^7.4.0":
|
||||
version "7.4.0"
|
||||
resolved "https://registry.yarnpkg.com/@electron-forge/maker-squirrel/-/maker-squirrel-7.4.0.tgz#982f3a7ad9d45258de8f78133eefdd79c90f870e"
|
||||
integrity sha512-mCQyufnSNfjffiKho59ZqVg4W601zGOl6h01OyfDwjOU/G4iQtpnnDEOXGe26q7OVT5ORb1WDnfyGgBeJ6Ge7g==
|
||||
dependencies:
|
||||
"@electron-forge/maker-base" "7.4.0"
|
||||
"@electron-forge/shared-types" "7.4.0"
|
||||
fs-extra "^10.0.0"
|
||||
optionalDependencies:
|
||||
electron-winstaller "^5.3.0"
|
||||
|
||||
"@electron-forge/maker-wix@^7.5.0":
|
||||
version "7.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@electron-forge/maker-wix/-/maker-wix-7.5.0.tgz#994e65e86a5bfab944a9847e146608d4b567197b"
|
||||
integrity sha512-6OmaviVRixGJ8EYvWik76zWJBvKW8XkQZ/mpwd4JjYcVKudA0zWbzEw2xBzf62TaRoXtSHTxbpfJRDiHyJK4dg==
|
||||
dependencies:
|
||||
"@electron-forge/maker-base" "7.5.0"
|
||||
"@electron-forge/shared-types" "7.5.0"
|
||||
chalk "^4.0.0"
|
||||
electron-wix-msi "^5.1.3"
|
||||
log-symbols "^4.0.0"
|
||||
parse-author "^2.0.0"
|
||||
|
||||
"@electron-forge/maker-zip@^7.5.0":
|
||||
version "7.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@electron-forge/maker-zip/-/maker-zip-7.5.0.tgz#e0d534cc5715055b611507e3754f9b4bbe31ffef"
|
||||
integrity sha512-gIO3bEbubOJqWV6kd0b9nBwTrFfFQv/K8PAqg6e0uSZiy7QuSCFZVAZse02gO3AzxVDSVjjTQ4nmXBXC4Glh1A==
|
||||
dependencies:
|
||||
"@electron-forge/maker-base" "7.5.0"
|
||||
"@electron-forge/shared-types" "7.5.0"
|
||||
cross-zip "^4.0.0"
|
||||
fs-extra "^10.0.0"
|
||||
got "^11.8.5"
|
||||
|
||||
"@electron-forge/plugin-auto-unpack-natives@^7.4.0":
|
||||
version "7.4.0"
|
||||
resolved "https://registry.yarnpkg.com/@electron-forge/plugin-auto-unpack-natives/-/plugin-auto-unpack-natives-7.4.0.tgz#34382b91d45691824f26503af7b5f585af7ee3f1"
|
||||
integrity sha512-jJ/v2blH32bcvdlJbeeW/yO99K9SduW8yrS7zuFN6y+B1cmzLd+S7L8oCcOghFDMAlYjQaBlnCe/nMJbT9mN4g==
|
||||
dependencies:
|
||||
"@electron-forge/plugin-base" "7.4.0"
|
||||
"@electron-forge/shared-types" "7.4.0"
|
||||
|
||||
"@electron-forge/plugin-base@7.4.0":
|
||||
version "7.4.0"
|
||||
resolved "https://registry.yarnpkg.com/@electron-forge/plugin-base/-/plugin-base-7.4.0.tgz#1c7743a528b1ec3e7a92580870c23398c5a5526f"
|
||||
@ -1508,7 +1432,7 @@
|
||||
dependencies:
|
||||
"@electron-forge/shared-types" "7.4.0"
|
||||
|
||||
"@electron-forge/plugin-fuses@^7.4.0":
|
||||
"@electron-forge/plugin-fuses@7.4.0":
|
||||
version "7.4.0"
|
||||
resolved "https://registry.yarnpkg.com/@electron-forge/plugin-fuses/-/plugin-fuses-7.4.0.tgz#3a3b78bf3c5d2b2d445825b05bce145d116df792"
|
||||
integrity sha512-LKcyIaO0sUkzZdOB1PySjG1R9KAl5Vi453ZQcambBI7RpZtPKozluNd0zlXey1cf7ycTwhzvmrI6ss3LHQyjvw==
|
||||
@ -1516,7 +1440,7 @@
|
||||
"@electron-forge/plugin-base" "7.4.0"
|
||||
"@electron-forge/shared-types" "7.4.0"
|
||||
|
||||
"@electron-forge/plugin-vite@^7.4.0":
|
||||
"@electron-forge/plugin-vite@7.4.0":
|
||||
version "7.4.0"
|
||||
resolved "https://registry.yarnpkg.com/@electron-forge/plugin-vite/-/plugin-vite-7.4.0.tgz#66b0154269205a0c4e70200b5f5f410833beea40"
|
||||
integrity sha512-GZqBUsyNH0XCvQlBKMS0aOJM6PX80irijgPR9Lfl6ctYIuKTo+82td+nIK8Fef/qSDUEt/U1f4Qb9GfLfhRRig==
|
||||
@ -1547,16 +1471,6 @@
|
||||
"@electron/rebuild" "^3.2.10"
|
||||
listr2 "^7.0.2"
|
||||
|
||||
"@electron-forge/shared-types@7.5.0":
|
||||
version "7.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@electron-forge/shared-types/-/shared-types-7.5.0.tgz#3e72aba15add1e5af45767640eeb43b59497f907"
|
||||
integrity sha512-VXuLVGYa3ZulBlmjA40ZEpk+iPH5ebN0v7t27wDt3rm23bph2aQrL7uSTLXhobenXYBVKggXnQt6rJ9A7FCDNQ==
|
||||
dependencies:
|
||||
"@electron-forge/tracer" "7.5.0"
|
||||
"@electron/packager" "^18.3.5"
|
||||
"@electron/rebuild" "^3.2.10"
|
||||
listr2 "^7.0.2"
|
||||
|
||||
"@electron-forge/template-base@7.4.0":
|
||||
version "7.4.0"
|
||||
resolved "https://registry.yarnpkg.com/@electron-forge/template-base/-/template-base-7.4.0.tgz#ef2e2fd9f3632860d7ad192dc4a2e09e98463d05"
|
||||
@ -1611,13 +1525,6 @@
|
||||
dependencies:
|
||||
chrome-trace-event "^1.0.3"
|
||||
|
||||
"@electron-forge/tracer@7.5.0":
|
||||
version "7.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@electron-forge/tracer/-/tracer-7.5.0.tgz#45288de6a99923073e69c74ec46c63e26ce1ef66"
|
||||
integrity sha512-1dE0wKCmv/K3BXCH70o2jp/y2kXgZQm73gIvzyadySXYwu2L4BWxhAO+Q+JsnbUk+nclHEup5ph4D0JoPIWLcQ==
|
||||
dependencies:
|
||||
chrome-trace-event "^1.0.3"
|
||||
|
||||
"@electron-forge/web-multi-logger@7.4.0":
|
||||
version "7.4.0"
|
||||
resolved "https://registry.yarnpkg.com/@electron-forge/web-multi-logger/-/web-multi-logger-7.4.0.tgz#13df3a84827d07fc41f53e83b8af3a639be2a03d"
|
||||
@ -1638,16 +1545,7 @@
|
||||
glob "^7.1.6"
|
||||
minimatch "^3.0.4"
|
||||
|
||||
"@electron/asar@^3.2.13":
|
||||
version "3.2.14"
|
||||
resolved "https://registry.yarnpkg.com/@electron/asar/-/asar-3.2.14.tgz#8ae3d49e8548aa0b6847c62e021f829a88794408"
|
||||
integrity sha512-hc52QkesULqbxTRC1vOmSBN1YndUkieoNMfvpe988h0MEoGGqbijkOqv4/2M9PufBJxiTVoDdBmBFfXPowZDQg==
|
||||
dependencies:
|
||||
commander "^5.0.0"
|
||||
glob "^7.1.6"
|
||||
minimatch "^3.0.4"
|
||||
|
||||
"@electron/fuses@^1.8.0":
|
||||
"@electron/fuses@1.8.0":
|
||||
version "1.8.0"
|
||||
resolved "https://registry.yarnpkg.com/@electron/fuses/-/fuses-1.8.0.tgz#ad34d3cc4703b1258b83f6989917052cfc1490a0"
|
||||
integrity sha512-zx0EIq78WlY/lBb1uXlziZmDZI4ubcCXIMJ4uGjXzZW0nS19TjSPeXPAjzzTmKQlJUZm0SbmZhPKP7tuQ1SsEw==
|
||||
@ -1753,32 +1651,7 @@
|
||||
semver "^7.1.3"
|
||||
yargs-parser "^21.1.1"
|
||||
|
||||
"@electron/packager@^18.3.5":
|
||||
version "18.3.5"
|
||||
resolved "https://registry.yarnpkg.com/@electron/packager/-/packager-18.3.5.tgz#571a8faa321bd9d9e5b97a9a71b8d40401980d8d"
|
||||
integrity sha512-ClgTxXTt3MesWAcjIxIkgxELjTcllw1FRoVsihP7uT48kpDMqI71p4XvnMWbq8PvU57TcrKICAaLkxRhbc+/wQ==
|
||||
dependencies:
|
||||
"@electron/asar" "^3.2.13"
|
||||
"@electron/get" "^3.0.0"
|
||||
"@electron/notarize" "^2.1.0"
|
||||
"@electron/osx-sign" "^1.0.5"
|
||||
"@electron/universal" "^2.0.1"
|
||||
"@electron/windows-sign" "^1.0.0"
|
||||
debug "^4.0.1"
|
||||
extract-zip "^2.0.0"
|
||||
filenamify "^4.1.0"
|
||||
fs-extra "^11.1.0"
|
||||
galactus "^1.0.0"
|
||||
get-package-info "^1.0.0"
|
||||
junk "^3.1.0"
|
||||
parse-author "^2.0.0"
|
||||
plist "^3.0.0"
|
||||
resedit "^2.0.0"
|
||||
resolve "^1.1.6"
|
||||
semver "^7.1.3"
|
||||
yargs-parser "^21.1.1"
|
||||
|
||||
"@electron/rebuild@^3.2.10", "@electron/rebuild@^3.6.0":
|
||||
"@electron/rebuild@^3.2.10":
|
||||
version "3.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@electron/rebuild/-/rebuild-3.6.0.tgz#60211375a5f8541a71eb07dd2f97354ad0b2b96f"
|
||||
integrity sha512-zF4x3QupRU3uNGaP5X1wjpmcjfw1H87kyqZ00Tc3HvriV+4gmOGuvQjGNkrJuXdsApssdNyVwLsy+TaeTGGcVw==
|
||||
@ -1824,7 +1697,7 @@
|
||||
minimatch "^9.0.3"
|
||||
plist "^3.1.0"
|
||||
|
||||
"@electron/windows-sign@^1.0.0", "@electron/windows-sign@^1.1.2":
|
||||
"@electron/windows-sign@^1.0.0":
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/@electron/windows-sign/-/windows-sign-1.1.3.tgz#52023d17d8f6c686d934f518be76736f6f2f0aef"
|
||||
integrity sha512-OqVSdAe+/88fIjvTDWiy+5Ho1nXsiBhE5RTsIQ6M/zcxcDAEP2TlQCkOyusItnmzXRN+XTFaK9gKhiZ6KGyXQw==
|
||||
@ -2176,7 +2049,7 @@
|
||||
dependencies:
|
||||
"@lezer/common" "^1.0.0"
|
||||
|
||||
"@malept/cross-spawn-promise@^1.0.0", "@malept/cross-spawn-promise@^1.1.0":
|
||||
"@malept/cross-spawn-promise@^1.1.0":
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@malept/cross-spawn-promise/-/cross-spawn-promise-1.1.1.tgz#504af200af6b98e198bce768bc1730c6936ae01d"
|
||||
integrity sha512-RTBGWL5FWQcg9orDOCcp4LvItNzUPcyEU9bwaeJX0rJ1IQxzucC48Y0/sQLp/g6t99IQgAlGIaesJS+gTn7tVQ==
|
||||
@ -2569,21 +2442,13 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.5.tgz#a6ce3e556e00fd9895dd872dd172ad0d4bd687f4"
|
||||
integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==
|
||||
|
||||
"@types/fs-extra@9.0.13", "@types/fs-extra@^9.0.1", "@types/fs-extra@^9.0.11":
|
||||
"@types/fs-extra@9.0.13", "@types/fs-extra@^9.0.11":
|
||||
version "9.0.13"
|
||||
resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-9.0.13.tgz#7594fbae04fe7f1918ce8b3d213f74ff44ac1f45"
|
||||
integrity sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/glob@^7.1.1":
|
||||
version "7.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.2.0.tgz#bc1b5bf3aa92f25bd5dd39f35c57361bdce5b2eb"
|
||||
integrity sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==
|
||||
dependencies:
|
||||
"@types/minimatch" "*"
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/http-cache-semantics@*":
|
||||
version "4.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz#b979ebad3919799c979b17c72621c0bc0a31c6c4"
|
||||
@ -2638,11 +2503,6 @@
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/minimatch@*":
|
||||
version "5.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-5.1.2.tgz#07508b45797cb81ec3f273011b054cd0755eddca"
|
||||
integrity sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==
|
||||
|
||||
"@types/minimist@^1.2.5":
|
||||
version "1.2.5"
|
||||
resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.5.tgz#ec10755e871497bcd83efe927e43ec46e8c0747e"
|
||||
@ -3333,18 +3193,6 @@ asap@~2.0.3:
|
||||
resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46"
|
||||
integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==
|
||||
|
||||
asar@^3.0.0:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/asar/-/asar-3.2.0.tgz#e6edb5edd6f627ebef04db62f771c61bea9c1221"
|
||||
integrity sha512-COdw2ZQvKdFGFxXwX3oYh2/sOsJWJegrdJCGxnN4MZ7IULgRBp9P6665aqj9z1v9VwP4oP1hRBojRDQ//IGgAg==
|
||||
dependencies:
|
||||
chromium-pickle-js "^0.2.0"
|
||||
commander "^5.0.0"
|
||||
glob "^7.1.6"
|
||||
minimatch "^3.0.4"
|
||||
optionalDependencies:
|
||||
"@types/glob" "^7.1.1"
|
||||
|
||||
assert-plus@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525"
|
||||
@ -3913,15 +3761,6 @@ client-only@^0.0.1:
|
||||
resolved "https://registry.yarnpkg.com/client-only/-/client-only-0.0.1.tgz#38bba5d403c41ab150bff64a95c85013cf73bca1"
|
||||
integrity sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==
|
||||
|
||||
cliui@^7.0.2:
|
||||
version "7.0.4"
|
||||
resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f"
|
||||
integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==
|
||||
dependencies:
|
||||
string-width "^4.2.0"
|
||||
strip-ansi "^6.0.0"
|
||||
wrap-ansi "^7.0.0"
|
||||
|
||||
cliui@^8.0.1:
|
||||
version "8.0.1"
|
||||
resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa"
|
||||
@ -4144,15 +3983,6 @@ cross-fetch@^3.1.5:
|
||||
dependencies:
|
||||
node-fetch "^2.6.12"
|
||||
|
||||
cross-spawn-windows-exe@^1.1.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/cross-spawn-windows-exe/-/cross-spawn-windows-exe-1.2.0.tgz#46253b0f497676e766faf4a7061004618b5ac5ec"
|
||||
integrity sha512-mkLtJJcYbDCxEG7Js6eUnUNndWjyUZwJ3H7bErmmtOYU/Zb99DyUkpamuIZE0b3bhmJyZ7D90uS6f+CGxRRjOw==
|
||||
dependencies:
|
||||
"@malept/cross-spawn-promise" "^1.1.0"
|
||||
is-wsl "^2.2.0"
|
||||
which "^2.0.2"
|
||||
|
||||
cross-spawn@^6.0.0, cross-spawn@^6.0.5:
|
||||
version "6.0.5"
|
||||
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4"
|
||||
@ -4173,11 +4003,6 @@ cross-spawn@^7.0.0, cross-spawn@^7.0.1, cross-spawn@^7.0.2, cross-spawn@^7.0.3:
|
||||
shebang-command "^2.0.0"
|
||||
which "^2.0.1"
|
||||
|
||||
cross-zip@^4.0.0:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/cross-zip/-/cross-zip-4.0.1.tgz#1bbf5d3b0e5a77b5f5ca130a6d38f770786e1270"
|
||||
integrity sha512-n63i0lZ0rvQ6FXiGQ+/JFCKAUyPFhLQYJIqKaa+tSJtfKeULF/IDNDAbdnSIxgS4NTuw2b0+lj8LzfITuq+ZxQ==
|
||||
|
||||
css-line-break@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/css-line-break/-/css-line-break-2.1.0.tgz#bfef660dfa6f5397ea54116bb3cb4873edbc4fa0"
|
||||
@ -4536,7 +4361,7 @@ ejs@^3.1.8:
|
||||
dependencies:
|
||||
jake "^10.8.5"
|
||||
|
||||
electron-builder@^24.13.3:
|
||||
electron-builder@24.13.3:
|
||||
version "24.13.3"
|
||||
resolved "https://registry.yarnpkg.com/electron-builder/-/electron-builder-24.13.3.tgz#c506dfebd36d9a50a83ee8aa32d803d83dbe4616"
|
||||
integrity sha512-yZSgVHft5dNVlo31qmJAe4BVKQfFdwpRw7sFp1iQglDRCDD6r22zfRJuZlhtB5gp9FHUxCMEoWGq10SkCnMAIg==
|
||||
@ -4553,51 +4378,7 @@ electron-builder@^24.13.3:
|
||||
simple-update-notifier "2.0.0"
|
||||
yargs "^17.6.2"
|
||||
|
||||
electron-installer-common@^0.10.2:
|
||||
version "0.10.3"
|
||||
resolved "https://registry.yarnpkg.com/electron-installer-common/-/electron-installer-common-0.10.3.tgz#40f9db644ca60eb28673d545b67ee0113aef4444"
|
||||
integrity sha512-mYbP+6i+nHMIm0WZHXgGdmmXMe+KXncl6jZYQNcCF9C1WsNA9C5SZ2VP4TLQMSIoFO+X4ugkMEA5uld1bmyEvA==
|
||||
dependencies:
|
||||
"@malept/cross-spawn-promise" "^1.0.0"
|
||||
asar "^3.0.0"
|
||||
debug "^4.1.1"
|
||||
fs-extra "^9.0.0"
|
||||
glob "^7.1.4"
|
||||
lodash "^4.17.15"
|
||||
parse-author "^2.0.0"
|
||||
semver "^7.1.1"
|
||||
tmp-promise "^3.0.2"
|
||||
optionalDependencies:
|
||||
"@types/fs-extra" "^9.0.1"
|
||||
|
||||
electron-installer-debian@^3.2.0:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/electron-installer-debian/-/electron-installer-debian-3.2.0.tgz#2a9c8220f50a57807de8f93619a0d61ec41271e0"
|
||||
integrity sha512-58ZrlJ1HQY80VucsEIG9tQ//HrTlG6sfofA3nRGr6TmkX661uJyu4cMPPh6kXW+aHdq/7+q25KyQhDrXvRL7jw==
|
||||
dependencies:
|
||||
"@malept/cross-spawn-promise" "^1.0.0"
|
||||
debug "^4.1.1"
|
||||
electron-installer-common "^0.10.2"
|
||||
fs-extra "^9.0.0"
|
||||
get-folder-size "^2.0.1"
|
||||
lodash "^4.17.4"
|
||||
word-wrap "^1.2.3"
|
||||
yargs "^16.0.2"
|
||||
|
||||
electron-installer-redhat@^3.2.0:
|
||||
version "3.4.0"
|
||||
resolved "https://registry.yarnpkg.com/electron-installer-redhat/-/electron-installer-redhat-3.4.0.tgz#4a7f8d67b48b7d5b23bd1eb074f4b684ae43b192"
|
||||
integrity sha512-gEISr3U32Sgtj+fjxUAlSDo3wyGGq6OBx7rF5UdpIgbnpUvMN4W5uYb0ThpnAZ42VEJh/3aODQXHbFS4f5J3Iw==
|
||||
dependencies:
|
||||
"@malept/cross-spawn-promise" "^1.0.0"
|
||||
debug "^4.1.1"
|
||||
electron-installer-common "^0.10.2"
|
||||
fs-extra "^9.0.0"
|
||||
lodash "^4.17.15"
|
||||
word-wrap "^1.2.3"
|
||||
yargs "^16.0.2"
|
||||
|
||||
electron-notarize@^1.2.2:
|
||||
electron-notarize@1.2.2:
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/electron-notarize/-/electron-notarize-1.2.2.tgz#ebf2b258e8e08c1c9f8ff61dc53d5b16b439daf4"
|
||||
integrity sha512-ZStVWYcWI7g87/PgjPJSIIhwQXOaw4/XeXU+pWqMMktSLHaGMLHdyPPN7Cmao7+Cr7fYufA16npdtMndYciHNw==
|
||||
@ -4618,13 +4399,6 @@ electron-publish@24.13.1:
|
||||
lazy-val "^1.0.5"
|
||||
mime "^2.5.2"
|
||||
|
||||
electron-squirrel-startup@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/electron-squirrel-startup/-/electron-squirrel-startup-1.0.1.tgz#c9171568d724884c7a2b03760bfeedcf921c63ab"
|
||||
integrity sha512-sTfFIHGku+7PsHLJ7v0dRcZNkALrV+YEozINTW8X1nM//e5O3L+rfYuvSW00lmGHnYmUjARZulD8F2V8ISI9RA==
|
||||
dependencies:
|
||||
debug "^2.2.0"
|
||||
|
||||
electron-to-chromium@^1.4.820:
|
||||
version "1.5.4"
|
||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.4.tgz#cd477c830dd6fca41fbd5465c1ff6ce08ac22343"
|
||||
@ -4649,36 +4423,7 @@ electron-updater@6.3.0:
|
||||
semver "^7.3.8"
|
||||
tiny-typed-emitter "^2.1.0"
|
||||
|
||||
electron-winstaller@^5.3.0:
|
||||
version "5.4.0"
|
||||
resolved "https://registry.yarnpkg.com/electron-winstaller/-/electron-winstaller-5.4.0.tgz#f0660d476d5c4f579fdf7edd2f0cf01d54c4d0b2"
|
||||
integrity sha512-bO3y10YikuUwUuDUQRM4KfwNkKhnpVO7IPdbsrejwN9/AABJzzTQ4GeHwyzNSrVO+tEH3/Np255a3sVZpZDjvg==
|
||||
dependencies:
|
||||
"@electron/asar" "^3.2.1"
|
||||
debug "^4.1.1"
|
||||
fs-extra "^7.0.1"
|
||||
lodash "^4.17.21"
|
||||
temp "^0.9.0"
|
||||
optionalDependencies:
|
||||
"@electron/windows-sign" "^1.1.2"
|
||||
|
||||
electron-wix-msi@^5.1.3:
|
||||
version "5.1.3"
|
||||
resolved "https://registry.yarnpkg.com/electron-wix-msi/-/electron-wix-msi-5.1.3.tgz#ab85dc1145a7ce7ae7724ed3ca3f92c447988c9a"
|
||||
integrity sha512-EYj1cm1nZoVHmIIx3o0aKt784lxdEpJnXbEnyypklUCnglqSb7ni+1xi1Vp/gtrGS/mzIxnWBT+x5fIfuDjhvA==
|
||||
dependencies:
|
||||
"@electron/windows-sign" "^1.1.2"
|
||||
debug "^4.3.4"
|
||||
fs-extra "^10.1.0"
|
||||
klaw "^4.1.0"
|
||||
lodash "^4.17.21"
|
||||
rcedit "^4.0.1"
|
||||
rcinfo "^0.1.3"
|
||||
semver "^7.6.0"
|
||||
optionalDependencies:
|
||||
"@bitdisaster/exe-icon-extractor" "^1.0.10"
|
||||
|
||||
electron@*, electron@^32.1.2:
|
||||
electron@*, electron@32.1.2:
|
||||
version "32.1.2"
|
||||
resolved "https://registry.yarnpkg.com/electron/-/electron-32.1.2.tgz#84d1efd95d41224e58a6a9bbd1db4ba80154fc02"
|
||||
integrity sha512-CXe6doFzhmh1U7daOvUzmF6Cj8hssdYWMeEPRnRO6rB9/bbwMlWctcQ7P8NJXhLQ88/vYUJQrJvlJPh8qM0BRQ==
|
||||
@ -5588,15 +5333,6 @@ fs-extra@^11.1.0, fs-extra@^11.1.1:
|
||||
jsonfile "^6.0.1"
|
||||
universalify "^2.0.0"
|
||||
|
||||
fs-extra@^7.0.1:
|
||||
version "7.0.1"
|
||||
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9"
|
||||
integrity sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==
|
||||
dependencies:
|
||||
graceful-fs "^4.1.2"
|
||||
jsonfile "^4.0.0"
|
||||
universalify "^0.1.0"
|
||||
|
||||
fs-extra@^8.1.0:
|
||||
version "8.1.0"
|
||||
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0"
|
||||
@ -5672,11 +5408,6 @@ galactus@^1.0.0:
|
||||
flora-colossus "^2.0.0"
|
||||
fs-extra "^10.1.0"
|
||||
|
||||
gar@^1.0.4:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/gar/-/gar-1.0.4.tgz#f777bc7db425c0572fdeb52676172ca1ae9888b8"
|
||||
integrity sha512-w4n9cPWyP7aHxKxYHFQMegj7WIAsL/YX/C4Bs5Rr8s1H9M1rNtRWRsw+ovYMkXDQ5S4ZbYHsHAPmevPjPgw44w==
|
||||
|
||||
gauge@^4.0.3:
|
||||
version "4.0.4"
|
||||
resolved "https://registry.yarnpkg.com/gauge/-/gauge-4.0.4.tgz#52ff0652f2bbf607a989793d53b751bef2328dce"
|
||||
@ -5701,14 +5432,6 @@ get-caller-file@^2.0.5:
|
||||
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
|
||||
integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
|
||||
|
||||
get-folder-size@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/get-folder-size/-/get-folder-size-2.0.1.tgz#3fe0524dd3bad05257ef1311331417bcd020a497"
|
||||
integrity sha512-+CEb+GDCM7tkOS2wdMKTn9vU7DgnKUTuDlehkNJKNSovdCOVxs14OfKCk4cvSaR3za4gj+OBdl9opPN9xrJ0zA==
|
||||
dependencies:
|
||||
gar "^1.0.4"
|
||||
tiny-each-async "2.0.3"
|
||||
|
||||
get-func-name@^2.0.1, get-func-name@^2.0.2:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.2.tgz#0d7cf20cd13fda808669ffa88f4ffc7a3943fc41"
|
||||
@ -6312,11 +6035,6 @@ is-date-object@^1.0.1, is-date-object@^1.0.5:
|
||||
dependencies:
|
||||
has-tostringtag "^1.0.0"
|
||||
|
||||
is-docker@^2.0.0:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa"
|
||||
integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==
|
||||
|
||||
is-extglob@^2.1.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
|
||||
@ -6471,13 +6189,6 @@ is-windows@^1.0.1:
|
||||
resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d"
|
||||
integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==
|
||||
|
||||
is-wsl@^2.2.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271"
|
||||
integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==
|
||||
dependencies:
|
||||
is-docker "^2.0.0"
|
||||
|
||||
isarray@^2.0.5:
|
||||
version "2.0.5"
|
||||
resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723"
|
||||
@ -6733,11 +6444,6 @@ kill-port@^2.0.1:
|
||||
get-them-args "1.3.2"
|
||||
shell-exec "1.0.2"
|
||||
|
||||
klaw@^4.1.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/klaw/-/klaw-4.1.0.tgz#5df608067d8cb62bbfb24374f8e5d956323338f3"
|
||||
integrity sha512-1zGZ9MF9H22UnkpVeuaGKOjfA2t6WrfdrJmGjy16ykcjnKQDmHVX+KI477rpbGevz/5FD4MC3xf1oxylBgcaQw==
|
||||
|
||||
language-subtag-registry@^0.3.20:
|
||||
version "0.3.23"
|
||||
resolved "https://registry.yarnpkg.com/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz#23529e04d9e3b74679d70142df3fd2eb6ec572e7"
|
||||
@ -6872,7 +6578,7 @@ lodash.merge@^4.6.2:
|
||||
resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
|
||||
integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
|
||||
|
||||
lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.2, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.4:
|
||||
lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.2, lodash@^4.17.20, lodash@^4.17.21:
|
||||
version "4.17.21"
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
|
||||
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
|
||||
@ -7176,7 +6882,7 @@ minizlib@^2.1.1, minizlib@^2.1.2:
|
||||
minipass "^3.0.0"
|
||||
yallist "^4.0.0"
|
||||
|
||||
mkdirp@^0.5.1, mkdirp@^0.5.6:
|
||||
mkdirp@^0.5.6:
|
||||
version "0.5.6"
|
||||
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6"
|
||||
integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==
|
||||
@ -8037,18 +7743,6 @@ raw-body@2.5.2:
|
||||
iconv-lite "0.4.24"
|
||||
unpipe "1.0.0"
|
||||
|
||||
rcedit@^4.0.1:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/rcedit/-/rcedit-4.0.1.tgz#892ac47a19204a380f49e00ea38ce070443343c2"
|
||||
integrity sha512-bZdaQi34krFWhrDn+O53ccBDw0MkAT2Vhu75SqhtvhQu4OPyFM4RoVheyYiVQYdjhUi6EJMVWQ0tR6bCIYVkUg==
|
||||
dependencies:
|
||||
cross-spawn-windows-exe "^1.1.0"
|
||||
|
||||
rcinfo@^0.1.3:
|
||||
version "0.1.3"
|
||||
resolved "https://registry.yarnpkg.com/rcinfo/-/rcinfo-0.1.3.tgz#ac36832d1f1e5970c6379e571480ea5826511fc6"
|
||||
integrity sha512-c2XV2aYgY7x3BscO+/B/nCTtMvnclZ8w5D7R6zgK4sGOQnE0MjlXhOPynno7yp6Iw1RPNSXBwXwB1svZVRfcSw==
|
||||
|
||||
re-resizable@^6.9.11:
|
||||
version "6.9.17"
|
||||
resolved "https://registry.yarnpkg.com/re-resizable/-/re-resizable-6.9.17.tgz#78e4349934ff24a8fcb4b6b5a43ff9ed5f319d2a"
|
||||
@ -8439,13 +8133,6 @@ rimraf@^3.0.2:
|
||||
dependencies:
|
||||
glob "^7.1.3"
|
||||
|
||||
rimraf@~2.6.2:
|
||||
version "2.6.3"
|
||||
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab"
|
||||
integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==
|
||||
dependencies:
|
||||
glob "^7.1.3"
|
||||
|
||||
roarr@^2.15.3:
|
||||
version "2.15.4"
|
||||
resolved "https://registry.yarnpkg.com/roarr/-/roarr-2.15.4.tgz#f5fe795b7b838ccfe35dc608e0282b9eba2e7afd"
|
||||
@ -8563,7 +8250,7 @@ semver@^6.2.0, semver@^6.3.1:
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"
|
||||
integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
|
||||
|
||||
semver@^7.1.1, semver@^7.1.3, semver@^7.2.1, semver@^7.3.2, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.3, semver@^7.6.0:
|
||||
semver@^7.1.3, semver@^7.2.1, semver@^7.3.2, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.3:
|
||||
version "7.6.3"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143"
|
||||
integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==
|
||||
@ -9108,14 +8795,6 @@ temp-file@^3.4.0:
|
||||
async-exit-hook "^2.0.1"
|
||||
fs-extra "^10.0.0"
|
||||
|
||||
temp@^0.9.0:
|
||||
version "0.9.4"
|
||||
resolved "https://registry.yarnpkg.com/temp/-/temp-0.9.4.tgz#cd20a8580cb63635d0e4e9d4bd989d44286e7620"
|
||||
integrity sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA==
|
||||
dependencies:
|
||||
mkdirp "^0.5.1"
|
||||
rimraf "~2.6.2"
|
||||
|
||||
text-segmentation@^1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/text-segmentation/-/text-segmentation-1.0.3.tgz#52a388159efffe746b24a63ba311b6ac9f2d7943"
|
||||
@ -9152,11 +8831,6 @@ thunky@^1.0.2:
|
||||
resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d"
|
||||
integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==
|
||||
|
||||
tiny-each-async@2.0.3:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/tiny-each-async/-/tiny-each-async-2.0.3.tgz#8ebbbfd6d6295f1370003fbb37162afe5a0a51d1"
|
||||
integrity sha512-5ROII7nElnAirvFn8g7H7MtpfV1daMcyfTGQwsn/x2VtyV+VPiO5CjReCJtWLvoKTDEDmZocf3cNPraiMnBXLA==
|
||||
|
||||
tiny-invariant@^1.3.3:
|
||||
version "1.3.3"
|
||||
resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.3.3.tgz#46680b7a873a0d5d10005995eb90a70d74d60127"
|
||||
@ -9821,7 +9495,7 @@ wide-align@^1.1.5:
|
||||
dependencies:
|
||||
string-width "^1.0.2 || 2 || 3 || 4"
|
||||
|
||||
word-wrap@^1.2.3, word-wrap@^1.2.5:
|
||||
word-wrap@^1.2.5:
|
||||
version "1.2.5"
|
||||
resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34"
|
||||
integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==
|
||||
@ -9928,29 +9602,11 @@ yaml@^2.3.4:
|
||||
resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.5.0.tgz#c6165a721cf8000e91c36490a41d7be25176cf5d"
|
||||
integrity sha512-2wWLbGbYDiSqqIKoPjar3MPgB94ErzCtrNE1FdqGuaO0pi2JGjmE8aW8TDZwzU7vuxcGRdL/4gPQwQ7hD5AMSw==
|
||||
|
||||
yargs-parser@^20.2.2:
|
||||
version "20.2.9"
|
||||
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee"
|
||||
integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==
|
||||
|
||||
yargs-parser@^21.1.1:
|
||||
version "21.1.1"
|
||||
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35"
|
||||
integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==
|
||||
|
||||
yargs@^16.0.2:
|
||||
version "16.2.0"
|
||||
resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66"
|
||||
integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==
|
||||
dependencies:
|
||||
cliui "^7.0.2"
|
||||
escalade "^3.1.1"
|
||||
get-caller-file "^2.0.5"
|
||||
require-directory "^2.1.1"
|
||||
string-width "^4.2.0"
|
||||
y18n "^5.0.5"
|
||||
yargs-parser "^20.2.2"
|
||||
|
||||
yargs@^17.0.1, yargs@^17.6.2, yargs@^17.7.2:
|
||||
version "17.7.2"
|
||||
resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269"
|
||||
|