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 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 false
], ],
[ [

File diff suppressed because one or more lines are too long

View File

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

View File

@ -1,19 +1,22 @@
@precedence { @precedence {
annotation annotation
typeCall
member member
call call
exp @left exp @left
mult @left mult @left
add @left add @left
ascription @left
comp @left comp @left
logic @left logic @left
pipe @left pipe @left
range range
statement
} }
@top Program { @top Program {
Shebang? Shebang?
statement* (statement !statement statement*)?
} }
statement[@isGroup=Statement] { statement[@isGroup=Statement] {
@ -52,12 +55,15 @@ expression[@isGroup=Expression] {
UnaryExpression { UnaryOp expression } | UnaryExpression { UnaryOp expression } |
ParenthesizedExpression { "(" expression ")" } | ParenthesizedExpression { "(" expression ")" } |
IfExpression { kw<"if"> expression Body kw<"else"> Body } | 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 }> "]" } | ArrayExpression { "[" commaSep<expression | IntegerRange { expression !range ".." expression }> "]" } |
ObjectExpression { "{" commaSep<ObjectProperty> "}" } | ObjectExpression { "{" commaSep<ObjectProperty> "}" } |
MemberExpression { expression !member "." PropertyName } | MemberExpression { expression !member "." PropertyName } |
SubscriptExpression { expression !member "[" expression "]" } | SubscriptExpression { expression !member "[" expression "]" } |
PipeExpression { expression (!pipe PipeOperator expression)+ } PipeExpression { expression (!pipe PipeOperator expression)+ } |
AscribedExpression { expression !ascription ":" type }
} }
UnaryOp { AddOp | BangOp } UnaryOp { AddOp | BangOp }
@ -75,7 +81,7 @@ LabeledArgument { ArgumentLabel Equals expression }
ArgumentList { "(" commaSep<LabeledArgument | expression> ")" } ArgumentList { "(" commaSep<LabeledArgument | expression> ")" }
type[@isGroup=Type] { type[@isGroup=Type] {
PrimitiveType { identifier } | PrimitiveType { identifier !typeCall ("(" identifier ")")? } |
ArrayType { "[" type !member (";" Number "+"?)? "]" } | ArrayType { "[" type !member (";" Number "+"?)? "]" } |
ObjectType { "{" commaSep<ObjectProperty { PropertyName ":" type }> "}" } 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")] #[serde(rename_all = "camelCase")]
pub struct KclErrorWithOutputs { pub struct KclErrorWithOutputs {
pub error: KclError, pub error: KclError,
pub non_fatal: Vec<CompilationError>,
#[cfg(feature = "artifact-graph")] #[cfg(feature = "artifact-graph")]
pub operations: Vec<Operation>, pub operations: Vec<Operation>,
#[cfg(feature = "artifact-graph")] #[cfg(feature = "artifact-graph")]
@ -141,8 +142,10 @@ pub struct KclErrorWithOutputs {
} }
impl KclErrorWithOutputs { impl KclErrorWithOutputs {
#[allow(clippy::too_many_arguments)]
pub fn new( pub fn new(
error: KclError, error: KclError,
non_fatal: Vec<CompilationError>,
#[cfg(feature = "artifact-graph")] operations: Vec<Operation>, #[cfg(feature = "artifact-graph")] operations: Vec<Operation>,
#[cfg(feature = "artifact-graph")] artifact_commands: Vec<ArtifactCommand>, #[cfg(feature = "artifact-graph")] artifact_commands: Vec<ArtifactCommand>,
#[cfg(feature = "artifact-graph")] artifact_graph: ArtifactGraph, #[cfg(feature = "artifact-graph")] artifact_graph: ArtifactGraph,
@ -152,6 +155,7 @@ impl KclErrorWithOutputs {
) -> Self { ) -> Self {
Self { Self {
error, error,
non_fatal,
#[cfg(feature = "artifact-graph")] #[cfg(feature = "artifact-graph")]
operations, operations,
#[cfg(feature = "artifact-graph")] #[cfg(feature = "artifact-graph")]
@ -166,6 +170,7 @@ impl KclErrorWithOutputs {
pub fn no_outputs(error: KclError) -> Self { pub fn no_outputs(error: KclError) -> Self {
Self { Self {
error, error,
non_fatal: Default::default(),
#[cfg(feature = "artifact-graph")] #[cfg(feature = "artifact-graph")]
operations: Default::default(), operations: Default::default(),
#[cfg(feature = "artifact-graph")] #[cfg(feature = "artifact-graph")]

View File

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

View File

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

View File

@ -1277,7 +1277,15 @@ impl KclValue {
.satisfied(values.len(), allow_shrink) .satisfied(values.len(), allow_shrink)
.ok_or(CoercionError::from(self))?; .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); values.truncate(len);
Ok(KclValue::HomArray { 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. /// // Create a spring by sweeping around the helix path.
/// springSketch = startSketchOn(YZ) /// springSketch = startSketchOn(XZ)
/// |> circle( center = [0, 0], radius = 1) /// |> circle( center = [5, 0], radius = 1)
/// |> sweep(path = helixPath, relativeTo = "sketchPlane") /// |> sweep(path = helixPath)
/// ``` /// ```
/// ///
/// ```no_run /// ```no_run

View File

@ -82,9 +82,9 @@ export END = 'end'
/// ) /// )
/// ///
/// // Create a spring by sweeping around the helix path. /// // Create a spring by sweeping around the helix path.
/// springSketch = startSketchOn(YZ) /// springSketch = startSketchOn(XZ)
/// |> circle( center = [0, 0], radius = 0.5) /// |> circle( center = [5, 0], radius = 0.5)
/// |> sweep(path = helixPath, relativeTo = sweep::SKETCH_PLANE) /// |> sweep(path = helixPath)
/// ``` /// ```
/// ///
/// ``` /// ```
@ -103,9 +103,9 @@ export END = 'end'
/// ) /// )
/// ///
/// // Create a spring by sweeping around the helix path. /// // Create a spring by sweeping around the helix path.
/// springSketch = startSketchOn(XY) /// springSketch = startSketchOn(XZ)
/// |> circle( center = [0, 0], radius = 0.5 ) /// |> circle( center = [5, 0], radius = 0.5 )
/// |> sweep(path = helixPath, relativeTo = sweep::SKETCH_PLANE) /// |> sweep(path = helixPath)
/// ``` /// ```
/// ///
/// ``` /// ```
@ -123,9 +123,9 @@ export END = 'end'
/// ) /// )
/// ///
/// // Create a spring by sweeping around the helix path. /// // Create a spring by sweeping around the helix path.
/// springSketch = startSketchOn(XY) /// springSketch = startSketchOn(XZ)
/// |> circle( center = [0, 0], radius = 1 ) /// |> circle( center = [5, 0], radius = 1 )
/// |> sweep(path = helixPath, relativeTo = sweep::SKETCH_PLANE) /// |> sweep(path = helixPath)
/// ``` /// ```
/// ///
/// ``` /// ```
@ -408,13 +408,13 @@ export fn offsetPlane(
/// ) /// )
/// ///
/// ///
/// springSketch = startSketchOn(YZ) /// springSketch = startSketchOn(XZ)
/// |> circle( center = [0, 0], radius = 1) /// |> circle( center = [0, 0], radius = 1)
/// ///
/// // Create a spring by sweeping around the helix path. /// // Create a spring by sweeping around the helix path.
/// sweepedSpring = clone(springSketch) /// sweepedSpring = clone(springSketch)
/// |> translate(x=100) /// |> translate(x=5)
/// |> sweep(path = helixPath, relativeTo = sweep::SKETCH_PLANE) /// |> sweep(path = helixPath)
/// ``` /// ```
/// ///
/// ```kcl /// ```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"> <div className="flex flex-1 flex-wrap gap-2">
<p <p
data-command-name={selectedCommand?.name} data-command-name={selectedCommand?.name}
className="pr-4 flex gap-2 items-center" className="pr-2 flex gap-2 items-center"
> >
{selectedCommand && {selectedCommand &&
'icon' in selectedCommand && 'icon' in selectedCommand &&
@ -93,6 +93,13 @@ function CommandBarHeader({ children }: React.PropsWithChildren<object>) {
<span data-testid="command-name"> <span data-testid="command-name">
{selectedCommand.displayName || selectedCommand.name} {selectedCommand.displayName || selectedCommand.name}
</span> </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> </p>
{Object.entries(nonHiddenArgs || {}) {Object.entries(nonHiddenArgs || {})
.filter(([_, argConfig]) => .filter(([_, argConfig]) =>
@ -124,7 +131,9 @@ function CommandBarHeader({ children }: React.PropsWithChildren<object>) {
key={argName} key={argName}
className={`relative w-fit px-2 py-1 rounded-sm flex gap-2 items-center border ${ className={`relative w-fit px-2 py-1 rounded-sm flex gap-2 items-center border ${
argName === currentArgument?.name 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' : '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> </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>
<div className="block w-full my-2 h-[1px] bg-chalkboard-20 dark:bg-chalkboard-80" /> <div className="block w-full my-2 h-[1px] bg-chalkboard-20 dark:bg-chalkboard-80" />
{children} {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 ( return (
<ActionButton <ActionButton
Element="button" Element="button"
@ -216,8 +248,8 @@ function ReviewingButton() {
data-testid="command-bar-submit" data-testid="command-bar-submit"
iconStart={{ iconStart={{
icon: 'checkmark', icon: 'checkmark',
bgClassName: 'p-1 rounded-sm !bg-primary hover:brightness-110', bgClassName: `p-1 rounded-sm !bg-primary hover:brightness-110 ${bgClassName}`,
iconClassName: '!text-chalkboard-10', iconClassName: `!text-chalkboard-10 ${iconClassName}`,
}} }}
> >
<span className="sr-only">Submit command</span> <span className="sr-only">Submit command</span>
@ -225,7 +257,7 @@ function ReviewingButton() {
) )
} }
function GatheringArgsButton() { function GatheringArgsButton({ bgClassName, iconClassName }: ButtonProps) {
return ( return (
<ActionButton <ActionButton
Element="button" Element="button"
@ -235,8 +267,8 @@ function GatheringArgsButton() {
data-testid="command-bar-continue" data-testid="command-bar-continue"
iconStart={{ iconStart={{
icon: 'arrowRight', icon: 'arrowRight',
bgClassName: 'p-1 rounded-sm !bg-primary hover:brightness-110', bgClassName: `p-1 rounded-sm !bg-primary hover:brightness-110 ${bgClassName}`,
iconClassName: '!text-chalkboard-10', iconClassName: `!text-chalkboard-10 ${iconClassName}`,
}} }}
> >
<span className="sr-only">Continue</span> <span className="sr-only">Continue</span>

View File

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

View File

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

View File

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

View File

@ -47,11 +47,11 @@ it('formats numbers with units', () => {
describe('test errFromErrWithOutputs', () => { describe('test errFromErrWithOutputs', () => {
it('converts KclErrorWithOutputs to KclError', () => { it('converts KclErrorWithOutputs to KclError', () => {
const blob = 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 error = errFromErrWithOutputs(blob)
const errorStr = JSON.stringify(error) const errorStr = JSON.stringify(error)
expect(errorStr).toEqual( 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, UNLABELED_ARG,
} from '@src/lang/queryAstConstants' } from '@src/lang/queryAstConstants'
import type { NumericType } from '@rust/kcl-lib/bindings/NumericType' 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 { ArrayExpression } from '@rust/kcl-lib/bindings/ArrayExpression'
export type { export type {
@ -157,10 +158,23 @@ export function defaultSourceRange(): SourceRange {
return [0, 0, 0] return [0, 0, 0]
} }
function firstSourceRange(error: RustKclError): SourceRange { function bestSourceRange(error: RustKclError): SourceRange {
return error.sourceRanges.length > 0 if (error.sourceRanges.length === 0) {
? sourceRangeFromRust(error.sourceRanges[0]) return defaultSourceRange()
: 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 = ( const splitErrors = (
@ -230,7 +244,8 @@ export const parse = (code: string | Error): ParseResult | Error => {
return new KCLError( return new KCLError(
parsed.kind, parsed.kind,
parsed.msg, parsed.msg,
firstSourceRange(parsed), bestSourceRange(parsed),
[],
[], [],
[], [],
defaultArtifactGraph(), defaultArtifactGraph(),
@ -386,7 +401,8 @@ export const errFromErrWithOutputs = (e: any): KCLError => {
return new KCLError( return new KCLError(
parsed.error.kind, parsed.error.kind,
parsed.error.msg, parsed.error.msg,
firstSourceRange(parsed.error), bestSourceRange(parsed.error),
parsed.nonFatal,
parsed.operations, parsed.operations,
parsed.artifactCommands, parsed.artifactCommands,
rustArtifactGraphToMap(parsed.artifactGraph), rustArtifactGraphToMap(parsed.artifactGraph),

View File

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

View File

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