Refactor SourceRange and ModuleId to make them better encapsualated (#4636)
Signed-off-by: Nick Cameron <nrc@ncameron.org>
This commit is contained in:
		@ -6,6 +6,7 @@ use kcmc::{
 | 
			
		||||
};
 | 
			
		||||
use kittycad_modeling_cmds as kcmc;
 | 
			
		||||
 | 
			
		||||
use super::types::Node;
 | 
			
		||||
use crate::{
 | 
			
		||||
    ast::types::{
 | 
			
		||||
        ArrayExpression, CallExpression, ConstraintLevel, FormatOptions, Literal, PipeExpression, PipeSubstitution,
 | 
			
		||||
@ -13,12 +14,11 @@ use crate::{
 | 
			
		||||
    },
 | 
			
		||||
    engine::EngineManager,
 | 
			
		||||
    errors::{KclError, KclErrorDetails},
 | 
			
		||||
    executor::{Point2d, SourceRange},
 | 
			
		||||
    executor::Point2d,
 | 
			
		||||
    source_range::{ModuleId, SourceRange},
 | 
			
		||||
    Program,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
use super::types::{ModuleId, Node};
 | 
			
		||||
 | 
			
		||||
type Point3d = kcmc::shared::Point3d<f64>;
 | 
			
		||||
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
 | 
			
		||||
@ -27,8 +27,9 @@ pub use crate::ast::types::{
 | 
			
		||||
use crate::{
 | 
			
		||||
    docs::StdLibFn,
 | 
			
		||||
    errors::KclError,
 | 
			
		||||
    executor::{ExecState, ExecutorContext, KclValue, Metadata, SourceRange, TagIdentifier},
 | 
			
		||||
    executor::{ExecState, ExecutorContext, KclValue, Metadata, TagIdentifier},
 | 
			
		||||
    parser::PIPE_OPERATOR,
 | 
			
		||||
    source_range::{ModuleId, SourceRange},
 | 
			
		||||
    std::kcl_stdlib::KclStdLibFn,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -62,7 +63,7 @@ pub struct Node<T> {
 | 
			
		||||
impl<T> Node<T> {
 | 
			
		||||
    pub fn metadata(&self) -> Metadata {
 | 
			
		||||
        Metadata {
 | 
			
		||||
            source_range: SourceRange([self.start, self.end, self.module_id.0 as usize]),
 | 
			
		||||
            source_range: SourceRange::new(self.start, self.end, self.module_id),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -122,7 +123,7 @@ impl<T> Node<T> {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn as_source_range(&self) -> SourceRange {
 | 
			
		||||
        SourceRange([self.start, self.end, self.module_id.as_usize()])
 | 
			
		||||
        SourceRange::new(self.start, self.end, self.module_id)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn as_source_ranges(&self) -> Vec<SourceRange> {
 | 
			
		||||
@ -150,21 +151,21 @@ impl<T: fmt::Display> fmt::Display for Node<T> {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T> From<Node<T>> for crate::executor::SourceRange {
 | 
			
		||||
impl<T> From<Node<T>> for SourceRange {
 | 
			
		||||
    fn from(v: Node<T>) -> Self {
 | 
			
		||||
        Self([v.start, v.end, v.module_id.as_usize()])
 | 
			
		||||
        Self::new(v.start, v.end, v.module_id)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T> From<&Node<T>> for crate::executor::SourceRange {
 | 
			
		||||
impl<T> From<&Node<T>> for SourceRange {
 | 
			
		||||
    fn from(v: &Node<T>) -> Self {
 | 
			
		||||
        Self([v.start, v.end, v.module_id.as_usize()])
 | 
			
		||||
        Self::new(v.start, v.end, v.module_id)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T> From<&BoxNode<T>> for crate::executor::SourceRange {
 | 
			
		||||
impl<T> From<&BoxNode<T>> for SourceRange {
 | 
			
		||||
    fn from(v: &BoxNode<T>) -> Self {
 | 
			
		||||
        Self([v.start, v.end, v.module_id.as_usize()])
 | 
			
		||||
        Self::new(v.start, v.end, v.module_id)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -553,29 +554,6 @@ impl Shebang {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Identifier of a source file.  Uses a u32 to keep the size small.
 | 
			
		||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, Deserialize, Serialize, ts_rs::TS, JsonSchema, Bake)]
 | 
			
		||||
#[cfg_attr(feature = "pyo3", pyo3::pyclass)]
 | 
			
		||||
#[databake(path = kcl_lib::ast::types)]
 | 
			
		||||
#[ts(export)]
 | 
			
		||||
pub struct ModuleId(pub u32);
 | 
			
		||||
 | 
			
		||||
impl ModuleId {
 | 
			
		||||
    pub fn from_usize(id: usize) -> Self {
 | 
			
		||||
        Self(u32::try_from(id).expect("module ID should fit in a u32"))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn as_usize(&self) -> usize {
 | 
			
		||||
        usize::try_from(self.0).expect("module ID should fit in a usize")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Top-level file is the one being executed.
 | 
			
		||||
    /// Represented by module ID of 0, i.e. the default value.
 | 
			
		||||
    pub fn is_top_level(&self) -> bool {
 | 
			
		||||
        *self == Self::default()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, Bake)]
 | 
			
		||||
#[databake(path = kcl_lib::ast::types)]
 | 
			
		||||
#[ts(export)]
 | 
			
		||||
@ -609,13 +587,13 @@ impl BodyItem {
 | 
			
		||||
 | 
			
		||||
impl From<BodyItem> for SourceRange {
 | 
			
		||||
    fn from(item: BodyItem) -> Self {
 | 
			
		||||
        Self([item.start(), item.end(), item.module_id().as_usize()])
 | 
			
		||||
        Self::new(item.start(), item.end(), item.module_id())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<&BodyItem> for SourceRange {
 | 
			
		||||
    fn from(item: &BodyItem) -> Self {
 | 
			
		||||
        Self([item.start(), item.end(), item.module_id().as_usize()])
 | 
			
		||||
        Self::new(item.start(), item.end(), item.module_id())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -837,13 +815,13 @@ impl Expr {
 | 
			
		||||
 | 
			
		||||
impl From<Expr> for SourceRange {
 | 
			
		||||
    fn from(value: Expr) -> Self {
 | 
			
		||||
        Self([value.start(), value.end(), value.module_id().as_usize()])
 | 
			
		||||
        Self::new(value.start(), value.end(), value.module_id())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<&Expr> for SourceRange {
 | 
			
		||||
    fn from(value: &Expr) -> Self {
 | 
			
		||||
        Self([value.start(), value.end(), value.module_id().as_usize()])
 | 
			
		||||
        Self::new(value.start(), value.end(), value.module_id())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -864,13 +842,13 @@ pub enum BinaryPart {
 | 
			
		||||
 | 
			
		||||
impl From<BinaryPart> for SourceRange {
 | 
			
		||||
    fn from(value: BinaryPart) -> Self {
 | 
			
		||||
        Self([value.start(), value.end(), value.module_id().as_usize()])
 | 
			
		||||
        Self::new(value.start(), value.end(), value.module_id())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<&BinaryPart> for SourceRange {
 | 
			
		||||
    fn from(value: &BinaryPart) -> Self {
 | 
			
		||||
        Self([value.start(), value.end(), value.module_id().as_usize()])
 | 
			
		||||
        Self::new(value.start(), value.end(), value.module_id())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -2329,13 +2307,13 @@ impl MemberObject {
 | 
			
		||||
 | 
			
		||||
impl From<MemberObject> for SourceRange {
 | 
			
		||||
    fn from(obj: MemberObject) -> Self {
 | 
			
		||||
        Self([obj.start(), obj.end(), obj.module_id().as_usize()])
 | 
			
		||||
        Self::new(obj.start(), obj.end(), obj.module_id())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<&MemberObject> for SourceRange {
 | 
			
		||||
    fn from(obj: &MemberObject) -> Self {
 | 
			
		||||
        Self([obj.start(), obj.end(), obj.module_id().as_usize()])
 | 
			
		||||
        Self::new(obj.start(), obj.end(), obj.module_id())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -2366,13 +2344,13 @@ impl LiteralIdentifier {
 | 
			
		||||
 | 
			
		||||
impl From<LiteralIdentifier> for SourceRange {
 | 
			
		||||
    fn from(id: LiteralIdentifier) -> Self {
 | 
			
		||||
        Self([id.start(), id.end(), id.module_id().as_usize()])
 | 
			
		||||
        Self::new(id.start(), id.end(), id.module_id())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<&LiteralIdentifier> for SourceRange {
 | 
			
		||||
    fn from(id: &LiteralIdentifier) -> Self {
 | 
			
		||||
        Self([id.start(), id.end(), id.module_id().as_usize()])
 | 
			
		||||
        Self::new(id.start(), id.end(), id.module_id())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,15 +1,10 @@
 | 
			
		||||
use crate::executor::SourceRange;
 | 
			
		||||
 | 
			
		||||
use super::BoxNode;
 | 
			
		||||
use super::ConstraintLevel;
 | 
			
		||||
use super::Hover;
 | 
			
		||||
use super::Node;
 | 
			
		||||
use super::NodeList;
 | 
			
		||||
use super::{Digest, Expr};
 | 
			
		||||
use databake::*;
 | 
			
		||||
use schemars::JsonSchema;
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
 | 
			
		||||
use super::{BoxNode, ConstraintLevel, Digest, Expr, Hover, Node, NodeList};
 | 
			
		||||
use crate::SourceRange;
 | 
			
		||||
 | 
			
		||||
// TODO: This should be its own type, similar to Program,
 | 
			
		||||
// but guaranteed to have an Expression as its final item.
 | 
			
		||||
// https://github.com/KittyCAD/modeling-app/issues/4015
 | 
			
		||||
@ -50,7 +45,7 @@ impl Node<IfExpression> {
 | 
			
		||||
impl Node<ElseIf> {
 | 
			
		||||
    #[allow(dead_code)]
 | 
			
		||||
    fn source_ranges(&self) -> Vec<SourceRange> {
 | 
			
		||||
        vec![SourceRange([self.start, self.end, self.module_id.as_usize()])]
 | 
			
		||||
        vec![SourceRange::new(self.start, self.end, self.module_id)]
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,7 @@
 | 
			
		||||
use std::collections::HashMap;
 | 
			
		||||
 | 
			
		||||
use async_recursion::async_recursion;
 | 
			
		||||
 | 
			
		||||
use super::{
 | 
			
		||||
    ArrayExpression, ArrayRangeExpression, BinaryExpression, BinaryOperator, BinaryPart, CallExpression,
 | 
			
		||||
    CallExpressionKw, Expr, IfExpression, KclNone, LiteralIdentifier, LiteralValue, MemberExpression, MemberObject,
 | 
			
		||||
@ -7,13 +9,10 @@ use super::{
 | 
			
		||||
};
 | 
			
		||||
use crate::{
 | 
			
		||||
    errors::{KclError, KclErrorDetails},
 | 
			
		||||
    executor::{
 | 
			
		||||
        BodyType, ExecState, ExecutorContext, KclValue, Metadata, SourceRange, StatementKind, TagEngineInfo,
 | 
			
		||||
        TagIdentifier,
 | 
			
		||||
    },
 | 
			
		||||
    executor::{BodyType, ExecState, ExecutorContext, KclValue, Metadata, StatementKind, TagEngineInfo, TagIdentifier},
 | 
			
		||||
    source_range::SourceRange,
 | 
			
		||||
    std::{args::Arg, FunctionKind},
 | 
			
		||||
};
 | 
			
		||||
use async_recursion::async_recursion;
 | 
			
		||||
 | 
			
		||||
const FLOAT_TO_INT_MAX_DELTA: f64 = 0.01;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -3,9 +3,8 @@ use schemars::JsonSchema;
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
use serde_json::Value as JValue;
 | 
			
		||||
 | 
			
		||||
use crate::ast::types::{Expr, Literal};
 | 
			
		||||
 | 
			
		||||
use super::Node;
 | 
			
		||||
use crate::ast::types::{Expr, Literal};
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, Bake)]
 | 
			
		||||
#[databake(path = kcl_lib::ast::types)]
 | 
			
		||||
 | 
			
		||||
@ -4,9 +4,8 @@ use databake::*;
 | 
			
		||||
use schemars::JsonSchema;
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
 | 
			
		||||
use crate::{ast::types::ConstraintLevel, executor::KclValue};
 | 
			
		||||
 | 
			
		||||
use super::Node;
 | 
			
		||||
use crate::{ast::types::ConstraintLevel, executor::KclValue};
 | 
			
		||||
 | 
			
		||||
const KCL_NONE_ID: &str = "KCL_NONE_ID";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,5 @@
 | 
			
		||||
use super::{BinaryPart, BodyItem, Expr, LiteralIdentifier, MemberObject, ModuleId};
 | 
			
		||||
use super::{BinaryPart, BodyItem, Expr, LiteralIdentifier, MemberObject};
 | 
			
		||||
use crate::source_range::ModuleId;
 | 
			
		||||
 | 
			
		||||
impl BodyItem {
 | 
			
		||||
    pub fn module_id(&self) -> ModuleId {
 | 
			
		||||
 | 
			
		||||
@ -18,14 +18,14 @@ use kittycad_modeling_cmds as kcmc;
 | 
			
		||||
use tokio::sync::{mpsc, oneshot, RwLock};
 | 
			
		||||
use tokio_tungstenite::tungstenite::Message as WsMsg;
 | 
			
		||||
 | 
			
		||||
use super::ExecutionKind;
 | 
			
		||||
use crate::{
 | 
			
		||||
    engine::EngineManager,
 | 
			
		||||
    errors::{KclError, KclErrorDetails},
 | 
			
		||||
    executor::{DefaultPlanes, IdGenerator},
 | 
			
		||||
    SourceRange,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
use super::ExecutionKind;
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, PartialEq)]
 | 
			
		||||
enum SocketHealth {
 | 
			
		||||
    Active,
 | 
			
		||||
@ -41,8 +41,8 @@ pub struct EngineConnection {
 | 
			
		||||
    #[allow(dead_code)]
 | 
			
		||||
    tcp_read_handle: Arc<TcpReadHandle>,
 | 
			
		||||
    socket_health: Arc<Mutex<SocketHealth>>,
 | 
			
		||||
    batch: Arc<Mutex<Vec<(WebSocketRequest, crate::executor::SourceRange)>>>,
 | 
			
		||||
    batch_end: Arc<Mutex<IndexMap<uuid::Uuid, (WebSocketRequest, crate::executor::SourceRange)>>>,
 | 
			
		||||
    batch: Arc<Mutex<Vec<(WebSocketRequest, SourceRange)>>>,
 | 
			
		||||
    batch_end: Arc<Mutex<IndexMap<uuid::Uuid, (WebSocketRequest, SourceRange)>>>,
 | 
			
		||||
 | 
			
		||||
    /// The default planes for the scene.
 | 
			
		||||
    default_planes: Arc<RwLock<Option<DefaultPlanes>>>,
 | 
			
		||||
@ -311,11 +311,11 @@ impl EngineConnection {
 | 
			
		||||
 | 
			
		||||
#[async_trait::async_trait]
 | 
			
		||||
impl EngineManager for EngineConnection {
 | 
			
		||||
    fn batch(&self) -> Arc<Mutex<Vec<(WebSocketRequest, crate::executor::SourceRange)>>> {
 | 
			
		||||
    fn batch(&self) -> Arc<Mutex<Vec<(WebSocketRequest, SourceRange)>>> {
 | 
			
		||||
        self.batch.clone()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn batch_end(&self) -> Arc<Mutex<IndexMap<uuid::Uuid, (WebSocketRequest, crate::executor::SourceRange)>>> {
 | 
			
		||||
    fn batch_end(&self) -> Arc<Mutex<IndexMap<uuid::Uuid, (WebSocketRequest, SourceRange)>>> {
 | 
			
		||||
        self.batch_end.clone()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -334,7 +334,7 @@ impl EngineManager for EngineConnection {
 | 
			
		||||
    async fn default_planes(
 | 
			
		||||
        &self,
 | 
			
		||||
        id_generator: &mut IdGenerator,
 | 
			
		||||
        source_range: crate::executor::SourceRange,
 | 
			
		||||
        source_range: SourceRange,
 | 
			
		||||
    ) -> Result<DefaultPlanes, KclError> {
 | 
			
		||||
        {
 | 
			
		||||
            let opt = self.default_planes.read().await.as_ref().cloned();
 | 
			
		||||
@ -352,7 +352,7 @@ impl EngineManager for EngineConnection {
 | 
			
		||||
    async fn clear_scene_post_hook(
 | 
			
		||||
        &self,
 | 
			
		||||
        id_generator: &mut IdGenerator,
 | 
			
		||||
        source_range: crate::executor::SourceRange,
 | 
			
		||||
        source_range: SourceRange,
 | 
			
		||||
    ) -> Result<(), KclError> {
 | 
			
		||||
        // Remake the default planes, since they would have been removed after the scene was cleared.
 | 
			
		||||
        let new_planes = self.new_default_planes(id_generator, source_range).await?;
 | 
			
		||||
@ -364,9 +364,9 @@ impl EngineManager for EngineConnection {
 | 
			
		||||
    async fn inner_send_modeling_cmd(
 | 
			
		||||
        &self,
 | 
			
		||||
        id: uuid::Uuid,
 | 
			
		||||
        source_range: crate::executor::SourceRange,
 | 
			
		||||
        source_range: SourceRange,
 | 
			
		||||
        cmd: WebSocketRequest,
 | 
			
		||||
        _id_to_source_range: std::collections::HashMap<uuid::Uuid, crate::executor::SourceRange>,
 | 
			
		||||
        _id_to_source_range: std::collections::HashMap<uuid::Uuid, SourceRange>,
 | 
			
		||||
    ) -> Result<WebSocketResponse, KclError> {
 | 
			
		||||
        let (tx, rx) = oneshot::channel();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -17,17 +17,17 @@ use kcmc::{
 | 
			
		||||
};
 | 
			
		||||
use kittycad_modeling_cmds::{self as kcmc};
 | 
			
		||||
 | 
			
		||||
use super::ExecutionKind;
 | 
			
		||||
use crate::{
 | 
			
		||||
    errors::KclError,
 | 
			
		||||
    executor::{DefaultPlanes, IdGenerator},
 | 
			
		||||
    SourceRange,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
use super::ExecutionKind;
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone)]
 | 
			
		||||
pub struct EngineConnection {
 | 
			
		||||
    batch: Arc<Mutex<Vec<(WebSocketRequest, crate::executor::SourceRange)>>>,
 | 
			
		||||
    batch_end: Arc<Mutex<IndexMap<uuid::Uuid, (WebSocketRequest, crate::executor::SourceRange)>>>,
 | 
			
		||||
    batch: Arc<Mutex<Vec<(WebSocketRequest, SourceRange)>>>,
 | 
			
		||||
    batch_end: Arc<Mutex<IndexMap<uuid::Uuid, (WebSocketRequest, SourceRange)>>>,
 | 
			
		||||
    execution_kind: Arc<Mutex<ExecutionKind>>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -43,11 +43,11 @@ impl EngineConnection {
 | 
			
		||||
 | 
			
		||||
#[async_trait::async_trait]
 | 
			
		||||
impl crate::engine::EngineManager for EngineConnection {
 | 
			
		||||
    fn batch(&self) -> Arc<Mutex<Vec<(WebSocketRequest, crate::executor::SourceRange)>>> {
 | 
			
		||||
    fn batch(&self) -> Arc<Mutex<Vec<(WebSocketRequest, SourceRange)>>> {
 | 
			
		||||
        self.batch.clone()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn batch_end(&self) -> Arc<Mutex<IndexMap<uuid::Uuid, (WebSocketRequest, crate::executor::SourceRange)>>> {
 | 
			
		||||
    fn batch_end(&self) -> Arc<Mutex<IndexMap<uuid::Uuid, (WebSocketRequest, SourceRange)>>> {
 | 
			
		||||
        self.batch_end.clone()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -66,7 +66,7 @@ impl crate::engine::EngineManager for EngineConnection {
 | 
			
		||||
    async fn default_planes(
 | 
			
		||||
        &self,
 | 
			
		||||
        _id_generator: &mut IdGenerator,
 | 
			
		||||
        _source_range: crate::executor::SourceRange,
 | 
			
		||||
        _source_range: SourceRange,
 | 
			
		||||
    ) -> Result<DefaultPlanes, KclError> {
 | 
			
		||||
        Ok(DefaultPlanes::default())
 | 
			
		||||
    }
 | 
			
		||||
@ -74,7 +74,7 @@ impl crate::engine::EngineManager for EngineConnection {
 | 
			
		||||
    async fn clear_scene_post_hook(
 | 
			
		||||
        &self,
 | 
			
		||||
        _id_generator: &mut IdGenerator,
 | 
			
		||||
        _source_range: crate::executor::SourceRange,
 | 
			
		||||
        _source_range: SourceRange,
 | 
			
		||||
    ) -> Result<(), KclError> {
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
@ -82,9 +82,9 @@ impl crate::engine::EngineManager for EngineConnection {
 | 
			
		||||
    async fn inner_send_modeling_cmd(
 | 
			
		||||
        &self,
 | 
			
		||||
        id: uuid::Uuid,
 | 
			
		||||
        _source_range: crate::executor::SourceRange,
 | 
			
		||||
        _source_range: SourceRange,
 | 
			
		||||
        cmd: WebSocketRequest,
 | 
			
		||||
        _id_to_source_range: std::collections::HashMap<uuid::Uuid, crate::executor::SourceRange>,
 | 
			
		||||
        _id_to_source_range: std::collections::HashMap<uuid::Uuid, SourceRange>,
 | 
			
		||||
    ) -> Result<WebSocketResponse, KclError> {
 | 
			
		||||
        match cmd {
 | 
			
		||||
            WebSocketRequest::ModelingCmdBatchReq(ModelingBatch {
 | 
			
		||||
 | 
			
		||||
@ -12,6 +12,7 @@ use crate::{
 | 
			
		||||
    engine::ExecutionKind,
 | 
			
		||||
    errors::{KclError, KclErrorDetails},
 | 
			
		||||
    executor::{DefaultPlanes, IdGenerator},
 | 
			
		||||
    SourceRange,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#[wasm_bindgen(module = "/../../lang/std/engineConnection.ts")]
 | 
			
		||||
@ -41,8 +42,8 @@ extern "C" {
 | 
			
		||||
#[derive(Debug, Clone)]
 | 
			
		||||
pub struct EngineConnection {
 | 
			
		||||
    manager: Arc<EngineCommandManager>,
 | 
			
		||||
    batch: Arc<Mutex<Vec<(WebSocketRequest, crate::executor::SourceRange)>>>,
 | 
			
		||||
    batch_end: Arc<Mutex<IndexMap<uuid::Uuid, (WebSocketRequest, crate::executor::SourceRange)>>>,
 | 
			
		||||
    batch: Arc<Mutex<Vec<(WebSocketRequest, SourceRange)>>>,
 | 
			
		||||
    batch_end: Arc<Mutex<IndexMap<uuid::Uuid, (WebSocketRequest, SourceRange)>>>,
 | 
			
		||||
    execution_kind: Arc<Mutex<ExecutionKind>>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -63,11 +64,11 @@ impl EngineConnection {
 | 
			
		||||
 | 
			
		||||
#[async_trait::async_trait]
 | 
			
		||||
impl crate::engine::EngineManager for EngineConnection {
 | 
			
		||||
    fn batch(&self) -> Arc<Mutex<Vec<(WebSocketRequest, crate::executor::SourceRange)>>> {
 | 
			
		||||
    fn batch(&self) -> Arc<Mutex<Vec<(WebSocketRequest, SourceRange)>>> {
 | 
			
		||||
        self.batch.clone()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn batch_end(&self) -> Arc<Mutex<IndexMap<uuid::Uuid, (WebSocketRequest, crate::executor::SourceRange)>>> {
 | 
			
		||||
    fn batch_end(&self) -> Arc<Mutex<IndexMap<uuid::Uuid, (WebSocketRequest, SourceRange)>>> {
 | 
			
		||||
        self.batch_end.clone()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -86,7 +87,7 @@ impl crate::engine::EngineManager for EngineConnection {
 | 
			
		||||
    async fn default_planes(
 | 
			
		||||
        &self,
 | 
			
		||||
        _id_generator: &mut IdGenerator,
 | 
			
		||||
        source_range: crate::executor::SourceRange,
 | 
			
		||||
        source_range: SourceRange,
 | 
			
		||||
    ) -> Result<DefaultPlanes, KclError> {
 | 
			
		||||
        // Get the default planes.
 | 
			
		||||
        let promise = self.manager.get_default_planes().map_err(|e| {
 | 
			
		||||
@ -128,7 +129,7 @@ impl crate::engine::EngineManager for EngineConnection {
 | 
			
		||||
    async fn clear_scene_post_hook(
 | 
			
		||||
        &self,
 | 
			
		||||
        _id_generator: &mut IdGenerator,
 | 
			
		||||
        source_range: crate::executor::SourceRange,
 | 
			
		||||
        source_range: SourceRange,
 | 
			
		||||
    ) -> Result<(), KclError> {
 | 
			
		||||
        self.manager.clear_default_planes().map_err(|e| {
 | 
			
		||||
            KclError::Engine(KclErrorDetails {
 | 
			
		||||
@ -158,9 +159,9 @@ impl crate::engine::EngineManager for EngineConnection {
 | 
			
		||||
    async fn inner_send_modeling_cmd(
 | 
			
		||||
        &self,
 | 
			
		||||
        id: uuid::Uuid,
 | 
			
		||||
        source_range: crate::executor::SourceRange,
 | 
			
		||||
        source_range: SourceRange,
 | 
			
		||||
        cmd: WebSocketRequest,
 | 
			
		||||
        id_to_source_range: std::collections::HashMap<uuid::Uuid, crate::executor::SourceRange>,
 | 
			
		||||
        id_to_source_range: std::collections::HashMap<uuid::Uuid, SourceRange>,
 | 
			
		||||
    ) -> Result<WebSocketResponse, KclError> {
 | 
			
		||||
        let source_range_str = serde_json::to_string(&source_range).map_err(|e| {
 | 
			
		||||
            KclError::Engine(KclErrorDetails {
 | 
			
		||||
 | 
			
		||||
@ -33,6 +33,7 @@ use uuid::Uuid;
 | 
			
		||||
use crate::{
 | 
			
		||||
    errors::{KclError, KclErrorDetails},
 | 
			
		||||
    executor::{DefaultPlanes, IdGenerator, Point3d},
 | 
			
		||||
    SourceRange,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
lazy_static::lazy_static! {
 | 
			
		||||
@ -61,10 +62,10 @@ impl ExecutionKind {
 | 
			
		||||
#[async_trait::async_trait]
 | 
			
		||||
pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
 | 
			
		||||
    /// Get the batch of commands to be sent to the engine.
 | 
			
		||||
    fn batch(&self) -> Arc<Mutex<Vec<(WebSocketRequest, crate::executor::SourceRange)>>>;
 | 
			
		||||
    fn batch(&self) -> Arc<Mutex<Vec<(WebSocketRequest, SourceRange)>>>;
 | 
			
		||||
 | 
			
		||||
    /// Get the batch of end commands to be sent to the engine.
 | 
			
		||||
    fn batch_end(&self) -> Arc<Mutex<IndexMap<uuid::Uuid, (WebSocketRequest, crate::executor::SourceRange)>>>;
 | 
			
		||||
    fn batch_end(&self) -> Arc<Mutex<IndexMap<uuid::Uuid, (WebSocketRequest, SourceRange)>>>;
 | 
			
		||||
 | 
			
		||||
    /// Get the current execution kind.
 | 
			
		||||
    fn execution_kind(&self) -> ExecutionKind;
 | 
			
		||||
@ -77,7 +78,7 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
 | 
			
		||||
    async fn default_planes(
 | 
			
		||||
        &self,
 | 
			
		||||
        id_generator: &mut IdGenerator,
 | 
			
		||||
        _source_range: crate::executor::SourceRange,
 | 
			
		||||
        _source_range: SourceRange,
 | 
			
		||||
    ) -> Result<DefaultPlanes, crate::errors::KclError>;
 | 
			
		||||
 | 
			
		||||
    /// Helpers to be called after clearing a scene.
 | 
			
		||||
@ -85,22 +86,22 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
 | 
			
		||||
    async fn clear_scene_post_hook(
 | 
			
		||||
        &self,
 | 
			
		||||
        id_generator: &mut IdGenerator,
 | 
			
		||||
        source_range: crate::executor::SourceRange,
 | 
			
		||||
        source_range: SourceRange,
 | 
			
		||||
    ) -> Result<(), crate::errors::KclError>;
 | 
			
		||||
 | 
			
		||||
    /// Send a modeling command and wait for the response message.
 | 
			
		||||
    async fn inner_send_modeling_cmd(
 | 
			
		||||
        &self,
 | 
			
		||||
        id: uuid::Uuid,
 | 
			
		||||
        source_range: crate::executor::SourceRange,
 | 
			
		||||
        source_range: SourceRange,
 | 
			
		||||
        cmd: WebSocketRequest,
 | 
			
		||||
        id_to_source_range: HashMap<uuid::Uuid, crate::executor::SourceRange>,
 | 
			
		||||
        id_to_source_range: HashMap<uuid::Uuid, SourceRange>,
 | 
			
		||||
    ) -> Result<kcmc::websocket::WebSocketResponse, crate::errors::KclError>;
 | 
			
		||||
 | 
			
		||||
    async fn clear_scene(
 | 
			
		||||
        &self,
 | 
			
		||||
        id_generator: &mut IdGenerator,
 | 
			
		||||
        source_range: crate::executor::SourceRange,
 | 
			
		||||
        source_range: SourceRange,
 | 
			
		||||
    ) -> Result<(), crate::errors::KclError> {
 | 
			
		||||
        self.batch_modeling_cmd(
 | 
			
		||||
            uuid::Uuid::new_v4(),
 | 
			
		||||
@ -123,7 +124,7 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
 | 
			
		||||
    async fn batch_modeling_cmd(
 | 
			
		||||
        &self,
 | 
			
		||||
        id: uuid::Uuid,
 | 
			
		||||
        source_range: crate::executor::SourceRange,
 | 
			
		||||
        source_range: SourceRange,
 | 
			
		||||
        cmd: &ModelingCmd,
 | 
			
		||||
    ) -> Result<(), crate::errors::KclError> {
 | 
			
		||||
        let execution_kind = self.execution_kind();
 | 
			
		||||
@ -147,7 +148,7 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
 | 
			
		||||
    async fn batch_end_cmd(
 | 
			
		||||
        &self,
 | 
			
		||||
        id: uuid::Uuid,
 | 
			
		||||
        source_range: crate::executor::SourceRange,
 | 
			
		||||
        source_range: SourceRange,
 | 
			
		||||
        cmd: &ModelingCmd,
 | 
			
		||||
    ) -> Result<(), crate::errors::KclError> {
 | 
			
		||||
        let req = WebSocketRequest::ModelingCmdReq(ModelingCmdReq {
 | 
			
		||||
@ -166,7 +167,7 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
 | 
			
		||||
    async fn send_modeling_cmd(
 | 
			
		||||
        &self,
 | 
			
		||||
        id: uuid::Uuid,
 | 
			
		||||
        source_range: crate::executor::SourceRange,
 | 
			
		||||
        source_range: SourceRange,
 | 
			
		||||
        cmd: ModelingCmd,
 | 
			
		||||
    ) -> Result<OkWebSocketResponseData, crate::errors::KclError> {
 | 
			
		||||
        self.batch_modeling_cmd(id, source_range, &cmd).await?;
 | 
			
		||||
@ -181,7 +182,7 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
 | 
			
		||||
        // Whether or not to flush the end commands as well.
 | 
			
		||||
        // We only do this at the very end of the file.
 | 
			
		||||
        batch_end: bool,
 | 
			
		||||
        source_range: crate::executor::SourceRange,
 | 
			
		||||
        source_range: SourceRange,
 | 
			
		||||
    ) -> Result<OkWebSocketResponseData, crate::errors::KclError> {
 | 
			
		||||
        let all_requests = if batch_end {
 | 
			
		||||
            let mut requests = self.batch().lock().unwrap().clone();
 | 
			
		||||
@ -303,7 +304,7 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
 | 
			
		||||
        x_axis: Point3d,
 | 
			
		||||
        y_axis: Point3d,
 | 
			
		||||
        color: Option<Color>,
 | 
			
		||||
        source_range: crate::executor::SourceRange,
 | 
			
		||||
        source_range: SourceRange,
 | 
			
		||||
    ) -> Result<uuid::Uuid, KclError> {
 | 
			
		||||
        // Create new default planes.
 | 
			
		||||
        let default_size = 100.0;
 | 
			
		||||
@ -339,7 +340,7 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
 | 
			
		||||
    async fn new_default_planes(
 | 
			
		||||
        &self,
 | 
			
		||||
        id_generator: &mut IdGenerator,
 | 
			
		||||
        source_range: crate::executor::SourceRange,
 | 
			
		||||
        source_range: SourceRange,
 | 
			
		||||
    ) -> Result<DefaultPlanes, KclError> {
 | 
			
		||||
        let plane_settings: HashMap<PlaneName, (Uuid, Point3d, Point3d, Option<Color>)> = HashMap::from([
 | 
			
		||||
            (
 | 
			
		||||
@ -450,7 +451,7 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
 | 
			
		||||
    fn parse_websocket_response(
 | 
			
		||||
        &self,
 | 
			
		||||
        response: WebSocketResponse,
 | 
			
		||||
        source_range: crate::executor::SourceRange,
 | 
			
		||||
        source_range: SourceRange,
 | 
			
		||||
    ) -> Result<OkWebSocketResponseData, crate::errors::KclError> {
 | 
			
		||||
        match response {
 | 
			
		||||
            WebSocketResponse::Success(success) => Ok(success.resp),
 | 
			
		||||
@ -469,7 +470,7 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
 | 
			
		||||
        // The last response we are looking for.
 | 
			
		||||
        id: uuid::Uuid,
 | 
			
		||||
        // The mapping of source ranges to command IDs.
 | 
			
		||||
        id_to_source_range: HashMap<uuid::Uuid, crate::executor::SourceRange>,
 | 
			
		||||
        id_to_source_range: HashMap<uuid::Uuid, SourceRange>,
 | 
			
		||||
        // The response from the engine.
 | 
			
		||||
        responses: HashMap<uuid::Uuid, BatchResponse>,
 | 
			
		||||
    ) -> Result<OkWebSocketResponseData, crate::errors::KclError> {
 | 
			
		||||
 | 
			
		||||
@ -2,7 +2,10 @@ use serde::{Deserialize, Serialize};
 | 
			
		||||
use thiserror::Error;
 | 
			
		||||
use tower_lsp::lsp_types::{Diagnostic, DiagnosticSeverity};
 | 
			
		||||
 | 
			
		||||
use crate::{ast::types::ModuleId, executor::SourceRange, lsp::IntoDiagnostic};
 | 
			
		||||
use crate::{
 | 
			
		||||
    lsp::IntoDiagnostic,
 | 
			
		||||
    source_range::{ModuleId, SourceRange},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/// How did the KCL execution fail
 | 
			
		||||
#[derive(thiserror::Error, Debug)]
 | 
			
		||||
 | 
			
		||||
@ -19,20 +19,18 @@ use kittycad_modeling_cmds::length_unit::LengthUnit;
 | 
			
		||||
use parse_display::{Display, FromStr};
 | 
			
		||||
use schemars::JsonSchema;
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
use tower_lsp::lsp_types::{Position as LspPosition, Range as LspRange};
 | 
			
		||||
 | 
			
		||||
type Point2D = kcmc::shared::Point2d<f64>;
 | 
			
		||||
type Point3D = kcmc::shared::Point3d<f64>;
 | 
			
		||||
 | 
			
		||||
pub use crate::kcl_value::KclValue;
 | 
			
		||||
use crate::{
 | 
			
		||||
    ast::types::{
 | 
			
		||||
        BodyItem, Expr, FunctionExpression, ItemVisibility, KclNone, ModuleId, Node, NodeRef, TagDeclarator, TagNode,
 | 
			
		||||
    },
 | 
			
		||||
    ast::types::{BodyItem, Expr, FunctionExpression, ItemVisibility, KclNone, Node, NodeRef, TagDeclarator, TagNode},
 | 
			
		||||
    engine::{EngineManager, ExecutionKind},
 | 
			
		||||
    errors::{KclError, KclErrorDetails},
 | 
			
		||||
    fs::{FileManager, FileSystem},
 | 
			
		||||
    settings::types::UnitLength,
 | 
			
		||||
    source_range::{ModuleId, SourceRange},
 | 
			
		||||
    std::{args::Arg, StdLib},
 | 
			
		||||
    ExecError, Program,
 | 
			
		||||
};
 | 
			
		||||
@ -1002,101 +1000,6 @@ pub struct ModuleInfo {
 | 
			
		||||
    path: std::path::PathBuf,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Default, Deserialize, Serialize, PartialEq, Copy, Clone, ts_rs::TS, JsonSchema, Hash, Eq)]
 | 
			
		||||
#[cfg_attr(feature = "pyo3", pyo3::pyclass)]
 | 
			
		||||
#[ts(export)]
 | 
			
		||||
pub struct SourceRange(#[ts(type = "[number, number]")] pub [usize; 3]);
 | 
			
		||||
 | 
			
		||||
impl From<[usize; 3]> for SourceRange {
 | 
			
		||||
    fn from(value: [usize; 3]) -> Self {
 | 
			
		||||
        Self(value)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<&SourceRange> for miette::SourceSpan {
 | 
			
		||||
    fn from(source_range: &SourceRange) -> Self {
 | 
			
		||||
        let length = source_range.end() - source_range.start();
 | 
			
		||||
        let start = miette::SourceOffset::from(source_range.start());
 | 
			
		||||
        Self::new(start, length)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<SourceRange> for miette::SourceSpan {
 | 
			
		||||
    fn from(source_range: SourceRange) -> Self {
 | 
			
		||||
        Self::from(&source_range)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl SourceRange {
 | 
			
		||||
    /// Create a new source range.
 | 
			
		||||
    pub fn new(start: usize, end: usize, module_id: ModuleId) -> Self {
 | 
			
		||||
        Self([start, end, module_id.as_usize()])
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// A source range that doesn't correspond to any source code.
 | 
			
		||||
    pub fn synthetic() -> Self {
 | 
			
		||||
        Self::default()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Get the start of the range.
 | 
			
		||||
    pub fn start(&self) -> usize {
 | 
			
		||||
        self.0[0]
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Get the end of the range.
 | 
			
		||||
    pub fn end(&self) -> usize {
 | 
			
		||||
        self.0[1]
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Get the module ID of the range.
 | 
			
		||||
    pub fn module_id(&self) -> ModuleId {
 | 
			
		||||
        ModuleId::from_usize(self.0[2])
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Check if the range contains a position.
 | 
			
		||||
    pub fn contains(&self, pos: usize) -> bool {
 | 
			
		||||
        pos >= self.start() && pos <= self.end()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn start_to_lsp_position(&self, code: &str) -> LspPosition {
 | 
			
		||||
        // Calculate the line and column of the error from the source range.
 | 
			
		||||
        // Lines are zero indexed in vscode so we need to subtract 1.
 | 
			
		||||
        let mut line = code.get(..self.start()).unwrap_or_default().lines().count();
 | 
			
		||||
        if line > 0 {
 | 
			
		||||
            line = line.saturating_sub(1);
 | 
			
		||||
        }
 | 
			
		||||
        let column = code[..self.start()].lines().last().map(|l| l.len()).unwrap_or_default();
 | 
			
		||||
 | 
			
		||||
        LspPosition {
 | 
			
		||||
            line: line as u32,
 | 
			
		||||
            character: column as u32,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn end_to_lsp_position(&self, code: &str) -> LspPosition {
 | 
			
		||||
        let lines = code.get(..self.end()).unwrap_or_default().lines();
 | 
			
		||||
        if lines.clone().count() == 0 {
 | 
			
		||||
            return LspPosition { line: 0, character: 0 };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Calculate the line and column of the error from the source range.
 | 
			
		||||
        // Lines are zero indexed in vscode so we need to subtract 1.
 | 
			
		||||
        let line = lines.clone().count() - 1;
 | 
			
		||||
        let column = lines.last().map(|l| l.len()).unwrap_or_default();
 | 
			
		||||
 | 
			
		||||
        LspPosition {
 | 
			
		||||
            line: line as u32,
 | 
			
		||||
            character: column as u32,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn to_lsp_range(&self, code: &str) -> LspRange {
 | 
			
		||||
        let start = self.start_to_lsp_position(code);
 | 
			
		||||
        let end = self.end_to_lsp_position(code);
 | 
			
		||||
        LspRange { start, end }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Deserialize, Serialize, PartialEq, Clone, Copy, ts_rs::TS, JsonSchema)]
 | 
			
		||||
#[ts(export)]
 | 
			
		||||
pub struct Point2d {
 | 
			
		||||
@ -2109,7 +2012,7 @@ impl ExecutorContext {
 | 
			
		||||
                    // True here tells the engine to flush all the end commands as well like fillets
 | 
			
		||||
                    // and chamfers where the engine would otherwise eat the ID of the segments.
 | 
			
		||||
                    true,
 | 
			
		||||
                    SourceRange([program.end, program.end, program.module_id.as_usize()]),
 | 
			
		||||
                    SourceRange::new(program.end, program.end, program.module_id),
 | 
			
		||||
                )
 | 
			
		||||
                .await?;
 | 
			
		||||
        }
 | 
			
		||||
@ -2715,7 +2618,10 @@ const answer = returnX()"#;
 | 
			
		||||
            err,
 | 
			
		||||
            KclError::UndefinedValue(KclErrorDetails {
 | 
			
		||||
                message: "memory item key `x` is not defined".to_owned(),
 | 
			
		||||
                source_ranges: vec![SourceRange([64, 65, 0]), SourceRange([97, 106, 0])],
 | 
			
		||||
                source_ranges: vec![
 | 
			
		||||
                    SourceRange::new(64, 65, ModuleId::default()),
 | 
			
		||||
                    SourceRange::new(97, 106, ModuleId::default())
 | 
			
		||||
                ],
 | 
			
		||||
            }),
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
@ -2750,7 +2656,7 @@ let shape = layer() |> patternTransform(10, transform, %)
 | 
			
		||||
            err,
 | 
			
		||||
            KclError::UndefinedValue(KclErrorDetails {
 | 
			
		||||
                message: "memory item key `x` is not defined".to_owned(),
 | 
			
		||||
                source_ranges: vec![SourceRange([80, 81, 0])],
 | 
			
		||||
                source_ranges: vec![SourceRange::new(80, 81, ModuleId::default())],
 | 
			
		||||
            }),
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
@ -2846,7 +2752,7 @@ let notNull = !myNull
 | 
			
		||||
            parse_execute(code1).await.unwrap_err().downcast::<KclError>().unwrap(),
 | 
			
		||||
            KclError::Semantic(KclErrorDetails {
 | 
			
		||||
                message: "Cannot apply unary operator ! to non-boolean value: number".to_owned(),
 | 
			
		||||
                source_ranges: vec![SourceRange([56, 63, 0])],
 | 
			
		||||
                source_ranges: vec![SourceRange::new(56, 63, ModuleId::default())],
 | 
			
		||||
            })
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
@ -2855,7 +2761,7 @@ let notNull = !myNull
 | 
			
		||||
            parse_execute(code2).await.unwrap_err().downcast::<KclError>().unwrap(),
 | 
			
		||||
            KclError::Semantic(KclErrorDetails {
 | 
			
		||||
                message: "Cannot apply unary operator ! to non-boolean value: number".to_owned(),
 | 
			
		||||
                source_ranges: vec![SourceRange([14, 16, 0])],
 | 
			
		||||
                source_ranges: vec![SourceRange::new(14, 16, ModuleId::default())],
 | 
			
		||||
            })
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
@ -2866,7 +2772,7 @@ let notEmptyString = !""
 | 
			
		||||
            parse_execute(code3).await.unwrap_err().downcast::<KclError>().unwrap(),
 | 
			
		||||
            KclError::Semantic(KclErrorDetails {
 | 
			
		||||
                message: "Cannot apply unary operator ! to non-boolean value: string (text)".to_owned(),
 | 
			
		||||
                source_ranges: vec![SourceRange([22, 25, 0])],
 | 
			
		||||
                source_ranges: vec![SourceRange::new(22, 25, ModuleId::default())],
 | 
			
		||||
            })
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
@ -2878,7 +2784,7 @@ let notMember = !obj.a
 | 
			
		||||
            parse_execute(code4).await.unwrap_err().downcast::<KclError>().unwrap(),
 | 
			
		||||
            KclError::Semantic(KclErrorDetails {
 | 
			
		||||
                message: "Cannot apply unary operator ! to non-boolean value: number".to_owned(),
 | 
			
		||||
                source_ranges: vec![SourceRange([36, 42, 0])],
 | 
			
		||||
                source_ranges: vec![SourceRange::new(36, 42, ModuleId::default())],
 | 
			
		||||
            })
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
@ -2889,7 +2795,7 @@ let notArray = !a";
 | 
			
		||||
            parse_execute(code5).await.unwrap_err().downcast::<KclError>().unwrap(),
 | 
			
		||||
            KclError::Semantic(KclErrorDetails {
 | 
			
		||||
                message: "Cannot apply unary operator ! to non-boolean value: array (list)".to_owned(),
 | 
			
		||||
                source_ranges: vec![SourceRange([27, 29, 0])],
 | 
			
		||||
                source_ranges: vec![SourceRange::new(27, 29, ModuleId::default())],
 | 
			
		||||
            })
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
@ -2900,7 +2806,7 @@ let notObject = !x";
 | 
			
		||||
            parse_execute(code6).await.unwrap_err().downcast::<KclError>().unwrap(),
 | 
			
		||||
            KclError::Semantic(KclErrorDetails {
 | 
			
		||||
                message: "Cannot apply unary operator ! to non-boolean value: object".to_owned(),
 | 
			
		||||
                source_ranges: vec![SourceRange([28, 30, 0])],
 | 
			
		||||
                source_ranges: vec![SourceRange::new(28, 30, ModuleId::default())],
 | 
			
		||||
            })
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
@ -2953,7 +2859,7 @@ let notTagIdentifier = !myTag";
 | 
			
		||||
            parse_execute(code10).await.unwrap_err().downcast::<KclError>().unwrap(),
 | 
			
		||||
            KclError::Syntax(KclErrorDetails {
 | 
			
		||||
                message: "Unexpected token: !".to_owned(),
 | 
			
		||||
                source_ranges: vec![SourceRange([14, 15, 0])],
 | 
			
		||||
                source_ranges: vec![SourceRange::new(14, 15, ModuleId::default())],
 | 
			
		||||
            })
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
@ -2966,7 +2872,7 @@ let notPipeSub = 1 |> identity(!%))";
 | 
			
		||||
            parse_execute(code11).await.unwrap_err().downcast::<KclError>().unwrap(),
 | 
			
		||||
            KclError::Syntax(KclErrorDetails {
 | 
			
		||||
                message: "Unexpected token: |>".to_owned(),
 | 
			
		||||
                source_ranges: vec![SourceRange([54, 56, 0])],
 | 
			
		||||
                source_ranges: vec![SourceRange::new(54, 56, ModuleId::default())],
 | 
			
		||||
            })
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
@ -3010,10 +2916,10 @@ test([0, 0])
 | 
			
		||||
"#;
 | 
			
		||||
        let result = parse_execute(ast).await;
 | 
			
		||||
        assert!(result.is_err());
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            result.unwrap_err().to_string(),
 | 
			
		||||
            r#"undefined value: KclErrorDetails { source_ranges: [SourceRange([10, 34, 0])], message: "Result of user-defined function test is undefined" }"#.to_owned()
 | 
			
		||||
        );
 | 
			
		||||
        assert!(result
 | 
			
		||||
            .unwrap_err()
 | 
			
		||||
            .to_string()
 | 
			
		||||
            .contains("Result of user-defined function test is undefined"),);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[tokio::test(flavor = "multi_thread")]
 | 
			
		||||
@ -3129,7 +3035,7 @@ let w = f() + f()
 | 
			
		||||
                vec![req_param("x")],
 | 
			
		||||
                vec![],
 | 
			
		||||
                Err(KclError::Semantic(KclErrorDetails {
 | 
			
		||||
                    source_ranges: vec![SourceRange([0, 0, 0])],
 | 
			
		||||
                    source_ranges: vec![SourceRange::default()],
 | 
			
		||||
                    message: "Expected 1 arguments, got 0".to_owned(),
 | 
			
		||||
                })),
 | 
			
		||||
            ),
 | 
			
		||||
@ -3147,7 +3053,7 @@ let w = f() + f()
 | 
			
		||||
                vec![req_param("x"), opt_param("y")],
 | 
			
		||||
                vec![],
 | 
			
		||||
                Err(KclError::Semantic(KclErrorDetails {
 | 
			
		||||
                    source_ranges: vec![SourceRange([0, 0, 0])],
 | 
			
		||||
                    source_ranges: vec![SourceRange::default()],
 | 
			
		||||
                    message: "Expected 1-2 arguments, got 0".to_owned(),
 | 
			
		||||
                })),
 | 
			
		||||
            ),
 | 
			
		||||
@ -3174,7 +3080,7 @@ let w = f() + f()
 | 
			
		||||
                vec![req_param("x"), opt_param("y")],
 | 
			
		||||
                vec![mem(1), mem(2), mem(3)],
 | 
			
		||||
                Err(KclError::Semantic(KclErrorDetails {
 | 
			
		||||
                    source_ranges: vec![SourceRange([0, 0, 0])],
 | 
			
		||||
                    source_ranges: vec![SourceRange::default()],
 | 
			
		||||
                    message: "Expected 1-2 arguments, got 3".to_owned(),
 | 
			
		||||
                })),
 | 
			
		||||
            ),
 | 
			
		||||
 | 
			
		||||
@ -5,6 +5,7 @@ use anyhow::Result;
 | 
			
		||||
use crate::{
 | 
			
		||||
    errors::{KclError, KclErrorDetails},
 | 
			
		||||
    fs::FileSystem,
 | 
			
		||||
    SourceRange,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone)]
 | 
			
		||||
@ -27,7 +28,7 @@ impl FileSystem for FileManager {
 | 
			
		||||
    async fn read<P: AsRef<std::path::Path> + std::marker::Send + std::marker::Sync>(
 | 
			
		||||
        &self,
 | 
			
		||||
        path: P,
 | 
			
		||||
        source_range: crate::executor::SourceRange,
 | 
			
		||||
        source_range: SourceRange,
 | 
			
		||||
    ) -> Result<Vec<u8>, KclError> {
 | 
			
		||||
        tokio::fs::read(&path).await.map_err(|e| {
 | 
			
		||||
            KclError::Engine(KclErrorDetails {
 | 
			
		||||
@ -40,7 +41,7 @@ impl FileSystem for FileManager {
 | 
			
		||||
    async fn read_to_string<P: AsRef<std::path::Path> + std::marker::Send + std::marker::Sync>(
 | 
			
		||||
        &self,
 | 
			
		||||
        path: P,
 | 
			
		||||
        source_range: crate::executor::SourceRange,
 | 
			
		||||
        source_range: SourceRange,
 | 
			
		||||
    ) -> Result<String, KclError> {
 | 
			
		||||
        tokio::fs::read_to_string(&path).await.map_err(|e| {
 | 
			
		||||
            KclError::Engine(KclErrorDetails {
 | 
			
		||||
@ -53,7 +54,7 @@ impl FileSystem for FileManager {
 | 
			
		||||
    async fn exists<P: AsRef<std::path::Path> + std::marker::Send + std::marker::Sync>(
 | 
			
		||||
        &self,
 | 
			
		||||
        path: P,
 | 
			
		||||
        source_range: crate::executor::SourceRange,
 | 
			
		||||
        source_range: SourceRange,
 | 
			
		||||
    ) -> Result<bool, crate::errors::KclError> {
 | 
			
		||||
        tokio::fs::metadata(&path).await.map(|_| true).or_else(|e| {
 | 
			
		||||
            if e.kind() == std::io::ErrorKind::NotFound {
 | 
			
		||||
@ -70,7 +71,7 @@ impl FileSystem for FileManager {
 | 
			
		||||
    async fn get_all_files<P: AsRef<std::path::Path> + std::marker::Send + std::marker::Sync>(
 | 
			
		||||
        &self,
 | 
			
		||||
        path: P,
 | 
			
		||||
        source_range: crate::executor::SourceRange,
 | 
			
		||||
        source_range: SourceRange,
 | 
			
		||||
    ) -> Result<Vec<std::path::PathBuf>, crate::errors::KclError> {
 | 
			
		||||
        let mut files = vec![];
 | 
			
		||||
        let mut stack = vec![path.as_ref().to_path_buf()];
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,9 @@
 | 
			
		||||
//! Functions for interacting with files on a machine.
 | 
			
		||||
 | 
			
		||||
use anyhow::Result;
 | 
			
		||||
 | 
			
		||||
use crate::SourceRange;
 | 
			
		||||
 | 
			
		||||
#[cfg(not(target_arch = "wasm32"))]
 | 
			
		||||
pub mod local;
 | 
			
		||||
#[cfg(not(target_arch = "wasm32"))]
 | 
			
		||||
@ -9,7 +13,6 @@ pub use local::FileManager;
 | 
			
		||||
#[cfg(not(test))]
 | 
			
		||||
pub mod wasm;
 | 
			
		||||
 | 
			
		||||
use anyhow::Result;
 | 
			
		||||
#[cfg(target_arch = "wasm32")]
 | 
			
		||||
#[cfg(not(test))]
 | 
			
		||||
pub use wasm::FileManager;
 | 
			
		||||
@ -20,27 +23,27 @@ pub trait FileSystem: Clone {
 | 
			
		||||
    async fn read<P: AsRef<std::path::Path> + std::marker::Send + std::marker::Sync>(
 | 
			
		||||
        &self,
 | 
			
		||||
        path: P,
 | 
			
		||||
        source_range: crate::executor::SourceRange,
 | 
			
		||||
        source_range: SourceRange,
 | 
			
		||||
    ) -> Result<Vec<u8>, crate::errors::KclError>;
 | 
			
		||||
 | 
			
		||||
    /// Read a file from the local file system.
 | 
			
		||||
    async fn read_to_string<P: AsRef<std::path::Path> + std::marker::Send + std::marker::Sync>(
 | 
			
		||||
        &self,
 | 
			
		||||
        path: P,
 | 
			
		||||
        source_range: crate::executor::SourceRange,
 | 
			
		||||
        source_range: SourceRange,
 | 
			
		||||
    ) -> Result<String, crate::errors::KclError>;
 | 
			
		||||
 | 
			
		||||
    /// Check if a file exists on the local file system.
 | 
			
		||||
    async fn exists<P: AsRef<std::path::Path> + std::marker::Send + std::marker::Sync>(
 | 
			
		||||
        &self,
 | 
			
		||||
        path: P,
 | 
			
		||||
        source_range: crate::executor::SourceRange,
 | 
			
		||||
        source_range: SourceRange,
 | 
			
		||||
    ) -> Result<bool, crate::errors::KclError>;
 | 
			
		||||
 | 
			
		||||
    /// Get all the files in a directory recursively.
 | 
			
		||||
    async fn get_all_files<P: AsRef<std::path::Path> + std::marker::Send + std::marker::Sync>(
 | 
			
		||||
        &self,
 | 
			
		||||
        path: P,
 | 
			
		||||
        source_range: crate::executor::SourceRange,
 | 
			
		||||
        source_range: SourceRange,
 | 
			
		||||
    ) -> Result<Vec<std::path::PathBuf>, crate::errors::KclError>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -7,6 +7,7 @@ use crate::{
 | 
			
		||||
    errors::{KclError, KclErrorDetails},
 | 
			
		||||
    fs::FileSystem,
 | 
			
		||||
    wasm::JsFuture,
 | 
			
		||||
    SourceRange,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#[wasm_bindgen(module = "/../../lang/std/fileSystemManager.ts")]
 | 
			
		||||
@ -43,7 +44,7 @@ impl FileSystem for FileManager {
 | 
			
		||||
    async fn read<P: AsRef<std::path::Path> + std::marker::Send + std::marker::Sync>(
 | 
			
		||||
        &self,
 | 
			
		||||
        path: P,
 | 
			
		||||
        source_range: crate::executor::SourceRange,
 | 
			
		||||
        source_range: SourceRange,
 | 
			
		||||
    ) -> Result<Vec<u8>, KclError> {
 | 
			
		||||
        let promise = self
 | 
			
		||||
            .manager
 | 
			
		||||
@ -81,7 +82,7 @@ impl FileSystem for FileManager {
 | 
			
		||||
    async fn read_to_string<P: AsRef<std::path::Path> + std::marker::Send + std::marker::Sync>(
 | 
			
		||||
        &self,
 | 
			
		||||
        path: P,
 | 
			
		||||
        source_range: crate::executor::SourceRange,
 | 
			
		||||
        source_range: SourceRange,
 | 
			
		||||
    ) -> Result<String, KclError> {
 | 
			
		||||
        let bytes = self.read(path, source_range).await?;
 | 
			
		||||
        let string = String::from_utf8(bytes).map_err(|e| {
 | 
			
		||||
@ -97,7 +98,7 @@ impl FileSystem for FileManager {
 | 
			
		||||
    async fn exists<P: AsRef<std::path::Path> + std::marker::Send + std::marker::Sync>(
 | 
			
		||||
        &self,
 | 
			
		||||
        path: P,
 | 
			
		||||
        source_range: crate::executor::SourceRange,
 | 
			
		||||
        source_range: SourceRange,
 | 
			
		||||
    ) -> Result<bool, crate::errors::KclError> {
 | 
			
		||||
        let promise = self
 | 
			
		||||
            .manager
 | 
			
		||||
@ -139,7 +140,7 @@ impl FileSystem for FileManager {
 | 
			
		||||
    async fn get_all_files<P: AsRef<std::path::Path> + std::marker::Send + std::marker::Sync>(
 | 
			
		||||
        &self,
 | 
			
		||||
        path: P,
 | 
			
		||||
        source_range: crate::executor::SourceRange,
 | 
			
		||||
        source_range: SourceRange,
 | 
			
		||||
    ) -> Result<Vec<std::path::PathBuf>, crate::errors::KclError> {
 | 
			
		||||
        let promise = self
 | 
			
		||||
            .manager
 | 
			
		||||
 | 
			
		||||
@ -123,7 +123,7 @@ impl From<Vec<Box<Solid>>> for KclValue {
 | 
			
		||||
impl From<KclValue> for Vec<SourceRange> {
 | 
			
		||||
    fn from(item: KclValue) -> Self {
 | 
			
		||||
        match item {
 | 
			
		||||
            KclValue::TagDeclarator(t) => vec![SourceRange([t.start, t.end, t.module_id.0 as usize])],
 | 
			
		||||
            KclValue::TagDeclarator(t) => vec![SourceRange::new(t.start, t.end, t.module_id)],
 | 
			
		||||
            KclValue::TagIdentifier(t) => to_vec_sr(&t.meta),
 | 
			
		||||
            KclValue::Solid(e) => to_vec_sr(&e.meta),
 | 
			
		||||
            KclValue::Solids { value } => value.iter().flat_map(|eg| to_vec_sr(&eg.meta)).collect(),
 | 
			
		||||
@ -152,7 +152,7 @@ fn to_vec_sr(meta: &[Metadata]) -> Vec<SourceRange> {
 | 
			
		||||
impl From<&KclValue> for Vec<SourceRange> {
 | 
			
		||||
    fn from(item: &KclValue) -> Self {
 | 
			
		||||
        match item {
 | 
			
		||||
            KclValue::TagDeclarator(t) => vec![SourceRange([t.start, t.end, t.module_id.0 as usize])],
 | 
			
		||||
            KclValue::TagDeclarator(t) => vec![SourceRange::new(t.start, t.end, t.module_id)],
 | 
			
		||||
            KclValue::TagIdentifier(t) => to_vec_sr(&t.meta),
 | 
			
		||||
            KclValue::Solid(e) => to_vec_sr(&e.meta),
 | 
			
		||||
            KclValue::Solids { value } => value.iter().flat_map(|eg| to_vec_sr(&eg.meta)).collect(),
 | 
			
		||||
 | 
			
		||||
@ -72,6 +72,7 @@ mod parser;
 | 
			
		||||
mod settings;
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod simulation_tests;
 | 
			
		||||
mod source_range;
 | 
			
		||||
mod std;
 | 
			
		||||
#[cfg(not(target_arch = "wasm32"))]
 | 
			
		||||
pub mod test_server;
 | 
			
		||||
@ -82,16 +83,17 @@ mod walk;
 | 
			
		||||
#[cfg(target_arch = "wasm32")]
 | 
			
		||||
mod wasm;
 | 
			
		||||
 | 
			
		||||
pub use ast::modify::modify_ast_for_sketch;
 | 
			
		||||
pub use ast::types::{FormatOptions, ModuleId};
 | 
			
		||||
pub use ast::{modify::modify_ast_for_sketch, types::FormatOptions};
 | 
			
		||||
pub use coredump::CoreDump;
 | 
			
		||||
pub use engine::{EngineManager, ExecutionKind};
 | 
			
		||||
pub use errors::{ConnectionError, ExecError, KclError};
 | 
			
		||||
pub use executor::{ExecState, ExecutorContext, ExecutorSettings, SourceRange};
 | 
			
		||||
pub use lsp::copilot::Backend as CopilotLspBackend;
 | 
			
		||||
pub use lsp::kcl::Backend as KclLspBackend;
 | 
			
		||||
pub use lsp::kcl::Server as KclLspServerSubCommand;
 | 
			
		||||
pub use executor::{ExecState, ExecutorContext, ExecutorSettings};
 | 
			
		||||
pub use lsp::{
 | 
			
		||||
    copilot::Backend as CopilotLspBackend,
 | 
			
		||||
    kcl::{Backend as KclLspBackend, Server as KclLspServerSubCommand},
 | 
			
		||||
};
 | 
			
		||||
pub use settings::types::{project::ProjectConfiguration, Configuration, UnitLength};
 | 
			
		||||
pub use source_range::{ModuleId, SourceRange};
 | 
			
		||||
 | 
			
		||||
// Rather than make executor public and make lots of it pub(crate), just re-export into a new module.
 | 
			
		||||
// Ideally we wouldn't export these things at all, they should only be used for testing.
 | 
			
		||||
@ -101,9 +103,11 @@ pub mod exec {
 | 
			
		||||
 | 
			
		||||
#[cfg(target_arch = "wasm32")]
 | 
			
		||||
pub mod wasm_engine {
 | 
			
		||||
    pub use crate::coredump::wasm::{CoreDumpManager, CoreDumper};
 | 
			
		||||
    pub use crate::engine::conn_wasm::{EngineCommandManager, EngineConnection};
 | 
			
		||||
    pub use crate::fs::wasm::FileSystemManager;
 | 
			
		||||
    pub use crate::{
 | 
			
		||||
        coredump::wasm::{CoreDumpManager, CoreDumper},
 | 
			
		||||
        engine::conn_wasm::{EngineCommandManager, EngineConnection},
 | 
			
		||||
        fs::wasm::FileSystemManager,
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(not(target_arch = "wasm32"))]
 | 
			
		||||
@ -115,9 +119,10 @@ pub mod std_utils {
 | 
			
		||||
    pub use crate::std::utils::{get_tangential_arc_to_info, is_points_ccw_wasm, TangentialArcInfoInput};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
 | 
			
		||||
#[allow(unused_imports)]
 | 
			
		||||
use crate::log::{log, logln};
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
 | 
			
		||||
pub struct Program {
 | 
			
		||||
 | 
			
		||||
@ -3,9 +3,9 @@ use convert_case::Casing;
 | 
			
		||||
 | 
			
		||||
use crate::{
 | 
			
		||||
    ast::types::{ObjectProperty, VariableDeclarator},
 | 
			
		||||
    executor::SourceRange,
 | 
			
		||||
    lint::rule::{def_finding, Discovered, Finding},
 | 
			
		||||
    walk::Node,
 | 
			
		||||
    SourceRange,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
def_finding!(
 | 
			
		||||
 | 
			
		||||
@ -1,11 +1,13 @@
 | 
			
		||||
use std::collections::HashMap;
 | 
			
		||||
 | 
			
		||||
use anyhow::Result;
 | 
			
		||||
 | 
			
		||||
use crate::{
 | 
			
		||||
    ast::types::{BinaryPart, Expr, LiteralValue, ObjectExpression, UnaryOperator},
 | 
			
		||||
    executor::SourceRange,
 | 
			
		||||
    lint::rule::{def_finding, Discovered, Finding},
 | 
			
		||||
    walk::Node,
 | 
			
		||||
    SourceRange,
 | 
			
		||||
};
 | 
			
		||||
use anyhow::Result;
 | 
			
		||||
use std::collections::HashMap;
 | 
			
		||||
 | 
			
		||||
def_finding!(
 | 
			
		||||
    Z0003,
 | 
			
		||||
 | 
			
		||||
@ -5,10 +5,10 @@ use anyhow::Result;
 | 
			
		||||
use crate::{
 | 
			
		||||
    ast::types::{CallExpression, NodeRef},
 | 
			
		||||
    docs::StdLibFn,
 | 
			
		||||
    executor::SourceRange,
 | 
			
		||||
    lint::rule::{def_finding, Discovered, Finding},
 | 
			
		||||
    std::{FunctionKind, StdLib},
 | 
			
		||||
    walk::Node,
 | 
			
		||||
    SourceRange,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
def_finding!(
 | 
			
		||||
 | 
			
		||||
@ -3,7 +3,7 @@ use schemars::JsonSchema;
 | 
			
		||||
use serde::Serialize;
 | 
			
		||||
use tower_lsp::lsp_types::{Diagnostic, DiagnosticSeverity};
 | 
			
		||||
 | 
			
		||||
use crate::{executor::SourceRange, lsp::IntoDiagnostic, walk::Node};
 | 
			
		||||
use crate::{lsp::IntoDiagnostic, walk::Node, SourceRange};
 | 
			
		||||
 | 
			
		||||
/// Check the provided AST for any found rule violations.
 | 
			
		||||
///
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,8 @@
 | 
			
		||||
#![allow(dead_code)]
 | 
			
		||||
use std::env;
 | 
			
		||||
 | 
			
		||||
#[cfg(feature = "dhat-heap")]
 | 
			
		||||
use dhat::{HeapStats, Profiler};
 | 
			
		||||
use std::env;
 | 
			
		||||
use web_time::Instant;
 | 
			
		||||
 | 
			
		||||
const LOG_ENV_VAR: &str = "ZOO_LOG";
 | 
			
		||||
 | 
			
		||||
@ -27,10 +27,12 @@ use tower_lsp::{
 | 
			
		||||
 | 
			
		||||
use crate::lsp::{
 | 
			
		||||
    backend::Backend as _,
 | 
			
		||||
    copilot::cache::CopilotCache,
 | 
			
		||||
    copilot::types::{
 | 
			
		||||
        CopilotAcceptCompletionParams, CopilotCompletionResponse, CopilotCompletionTelemetry, CopilotEditorInfo,
 | 
			
		||||
        CopilotLspCompletionParams, CopilotRejectCompletionParams, DocParams,
 | 
			
		||||
    copilot::{
 | 
			
		||||
        cache::CopilotCache,
 | 
			
		||||
        types::{
 | 
			
		||||
            CopilotAcceptCompletionParams, CopilotCompletionResponse, CopilotCompletionTelemetry, CopilotEditorInfo,
 | 
			
		||||
            CopilotLspCompletionParams, CopilotRejectCompletionParams, DocParams,
 | 
			
		||||
        },
 | 
			
		||||
    },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -41,11 +41,11 @@ use tower_lsp::{
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
use crate::{
 | 
			
		||||
    ast::types::{Expr, ModuleId, Node, VariableKind},
 | 
			
		||||
    ast::types::{Expr, Node, VariableKind},
 | 
			
		||||
    lsp::{backend::Backend as _, util::IntoDiagnostic},
 | 
			
		||||
    parser::PIPE_OPERATOR,
 | 
			
		||||
    token::TokenType,
 | 
			
		||||
    ExecState, Program, SourceRange,
 | 
			
		||||
    ExecState, ModuleId, Program, SourceRange,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
lazy_static::lazy_static! {
 | 
			
		||||
 | 
			
		||||
@ -1,9 +1,9 @@
 | 
			
		||||
use parser_impl::ParseContext;
 | 
			
		||||
 | 
			
		||||
use crate::{
 | 
			
		||||
    ast::types::{ModuleId, Node, Program},
 | 
			
		||||
    ast::types::{Node, Program},
 | 
			
		||||
    errors::{KclError, KclErrorDetails},
 | 
			
		||||
    executor::SourceRange,
 | 
			
		||||
    source_range::{ModuleId, SourceRange},
 | 
			
		||||
    token::{Token, TokenType},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,7 @@
 | 
			
		||||
use crate::{
 | 
			
		||||
    ast::types::{BinaryExpression, BinaryOperator, BinaryPart, Node},
 | 
			
		||||
    errors::{KclError, KclErrorDetails},
 | 
			
		||||
    executor::SourceRange,
 | 
			
		||||
    SourceRange,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/// Parses a list of tokens (in infix order, i.e. as the user typed them)
 | 
			
		||||
@ -66,7 +66,7 @@ fn source_range(tokens: &[BinaryExpressionToken]) -> Vec<SourceRange> {
 | 
			
		||||
        })
 | 
			
		||||
        .collect();
 | 
			
		||||
    match (sources.first(), sources.last()) {
 | 
			
		||||
        (Some((start, _, module_id)), Some((_, end, _))) => vec![SourceRange([*start, *end, module_id.as_usize()])],
 | 
			
		||||
        (Some((start, _, module_id)), Some((_, end, _))) => vec![SourceRange::new(*start, *end, *module_id)],
 | 
			
		||||
        _ => Vec::new(),
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -126,7 +126,7 @@ impl From<BinaryOperator> for BinaryExpressionToken {
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
    use super::*;
 | 
			
		||||
    use crate::ast::types::{Literal, ModuleId};
 | 
			
		||||
    use crate::{ast::types::Literal, source_range::ModuleId};
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn parse_and_evaluate() {
 | 
			
		||||
 | 
			
		||||
@ -20,12 +20,12 @@ use crate::{
 | 
			
		||||
    },
 | 
			
		||||
    docs::StdLibFn,
 | 
			
		||||
    errors::{KclError, KclErrorDetails},
 | 
			
		||||
    executor::SourceRange,
 | 
			
		||||
    parser::{
 | 
			
		||||
        math::BinaryExpressionToken, parser_impl::error::ContextError, PIPE_OPERATOR, PIPE_SUBSTITUTION_OPERATOR,
 | 
			
		||||
    },
 | 
			
		||||
    token::{Token, TokenType},
 | 
			
		||||
    unparser::ExprContext,
 | 
			
		||||
    SourceRange,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
pub(crate) mod error;
 | 
			
		||||
@ -1605,7 +1605,7 @@ fn declaration(i: TokenSlice) -> PResult<BoxNode<VariableDeclaration>> {
 | 
			
		||||
            let ctxt_end = val.as_ref().map(|e| e.end()).unwrap_or(t.end);
 | 
			
		||||
            ParseContext::warn(ParseError::with_suggestion(
 | 
			
		||||
                t.as_source_range(),
 | 
			
		||||
                Some(SourceRange([id.start, ctxt_end, module_id.as_usize()])),
 | 
			
		||||
                Some(SourceRange::new(id.start, ctxt_end, module_id)),
 | 
			
		||||
                "Unnecessary `=` in function declaration",
 | 
			
		||||
                Some(""),
 | 
			
		||||
            ));
 | 
			
		||||
@ -1622,7 +1622,7 @@ fn declaration(i: TokenSlice) -> PResult<BoxNode<VariableDeclaration>> {
 | 
			
		||||
                // Check the 'if' direction:
 | 
			
		||||
                if matches!(val, Expr::FunctionExpression(_)) {
 | 
			
		||||
                    return Err(KclError::Syntax(KclErrorDetails {
 | 
			
		||||
                        source_ranges: vec![SourceRange([start, dec_end, module_id.as_usize()])],
 | 
			
		||||
                        source_ranges: vec![SourceRange::new(start, dec_end, module_id)],
 | 
			
		||||
                        message: format!("Expected a `fn` variable kind, found: `{}`", kind),
 | 
			
		||||
                    }));
 | 
			
		||||
                }
 | 
			
		||||
@ -2287,7 +2287,10 @@ mod tests {
 | 
			
		||||
    use pretty_assertions::assert_eq;
 | 
			
		||||
 | 
			
		||||
    use super::*;
 | 
			
		||||
    use crate::ast::types::{BodyItem, Expr, ModuleId, VariableKind};
 | 
			
		||||
    use crate::{
 | 
			
		||||
        ast::types::{BodyItem, Expr, VariableKind},
 | 
			
		||||
        ModuleId,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    fn assert_reserved(word: &str) {
 | 
			
		||||
        // Try to use it as a variable name.
 | 
			
		||||
@ -2345,7 +2348,10 @@ mod tests {
 | 
			
		||||
        let tokens = crate::token::lexer("|", ModuleId::default()).unwrap();
 | 
			
		||||
        let err: super::error::ErrorKind = program.parse(&tokens).unwrap_err().into();
 | 
			
		||||
        let err = err.unwrap_parse_error();
 | 
			
		||||
        assert_eq!(vec![err.source_range], vec![SourceRange([0, 1, 0])]);
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            vec![err.source_range],
 | 
			
		||||
            vec![SourceRange::new(0, 1, ModuleId::default())]
 | 
			
		||||
        );
 | 
			
		||||
        assert_eq!(err.message, "Unexpected token: |");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -2969,7 +2975,10 @@ const mySk1 = startSketchAt([0, 0])"#;
 | 
			
		||||
        let err = function_decl.parse(&tokens).unwrap_err().into_inner();
 | 
			
		||||
        let cause = err.cause.unwrap();
 | 
			
		||||
        // This is the token `let`
 | 
			
		||||
        assert_eq!(cause.source_ranges(), vec![SourceRange([1, 4, 2])]);
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            cause.source_ranges(),
 | 
			
		||||
            vec![SourceRange::new(1, 4, ModuleId::from_usize(2))]
 | 
			
		||||
        );
 | 
			
		||||
        assert_eq!(cause.message(), "Cannot assign a variable to a reserved keyword: let");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -3289,7 +3298,8 @@ const mySk1 = startSketchAt([0, 0])"#;
 | 
			
		||||
        let result = crate::parser::top_level_parse(p);
 | 
			
		||||
        let err = &result.unwrap_errs()[0];
 | 
			
		||||
        assert_eq!(err.message, msg);
 | 
			
		||||
        assert_eq!(&err.source_range.0[..2], &src);
 | 
			
		||||
        assert_eq!(err.source_range.start(), src[0]);
 | 
			
		||||
        assert_eq!(err.source_range.end(), src[1]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[track_caller]
 | 
			
		||||
@ -3412,7 +3422,8 @@ const secondExtrude = startSketchOn('XY')
 | 
			
		||||
        // TODO: Better errors when program cannot tokenize.
 | 
			
		||||
        // https://github.com/KittyCAD/modeling-app/issues/696
 | 
			
		||||
        assert_eq!(details.message, "found unknown token 'ޜ'");
 | 
			
		||||
        assert_eq!(&details.source_ranges[0].0[..2], &[1, 2]);
 | 
			
		||||
        assert_eq!(details.source_ranges[0].start(), 1);
 | 
			
		||||
        assert_eq!(details.source_ranges[0].end(), 2);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
@ -3889,6 +3900,7 @@ int(42.3)"#;
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod snapshot_math_tests {
 | 
			
		||||
    use super::*;
 | 
			
		||||
    use crate::ModuleId;
 | 
			
		||||
 | 
			
		||||
    // This macro generates a test function with the given function name.
 | 
			
		||||
    // The macro takes a KCL program, ensures it tokenizes and parses, then compares
 | 
			
		||||
@ -3897,7 +3909,7 @@ mod snapshot_math_tests {
 | 
			
		||||
        ($func_name:ident, $test_kcl_program:expr) => {
 | 
			
		||||
            #[test]
 | 
			
		||||
            fn $func_name() {
 | 
			
		||||
                let module_id = crate::ast::types::ModuleId::default();
 | 
			
		||||
                let module_id = ModuleId::default();
 | 
			
		||||
                let tokens = crate::token::lexer($test_kcl_program, module_id).unwrap();
 | 
			
		||||
                ParseContext::init();
 | 
			
		||||
 | 
			
		||||
@ -3927,6 +3939,7 @@ mod snapshot_math_tests {
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod snapshot_tests {
 | 
			
		||||
    use super::*;
 | 
			
		||||
    use crate::ModuleId;
 | 
			
		||||
 | 
			
		||||
    // This macro generates a test function with the given function name.
 | 
			
		||||
    // The macro takes a KCL program, ensures it tokenizes and parses, then compares
 | 
			
		||||
@ -3935,7 +3948,7 @@ mod snapshot_tests {
 | 
			
		||||
        ($func_name:ident, $test_kcl_program:expr) => {
 | 
			
		||||
            #[test]
 | 
			
		||||
            fn $func_name() {
 | 
			
		||||
                let module_id = crate::ast::types::ModuleId::default();
 | 
			
		||||
                let module_id = ModuleId::default();
 | 
			
		||||
                let tokens = crate::token::lexer($test_kcl_program, module_id).unwrap();
 | 
			
		||||
                print_tokens(&tokens);
 | 
			
		||||
                ParseContext::init();
 | 
			
		||||
 | 
			
		||||
@ -2,8 +2,8 @@ use winnow::{error::StrContext, stream::Stream};
 | 
			
		||||
 | 
			
		||||
use crate::{
 | 
			
		||||
    errors::{KclError, KclErrorDetails},
 | 
			
		||||
    executor::SourceRange,
 | 
			
		||||
    token::Token,
 | 
			
		||||
    SourceRange,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/// Accumulate context while backtracking errors
 | 
			
		||||
 | 
			
		||||
@ -1,8 +1,9 @@
 | 
			
		||||
use insta::rounded_redaction;
 | 
			
		||||
 | 
			
		||||
use crate::{
 | 
			
		||||
    ast::types::{ModuleId, Node, Program},
 | 
			
		||||
    ast::types::{Node, Program},
 | 
			
		||||
    errors::KclError,
 | 
			
		||||
    source_range::ModuleId,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/// Deserialize the data from a snapshot.
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										132
									
								
								src/wasm-lib/kcl/src/source_range.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										132
									
								
								src/wasm-lib/kcl/src/source_range.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,132 @@
 | 
			
		||||
use databake::*;
 | 
			
		||||
use schemars::JsonSchema;
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
use tower_lsp::lsp_types::{Position as LspPosition, Range as LspRange};
 | 
			
		||||
 | 
			
		||||
/// Identifier of a source file.  Uses a u32 to keep the size small.
 | 
			
		||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, Deserialize, Serialize, ts_rs::TS, JsonSchema, Bake)]
 | 
			
		||||
#[cfg_attr(feature = "pyo3", pyo3::pyclass)]
 | 
			
		||||
#[databake(path = kcl_lib::ast::types)]
 | 
			
		||||
#[ts(export)]
 | 
			
		||||
pub struct ModuleId(u32);
 | 
			
		||||
 | 
			
		||||
impl ModuleId {
 | 
			
		||||
    pub fn from_usize(id: usize) -> Self {
 | 
			
		||||
        Self(u32::try_from(id).expect("module ID should fit in a u32"))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn as_usize(&self) -> usize {
 | 
			
		||||
        usize::try_from(self.0).expect("module ID should fit in a usize")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Top-level file is the one being executed.
 | 
			
		||||
    /// Represented by module ID of 0, i.e. the default value.
 | 
			
		||||
    pub fn is_top_level(&self) -> bool {
 | 
			
		||||
        *self == Self::default()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TODO Serialization and Python bindings expose the implementation of source ranges.
 | 
			
		||||
#[derive(Debug, Default, Deserialize, Serialize, PartialEq, Copy, Clone, ts_rs::TS, JsonSchema, Hash, Eq)]
 | 
			
		||||
#[cfg_attr(feature = "pyo3", pyo3::pyclass)]
 | 
			
		||||
#[ts(export, as = "TsSourceRange")]
 | 
			
		||||
pub struct SourceRange([usize; 3]);
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Default, Deserialize, Serialize, PartialEq, Copy, Clone, ts_rs::TS, JsonSchema, Hash, Eq)]
 | 
			
		||||
struct TsSourceRange(#[ts(type = "[number, number]")] [usize; 2]);
 | 
			
		||||
 | 
			
		||||
impl From<[usize; 3]> for SourceRange {
 | 
			
		||||
    fn from(value: [usize; 3]) -> Self {
 | 
			
		||||
        Self(value)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<SourceRange> for TsSourceRange {
 | 
			
		||||
    fn from(value: SourceRange) -> Self {
 | 
			
		||||
        Self([value.start(), value.end()])
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<&SourceRange> for miette::SourceSpan {
 | 
			
		||||
    fn from(source_range: &SourceRange) -> Self {
 | 
			
		||||
        let length = source_range.end() - source_range.start();
 | 
			
		||||
        let start = miette::SourceOffset::from(source_range.start());
 | 
			
		||||
        Self::new(start, length)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<SourceRange> for miette::SourceSpan {
 | 
			
		||||
    fn from(source_range: SourceRange) -> Self {
 | 
			
		||||
        Self::from(&source_range)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl SourceRange {
 | 
			
		||||
    /// Create a new source range.
 | 
			
		||||
    pub fn new(start: usize, end: usize, module_id: ModuleId) -> Self {
 | 
			
		||||
        Self([start, end, module_id.as_usize()])
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// A source range that doesn't correspond to any source code.
 | 
			
		||||
    pub fn synthetic() -> Self {
 | 
			
		||||
        Self::default()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Get the start of the range.
 | 
			
		||||
    pub fn start(&self) -> usize {
 | 
			
		||||
        self.0[0]
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Get the end of the range.
 | 
			
		||||
    pub fn end(&self) -> usize {
 | 
			
		||||
        self.0[1]
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Get the module ID of the range.
 | 
			
		||||
    pub fn module_id(&self) -> ModuleId {
 | 
			
		||||
        ModuleId::from_usize(self.0[2])
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Check if the range contains a position.
 | 
			
		||||
    pub fn contains(&self, pos: usize) -> bool {
 | 
			
		||||
        pos >= self.start() && pos <= self.end()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn start_to_lsp_position(&self, code: &str) -> LspPosition {
 | 
			
		||||
        // Calculate the line and column of the error from the source range.
 | 
			
		||||
        // Lines are zero indexed in vscode so we need to subtract 1.
 | 
			
		||||
        let mut line = code.get(..self.start()).unwrap_or_default().lines().count();
 | 
			
		||||
        if line > 0 {
 | 
			
		||||
            line = line.saturating_sub(1);
 | 
			
		||||
        }
 | 
			
		||||
        let column = code[..self.start()].lines().last().map(|l| l.len()).unwrap_or_default();
 | 
			
		||||
 | 
			
		||||
        LspPosition {
 | 
			
		||||
            line: line as u32,
 | 
			
		||||
            character: column as u32,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn end_to_lsp_position(&self, code: &str) -> LspPosition {
 | 
			
		||||
        let lines = code.get(..self.end()).unwrap_or_default().lines();
 | 
			
		||||
        if lines.clone().count() == 0 {
 | 
			
		||||
            return LspPosition { line: 0, character: 0 };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Calculate the line and column of the error from the source range.
 | 
			
		||||
        // Lines are zero indexed in vscode so we need to subtract 1.
 | 
			
		||||
        let line = lines.clone().count() - 1;
 | 
			
		||||
        let column = lines.last().map(|l| l.len()).unwrap_or_default();
 | 
			
		||||
 | 
			
		||||
        LspPosition {
 | 
			
		||||
            line: line as u32,
 | 
			
		||||
            character: column as u32,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn to_lsp_range(&self, code: &str) -> LspRange {
 | 
			
		||||
        let start = self.start_to_lsp_position(code);
 | 
			
		||||
        let end = self.end_to_lsp_position(code);
 | 
			
		||||
        LspRange { start, end }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -4,19 +4,20 @@ use anyhow::Result;
 | 
			
		||||
use kcmc::{websocket::OkWebSocketResponseData, ModelingCmd};
 | 
			
		||||
use kittycad_modeling_cmds as kcmc;
 | 
			
		||||
 | 
			
		||||
use super::shapes::PolygonType;
 | 
			
		||||
use crate::{
 | 
			
		||||
    ast::types::TagNode,
 | 
			
		||||
    errors::{KclError, KclErrorDetails},
 | 
			
		||||
    executor::{
 | 
			
		||||
        ExecState, ExecutorContext, ExtrudeSurface, KclValue, Metadata, Sketch, SketchSet, SketchSurface, Solid,
 | 
			
		||||
        SolidSet, SourceRange, TagIdentifier,
 | 
			
		||||
        SolidSet, TagIdentifier,
 | 
			
		||||
    },
 | 
			
		||||
    kcl_value::KclObjectFields,
 | 
			
		||||
    source_range::SourceRange,
 | 
			
		||||
    std::{shapes::SketchOrSurface, sketch::FaceTag, FnAsArg},
 | 
			
		||||
    ModuleId,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
use super::shapes::PolygonType;
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone)]
 | 
			
		||||
pub struct Arg {
 | 
			
		||||
    /// The evaluated argument.
 | 
			
		||||
@ -1221,7 +1222,6 @@ impl<'a> FromKclValue<'a> for crate::executor::GeoMeta {
 | 
			
		||||
        let obj = arg.as_object()?;
 | 
			
		||||
        let_field_of!(obj, id);
 | 
			
		||||
        let_field_of!(obj, source_range "sourceRange");
 | 
			
		||||
        let source_range = SourceRange(source_range);
 | 
			
		||||
        Some(Self {
 | 
			
		||||
            id,
 | 
			
		||||
            metadata: Metadata { source_range },
 | 
			
		||||
@ -1314,9 +1314,22 @@ impl_from_kcl_for_vec!(super::fillet::EdgeReference);
 | 
			
		||||
impl_from_kcl_for_vec!(ExtrudeSurface);
 | 
			
		||||
impl_from_kcl_for_vec!(Sketch);
 | 
			
		||||
 | 
			
		||||
impl<'a> FromKclValue<'a> for crate::executor::SourceRange {
 | 
			
		||||
impl<'a> FromKclValue<'a> for SourceRange {
 | 
			
		||||
    fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
 | 
			
		||||
        FromKclValue::from_kcl_val(arg).map(crate::executor::SourceRange)
 | 
			
		||||
        let KclValue::Array { value, meta: _ } = arg else {
 | 
			
		||||
            return None;
 | 
			
		||||
        };
 | 
			
		||||
        if value.len() != 3 {
 | 
			
		||||
            return None;
 | 
			
		||||
        }
 | 
			
		||||
        let v0 = value.first()?;
 | 
			
		||||
        let v1 = value.get(1)?;
 | 
			
		||||
        let v2 = value.get(2)?;
 | 
			
		||||
        Some(SourceRange::new(
 | 
			
		||||
            v0.as_usize()?,
 | 
			
		||||
            v1.as_usize()?,
 | 
			
		||||
            ModuleId::from_usize(v2.as_usize()?),
 | 
			
		||||
        ))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -6,8 +6,9 @@ use super::{
 | 
			
		||||
};
 | 
			
		||||
use crate::{
 | 
			
		||||
    errors::{KclError, KclErrorDetails},
 | 
			
		||||
    executor::{ExecState, KclValue, SourceRange},
 | 
			
		||||
    executor::{ExecState, KclValue},
 | 
			
		||||
    function_param::FunctionParam,
 | 
			
		||||
    source_range::SourceRange,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/// Apply a function to each element of an array.
 | 
			
		||||
 | 
			
		||||
@ -16,18 +16,16 @@ use schemars::JsonSchema;
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
use uuid::Uuid;
 | 
			
		||||
 | 
			
		||||
use super::args::Arg;
 | 
			
		||||
use crate::{
 | 
			
		||||
    errors::{KclError, KclErrorDetails},
 | 
			
		||||
    executor::{
 | 
			
		||||
        ExecState, Geometries, Geometry, KclValue, Point2d, Point3d, Sketch, SketchSet, Solid, SolidSet, SourceRange,
 | 
			
		||||
    },
 | 
			
		||||
    executor::{ExecState, Geometries, Geometry, KclValue, Point2d, Point3d, Sketch, SketchSet, Solid, SolidSet},
 | 
			
		||||
    function_param::FunctionParam,
 | 
			
		||||
    kcl_value::KclObjectFields,
 | 
			
		||||
    std::Args,
 | 
			
		||||
    SourceRange,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
use super::args::Arg;
 | 
			
		||||
 | 
			
		||||
const MUST_HAVE_ONE_INSTANCE: &str = "There must be at least 1 instance of your geometry";
 | 
			
		||||
 | 
			
		||||
/// Data for a linear pattern on a 2D sketch.
 | 
			
		||||
 | 
			
		||||
@ -12,8 +12,8 @@ use parse_display::{Display, FromStr};
 | 
			
		||||
use schemars::JsonSchema;
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
 | 
			
		||||
use crate::ast::types::TagNode;
 | 
			
		||||
use crate::{
 | 
			
		||||
    ast::types::TagNode,
 | 
			
		||||
    errors::{KclError, KclErrorDetails},
 | 
			
		||||
    executor::{
 | 
			
		||||
        BasePath, ExecState, Face, GeoMeta, KclValue, Path, Plane, Point2d, Point3d, Sketch, SketchSet, SketchSurface,
 | 
			
		||||
@ -2250,7 +2250,10 @@ mod tests {
 | 
			
		||||
 | 
			
		||||
    use pretty_assertions::assert_eq;
 | 
			
		||||
 | 
			
		||||
    use crate::{executor::TagIdentifier, std::sketch::calculate_circle_center, std::sketch::PlaneData};
 | 
			
		||||
    use crate::{
 | 
			
		||||
        executor::TagIdentifier,
 | 
			
		||||
        std::sketch::{calculate_circle_center, PlaneData},
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_deserialize_plane_data() {
 | 
			
		||||
 | 
			
		||||
@ -4,7 +4,8 @@ use kittycad_modeling_cmds::shared::Angle;
 | 
			
		||||
 | 
			
		||||
use crate::{
 | 
			
		||||
    errors::{KclError, KclErrorDetails},
 | 
			
		||||
    executor::{Point2d, SourceRange},
 | 
			
		||||
    executor::Point2d,
 | 
			
		||||
    source_range::SourceRange,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/// Get the angle between these points
 | 
			
		||||
@ -234,7 +235,7 @@ mod tests {
 | 
			
		||||
    use pretty_assertions::assert_eq;
 | 
			
		||||
 | 
			
		||||
    use super::{get_x_component, get_y_component, Angle};
 | 
			
		||||
    use crate::executor::SourceRange;
 | 
			
		||||
    use crate::SourceRange;
 | 
			
		||||
 | 
			
		||||
    static EACH_QUAD: [(i32, [i32; 2]); 12] = [
 | 
			
		||||
        (-315, [1, 1]),
 | 
			
		||||
@ -354,7 +355,7 @@ mod tests {
 | 
			
		||||
            super::Point2d { x: -1.0, y: 1.0 },
 | 
			
		||||
            super::Point2d { x: -1.0, y: 0.0 },
 | 
			
		||||
            1.0,
 | 
			
		||||
            SourceRange(Default::default()),
 | 
			
		||||
            SourceRange::default(),
 | 
			
		||||
        )
 | 
			
		||||
        .unwrap();
 | 
			
		||||
        assert_eq!(angle_start.to_degrees().round(), 0.0);
 | 
			
		||||
@ -365,7 +366,7 @@ mod tests {
 | 
			
		||||
            super::Point2d { x: -2.0, y: 0.0 },
 | 
			
		||||
            super::Point2d { x: -1.0, y: 0.0 },
 | 
			
		||||
            1.0,
 | 
			
		||||
            SourceRange(Default::default()),
 | 
			
		||||
            SourceRange::default(),
 | 
			
		||||
        )
 | 
			
		||||
        .unwrap();
 | 
			
		||||
        assert_eq!(angle_start.to_degrees().round(), 0.0);
 | 
			
		||||
@ -376,7 +377,7 @@ mod tests {
 | 
			
		||||
            super::Point2d { x: -20.0, y: 0.0 },
 | 
			
		||||
            super::Point2d { x: -10.0, y: 0.0 },
 | 
			
		||||
            10.0,
 | 
			
		||||
            SourceRange(Default::default()),
 | 
			
		||||
            SourceRange::default(),
 | 
			
		||||
        )
 | 
			
		||||
        .unwrap();
 | 
			
		||||
        assert_eq!(angle_start.to_degrees().round(), 0.0);
 | 
			
		||||
@ -387,7 +388,7 @@ mod tests {
 | 
			
		||||
            super::Point2d { x: 5.0, y: 5.0 },
 | 
			
		||||
            super::Point2d { x: 10.0, y: -10.0 },
 | 
			
		||||
            10.0,
 | 
			
		||||
            SourceRange(Default::default()),
 | 
			
		||||
            SourceRange::default(),
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        if let Err(err) = result {
 | 
			
		||||
 | 
			
		||||
@ -8,9 +8,9 @@ use tower_lsp::lsp_types::SemanticTokenType;
 | 
			
		||||
use winnow::{error::ParseError, stream::ContainsToken};
 | 
			
		||||
 | 
			
		||||
use crate::{
 | 
			
		||||
    ast::types::{ItemVisibility, ModuleId, VariableKind},
 | 
			
		||||
    ast::types::{ItemVisibility, VariableKind},
 | 
			
		||||
    errors::KclError,
 | 
			
		||||
    executor::SourceRange,
 | 
			
		||||
    source_range::{ModuleId, SourceRange},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
mod tokeniser;
 | 
			
		||||
@ -207,7 +207,7 @@ impl Token {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn as_source_range(&self) -> SourceRange {
 | 
			
		||||
        SourceRange([self.start, self.end, self.module_id.as_usize()])
 | 
			
		||||
        SourceRange::new(self.start, self.end, self.module_id)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn as_source_ranges(&self) -> Vec<SourceRange> {
 | 
			
		||||
@ -241,13 +241,13 @@ impl Token {
 | 
			
		||||
 | 
			
		||||
impl From<Token> for SourceRange {
 | 
			
		||||
    fn from(token: Token) -> Self {
 | 
			
		||||
        Self([token.start, token.end, token.module_id.as_usize()])
 | 
			
		||||
        Self::new(token.start, token.end, token.module_id)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<&Token> for SourceRange {
 | 
			
		||||
    fn from(token: &Token) -> Self {
 | 
			
		||||
        Self([token.start, token.end, token.module_id.as_usize()])
 | 
			
		||||
        Self::new(token.start, token.end, token.module_id)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -267,7 +267,7 @@ impl From<ParseError<Input<'_>, winnow::error::ContextError>> for KclError {
 | 
			
		||||
            // the end of input (input.len()) on eof errors.
 | 
			
		||||
 | 
			
		||||
            return KclError::Lexical(crate::errors::KclErrorDetails {
 | 
			
		||||
                source_ranges: vec![SourceRange([offset, offset, module_id.as_usize()])],
 | 
			
		||||
                source_ranges: vec![SourceRange::new(offset, offset, module_id)],
 | 
			
		||||
                message: "unexpected EOF while parsing".to_string(),
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
@ -278,7 +278,7 @@ impl From<ParseError<Input<'_>, winnow::error::ContextError>> for KclError {
 | 
			
		||||
        // TODO: Add the Winnow parser context to the error.
 | 
			
		||||
        // See https://github.com/KittyCAD/modeling-app/issues/784
 | 
			
		||||
        KclError::Lexical(crate::errors::KclErrorDetails {
 | 
			
		||||
            source_ranges: vec![SourceRange([offset, offset + 1, module_id.as_usize()])],
 | 
			
		||||
            source_ranges: vec![SourceRange::new(offset, offset + 1, module_id)],
 | 
			
		||||
            message: format!("found unknown token '{}'", bad_token),
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -11,7 +11,7 @@ use winnow::{
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
use crate::{
 | 
			
		||||
    ast::types::ModuleId,
 | 
			
		||||
    source_range::ModuleId,
 | 
			
		||||
    token::{Token, TokenType},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -683,7 +683,7 @@ mod tests {
 | 
			
		||||
    use pretty_assertions::assert_eq;
 | 
			
		||||
 | 
			
		||||
    use super::*;
 | 
			
		||||
    use crate::ast::types::{FormatOptions, ModuleId};
 | 
			
		||||
    use crate::{ast::types::FormatOptions, source_range::ModuleId};
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_recast_if_else_if_same() {
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,6 @@
 | 
			
		||||
use crate::{
 | 
			
		||||
    ast::types::{self, NodeRef},
 | 
			
		||||
    executor::SourceRange,
 | 
			
		||||
    source_range::SourceRange,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/// The "Node" type wraps all the AST elements we're able to find in a KCL
 | 
			
		||||
@ -65,9 +65,9 @@ impl From<&Node<'_>> for SourceRange {
 | 
			
		||||
            Node::UnaryExpression(n) => SourceRange::from(*n),
 | 
			
		||||
            Node::Parameter(p) => SourceRange::from(&p.identifier),
 | 
			
		||||
            Node::ObjectProperty(n) => SourceRange::from(*n),
 | 
			
		||||
            Node::MemberObject(m) => SourceRange([m.start(), m.end(), m.module_id().as_usize()]),
 | 
			
		||||
            Node::MemberObject(m) => SourceRange::new(m.start(), m.end(), m.module_id()),
 | 
			
		||||
            Node::IfExpression(n) => SourceRange::from(*n),
 | 
			
		||||
            Node::LiteralIdentifier(l) => SourceRange([l.start(), l.end(), l.module_id().as_usize()]),
 | 
			
		||||
            Node::LiteralIdentifier(l) => SourceRange::new(l.start(), l.end(), l.module_id()),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user