Chore: Return file paths to map SourceRange index to filename (#5471)

* chore: dumping progress

* chore: saving progress

* fix: Got a working example of filenames piped from Rust to TS

* fix: cleaning up debugging code

* fix: TS type for filenames

* fix: rust linter errors

* fix: cargo fmt

* fix: testing code, updating KCLError class for filenames

* fix: auto fixes

* fix: addressing PR comments

* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)

* fix: fixing the rust struct?

* fix: cargo fmt

* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
This commit is contained in:
Kevin Nadro
2025-02-25 11:51:54 -06:00
committed by GitHub
parent 4297dc43ae
commit 1e539cc134
24 changed files with 501 additions and 44 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 145 KiB

After

Width:  |  Height:  |  Size: 145 KiB

View File

@ -13,6 +13,7 @@ describe('test kclErrToDiagnostic', () => {
operations: [], operations: [],
artifactCommands: [], artifactCommands: [],
artifactGraph: defaultArtifactGraph(), artifactGraph: defaultArtifactGraph(),
filenames: {},
}, },
{ {
name: '', name: '',
@ -23,6 +24,7 @@ describe('test kclErrToDiagnostic', () => {
operations: [], operations: [],
artifactCommands: [], artifactCommands: [],
artifactGraph: defaultArtifactGraph(), artifactGraph: defaultArtifactGraph(),
filenames: {},
}, },
] ]
const diagnostics = kclErrorsToDiagnostics(errors) const diagnostics = kclErrorsToDiagnostics(errors)

View File

