Compare commits

...

8 Commits

Author SHA1 Message Date
a5daa38ea7 Trying to add units parsing, but getting shift/reduce conflict 2025-05-16 19:33:18 -04:00
744da59a4b wip: Add parsing ascription 2025-05-16 00:00:48 -04:00
1506de92f5 ML commands green branding and experimental badge (#6989)
* Remove tan warning from ml commands, move to experimental green branding

* Oops

* Removed unused var

---------

Co-authored-by: Frank Noirot <frankjohnson1993@gmail.com>
2025-05-16 03:42:35 +00:00
8a03413643 Deflake onboarding test (#6996)
1. Remove all `waitFor` hidden states for the post-dismiss toast:
something about Playwright is making toasts hang in a way I've never
seen in real use. Not something I'm worried about, and we still test
that the toast fires the first time we dismiss.
2. Remove all `scene.connectionEstablished()` calls. This has me more
worried because some of the flakiness on this front seems like we're not
able to handle the rapid machine-speed navigation and overwriting that
this test does when it doesn't have to `waitFor` toasts to disappear.
But that is not the point of this test, which is just to ensure the
onboarding plays correctly and initiates correctly.
2025-05-15 23:27:58 -04:00
f59b806a88 Fix to display warnings when there's a fatal error (#6995)
* Fix to display warnings when there's a fatal error

* Fix JSON test
2025-05-16 03:22:21 +00:00
23a0085c78 Change runtime assert to runtime error and debug assert (#6987) 2025-05-15 22:55:16 -04:00
a280a8c3f0 Nickmccleery/i have no idea what im doing (#6967)
* Yeet in alt param structure.

* updates

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

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
Co-authored-by: Jess Frazelle <github@jessfraz.com>
2025-05-16 02:16:32 +00:00
11620dfa6b Fix to display errors at the call site (#6991) 2025-05-15 22:11:37 -04:00
26 changed files with 175 additions and 73 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -256720,7 +256720,7 @@
false
],
[
"// Create a spring by sweeping around a helix path.\n\n// Create a helix around the Z axis.\nhelixPath = helix(\n angleStart = 0,\n ccw = true,\n revolutions = 4,\n length = 10,\n radius = 5,\n axis = Z,\n)\n\n// Create a spring by sweeping around the helix path.\nspringSketch = startSketchOn(YZ)\n |> circle(center = [0, 0], radius = 1)\n |> sweep(path = helixPath, relativeTo = \"sketchPlane\")",
"// Create a spring by sweeping around a helix path.\n\n// Create a helix around the Z axis.\nhelixPath = helix(\n angleStart = 0,\n ccw = true,\n revolutions = 4,\n length = 10,\n radius = 5,\n axis = Z,\n)\n\n// Create a spring by sweeping around the helix path.\nspringSketch = startSketchOn(XZ)\n |> circle(center = [5, 0], radius = 1)\n |> sweep(path = helixPath)",
false
],
[

File diff suppressed because one or more lines are too long

View File

@ -6,7 +6,6 @@ test.describe('Onboarding tests', () => {
homePage,
toolbar,
editor,
scene,
tronApp,
}) => {
if (!tronApp) {
@ -62,7 +61,6 @@ test.describe('Onboarding tests', () => {
await editor.expectEditor.toContain('@settings(defaultLengthUnit = in)', {
shouldNormalise: true,
})
await scene.connectionEstablished()
})
await test.step('Go home and verify we still see the tutorial button, then begin it.', async () => {
@ -132,9 +130,7 @@ test.describe('Onboarding tests', () => {
})
await test.step('Dismiss the onboarding', async () => {
await postDismissToast.waitFor({ state: 'hidden' })
await page.keyboard.press('Escape')
await expect(postDismissToast).toBeVisible()
await expect(page.getByTestId('onboarding-content')).not.toBeVisible()
await expect.poll(() => page.url()).not.toContain('/onboarding')
})
@ -162,13 +158,10 @@ test.describe('Onboarding tests', () => {
await test.step('Gets to the onboarding start', async () => {
await expect(toolbar.projectName).toContainText('tutorial-project')
await expect(tutorialWelcomeHeading).toBeVisible()
await scene.connectionEstablished()
})
await test.step('Dismiss the onboarding', async () => {
await postDismissToast.waitFor({ state: 'hidden' })
await page.keyboard.press('Escape')
await expect(postDismissToast).toBeVisible()
await expect(page.getByTestId('onboarding-content')).not.toBeVisible()
await expect.poll(() => page.url()).not.toContain('/onboarding')
})

View File

@ -1,19 +1,22 @@
@precedence {
annotation
typeCall
member
call
exp @left
mult @left
add @left
ascription @left
comp @left
logic @left
pipe @left
range
statement
}
@top Program {
Shebang?
statement*
(statement !statement statement*)?
}
statement[@isGroup=Statement] {
@ -52,12 +55,15 @@ expression[@isGroup=Expression] {
UnaryExpression { UnaryOp expression } |
ParenthesizedExpression { "(" expression ")" } |
IfExpression { kw<"if"> expression Body kw<"else"> Body } |
CallExpression { expression !call ArgumentList } |
// We don't currently support arbitrary expressions as the callee part of a
// function call.
CallExpression { identifier !call ArgumentList } |
ArrayExpression { "[" commaSep<expression | IntegerRange { expression !range ".." expression }> "]" } |
ObjectExpression { "{" commaSep<ObjectProperty> "}" } |
MemberExpression { expression !member "." PropertyName } |
SubscriptExpression { expression !member "[" expression "]" } |
PipeExpression { expression (!pipe PipeOperator expression)+ }
PipeExpression { expression (!pipe PipeOperator expression)+ } |
AscribedExpression { expression !ascription ":" type }
}
UnaryOp { AddOp | BangOp }
@ -75,7 +81,7 @@ LabeledArgument { ArgumentLabel Equals expression }
ArgumentList { "(" commaSep<LabeledArgument | expression> ")" }
type[@isGroup=Type] {
PrimitiveType { identifier } |
PrimitiveType { identifier !typeCall ("(" identifier ")")? } |
ArrayType { "[" type !member (";" Number "+"?)? "]" } |
ObjectType { "{" commaSep<ObjectProperty { PropertyName ":" type }> "}" }
}

View File

@ -0,0 +1,13 @@
# primitive
true: bool
==>
Program(ExpressionStatement(AscribedExpression(true, ":", PrimitiveType)))
# numeric units
3.5: number(mm)
==>
Program(ExpressionStatement(AscribedExpression(3.5, ":", PrimitiveType)))

View File

@ -129,6 +129,7 @@ impl From<KclErrorWithOutputs> for KclError {
#[serde(rename_all = "camelCase")]
pub struct KclErrorWithOutputs {
pub error: KclError,
pub non_fatal: Vec<CompilationError>,
#[cfg(feature = "artifact-graph")]
pub operations: Vec<Operation>,
#[cfg(feature = "artifact-graph")]
@ -141,8 +142,10 @@ pub struct KclErrorWithOutputs {
}
impl KclErrorWithOutputs {
#[allow(clippy::too_many_arguments)]
pub fn new(
error: KclError,
non_fatal: Vec<CompilationError>,
#[cfg(feature = "artifact-graph")] operations: Vec<Operation>,
#[cfg(feature = "artifact-graph")] artifact_commands: Vec<ArtifactCommand>,
#[cfg(feature = "artifact-graph")] artifact_graph: ArtifactGraph,
@ -152,6 +155,7 @@ impl KclErrorWithOutputs {
) -> Self {
Self {
error,
non_fatal,
#[cfg(feature = "artifact-graph")]
operations,
#[cfg(feature = "artifact-graph")]
@ -166,6 +170,7 @@ impl KclErrorWithOutputs {
pub fn no_outputs(error: KclError) -> Self {
Self {
error,
non_fatal: Default::default(),
#[cfg(feature = "artifact-graph")]
operations: Default::default(),
#[cfg(feature = "artifact-graph")]

View File

@ -1467,7 +1467,6 @@ impl Node<CallExpressionKw> {
.await
.map_err(|e| {
// Add the call expression to the source ranges.
// TODO currently ignored by the frontend
e.add_source_ranges(vec![callsite])
})?;

View File

@ -823,6 +823,7 @@ impl ExecutorContext {
KclErrorWithOutputs::new(
err,
exec_state.errors().to_vec(),
#[cfg(feature = "artifact-graph")]
exec_state.global.operations.clone(),
#[cfg(feature = "artifact-graph")]
@ -999,6 +1000,7 @@ impl ExecutorContext {
return Err(KclErrorWithOutputs::new(
e,
exec_state.errors().to_vec(),
#[cfg(feature = "artifact-graph")]
exec_state.global.operations.clone(),
#[cfg(feature = "artifact-graph")]
@ -1048,6 +1050,7 @@ impl ExecutorContext {
KclErrorWithOutputs::new(
err,
exec_state.errors().to_vec(),
#[cfg(feature = "artifact-graph")]
exec_state.global.operations.clone(),
#[cfg(feature = "artifact-graph")]
@ -1100,6 +1103,7 @@ impl ExecutorContext {
KclErrorWithOutputs::new(
e,
exec_state.errors().to_vec(),
#[cfg(feature = "artifact-graph")]
exec_state.global.operations.clone(),
#[cfg(feature = "artifact-graph")]

View File

@ -1277,7 +1277,15 @@ impl KclValue {
.satisfied(values.len(), allow_shrink)
.ok_or(CoercionError::from(self))?;
assert!(len <= values.len());
if len > values.len() {
let message = format!(
"Internal: Expected coerced array length {len} to be less than or equal to original length {}",
values.len()
);
exec_state.err(CompilationError::err(self.into(), message.clone()));
#[cfg(debug_assertions)]
panic!("{message}");
}
values.truncate(len);
Ok(KclValue::HomArray {

View File

@ -110,9 +110,9 @@ pub async fn sweep(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
///
///
/// // Create a spring by sweeping around the helix path.
/// springSketch = startSketchOn(YZ)
/// |> circle( center = [0, 0], radius = 1)
/// |> sweep(path = helixPath, relativeTo = "sketchPlane")
/// springSketch = startSketchOn(XZ)
/// |> circle( center = [5, 0], radius = 1)
/// |> sweep(path = helixPath)
/// ```
///
/// ```no_run

View File

@ -82,9 +82,9 @@ export END = 'end'
/// )
///
/// // Create a spring by sweeping around the helix path.
/// springSketch = startSketchOn(YZ)
/// |> circle( center = [0, 0], radius = 0.5)
/// |> sweep(path = helixPath, relativeTo = sweep::SKETCH_PLANE)
/// springSketch = startSketchOn(XZ)
/// |> circle( center = [5, 0], radius = 0.5)
/// |> sweep(path = helixPath)
/// ```
///
/// ```
@ -103,9 +103,9 @@ export END = 'end'
/// )
///
/// // Create a spring by sweeping around the helix path.
/// springSketch = startSketchOn(XY)
/// |> circle( center = [0, 0], radius = 0.5 )
/// |> sweep(path = helixPath, relativeTo = sweep::SKETCH_PLANE)
/// springSketch = startSketchOn(XZ)
/// |> circle( center = [5, 0], radius = 0.5 )
/// |> sweep(path = helixPath)
/// ```
///
/// ```
@ -123,9 +123,9 @@ export END = 'end'
/// )
///
/// // Create a spring by sweeping around the helix path.
/// springSketch = startSketchOn(XY)
/// |> circle( center = [0, 0], radius = 1 )
/// |> sweep(path = helixPath, relativeTo = sweep::SKETCH_PLANE)
/// springSketch = startSketchOn(XZ)
/// |> circle( center = [5, 0], radius = 1 )
/// |> sweep(path = helixPath)
/// ```
///
/// ```
@ -408,13 +408,13 @@ export fn offsetPlane(
/// )
///
///
/// springSketch = startSketchOn(YZ)
/// springSketch = startSketchOn(XZ)
/// |> circle( center = [0, 0], radius = 1)
///
/// // Create a spring by sweeping around the helix path.
/// sweepedSpring = clone(springSketch)
/// |> translate(x=100)
/// |> sweep(path = helixPath, relativeTo = sweep::SKETCH_PLANE)
/// |> translate(x=5)
/// |> sweep(path = helixPath)
/// ```
///
/// ```kcl

Binary file not shown.

Before

Width:  |  Height:  |  Size: 174 KiB

After

Width:  |  Height:  |  Size: 185 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 194 KiB

After

Width:  |  Height:  |  Size: 202 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 194 KiB

After

Width:  |  Height:  |  Size: 202 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 174 KiB

After

Width:  |  Height:  |  Size: 187 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 174 KiB

After

Width:  |  Height:  |  Size: 185 KiB

View File

@ -83,7 +83,7 @@ function CommandBarHeader({ children }: React.PropsWithChildren<object>) {
<div className="flex flex-1 flex-wrap gap-2">
<p
data-command-name={selectedCommand?.name}
className="pr-4 flex gap-2 items-center"
className="pr-2 flex gap-2 items-center"
>
{selectedCommand &&
'icon' in selectedCommand &&
@ -93,6 +93,13 @@ function CommandBarHeader({ children }: React.PropsWithChildren<object>) {
<span data-testid="command-name">
{selectedCommand.displayName || selectedCommand.name}
</span>
{selectedCommand.status === 'experimental' ? (
<span className="text-ml-black text-xs bg-ml-green rounded-full ml-2 px-2 py-1">
experimental
</span>
) : (
<span className="pr-2" />
)}
</p>
{Object.entries(nonHiddenArgs || {})
.filter(([_, argConfig]) =>
@ -124,7 +131,9 @@ function CommandBarHeader({ children }: React.PropsWithChildren<object>) {
key={argName}
className={`relative w-fit px-2 py-1 rounded-sm flex gap-2 items-center border ${
argName === currentArgument?.name
? 'disabled:bg-primary/10 dark:disabled:bg-primary/20 disabled:border-primary dark:disabled:border-primary disabled:text-chalkboard-100 dark:disabled:text-chalkboard-10'
? selectedCommand.status === 'experimental'
? 'disabled:bg-ml-green/10 dark:disabled:bg-ml-green/20 disabled:border-ml-green dark:disabled:border-ml-green disabled:text-chalkboard-100 dark:disabled:text-chalkboard-10'
: 'disabled:bg-primary/10 dark:disabled:bg-primary/20 disabled:border-primary dark:disabled:border-primary disabled:text-chalkboard-100 dark:disabled:text-chalkboard-10'
: 'bg-chalkboard-20/50 dark:bg-chalkboard-80/50 border-chalkboard-20 dark:border-chalkboard-80'
}`}
>
@ -196,7 +205,29 @@ function CommandBarHeader({ children }: React.PropsWithChildren<object>) {
)
})}
</div>
{isReviewing ? <ReviewingButton /> : <GatheringArgsButton />}
{isReviewing ? (
<ReviewingButton
bgClassName={
selectedCommand.status === 'experimental' ? '!bg-ml-green' : ''
}
iconClassName={
selectedCommand.status === 'experimental'
? '!text-ml-black'
: ''
}
/>
) : (
<GatheringArgsButton
bgClassName={
selectedCommand.status === 'experimental' ? '!bg-ml-green' : ''
}
iconClassName={
selectedCommand.status === 'experimental'
? '!text-ml-black'
: ''
}
/>
)}
</div>
<div className="block w-full my-2 h-[1px] bg-chalkboard-20 dark:bg-chalkboard-80" />
{children}
@ -205,7 +236,8 @@ function CommandBarHeader({ children }: React.PropsWithChildren<object>) {
)
}
function ReviewingButton() {
type ButtonProps = { bgClassName?: string; iconClassName?: string }
function ReviewingButton({ bgClassName, iconClassName }: ButtonProps) {
return (
<ActionButton
Element="button"
@ -216,8 +248,8 @@ function ReviewingButton() {
data-testid="command-bar-submit"
iconStart={{
icon: 'checkmark',
bgClassName: 'p-1 rounded-sm !bg-primary hover:brightness-110',
iconClassName: '!text-chalkboard-10',
bgClassName: `p-1 rounded-sm !bg-primary hover:brightness-110 ${bgClassName}`,
iconClassName: `!text-chalkboard-10 ${iconClassName}`,
}}
>
<span className="sr-only">Submit command</span>
@ -225,7 +257,7 @@ function ReviewingButton() {
)
}
function GatheringArgsButton() {
function GatheringArgsButton({ bgClassName, iconClassName }: ButtonProps) {
return (
<ActionButton
Element="button"
@ -235,8 +267,8 @@ function GatheringArgsButton() {
data-testid="command-bar-continue"
iconStart={{
icon: 'arrowRight',
bgClassName: 'p-1 rounded-sm !bg-primary hover:brightness-110',
iconClassName: '!text-chalkboard-10',
bgClassName: `p-1 rounded-sm !bg-primary hover:brightness-110 ${bgClassName}`,
iconClassName: `!text-chalkboard-10 ${iconClassName}`,
}}
>
<span className="sr-only">Continue</span>

View File

@ -12,6 +12,7 @@ describe('test kclErrToDiagnostic', () => {
kind: 'semantic',
msg: 'Semantic error',
sourceRange: topLevelRange(0, 1),
nonFatal: [],
operations: [],
artifactCommands: [],
artifactGraph: defaultArtifactGraph(),
@ -24,6 +25,7 @@ describe('test kclErrToDiagnostic', () => {
kind: 'type',
msg: 'Type error',
sourceRange: topLevelRange(4, 5),
nonFatal: [],
operations: [],
artifactCommands: [],
artifactGraph: defaultArtifactGraph(),

View File

@ -24,6 +24,7 @@ export class KCLError extends Error {
kind: ExtractKind<RustKclError> | 'name'
sourceRange: SourceRange
msg: string
nonFatal: CompilationError[]
operations: Operation[]
artifactCommands: ArtifactCommand[]
artifactGraph: ArtifactGraph
@ -34,6 +35,7 @@ export class KCLError extends Error {
kind: ExtractKind<RustKclError> | 'name',
msg: string,
sourceRange: SourceRange,
nonFatal: CompilationError[] = [],
operations: Operation[],
artifactCommands: ArtifactCommand[],
artifactGraph: ArtifactGraph,
@ -44,6 +46,7 @@ export class KCLError extends Error {
this.kind = kind
this.msg = msg
this.sourceRange = sourceRange
this.nonFatal = nonFatal
this.operations = operations
this.artifactCommands = artifactCommands
this.artifactGraph = artifactGraph
@ -57,6 +60,7 @@ export class KCLLexicalError extends KCLError {
constructor(
msg: string,
sourceRange: SourceRange,
nonFatal: CompilationError[],
operations: Operation[],
artifactCommands: ArtifactCommand[],
artifactGraph: ArtifactGraph,
@ -67,6 +71,7 @@ export class KCLLexicalError extends KCLError {
'lexical',
msg,
sourceRange,
nonFatal,
operations,
artifactCommands,
artifactGraph,
@ -81,6 +86,7 @@ export class KCLInternalError extends KCLError {
constructor(
msg: string,
sourceRange: SourceRange,
nonFatal: CompilationError[],
operations: Operation[],
artifactCommands: ArtifactCommand[],
artifactGraph: ArtifactGraph,
@ -91,6 +97,7 @@ export class KCLInternalError extends KCLError {
'internal',
msg,
sourceRange,
nonFatal,
operations,
artifactCommands,
artifactGraph,
@ -105,6 +112,7 @@ export class KCLSyntaxError extends KCLError {
constructor(
msg: string,
sourceRange: SourceRange,
nonFatal: CompilationError[],
operations: Operation[],
artifactCommands: ArtifactCommand[],
artifactGraph: ArtifactGraph,
@ -115,6 +123,7 @@ export class KCLSyntaxError extends KCLError {
'syntax',
msg,
sourceRange,
nonFatal,
operations,
artifactCommands,
artifactGraph,
@ -129,6 +138,7 @@ export class KCLSemanticError extends KCLError {
constructor(
msg: string,
sourceRange: SourceRange,
nonFatal: CompilationError[],
operations: Operation[],
artifactCommands: ArtifactCommand[],
artifactGraph: ArtifactGraph,
@ -139,6 +149,7 @@ export class KCLSemanticError extends KCLError {
'semantic',
msg,
sourceRange,
nonFatal,
operations,
artifactCommands,
artifactGraph,
@ -153,6 +164,7 @@ export class KCLTypeError extends KCLError {
constructor(
msg: string,
sourceRange: SourceRange,
nonFatal: CompilationError[],
operations: Operation[],
artifactCommands: ArtifactCommand[],
artifactGraph: ArtifactGraph,
@ -163,6 +175,7 @@ export class KCLTypeError extends KCLError {
'type',
msg,
sourceRange,
nonFatal,
operations,
artifactCommands,
artifactGraph,
@ -177,6 +190,7 @@ export class KCLIoError extends KCLError {
constructor(
msg: string,
sourceRange: SourceRange,
nonFatal: CompilationError[],
operations: Operation[],
artifactCommands: ArtifactCommand[],
artifactGraph: ArtifactGraph,
@ -187,6 +201,7 @@ export class KCLIoError extends KCLError {
'io',
msg,
sourceRange,
nonFatal,
operations,
artifactCommands,
artifactGraph,
@ -201,6 +216,7 @@ export class KCLUnexpectedError extends KCLError {
constructor(
msg: string,
sourceRange: SourceRange,
nonFatal: CompilationError[],
operations: Operation[],
artifactCommands: ArtifactCommand[],
artifactGraph: ArtifactGraph,
@ -211,6 +227,7 @@ export class KCLUnexpectedError extends KCLError {
'unexpected',
msg,
sourceRange,
nonFatal,
operations,
artifactCommands,
artifactGraph,
@ -225,6 +242,7 @@ export class KCLValueAlreadyDefined extends KCLError {
constructor(
key: string,
sourceRange: SourceRange,
nonFatal: CompilationError[],
operations: Operation[],
artifactCommands: ArtifactCommand[],
artifactGraph: ArtifactGraph,
@ -235,6 +253,7 @@ export class KCLValueAlreadyDefined extends KCLError {
'name',
`Key ${key} was already defined elsewhere`,
sourceRange,
nonFatal,
operations,
artifactCommands,
artifactGraph,
@ -249,6 +268,7 @@ export class KCLUndefinedValueError extends KCLError {
constructor(
key: string,
sourceRange: SourceRange,
nonFatal: CompilationError[],
operations: Operation[],
artifactCommands: ArtifactCommand[],
artifactGraph: ArtifactGraph,
@ -259,6 +279,7 @@ export class KCLUndefinedValueError extends KCLError {
'name',
`Key ${key} has not been defined`,
sourceRange,
nonFatal,
operations,
artifactCommands,
artifactGraph,
@ -286,6 +307,7 @@ export function lspDiagnosticsToKclErrors(
[posToOffset(doc, range.start)!, posToOffset(doc, range.end)!, 0],
[],
[],
[],
defaultArtifactGraph(),
{},
null
@ -311,9 +333,13 @@ export function lspDiagnosticsToKclErrors(
export function kclErrorsToDiagnostics(
errors: KCLError[]
): CodeMirrorDiagnostic[] {
return errors
let nonFatal: CodeMirrorDiagnostic[] = []
const errs = errors
?.filter((err) => isTopLevelModule(err.sourceRange))
.map((err) => {
.map((err): CodeMirrorDiagnostic => {
if (err.nonFatal.length > 0) {
nonFatal = nonFatal.concat(compilationErrorsToDiagnostics(err.nonFatal))
}
return {
from: err.sourceRange[0],
to: err.sourceRange[1],
@ -321,6 +347,7 @@ export function kclErrorsToDiagnostics(
severity: 'error',
}
})
return errs.concat(nonFatal)
}
export function compilationErrorsToDiagnostics(

View File

@ -463,6 +463,7 @@ theExtrude = startSketchOn(XY)
expect.any(Object),
expect.any(Object),
expect.any(Object),
expect.any(Object),
null
)
)

View File

@ -47,11 +47,11 @@ it('formats numbers with units', () => {
describe('test errFromErrWithOutputs', () => {
it('converts KclErrorWithOutputs to KclError', () => {
const blob =
'{"error":{"kind":"internal","sourceRanges":[],"msg":"Cache busted"},"operations":[],"artifactCommands":[],"artifactGraph":{"map":{}},"filenames":{},"sourceFiles":{},"defaultPlanes":null}'
'{"error":{"kind":"internal","sourceRanges":[],"msg":"Cache busted"},"nonFatal":[],"operations":[],"artifactCommands":[],"artifactGraph":{"map":{}},"filenames":{},"sourceFiles":{},"defaultPlanes":null}'
const error = errFromErrWithOutputs(blob)
const errorStr = JSON.stringify(error)
expect(errorStr).toEqual(
'{"kind":"internal","sourceRange":[0,0,0],"msg":"Cache busted","operations":[],"artifactCommands":[],"artifactGraph":{},"filenames":{},"defaultPlanes":null}'
'{"kind":"internal","sourceRange":[0,0,0],"msg":"Cache busted","nonFatal":[],"operations":[],"artifactCommands":[],"artifactGraph":{},"filenames":{},"defaultPlanes":null}'
)
})
})

View File

@ -69,6 +69,7 @@ import {
UNLABELED_ARG,
} from '@src/lang/queryAstConstants'
import type { NumericType } from '@rust/kcl-lib/bindings/NumericType'
import { isTopLevelModule } from '@src/lang/util'
export type { ArrayExpression } from '@rust/kcl-lib/bindings/ArrayExpression'
export type {
@ -157,10 +158,23 @@ export function defaultSourceRange(): SourceRange {
return [0, 0, 0]
}
function firstSourceRange(error: RustKclError): SourceRange {
return error.sourceRanges.length > 0
? sourceRangeFromRust(error.sourceRanges[0])
: defaultSourceRange()
function bestSourceRange(error: RustKclError): SourceRange {
if (error.sourceRanges.length === 0) {
return defaultSourceRange()
}
// When there's an error, the call stack is unwound, and the locations are
// built up from deepest location to shallowest. So the shallowest call is
// last. That's the most useful to the user.
for (let i = error.sourceRanges.length - 1; i >= 0; i--) {
const range = error.sourceRanges[i]
// Skip ranges pointing into files that aren't the top-level module.
if (isTopLevelModule(range)) {
return sourceRangeFromRust(range)
}
}
// We didn't find a top-level module range, so just use the last one.
return sourceRangeFromRust(error.sourceRanges[error.sourceRanges.length - 1])
}
const splitErrors = (
@ -230,7 +244,8 @@ export const parse = (code: string | Error): ParseResult | Error => {
return new KCLError(
parsed.kind,
parsed.msg,
firstSourceRange(parsed),
bestSourceRange(parsed),
[],
[],
[],
defaultArtifactGraph(),
@ -386,7 +401,8 @@ export const errFromErrWithOutputs = (e: any): KCLError => {
return new KCLError(
parsed.error.kind,
parsed.error.msg,
firstSourceRange(parsed.error),
bestSourceRange(parsed.error),
parsed.nonFatal,
parsed.operations,
parsed.artifactCommands,
rustArtifactGraphToMap(parsed.artifactGraph),

View File

@ -10,7 +10,7 @@ import {
kclSamplesManifestWithNoMultipleFiles,
} from '@src/lib/kclSamples'
import { getUniqueProjectName } from '@src/lib/desktopFS'
import { IS_ML_EXPERIMENTAL, ML_EXPERIMENTAL_MESSAGE } from '@src/lib/constants'
import { IS_ML_EXPERIMENTAL } from '@src/lib/constants'
import toast from 'react-hot-toast'
import { reportRejection } from '@src/lib/trap'
import { relevantFileExtensions } from '@src/lang/wasmUtils'
@ -168,7 +168,6 @@ export function createApplicationCommands({
prompt: {
inputType: 'text',
required: true,
warningMessage: ML_EXPERIMENTAL_MESSAGE,
},
},
}

View File

@ -22,7 +22,6 @@ import {
KCL_DEFAULT_DEGREE,
KCL_DEFAULT_LENGTH,
KCL_DEFAULT_TRANSFORM,
ML_EXPERIMENTAL_MESSAGE,
} from '@src/lib/constants'
import type { components } from '@src/lib/machine-api'
import type { Selections } from '@src/lib/selections'
@ -964,12 +963,10 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
allowCodeSelection: true,
},
skip: true,
warningMessage: ML_EXPERIMENTAL_MESSAGE,
},
prompt: {
inputType: 'text',
required: true,
warningMessage: ML_EXPERIMENTAL_MESSAGE,
},
},
},