@ -13,6 +13,7 @@ import {
SourceRange, SourceRange,
} from 'lang/wasm' } from 'lang/wasm'
import { Operation } from 'wasm-lib/kcl/bindings/Operation' import { Operation } from 'wasm-lib/kcl/bindings/Operation'
import { ModulePath } from 'wasm-lib/kcl/bindings/ModulePath'
type ExtractKind<T> = T extends { kind: infer K } ? K : never type ExtractKind<T> = T extends { kind: infer K } ? K : never
export class KCLError extends Error { export class KCLError extends Error {
@ -22,6 +23,7 @@ export class KCLError extends Error {
operations: Operation[] operations: Operation[]
artifactCommands: ArtifactCommand[] artifactCommands: ArtifactCommand[]
artifactGraph: ArtifactGraph artifactGraph: ArtifactGraph
filenames: { [x: number]: ModulePath | undefined }
constructor( constructor(
kind: ExtractKind<RustKclError> | 'name', kind: ExtractKind<RustKclError> | 'name',
@ -29,7 +31,8 @@ export class KCLError extends Error {
sourceRange: SourceRange, sourceRange: SourceRange,
operations: Operation[], operations: Operation[],
artifactCommands: ArtifactCommand[], artifactCommands: ArtifactCommand[],
artifactGraph: ArtifactGraph artifactGraph: ArtifactGraph,
filenames: { [x: number]: ModulePath | undefined }
) { ) {
super() super()
this.kind = kind this.kind = kind
@ -38,6 +41,7 @@ export class KCLError extends Error {
this.operations = operations this.operations = operations
this.artifactCommands = artifactCommands this.artifactCommands = artifactCommands
this.artifactGraph = artifactGraph this.artifactGraph = artifactGraph
this.filenames = filenames
Object.setPrototypeOf(this, KCLError.prototype) Object.setPrototypeOf(this, KCLError.prototype)
} }
} }
@ -48,7 +52,8 @@ export class KCLLexicalError extends KCLError {
sourceRange: SourceRange, sourceRange: SourceRange,
operations: Operation[], operations: Operation[],
artifactCommands: ArtifactCommand[], artifactCommands: ArtifactCommand[],
artifactGraph: ArtifactGraph artifactGraph: ArtifactGraph,
filenames: { [x: number]: ModulePath | undefined }
) { ) {
super( super(
'lexical', 'lexical',
@ -56,7 +61,8 @@ export class KCLLexicalError extends KCLError {
sourceRange, sourceRange,
operations, operations,
artifactCommands, artifactCommands,
artifactGraph artifactGraph,
filenames
) )
Object.setPrototypeOf(this, KCLSyntaxError.prototype) Object.setPrototypeOf(this, KCLSyntaxError.prototype)
} }
@ -68,7 +74,8 @@ export class KCLInternalError extends KCLError {
sourceRange: SourceRange, sourceRange: SourceRange,
operations: Operation[], operations: Operation[],
artifactCommands: ArtifactCommand[], artifactCommands: ArtifactCommand[],
artifactGraph: ArtifactGraph artifactGraph: ArtifactGraph,
filenames: { [x: number]: ModulePath | undefined }
) { ) {
super( super(
'internal', 'internal',
@ -76,7 +83,8 @@ export class KCLInternalError extends KCLError {
sourceRange, sourceRange,
operations, operations,
artifactCommands, artifactCommands,
artifactGraph artifactGraph,
filenames
) )
Object.setPrototypeOf(this, KCLSyntaxError.prototype) Object.setPrototypeOf(this, KCLSyntaxError.prototype)
} }
@ -88,7 +96,8 @@ export class KCLSyntaxError extends KCLError {
sourceRange: SourceRange, sourceRange: SourceRange,
operations: Operation[], operations: Operation[],
artifactCommands: ArtifactCommand[], artifactCommands: ArtifactCommand[],
artifactGraph: ArtifactGraph artifactGraph: ArtifactGraph,
filenames: { [x: number]: ModulePath | undefined }
) { ) {
super( super(
'syntax', 'syntax',
@ -96,7 +105,8 @@ export class KCLSyntaxError extends KCLError {
sourceRange, sourceRange,
operations, operations,
artifactCommands, artifactCommands,
artifactGraph artifactGraph,
filenames
) )
Object.setPrototypeOf(this, KCLSyntaxError.prototype) Object.setPrototypeOf(this, KCLSyntaxError.prototype)
} }
@ -108,7 +118,8 @@ export class KCLSemanticError extends KCLError {
sourceRange: SourceRange, sourceRange: SourceRange,
operations: Operation[], operations: Operation[],
artifactCommands: ArtifactCommand[], artifactCommands: ArtifactCommand[],
artifactGraph: ArtifactGraph artifactGraph: ArtifactGraph,
filenames: { [x: number]: ModulePath | undefined }
) { ) {
super( super(
'semantic', 'semantic',
@ -116,7 +127,8 @@ export class KCLSemanticError extends KCLError {
sourceRange, sourceRange,
operations, operations,
artifactCommands, artifactCommands,
artifactGraph artifactGraph,
filenames
) )
Object.setPrototypeOf(this, KCLSemanticError.prototype) Object.setPrototypeOf(this, KCLSemanticError.prototype)
} }
@ -128,9 +140,18 @@ export class KCLTypeError extends KCLError {
sourceRange: SourceRange, sourceRange: SourceRange,
operations: Operation[], operations: Operation[],
artifactCommands: ArtifactCommand[], artifactCommands: ArtifactCommand[],
artifactGraph: ArtifactGraph artifactGraph: ArtifactGraph,
filenames: { [x: number]: ModulePath | undefined }
) { ) {
super('type', msg, sourceRange, operations, artifactCommands, artifactGraph) super(
'type',
msg,
sourceRange,
operations,
artifactCommands,
artifactGraph,
filenames
)
Object.setPrototypeOf(this, KCLTypeError.prototype) Object.setPrototypeOf(this, KCLTypeError.prototype)
} }
} }
@ -141,7 +162,8 @@ export class KCLUnimplementedError extends KCLError {
sourceRange: SourceRange, sourceRange: SourceRange,
operations: Operation[], operations: Operation[],
artifactCommands: ArtifactCommand[], artifactCommands: ArtifactCommand[],
artifactGraph: ArtifactGraph artifactGraph: ArtifactGraph,
filenames: { [x: number]: ModulePath | undefined }
) { ) {
super( super(
'unimplemented', 'unimplemented',
@ -149,7 +171,8 @@ export class KCLUnimplementedError extends KCLError {
sourceRange, sourceRange,
operations, operations,
artifactCommands, artifactCommands,
artifactGraph artifactGraph,
filenames
) )
Object.setPrototypeOf(this, KCLUnimplementedError.prototype) Object.setPrototypeOf(this, KCLUnimplementedError.prototype)
} }
@ -161,7 +184,8 @@ export class KCLUnexpectedError extends KCLError {
sourceRange: SourceRange, sourceRange: SourceRange,
operations: Operation[], operations: Operation[],
artifactCommands: ArtifactCommand[], artifactCommands: ArtifactCommand[],
artifactGraph: ArtifactGraph artifactGraph: ArtifactGraph,
filenames: { [x: number]: ModulePath | undefined }
) { ) {
super( super(
'unexpected', 'unexpected',
@ -169,7 +193,8 @@ export class KCLUnexpectedError extends KCLError {
sourceRange, sourceRange,
operations, operations,
artifactCommands, artifactCommands,
artifactGraph artifactGraph,
filenames
) )
Object.setPrototypeOf(this, KCLUnexpectedError.prototype) Object.setPrototypeOf(this, KCLUnexpectedError.prototype)
} }
@ -181,7 +206,8 @@ export class KCLValueAlreadyDefined extends KCLError {
sourceRange: SourceRange, sourceRange: SourceRange,
operations: Operation[], operations: Operation[],
artifactCommands: ArtifactCommand[], artifactCommands: ArtifactCommand[],
artifactGraph: ArtifactGraph artifactGraph: ArtifactGraph,
filenames: { [x: number]: ModulePath | undefined }
) { ) {
super( super(
'name', 'name',
@ -189,7 +215,8 @@ export class KCLValueAlreadyDefined extends KCLError {
sourceRange, sourceRange,
operations, operations,
artifactCommands, artifactCommands,
artifactGraph artifactGraph,
filenames
) )
Object.setPrototypeOf(this, KCLValueAlreadyDefined.prototype) Object.setPrototypeOf(this, KCLValueAlreadyDefined.prototype)
} }
@ -201,7 +228,8 @@ export class KCLUndefinedValueError extends KCLError {
sourceRange: SourceRange, sourceRange: SourceRange,
operations: Operation[], operations: Operation[],
artifactCommands: ArtifactCommand[], artifactCommands: ArtifactCommand[],
artifactGraph: ArtifactGraph artifactGraph: ArtifactGraph,
filenames: { [x: number]: ModulePath | undefined }
) { ) {
super( super(
'name', 'name',
@ -209,7 +237,8 @@ export class KCLUndefinedValueError extends KCLError {
sourceRange, sourceRange,
operations, operations,
artifactCommands, artifactCommands,
artifactGraph artifactGraph,
filenames
) )
Object.setPrototypeOf(this, KCLUndefinedValueError.prototype) Object.setPrototypeOf(this, KCLUndefinedValueError.prototype)
} }
@ -232,7 +261,8 @@ export function lspDiagnosticsToKclErrors(
[posToOffset(doc, range.start)!, posToOffset(doc, range.end)!, 0], [posToOffset(doc, range.start)!, posToOffset(doc, range.end)!, 0],
[], [],
[], [],
defaultArtifactGraph() defaultArtifactGraph(),
{}
) )
) )
.sort((a, b) => { .sort((a, b) => {

View File

@ -511,7 +511,8 @@ const theExtrude = startSketchOn('XY')
topLevelRange(129, 135), topLevelRange(129, 135),
[], [],
[], [],
defaultArtifactGraph() defaultArtifactGraph(),
{}
) )
) )
}) })

View File

@ -60,6 +60,7 @@ import { MetaSettings } from 'wasm-lib/kcl/bindings/MetaSettings'
import { UnitAngle, UnitLength } from 'wasm-lib/kcl/bindings/ModelingCmd' import { UnitAngle, UnitLength } from 'wasm-lib/kcl/bindings/ModelingCmd'
import { UnitLen } from 'wasm-lib/kcl/bindings/UnitLen' import { UnitLen } from 'wasm-lib/kcl/bindings/UnitLen'
import { UnitAngle as UnitAng } from 'wasm-lib/kcl/bindings/UnitAngle' import { UnitAngle as UnitAng } from 'wasm-lib/kcl/bindings/UnitAngle'
import { ModulePath } from 'wasm-lib/kcl/bindings/ModulePath'
export type { Artifact } from 'wasm-lib/kcl/bindings/Artifact' export type { Artifact } from 'wasm-lib/kcl/bindings/Artifact'
export type { ArtifactCommand } from 'wasm-lib/kcl/bindings/Artifact' export type { ArtifactCommand } from 'wasm-lib/kcl/bindings/Artifact'
@ -266,7 +267,8 @@ export const parse = (code: string | Error): ParseResult | Error => {
firstSourceRange(parsed), firstSourceRange(parsed),
[], [],
[], [],
defaultArtifactGraph() defaultArtifactGraph(),
{}
) )
} }
} }
@ -296,6 +298,7 @@ export interface ExecState {
artifactCommands: ArtifactCommand[] artifactCommands: ArtifactCommand[]
artifactGraph: ArtifactGraph artifactGraph: ArtifactGraph
errors: CompilationError[] errors: CompilationError[]
filenames: { [x: number]: ModulePath | undefined }
} }
/** /**
@ -310,6 +313,7 @@ export function emptyExecState(): ExecState {
artifactCommands: [], artifactCommands: [],
artifactGraph: defaultArtifactGraph(), artifactGraph: defaultArtifactGraph(),
errors: [], errors: [],
filenames: [],
} }
} }
@ -336,6 +340,7 @@ function execStateFromRust(
artifactCommands: execOutcome.artifactCommands, artifactCommands: execOutcome.artifactCommands,
artifactGraph, artifactGraph,
errors: execOutcome.errors, errors: execOutcome.errors,
filenames: execOutcome.filenames,
} }
} }
@ -347,6 +352,7 @@ function mockExecStateFromRust(execOutcome: RustExecOutcome): ExecState {
artifactCommands: execOutcome.artifactCommands, artifactCommands: execOutcome.artifactCommands,
artifactGraph: new Map<ArtifactId, Artifact>(), artifactGraph: new Map<ArtifactId, Artifact>(),
errors: execOutcome.errors, errors: execOutcome.errors,
filenames: execOutcome.filenames,
} }
} }
@ -474,7 +480,7 @@ const jsAppSettings = async () => {
} }
const errFromErrWithOutputs = (e: any): KCLError => { const errFromErrWithOutputs = (e: any): KCLError => {
console.log('execute error', e) console.log(e)
const parsed: KclErrorWithOutputs = JSON.parse(e.toString()) const parsed: KclErrorWithOutputs = JSON.parse(e.toString())
return new KCLError( return new KCLError(
parsed.error.kind, parsed.error.kind,
@ -482,7 +488,8 @@ const errFromErrWithOutputs = (e: any): KCLError => {
firstSourceRange(parsed.error), firstSourceRange(parsed.error),
parsed.operations, parsed.operations,
parsed.artifactCommands, parsed.artifactCommands,
rustArtifactGraphToMap(parsed.artifactGraph) rustArtifactGraphToMap(parsed.artifactGraph),
parsed.filenames
) )
} }
@ -548,7 +555,8 @@ export const modifyAstForSketch = async (
firstSourceRange(parsed), firstSourceRange(parsed),
[], [],
[], [],
defaultArtifactGraph() defaultArtifactGraph(),
{}
) )
return Promise.reject(kclError) return Promise.reject(kclError)

View File

@ -103,3 +103,7 @@ path = "tests/modify/main.rs"
#[patch.crates-io] #[patch.crates-io]
#kittycad-modeling-cmds = { path = "../../../modeling-api/modeling-cmds" } #kittycad-modeling-cmds = { path = "../../../modeling-api/modeling-cmds" }
#kittycad-modeling-session = { path = "../../../modeling-api/modeling-session" } #kittycad-modeling-session = { path = "../../../modeling-api/modeling-session" }
# Local development only. Placeholder to speed up development cycle
#[package.metadata.wasm-pack.profile.release]
#wasm-opt = false

View File

@ -2,9 +2,12 @@ use serde::{Deserialize, Serialize};
use thiserror::Error; use thiserror::Error;
use tower_lsp::lsp_types::{Diagnostic, DiagnosticSeverity}; use tower_lsp::lsp_types::{Diagnostic, DiagnosticSeverity};
use indexmap::IndexMap;
use crate::{ use crate::{
execution::{ArtifactCommand, ArtifactGraph, Operation}, execution::{ArtifactCommand, ArtifactGraph, Operation},
lsp::IntoDiagnostic, lsp::IntoDiagnostic,
modules::ModulePath,
source_range::SourceRange, source_range::SourceRange,
ModuleId, ModuleId,
}; };
@ -116,6 +119,7 @@ pub struct KclErrorWithOutputs {
pub operations: Vec<Operation>, pub operations: Vec<Operation>,
pub artifact_commands: Vec<ArtifactCommand>, pub artifact_commands: Vec<ArtifactCommand>,
pub artifact_graph: ArtifactGraph, pub artifact_graph: ArtifactGraph,
pub filenames: IndexMap<ModuleId, ModulePath>,
} }
impl KclErrorWithOutputs { impl KclErrorWithOutputs {
@ -124,12 +128,14 @@ impl KclErrorWithOutputs {
operations: Vec<Operation>, operations: Vec<Operation>,
artifact_commands: Vec<ArtifactCommand>, artifact_commands: Vec<ArtifactCommand>,
artifact_graph: ArtifactGraph, artifact_graph: ArtifactGraph,
filenames: IndexMap<ModuleId, ModulePath>,
) -> Self { ) -> Self {
Self { Self {
error, error,
operations, operations,
artifact_commands, artifact_commands,
artifact_graph, artifact_graph,
filenames,
} }
} }
pub fn no_outputs(error: KclError) -> Self { pub fn no_outputs(error: KclError) -> Self {
@ -138,6 +144,7 @@ impl KclErrorWithOutputs {
operations: Default::default(), operations: Default::default(),
artifact_commands: Default::default(), artifact_commands: Default::default(),
artifact_graph: Default::default(), artifact_graph: Default::default(),
filenames: Default::default(),
} }
} }
} }

View File

@ -329,6 +329,8 @@ impl ExecutorContext {
} }
let id = exec_state.next_module_id(); let id = exec_state.next_module_id();
// Add file path string to global state even if it fails to import
exec_state.add_path_to_source_id(resolved_path.clone(), id);
let source = resolved_path.source(&self.fs, source_range).await?; let source = resolved_path.source(&self.fs, source_range).await?;
// TODO handle parsing errors properly // TODO handle parsing errors properly
let parsed = crate::parsing::parse_str(&source, id).parse_errs_as_err()?; let parsed = crate::parsing::parse_str(&source, id).parse_errs_as_err()?;
@ -343,6 +345,8 @@ impl ExecutorContext {
let id = exec_state.next_module_id(); let id = exec_state.next_module_id();
let path = resolved_path.expect_path(); let path = resolved_path.expect_path();
// Add file path string to global state even if it fails to import
exec_state.add_path_to_source_id(resolved_path.clone(), id);
let format = super::import::format_from_annotations(attrs, path, source_range)?; let format = super::import::format_from_annotations(attrs, path, source_range)?;
let geom = super::import::import_foreign(path, format, exec_state, self, source_range).await?; let geom = super::import::import_foreign(path, format, exec_state, self, source_range).await?;
exec_state.add_module(id, resolved_path, ModuleRepr::Foreign(geom)); exec_state.add_module(id, resolved_path, ModuleRepr::Foreign(geom));
@ -354,6 +358,8 @@ impl ExecutorContext {
} }
let id = exec_state.next_module_id(); let id = exec_state.next_module_id();
// Add file path string to global state even if it fails to import
exec_state.add_path_to_source_id(resolved_path.clone(), id);
let source = resolved_path.source(&self.fs, source_range).await?; let source = resolved_path.source(&self.fs, source_range).await?;
let parsed = crate::parsing::parse_str(&source, id).parse_errs_as_err().unwrap(); let parsed = crate::parsing::parse_str(&source, id).parse_errs_as_err().unwrap();
exec_state.add_module(id, resolved_path, ModuleRepr::Kcl(parsed, None)); exec_state.add_module(id, resolved_path, ModuleRepr::Kcl(parsed, None));

View File

@ -23,6 +23,7 @@ use crate::{
cache::{CacheInformation, CacheResult}, cache::{CacheInformation, CacheResult},
}, },
fs::FileManager, fs::FileManager,
modules::{ModuleId, ModulePath},
parsing::ast::types::{Expr, ImportPath, Node, NodeRef, Program}, parsing::ast::types::{Expr, ImportPath, Node, NodeRef, Program},
settings::types::UnitLength, settings::types::UnitLength,
source_range::SourceRange, source_range::SourceRange,
@ -70,6 +71,8 @@ pub struct ExecOutcome {
pub artifact_graph: ArtifactGraph, pub artifact_graph: ArtifactGraph,
/// Non-fatal errors and warnings. /// Non-fatal errors and warnings.
pub errors: Vec<CompilationError>, pub errors: Vec<CompilationError>,
/// File Names in module Id array index order
pub filenames: IndexMap<ModuleId, ModulePath>,
} }
#[derive(Debug, Default, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)] #[derive(Debug, Default, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
@ -712,11 +715,19 @@ impl ExecutorContext {
.execute_and_build_graph(program, exec_state, preserve_mem) .execute_and_build_graph(program, exec_state, preserve_mem)
.await .await
.map_err(|e| { .map_err(|e| {
let module_id_to_module_path: IndexMap<ModuleId, ModulePath> = exec_state
.global
.path_to_source_id
.iter()
.map(|(k, v)| ((*v), k.clone()))
.collect();
KclErrorWithOutputs::new( KclErrorWithOutputs::new(
e, e,
exec_state.mod_local.operations.clone(), exec_state.mod_local.operations.clone(),
exec_state.global.artifact_commands.clone(), exec_state.global.artifact_commands.clone(),
exec_state.global.artifact_graph.clone(), exec_state.global.artifact_graph.clone(),
module_id_to_module_path,
) )
})?; })?;

View File

@ -124,6 +124,12 @@ impl ExecState {
artifact_commands: self.global.artifact_commands, artifact_commands: self.global.artifact_commands,
artifact_graph: self.global.artifact_graph, artifact_graph: self.global.artifact_graph,
errors: self.global.errors, errors: self.global.errors,
filenames: self
.global
.path_to_source_id
.iter()
.map(|(k, v)| ((*v), k.clone()))
.collect(),
} }
} }
@ -141,6 +147,7 @@ impl ExecState {
artifact_commands: Default::default(), artifact_commands: Default::default(),
artifact_graph: Default::default(), artifact_graph: Default::default(),
errors: self.global.errors, errors: self.global.errors,
filenames: Default::default(),
} }
} }
@ -169,11 +176,13 @@ impl ExecState {
self.global.path_to_source_id.get(path).cloned() self.global.path_to_source_id.get(path).cloned()
} }
pub(super) fn add_module(&mut self, id: ModuleId, path: ModulePath, repr: ModuleRepr) { pub(super) fn add_path_to_source_id(&mut self, path: ModulePath, id: ModuleId) {
debug_assert!(!self.global.path_to_source_id.contains_key(&path)); debug_assert!(!self.global.path_to_source_id.contains_key(&path));
self.global.path_to_source_id.insert(path.clone(), id); self.global.path_to_source_id.insert(path.clone(), id);
}
pub(super) fn add_module(&mut self, id: ModuleId, path: ModulePath, repr: ModuleRepr) {
debug_assert!(self.global.path_to_source_id.contains_key(&path));
let module_info = ModuleInfo { id, repr, path }; let module_info = ModuleInfo { id, repr, path };
self.global.module_infos.insert(id, module_info); self.global.module_infos.insert(id, module_info);
} }
@ -225,11 +234,15 @@ impl GlobalState {
root_id, root_id,
ModuleInfo { ModuleInfo {
id: root_id, id: root_id,
path: ModulePath::Local(root_path.clone()), path: ModulePath::Local {
value: root_path.clone(),
},
repr: ModuleRepr::Root, repr: ModuleRepr::Root,
}, },
); );
global.path_to_source_id.insert(ModulePath::Local(root_path), root_id); global
.path_to_source_id
.insert(ModulePath::Local { value: root_path }, root_id);
global global
} }
} }

View File

@ -64,13 +64,13 @@ impl ModuleLoader {
} }
pub(crate) fn enter_module(&mut self, path: &ModulePath) { pub(crate) fn enter_module(&mut self, path: &ModulePath) {
if let ModulePath::Local(ref path) = path { if let ModulePath::Local { value: ref path } = path {
self.import_stack.push(path.clone()); self.import_stack.push(path.clone());
} }
} }
pub(crate) fn leave_module(&mut self, path: &ModulePath) { pub(crate) fn leave_module(&mut self, path: &ModulePath) {
if let ModulePath::Local(ref path) = path { if let ModulePath::Local { value: ref path } = path {
let popped = self.import_stack.pop().unwrap(); let popped = self.import_stack.pop().unwrap();
assert_eq!(path, &popped); assert_eq!(path, &popped);
} }
@ -119,31 +119,32 @@ pub enum ModuleRepr {
} }
#[allow(clippy::large_enum_variant)] #[allow(clippy::large_enum_variant)]
#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize, Hash)] #[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize, Hash, ts_rs::TS)]
#[serde(tag = "type")]
pub enum ModulePath { pub enum ModulePath {
Local(PathBuf), Local { value: PathBuf },
Std(String), Std { value: String },
} }
impl ModulePath { impl ModulePath {
pub(crate) fn expect_path(&self) -> &PathBuf { pub(crate) fn expect_path(&self) -> &PathBuf {
match self { match self {
ModulePath::Local(p) => p, ModulePath::Local { value: p } => p,
_ => unreachable!(), _ => unreachable!(),
} }
} }
pub(crate) fn std_path(&self) -> Option<String> { pub(crate) fn std_path(&self) -> Option<String> {
match self { match self {
ModulePath::Local(_) => None, ModulePath::Local { value: _ } => None,
ModulePath::Std(p) => Some(p.clone()), ModulePath::Std { value: p } => Some(p.clone()),
} }
} }
pub(crate) async fn source(&self, fs: &FileManager, source_range: SourceRange) -> Result<String, KclError> { pub(crate) async fn source(&self, fs: &FileManager, source_range: SourceRange) -> Result<String, KclError> {
match self { match self {
ModulePath::Local(p) => fs.read_to_string(p, source_range).await, ModulePath::Local { value: p } => fs.read_to_string(p, source_range).await,
ModulePath::Std(name) => read_std(name) ModulePath::Std { value: name } => read_std(name)
.ok_or_else(|| { .ok_or_else(|| {
KclError::Semantic(KclErrorDetails { KclError::Semantic(KclErrorDetails {
message: format!("Cannot find standard library module to import: std::{name}."), message: format!("Cannot find standard library module to import: std::{name}."),
@ -162,14 +163,14 @@ impl ModulePath {
} else { } else {
std::path::PathBuf::from(path) std::path::PathBuf::from(path)
}; };
ModulePath::Local(resolved_path) ModulePath::Local { value: resolved_path }
} }
ImportPath::Std { path } => { ImportPath::Std { path } => {
// For now we only support importing from singly-nested modules inside std. // For now we only support importing from singly-nested modules inside std.
assert_eq!(path.len(), 2); assert_eq!(path.len(), 2);
assert_eq!(&path[0], "std"); assert_eq!(&path[0], "std");
ModulePath::Std(path[1].clone()) ModulePath::Std { value: path[1].clone() }
} }
} }
} }
@ -178,8 +179,8 @@ impl ModulePath {
impl fmt::Display for ModulePath { impl fmt::Display for ModulePath {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
ModulePath::Local(path) => path.display().fmt(f), ModulePath::Local { value: path } => path.display().fmt(f),
ModulePath::Std(s) => write!(f, "std::{s}"), ModulePath::Std { value: s } => write!(f, "std::{s}"),
} }
} }
} }

View File

@ -1990,7 +1990,29 @@ mod helix_simple {
/// Test parsing KCL. /// Test parsing KCL.
#[test] #[test]
fn parse() { fn parse() {
super::parse(TEST_NAME) super::parse(TEST_NAME);
}
/// Test that parsing and unparsing KCL produces the original KCL input.
#[test]
fn unparse() {
super::unparse(TEST_NAME)
}
/// Test that KCL is executed correctly.
#[tokio::test(flavor = "multi_thread")]
async fn kcl_test_execute() {
super::execute(TEST_NAME, true).await
}
}
mod import_file_parse_error {
const TEST_NAME: &str = "import_file_parse_error";
/// Test parsing KCL.
#[test]
fn parse() {
super::parse(TEST_NAME);
} }
/// Test that parsing and unparsing KCL produces the original KCL input. /// Test that parsing and unparsing KCL produces the original KCL input.

View File

@ -0,0 +1,284 @@
---
source: kcl/src/simulation_tests.rs
description: Artifact commands import_file_parse_error.kcl
---
[
{
"cmdId": "[uuid]",
"range": [
0,
0,
0
],
"command": {
"type": "make_plane",
"origin": {
"x": 0.0,
"y": 0.0,
"z": 0.0
},
"x_axis": {
"x": 1.0,
"y": 0.0,
"z": 0.0
},
"y_axis": {
"x": 0.0,
"y": 1.0,
"z": 0.0
},
"size": 100.0,
"clobber": false,
"hide": true
}
},
{
"cmdId": "[uuid]",
"range": [
0,
0,
0
],
"command": {
"type": "plane_set_color",
"plane_id": "[uuid]",
"color": {
"r": 0.7,
"g": 0.28,
"b": 0.28,
"a": 0.4
}
}
},
{
"cmdId": "[uuid]",
"range": [
0,
0,
0
],
"command": {
"type": "make_plane",
"origin": {
"x": 0.0,
"y": 0.0,
"z": 0.0
},
"x_axis": {
"x": 0.0,
"y": 1.0,
"z": 0.0
},
"y_axis": {
"x": 0.0,
"y": 0.0,
"z": 1.0
},
"size": 100.0,
"clobber": false,
"hide": true
}
},
{
"cmdId": "[uuid]",
"range": [
0,
0,
0
],
"command": {
"type": "plane_set_color",
"plane_id": "[uuid]",
"color": {
"r": 0.28,
"g": 0.7,
"b": 0.28,
"a": 0.4
}
}
},
{
"cmdId": "[uuid]",
"range": [
0,
0,
0
],
"command": {
"type": "make_plane",
"origin": {
"x": 0.0,
"y": 0.0,
"z": 0.0
},
"x_axis": {
"x": 1.0,
"y": 0.0,
"z": 0.0
},
"y_axis": {
"x": 0.0,
"y": 0.0,
"z": 1.0
},
"size": 100.0,
"clobber": false,
"hide": true
}
},
{
"cmdId": "[uuid]",
"range": [
0,
0,
0
],
"command": {
"type": "plane_set_color",
"plane_id": "[uuid]",
"color": {
"r": 0.28,
"g": 0.28,
"b": 0.7,
"a": 0.4
}
}
},
{
"cmdId": "[uuid]",
"range": [
0,
0,
0
],
"command": {
"type": "make_plane",
"origin": {
"x": 0.0,
"y": 0.0,
"z": 0.0
},
"x_axis": {
"x": -1.0,
"y": 0.0,
"z": 0.0
},
"y_axis": {
"x": 0.0,
"y": 1.0,
"z": 0.0
},
"size": 100.0,
"clobber": false,
"hide": true
}
},
{
"cmdId": "[uuid]",
"range": [
0,
0,
0
],
"command": {
"type": "make_plane",
"origin": {
"x": 0.0,
"y": 0.0,
"z": 0.0
},
"x_axis": {
"x": 0.0,
"y": -1.0,
"z": 0.0
},
"y_axis": {
"x": 0.0,
"y": 0.0,
"z": 1.0
},
"size": 100.0,
"clobber": false,
"hide": true
}
},
{
"cmdId": "[uuid]",
"range": [
0,
0,
0
],
"command": {
"type": "make_plane",
"origin": {
"x": 0.0,
"y": 0.0,
"z": 0.0
},
"x_axis": {
"x": -1.0,
"y": 0.0,
"z": 0.0
},
"y_axis": {
"x": 0.0,
"y": 0.0,
"z": 1.0
},
"size": 100.0,
"clobber": false,
"hide": true
}
},
{
"cmdId": "[uuid]",
"range": [
0,
0,
0
],
"command": {
"type": "edge_lines_visible",
"hidden": false
}
},
{
"cmdId": "[uuid]",
"range": [
0,
0,
0
],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [
0,
0,
0
],
"command": {
"type": "object_visible",
"object_id": "[uuid]",
"hidden": true
}
},
{
"cmdId": "[uuid]",
"range": [
0,
0,
0
],
"command": {
"type": "object_visible",
"object_id": "[uuid]",
"hidden": true
}
}
]

View File

@ -0,0 +1,6 @@
---
source: kcl/src/simulation_tests.rs
description: Artifact graph flowchart import_file_parse_error.kcl
extension: md
snapshot_kind: binary
---

View File

@ -0,0 +1,3 @@
```mermaid
flowchart LR
```

View File

@ -0,0 +1,39 @@
---
source: kcl/src/simulation_tests.rs
description: Result of parsing import_file_parse_error.kcl
---
{
"Ok": {
"body": [
{
"end": 38,
"path": {
"type": "Kcl",
"filename": "parse-failure.kcl"
},
"selector": {
"type": "List",
"items": [
{
"alias": null,
"end": 13,
"name": {
"end": 13,
"name": "hotdog",
"start": 7,
"type": "Identifier"
},
"start": 7,
"type": "ImportItem"
}
]
},
"start": 0,
"type": "ImportStatement",
"type": "ImportStatement"
}
],
"end": 39,
"start": 0
}
}

View File

@ -0,0 +1,11 @@
---
source: kcl/src/simulation_tests.rs
description: Error from executing import_file_parse_error.kcl
---
KCL Syntax error
× syntax: Unexpected token: }
╭────
1 │ import hotdog from "parse-failure.kcl"
· ─
╰────

View File

@ -0,0 +1 @@
import hotdog from "parse-failure.kcl"

View File

@ -0,0 +1,5 @@
---
source: kcl/src/simulation_tests.rs
description: Operations executed import_file_parse_error.kcl
---
[]

View File

@ -0,0 +1,3 @@
export fn hotdog () {
return
}