Display numeric units in the variables pane (#6683)
This commit is contained in:
		@ -126685,7 +126685,8 @@
 | 
			
		||||
                {
 | 
			
		||||
                  "type": "object",
 | 
			
		||||
                  "required": [
 | 
			
		||||
                    "type"
 | 
			
		||||
                    "type",
 | 
			
		||||
                    "value"
 | 
			
		||||
                  ],
 | 
			
		||||
                  "properties": {
 | 
			
		||||
                    "type": {
 | 
			
		||||
@ -126693,6 +126694,9 @@
 | 
			
		||||
                      "enum": [
 | 
			
		||||
                        "Function"
 | 
			
		||||
                      ]
 | 
			
		||||
                    },
 | 
			
		||||
                    "value": {
 | 
			
		||||
                      "$ref": "#/components/schemas/FunctionSource"
 | 
			
		||||
                    }
 | 
			
		||||
                  }
 | 
			
		||||
                },
 | 
			
		||||
@ -128804,6 +128808,9 @@
 | 
			
		||||
                }
 | 
			
		||||
              }
 | 
			
		||||
            },
 | 
			
		||||
            "FunctionSource": {
 | 
			
		||||
              "type": "null"
 | 
			
		||||
            },
 | 
			
		||||
            "ModuleId": {
 | 
			
		||||
              "description": "Identifier of a source file.  Uses a u32 to keep the size small.",
 | 
			
		||||
              "type": "integer",
 | 
			
		||||
@ -129165,7 +129172,8 @@
 | 
			
		||||
                {
 | 
			
		||||
                  "type": "object",
 | 
			
		||||
                  "required": [
 | 
			
		||||
                    "type"
 | 
			
		||||
                    "type",
 | 
			
		||||
                    "value"
 | 
			
		||||
                  ],
 | 
			
		||||
                  "properties": {
 | 
			
		||||
                    "type": {
 | 
			
		||||
@ -129173,6 +129181,9 @@
 | 
			
		||||
                      "enum": [
 | 
			
		||||
                        "Function"
 | 
			
		||||
                      ]
 | 
			
		||||
                    },
 | 
			
		||||
                    "value": {
 | 
			
		||||
                      "$ref": "#/components/schemas/FunctionSource"
 | 
			
		||||
                    }
 | 
			
		||||
                  }
 | 
			
		||||
                },
 | 
			
		||||
@ -131284,6 +131295,9 @@
 | 
			
		||||
                }
 | 
			
		||||
              }
 | 
			
		||||
            },
 | 
			
		||||
            "FunctionSource": {
 | 
			
		||||
              "type": "null"
 | 
			
		||||
            },
 | 
			
		||||
            "ModuleId": {
 | 
			
		||||
              "description": "Identifier of a source file.  Uses a u32 to keep the size small.",
 | 
			
		||||
              "type": "integer",
 | 
			
		||||
@ -131649,7 +131663,8 @@
 | 
			
		||||
              {
 | 
			
		||||
                "type": "object",
 | 
			
		||||
                "required": [
 | 
			
		||||
                  "type"
 | 
			
		||||
                  "type",
 | 
			
		||||
                  "value"
 | 
			
		||||
                ],
 | 
			
		||||
                "properties": {
 | 
			
		||||
                  "type": {
 | 
			
		||||
@ -131657,6 +131672,9 @@
 | 
			
		||||
                    "enum": [
 | 
			
		||||
                      "Function"
 | 
			
		||||
                    ]
 | 
			
		||||
                  },
 | 
			
		||||
                  "value": {
 | 
			
		||||
                    "$ref": "#/components/schemas/FunctionSource"
 | 
			
		||||
                  }
 | 
			
		||||
                }
 | 
			
		||||
              },
 | 
			
		||||
@ -133768,6 +133786,9 @@
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
          },
 | 
			
		||||
          "FunctionSource": {
 | 
			
		||||
            "type": "null"
 | 
			
		||||
          },
 | 
			
		||||
          "ModuleId": {
 | 
			
		||||
            "description": "Identifier of a source file.  Uses a u32 to keep the size small.",
 | 
			
		||||
            "type": "integer",
 | 
			
		||||
@ -203436,7 +203457,8 @@
 | 
			
		||||
                {
 | 
			
		||||
                  "type": "object",
 | 
			
		||||
                  "required": [
 | 
			
		||||
                    "type"
 | 
			
		||||
                    "type",
 | 
			
		||||
                    "value"
 | 
			
		||||
                  ],
 | 
			
		||||
                  "properties": {
 | 
			
		||||
                    "type": {
 | 
			
		||||
@ -203444,6 +203466,9 @@
 | 
			
		||||
                      "enum": [
 | 
			
		||||
                        "Function"
 | 
			
		||||
                      ]
 | 
			
		||||
                    },
 | 
			
		||||
                    "value": {
 | 
			
		||||
                      "$ref": "#/components/schemas/FunctionSource"
 | 
			
		||||
                    }
 | 
			
		||||
                  }
 | 
			
		||||
                },
 | 
			
		||||
@ -205555,6 +205580,9 @@
 | 
			
		||||
                }
 | 
			
		||||
              }
 | 
			
		||||
            },
 | 
			
		||||
            "FunctionSource": {
 | 
			
		||||
              "type": "null"
 | 
			
		||||
            },
 | 
			
		||||
            "ModuleId": {
 | 
			
		||||
              "description": "Identifier of a source file.  Uses a u32 to keep the size small.",
 | 
			
		||||
              "type": "integer",
 | 
			
		||||
@ -205914,7 +205942,8 @@
 | 
			
		||||
          {
 | 
			
		||||
            "type": "object",
 | 
			
		||||
            "required": [
 | 
			
		||||
              "type"
 | 
			
		||||
              "type",
 | 
			
		||||
              "value"
 | 
			
		||||
            ],
 | 
			
		||||
            "properties": {
 | 
			
		||||
              "type": {
 | 
			
		||||
@ -205922,6 +205951,9 @@
 | 
			
		||||
                "enum": [
 | 
			
		||||
                  "Function"
 | 
			
		||||
                ]
 | 
			
		||||
              },
 | 
			
		||||
              "value": {
 | 
			
		||||
                "$ref": "#/components/schemas/FunctionSource"
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
          },
 | 
			
		||||
@ -206708,7 +206740,8 @@
 | 
			
		||||
              {
 | 
			
		||||
                "type": "object",
 | 
			
		||||
                "required": [
 | 
			
		||||
                  "type"
 | 
			
		||||
                  "type",
 | 
			
		||||
                  "value"
 | 
			
		||||
                ],
 | 
			
		||||
                "properties": {
 | 
			
		||||
                  "type": {
 | 
			
		||||
@ -206716,6 +206749,9 @@
 | 
			
		||||
                    "enum": [
 | 
			
		||||
                      "Function"
 | 
			
		||||
                    ]
 | 
			
		||||
                  },
 | 
			
		||||
                  "value": {
 | 
			
		||||
                    "$ref": "#/components/schemas/FunctionSource"
 | 
			
		||||
                  }
 | 
			
		||||
                }
 | 
			
		||||
              },
 | 
			
		||||
@ -208419,6 +208455,9 @@
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
          },
 | 
			
		||||
          "FunctionSource": {
 | 
			
		||||
            "type": "null"
 | 
			
		||||
          },
 | 
			
		||||
          "ModuleId": {
 | 
			
		||||
            "description": "Identifier of a source file.  Uses a u32 to keep the size small.",
 | 
			
		||||
            "type": "integer",
 | 
			
		||||
@ -213896,7 +213935,8 @@
 | 
			
		||||
                {
 | 
			
		||||
                  "type": "object",
 | 
			
		||||
                  "required": [
 | 
			
		||||
                    "type"
 | 
			
		||||
                    "type",
 | 
			
		||||
                    "value"
 | 
			
		||||
                  ],
 | 
			
		||||
                  "properties": {
 | 
			
		||||
                    "type": {
 | 
			
		||||
@ -213904,6 +213944,9 @@
 | 
			
		||||
                      "enum": [
 | 
			
		||||
                        "Function"
 | 
			
		||||
                      ]
 | 
			
		||||
                    },
 | 
			
		||||
                    "value": {
 | 
			
		||||
                      "$ref": "#/components/schemas/FunctionSource"
 | 
			
		||||
                    }
 | 
			
		||||
                  }
 | 
			
		||||
                },
 | 
			
		||||
@ -216015,6 +216058,9 @@
 | 
			
		||||
                }
 | 
			
		||||
              }
 | 
			
		||||
            },
 | 
			
		||||
            "FunctionSource": {
 | 
			
		||||
              "type": "null"
 | 
			
		||||
            },
 | 
			
		||||
            "ModuleId": {
 | 
			
		||||
              "description": "Identifier of a source file.  Uses a u32 to keep the size small.",
 | 
			
		||||
              "type": "integer",
 | 
			
		||||
@ -216373,7 +216419,8 @@
 | 
			
		||||
            {
 | 
			
		||||
              "type": "object",
 | 
			
		||||
              "required": [
 | 
			
		||||
                "type"
 | 
			
		||||
                "type",
 | 
			
		||||
                "value"
 | 
			
		||||
              ],
 | 
			
		||||
              "properties": {
 | 
			
		||||
                "type": {
 | 
			
		||||
@ -216381,6 +216428,9 @@
 | 
			
		||||
                  "enum": [
 | 
			
		||||
                    "Function"
 | 
			
		||||
                  ]
 | 
			
		||||
                },
 | 
			
		||||
                "value": {
 | 
			
		||||
                  "$ref": "#/components/schemas/FunctionSource"
 | 
			
		||||
                }
 | 
			
		||||
              }
 | 
			
		||||
            },
 | 
			
		||||
@ -216759,7 +216809,8 @@
 | 
			
		||||
                {
 | 
			
		||||
                  "type": "object",
 | 
			
		||||
                  "required": [
 | 
			
		||||
                    "type"
 | 
			
		||||
                    "type",
 | 
			
		||||
                    "value"
 | 
			
		||||
                  ],
 | 
			
		||||
                  "properties": {
 | 
			
		||||
                    "type": {
 | 
			
		||||
@ -216767,6 +216818,9 @@
 | 
			
		||||
                      "enum": [
 | 
			
		||||
                        "Function"
 | 
			
		||||
                      ]
 | 
			
		||||
                    },
 | 
			
		||||
                    "value": {
 | 
			
		||||
                      "$ref": "#/components/schemas/FunctionSource"
 | 
			
		||||
                    }
 | 
			
		||||
                  }
 | 
			
		||||
                },
 | 
			
		||||
@ -218878,6 +218932,9 @@
 | 
			
		||||
                }
 | 
			
		||||
              }
 | 
			
		||||
            },
 | 
			
		||||
            "FunctionSource": {
 | 
			
		||||
              "type": "null"
 | 
			
		||||
            },
 | 
			
		||||
            "ModuleId": {
 | 
			
		||||
              "description": "Identifier of a source file.  Uses a u32 to keep the size small.",
 | 
			
		||||
              "type": "integer",
 | 
			
		||||
@ -219237,7 +219294,8 @@
 | 
			
		||||
          {
 | 
			
		||||
            "type": "object",
 | 
			
		||||
            "required": [
 | 
			
		||||
              "type"
 | 
			
		||||
              "type",
 | 
			
		||||
              "value"
 | 
			
		||||
            ],
 | 
			
		||||
            "properties": {
 | 
			
		||||
              "type": {
 | 
			
		||||
@ -219245,6 +219303,9 @@
 | 
			
		||||
                "enum": [
 | 
			
		||||
                  "Function"
 | 
			
		||||
                ]
 | 
			
		||||
              },
 | 
			
		||||
              "value": {
 | 
			
		||||
                "$ref": "#/components/schemas/FunctionSource"
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
          },
 | 
			
		||||
@ -220031,7 +220092,8 @@
 | 
			
		||||
              {
 | 
			
		||||
                "type": "object",
 | 
			
		||||
                "required": [
 | 
			
		||||
                  "type"
 | 
			
		||||
                  "type",
 | 
			
		||||
                  "value"
 | 
			
		||||
                ],
 | 
			
		||||
                "properties": {
 | 
			
		||||
                  "type": {
 | 
			
		||||
@ -220039,6 +220101,9 @@
 | 
			
		||||
                    "enum": [
 | 
			
		||||
                      "Function"
 | 
			
		||||
                    ]
 | 
			
		||||
                  },
 | 
			
		||||
                  "value": {
 | 
			
		||||
                    "$ref": "#/components/schemas/FunctionSource"
 | 
			
		||||
                  }
 | 
			
		||||
                }
 | 
			
		||||
              },
 | 
			
		||||
@ -221742,6 +221807,9 @@
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
          },
 | 
			
		||||
          "FunctionSource": {
 | 
			
		||||
            "type": "null"
 | 
			
		||||
          },
 | 
			
		||||
          "ModuleId": {
 | 
			
		||||
            "description": "Identifier of a source file.  Uses a u32 to keep the size small.",
 | 
			
		||||
            "type": "integer",
 | 
			
		||||
@ -222123,7 +222191,8 @@
 | 
			
		||||
                {
 | 
			
		||||
                  "type": "object",
 | 
			
		||||
                  "required": [
 | 
			
		||||
                    "type"
 | 
			
		||||
                    "type",
 | 
			
		||||
                    "value"
 | 
			
		||||
                  ],
 | 
			
		||||
                  "properties": {
 | 
			
		||||
                    "type": {
 | 
			
		||||
@ -222131,6 +222200,9 @@
 | 
			
		||||
                      "enum": [
 | 
			
		||||
                        "Function"
 | 
			
		||||
                      ]
 | 
			
		||||
                    },
 | 
			
		||||
                    "value": {
 | 
			
		||||
                      "$ref": "#/components/schemas/FunctionSource"
 | 
			
		||||
                    }
 | 
			
		||||
                  }
 | 
			
		||||
                },
 | 
			
		||||
@ -224242,6 +224314,9 @@
 | 
			
		||||
                }
 | 
			
		||||
              }
 | 
			
		||||
            },
 | 
			
		||||
            "FunctionSource": {
 | 
			
		||||
              "type": "null"
 | 
			
		||||
            },
 | 
			
		||||
            "ModuleId": {
 | 
			
		||||
              "description": "Identifier of a source file.  Uses a u32 to keep the size small.",
 | 
			
		||||
              "type": "integer",
 | 
			
		||||
@ -224600,7 +224675,8 @@
 | 
			
		||||
            {
 | 
			
		||||
              "type": "object",
 | 
			
		||||
              "required": [
 | 
			
		||||
                "type"
 | 
			
		||||
                "type",
 | 
			
		||||
                "value"
 | 
			
		||||
              ],
 | 
			
		||||
              "properties": {
 | 
			
		||||
                "type": {
 | 
			
		||||
@ -224608,6 +224684,9 @@
 | 
			
		||||
                  "enum": [
 | 
			
		||||
                    "Function"
 | 
			
		||||
                  ]
 | 
			
		||||
                },
 | 
			
		||||
                "value": {
 | 
			
		||||
                  "$ref": "#/components/schemas/FunctionSource"
 | 
			
		||||
                }
 | 
			
		||||
              }
 | 
			
		||||
            },
 | 
			
		||||
@ -224986,7 +225065,8 @@
 | 
			
		||||
                {
 | 
			
		||||
                  "type": "object",
 | 
			
		||||
                  "required": [
 | 
			
		||||
                    "type"
 | 
			
		||||
                    "type",
 | 
			
		||||
                    "value"
 | 
			
		||||
                  ],
 | 
			
		||||
                  "properties": {
 | 
			
		||||
                    "type": {
 | 
			
		||||
@ -224994,6 +225074,9 @@
 | 
			
		||||
                      "enum": [
 | 
			
		||||
                        "Function"
 | 
			
		||||
                      ]
 | 
			
		||||
                    },
 | 
			
		||||
                    "value": {
 | 
			
		||||
                      "$ref": "#/components/schemas/FunctionSource"
 | 
			
		||||
                    }
 | 
			
		||||
                  }
 | 
			
		||||
                },
 | 
			
		||||
@ -227105,6 +227188,9 @@
 | 
			
		||||
                }
 | 
			
		||||
              }
 | 
			
		||||
            },
 | 
			
		||||
            "FunctionSource": {
 | 
			
		||||
              "type": "null"
 | 
			
		||||
            },
 | 
			
		||||
            "ModuleId": {
 | 
			
		||||
              "description": "Identifier of a source file.  Uses a u32 to keep the size small.",
 | 
			
		||||
              "type": "integer",
 | 
			
		||||
@ -227466,7 +227552,8 @@
 | 
			
		||||
                {
 | 
			
		||||
                  "type": "object",
 | 
			
		||||
                  "required": [
 | 
			
		||||
                    "type"
 | 
			
		||||
                    "type",
 | 
			
		||||
                    "value"
 | 
			
		||||
                  ],
 | 
			
		||||
                  "properties": {
 | 
			
		||||
                    "type": {
 | 
			
		||||
@ -227474,6 +227561,9 @@
 | 
			
		||||
                      "enum": [
 | 
			
		||||
                        "Function"
 | 
			
		||||
                      ]
 | 
			
		||||
                    },
 | 
			
		||||
                    "value": {
 | 
			
		||||
                      "$ref": "#/components/schemas/FunctionSource"
 | 
			
		||||
                    }
 | 
			
		||||
                  }
 | 
			
		||||
                },
 | 
			
		||||
@ -229585,6 +229675,9 @@
 | 
			
		||||
                }
 | 
			
		||||
              }
 | 
			
		||||
            },
 | 
			
		||||
            "FunctionSource": {
 | 
			
		||||
              "type": "null"
 | 
			
		||||
            },
 | 
			
		||||
            "ModuleId": {
 | 
			
		||||
              "description": "Identifier of a source file.  Uses a u32 to keep the size small.",
 | 
			
		||||
              "type": "integer",
 | 
			
		||||
@ -229944,7 +230037,8 @@
 | 
			
		||||
          {
 | 
			
		||||
            "type": "object",
 | 
			
		||||
            "required": [
 | 
			
		||||
              "type"
 | 
			
		||||
              "type",
 | 
			
		||||
              "value"
 | 
			
		||||
            ],
 | 
			
		||||
            "properties": {
 | 
			
		||||
              "type": {
 | 
			
		||||
@ -229952,6 +230046,9 @@
 | 
			
		||||
                "enum": [
 | 
			
		||||
                  "Function"
 | 
			
		||||
                ]
 | 
			
		||||
              },
 | 
			
		||||
              "value": {
 | 
			
		||||
                "$ref": "#/components/schemas/FunctionSource"
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
          },
 | 
			
		||||
@ -230738,7 +230835,8 @@
 | 
			
		||||
              {
 | 
			
		||||
                "type": "object",
 | 
			
		||||
                "required": [
 | 
			
		||||
                  "type"
 | 
			
		||||
                  "type",
 | 
			
		||||
                  "value"
 | 
			
		||||
                ],
 | 
			
		||||
                "properties": {
 | 
			
		||||
                  "type": {
 | 
			
		||||
@ -230746,6 +230844,9 @@
 | 
			
		||||
                    "enum": [
 | 
			
		||||
                      "Function"
 | 
			
		||||
                    ]
 | 
			
		||||
                  },
 | 
			
		||||
                  "value": {
 | 
			
		||||
                    "$ref": "#/components/schemas/FunctionSource"
 | 
			
		||||
                  }
 | 
			
		||||
                }
 | 
			
		||||
              },
 | 
			
		||||
@ -232449,6 +232550,9 @@
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
          },
 | 
			
		||||
          "FunctionSource": {
 | 
			
		||||
            "type": "null"
 | 
			
		||||
          },
 | 
			
		||||
          "ModuleId": {
 | 
			
		||||
            "description": "Identifier of a source file.  Uses a u32 to keep the size small.",
 | 
			
		||||
            "type": "integer",
 | 
			
		||||
 | 
			
		||||
@ -170,7 +170,9 @@ extrude001 = extrude(sketch001, length = 50)
 | 
			
		||||
    await variablesTabButton.click()
 | 
			
		||||
    // expect to see "myVar:5"
 | 
			
		||||
    await expect(
 | 
			
		||||
      page.locator('.pretty-json-container >> text=myVar:5')
 | 
			
		||||
      // There's a double quote before the number value since the units are
 | 
			
		||||
      // formatted into a string.
 | 
			
		||||
      page.locator('.pretty-json-container >> text=myVar:"5')
 | 
			
		||||
    ).toBeVisible()
 | 
			
		||||
 | 
			
		||||
    // change 5 to 67
 | 
			
		||||
@ -180,7 +182,7 @@ extrude001 = extrude(sketch001, length = 50)
 | 
			
		||||
    await page.keyboard.type('67')
 | 
			
		||||
 | 
			
		||||
    await expect(
 | 
			
		||||
      page.locator('.pretty-json-container >> text=myVar:67')
 | 
			
		||||
      page.locator('.pretty-json-container >> text=myVar:"67')
 | 
			
		||||
    ).toBeVisible()
 | 
			
		||||
  })
 | 
			
		||||
  test('ProgramMemory can be serialised', async ({ page, homePage }) => {
 | 
			
		||||
 | 
			
		||||
@ -83,9 +83,9 @@ pub enum KclValue {
 | 
			
		||||
        value: Box<Helix>,
 | 
			
		||||
    },
 | 
			
		||||
    ImportedGeometry(ImportedGeometry),
 | 
			
		||||
    #[ts(skip)]
 | 
			
		||||
    Function {
 | 
			
		||||
        #[serde(skip)]
 | 
			
		||||
        #[serde(serialize_with = "function_value_stub")]
 | 
			
		||||
        #[ts(type = "null")]
 | 
			
		||||
        value: FunctionSource,
 | 
			
		||||
        #[serde(skip)]
 | 
			
		||||
        meta: Vec<Metadata>,
 | 
			
		||||
@ -109,6 +109,13 @@ pub enum KclValue {
 | 
			
		||||
    },
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn function_value_stub<S>(_value: &FunctionSource, serializer: S) -> Result<S::Ok, S::Error>
 | 
			
		||||
where
 | 
			
		||||
    S: serde::Serializer,
 | 
			
		||||
{
 | 
			
		||||
    serializer.serialize_unit()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, PartialEq, Default)]
 | 
			
		||||
pub enum FunctionSource {
 | 
			
		||||
    #[default]
 | 
			
		||||
 | 
			
		||||
@ -399,7 +399,7 @@ impl fmt::Display for PrimitiveType {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
 | 
			
		||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, ts_rs::TS, JsonSchema)]
 | 
			
		||||
#[ts(export)]
 | 
			
		||||
#[serde(tag = "type")]
 | 
			
		||||
pub enum NumericType {
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										137
									
								
								rust/kcl-lib/src/fmt.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										137
									
								
								rust/kcl-lib/src/fmt.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,137 @@
 | 
			
		||||
use serde::Serialize;
 | 
			
		||||
 | 
			
		||||
use crate::{execution::types::NumericType, pretty::NumericSuffix};
 | 
			
		||||
 | 
			
		||||
/// For the UI, display a number and its type for debugging purposes. This is
 | 
			
		||||
/// used by TS.
 | 
			
		||||
pub fn human_display_number(value: f64, ty: NumericType) -> String {
 | 
			
		||||
    match ty {
 | 
			
		||||
        NumericType::Known(unit_type) => format!("{value}: number({unit_type})"),
 | 
			
		||||
        NumericType::Default { len, angle } => format!("{value} (no units, defaulting to {len} or {angle})"),
 | 
			
		||||
        NumericType::Unknown => format!("{value} (number with unknown units)"),
 | 
			
		||||
        NumericType::Any => format!("{value} (number with any units)"),
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, thiserror::Error)]
 | 
			
		||||
#[serde(tag = "type")]
 | 
			
		||||
pub enum FormatNumericSuffixError {
 | 
			
		||||
    #[error("Invalid numeric suffix: {0}")]
 | 
			
		||||
    Invalid(NumericSuffix),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// For UI code generation, format a number with a suffix. The result must parse
 | 
			
		||||
/// as a literal. If it can't be done, returns an error.
 | 
			
		||||
///
 | 
			
		||||
/// This is used by TS.
 | 
			
		||||
pub fn format_number_literal(value: f64, suffix: NumericSuffix) -> Result<String, FormatNumericSuffixError> {
 | 
			
		||||
    match suffix {
 | 
			
		||||
        // There isn't a syntactic suffix for these. For unknown, we don't want
 | 
			
		||||
        // to ever generate the unknown suffix. We currently warn on it, and we
 | 
			
		||||
        // may remove it in the future.
 | 
			
		||||
        NumericSuffix::Length | NumericSuffix::Angle | NumericSuffix::Unknown => {
 | 
			
		||||
            Err(FormatNumericSuffixError::Invalid(suffix))
 | 
			
		||||
        }
 | 
			
		||||
        NumericSuffix::None
 | 
			
		||||
        | NumericSuffix::Count
 | 
			
		||||
        | NumericSuffix::Mm
 | 
			
		||||
        | NumericSuffix::Cm
 | 
			
		||||
        | NumericSuffix::M
 | 
			
		||||
        | NumericSuffix::Inch
 | 
			
		||||
        | NumericSuffix::Ft
 | 
			
		||||
        | NumericSuffix::Yd
 | 
			
		||||
        | NumericSuffix::Deg
 | 
			
		||||
        | NumericSuffix::Rad => Ok(format!("{value}{suffix}")),
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
    use pretty_assertions::assert_eq;
 | 
			
		||||
 | 
			
		||||
    use super::*;
 | 
			
		||||
    use crate::execution::types::{UnitAngle, UnitLen, UnitType};
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_human_display_number() {
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            human_display_number(1.0, NumericType::Known(UnitType::Count)),
 | 
			
		||||
            "1: number(Count)"
 | 
			
		||||
        );
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            human_display_number(1.0, NumericType::Known(UnitType::Length(UnitLen::M))),
 | 
			
		||||
            "1: number(m)"
 | 
			
		||||
        );
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            human_display_number(1.0, NumericType::Known(UnitType::Length(UnitLen::Mm))),
 | 
			
		||||
            "1: number(mm)"
 | 
			
		||||
        );
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            human_display_number(1.0, NumericType::Known(UnitType::Length(UnitLen::Inches))),
 | 
			
		||||
            "1: number(in)"
 | 
			
		||||
        );
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            human_display_number(1.0, NumericType::Known(UnitType::Length(UnitLen::Feet))),
 | 
			
		||||
            "1: number(ft)"
 | 
			
		||||
        );
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            human_display_number(1.0, NumericType::Known(UnitType::Angle(UnitAngle::Degrees))),
 | 
			
		||||
            "1: number(deg)"
 | 
			
		||||
        );
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            human_display_number(1.0, NumericType::Known(UnitType::Angle(UnitAngle::Radians))),
 | 
			
		||||
            "1: number(rad)"
 | 
			
		||||
        );
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            human_display_number(
 | 
			
		||||
                1.0,
 | 
			
		||||
                NumericType::Default {
 | 
			
		||||
                    len: UnitLen::Mm,
 | 
			
		||||
                    angle: UnitAngle::Degrees,
 | 
			
		||||
                }
 | 
			
		||||
            ),
 | 
			
		||||
            "1 (no units, defaulting to mm or deg)"
 | 
			
		||||
        );
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            human_display_number(
 | 
			
		||||
                1.0,
 | 
			
		||||
                NumericType::Default {
 | 
			
		||||
                    len: UnitLen::Feet,
 | 
			
		||||
                    angle: UnitAngle::Radians,
 | 
			
		||||
                }
 | 
			
		||||
            ),
 | 
			
		||||
            "1 (no units, defaulting to ft or rad)"
 | 
			
		||||
        );
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            human_display_number(1.0, NumericType::Unknown),
 | 
			
		||||
            "1 (number with unknown units)"
 | 
			
		||||
        );
 | 
			
		||||
        assert_eq!(human_display_number(1.0, NumericType::Any), "1 (number with any units)");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_format_number_literal() {
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            format_number_literal(1.0, NumericSuffix::Length),
 | 
			
		||||
            Err(FormatNumericSuffixError::Invalid(NumericSuffix::Length))
 | 
			
		||||
        );
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            format_number_literal(1.0, NumericSuffix::Angle),
 | 
			
		||||
            Err(FormatNumericSuffixError::Invalid(NumericSuffix::Angle))
 | 
			
		||||
        );
 | 
			
		||||
        assert_eq!(format_number_literal(1.0, NumericSuffix::None), Ok("1".to_owned()));
 | 
			
		||||
        assert_eq!(format_number_literal(1.0, NumericSuffix::Count), Ok("1_".to_owned()));
 | 
			
		||||
        assert_eq!(format_number_literal(1.0, NumericSuffix::Mm), Ok("1mm".to_owned()));
 | 
			
		||||
        assert_eq!(format_number_literal(1.0, NumericSuffix::Cm), Ok("1cm".to_owned()));
 | 
			
		||||
        assert_eq!(format_number_literal(1.0, NumericSuffix::M), Ok("1m".to_owned()));
 | 
			
		||||
        assert_eq!(format_number_literal(1.0, NumericSuffix::Inch), Ok("1in".to_owned()));
 | 
			
		||||
        assert_eq!(format_number_literal(1.0, NumericSuffix::Ft), Ok("1ft".to_owned()));
 | 
			
		||||
        assert_eq!(format_number_literal(1.0, NumericSuffix::Yd), Ok("1yd".to_owned()));
 | 
			
		||||
        assert_eq!(format_number_literal(1.0, NumericSuffix::Deg), Ok("1deg".to_owned()));
 | 
			
		||||
        assert_eq!(format_number_literal(1.0, NumericSuffix::Rad), Ok("1rad".to_owned()));
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            format_number_literal(1.0, NumericSuffix::Unknown),
 | 
			
		||||
            Err(FormatNumericSuffixError::Invalid(NumericSuffix::Unknown))
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -61,6 +61,7 @@ mod docs;
 | 
			
		||||
mod engine;
 | 
			
		||||
mod errors;
 | 
			
		||||
mod execution;
 | 
			
		||||
mod fmt;
 | 
			
		||||
mod fs;
 | 
			
		||||
pub mod lint;
 | 
			
		||||
mod log;
 | 
			
		||||
@ -108,7 +109,10 @@ pub use unparser::{recast_dir, walk_dir};
 | 
			
		||||
pub mod exec {
 | 
			
		||||
    #[cfg(feature = "artifact-graph")]
 | 
			
		||||
    pub use crate::execution::ArtifactCommand;
 | 
			
		||||
    pub use crate::execution::{DefaultPlanes, IdGenerator, KclValue, PlaneType, Sketch};
 | 
			
		||||
    pub use crate::execution::{
 | 
			
		||||
        types::{NumericType, UnitAngle, UnitLen, UnitType},
 | 
			
		||||
        DefaultPlanes, IdGenerator, KclValue, PlaneType, Sketch,
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(target_arch = "wasm32")]
 | 
			
		||||
@ -134,7 +138,10 @@ pub mod std_utils {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub mod pretty {
 | 
			
		||||
    pub use crate::{parsing::token::NumericSuffix, unparser::format_number};
 | 
			
		||||
    pub use crate::{
 | 
			
		||||
        fmt::{format_number_literal, human_display_number},
 | 
			
		||||
        parsing::token::NumericSuffix,
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(feature = "cli")]
 | 
			
		||||
 | 
			
		||||
@ -8,9 +8,7 @@ use crate::parsing::{
 | 
			
		||||
        MemberExpression, MemberObject, Node, NonCodeNode, NonCodeValue, ObjectExpression, Parameter, PipeExpression,
 | 
			
		||||
        Program, TagDeclarator, TypeDeclaration, UnaryExpression, VariableDeclaration, VariableKind,
 | 
			
		||||
    },
 | 
			
		||||
    deprecation,
 | 
			
		||||
    token::NumericSuffix,
 | 
			
		||||
    DeprecationKind, PIPE_OPERATOR,
 | 
			
		||||
    deprecation, DeprecationKind, PIPE_OPERATOR,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
impl Program {
 | 
			
		||||
@ -465,11 +463,6 @@ impl TypeDeclaration {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Used by TS.
 | 
			
		||||
pub fn format_number(value: f64, suffix: NumericSuffix) -> String {
 | 
			
		||||
    format!("{value}{suffix}")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Literal {
 | 
			
		||||
    fn recast(&self) -> String {
 | 
			
		||||
        match self.value {
 | 
			
		||||
 | 
			
		||||
@ -4,7 +4,8 @@ description: Variables in memory after executing add_lots.kcl
 | 
			
		||||
---
 | 
			
		||||
{
 | 
			
		||||
  "f": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  },
 | 
			
		||||
  "x": {
 | 
			
		||||
    "type": "Number",
 | 
			
		||||
 | 
			
		||||
@ -4,7 +4,8 @@ description: Variables in memory after executing cube.kcl
 | 
			
		||||
---
 | 
			
		||||
{
 | 
			
		||||
  "cube": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  },
 | 
			
		||||
  "myCube": {
 | 
			
		||||
    "type": "Solid",
 | 
			
		||||
 | 
			
		||||
@ -4,7 +4,8 @@ description: Variables in memory after executing double_map_fn.kcl
 | 
			
		||||
---
 | 
			
		||||
{
 | 
			
		||||
  "increment": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  },
 | 
			
		||||
  "xs": {
 | 
			
		||||
    "type": "MixedArray",
 | 
			
		||||
 | 
			
		||||
@ -435,7 +435,8 @@ description: Variables in memory after executing fillet-and-shell.kcl
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  "m25Screw": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  },
 | 
			
		||||
  "microUsb1Distance": {
 | 
			
		||||
    "type": "Number",
 | 
			
		||||
 | 
			
		||||
@ -4,7 +4,8 @@ description: Variables in memory after executing function_sketch.kcl
 | 
			
		||||
---
 | 
			
		||||
{
 | 
			
		||||
  "box": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  },
 | 
			
		||||
  "fnBox": {
 | 
			
		||||
    "type": "Solid",
 | 
			
		||||
 | 
			
		||||
@ -4,7 +4,8 @@ description: Variables in memory after executing function_sketch_with_position.k
 | 
			
		||||
---
 | 
			
		||||
{
 | 
			
		||||
  "box": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  },
 | 
			
		||||
  "thing": {
 | 
			
		||||
    "type": "Solid",
 | 
			
		||||
 | 
			
		||||
@ -2201,7 +2201,8 @@ description: Variables in memory after executing import_async.kcl
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  "leftInvolute": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  },
 | 
			
		||||
  "module": {
 | 
			
		||||
    "type": "Number",
 | 
			
		||||
@ -2263,7 +2264,8 @@ description: Variables in memory after executing import_async.kcl
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  "rightInvolute": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  },
 | 
			
		||||
  "rs": {
 | 
			
		||||
    "type": "MixedArray",
 | 
			
		||||
 | 
			
		||||
@ -4,9 +4,11 @@ description: Variables in memory after executing import_function_not_sketch.kcl
 | 
			
		||||
---
 | 
			
		||||
{
 | 
			
		||||
  "one": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  },
 | 
			
		||||
  "two": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -4,7 +4,8 @@ description: Variables in memory after executing import_glob.kcl
 | 
			
		||||
---
 | 
			
		||||
{
 | 
			
		||||
  "foo": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  },
 | 
			
		||||
  "three": {
 | 
			
		||||
    "type": "Number",
 | 
			
		||||
 | 
			
		||||
@ -4,6 +4,7 @@ description: Variables in memory after executing import_side_effect.kcl
 | 
			
		||||
---
 | 
			
		||||
{
 | 
			
		||||
  "foo": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -4,7 +4,8 @@ description: Variables in memory after executing intersect_cubes.kcl
 | 
			
		||||
---
 | 
			
		||||
{
 | 
			
		||||
  "cube": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  },
 | 
			
		||||
  "fullPart": {
 | 
			
		||||
    "type": "Solid",
 | 
			
		||||
 | 
			
		||||
@ -4,6 +4,7 @@ description: Variables in memory after executing 80-20-rail.kcl
 | 
			
		||||
---
 | 
			
		||||
{
 | 
			
		||||
  "rail8020": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -4,10 +4,12 @@ description: Variables in memory after executing bench.kcl
 | 
			
		||||
---
 | 
			
		||||
{
 | 
			
		||||
  "armRest": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  },
 | 
			
		||||
  "backSlats": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  },
 | 
			
		||||
  "benchLength": {
 | 
			
		||||
    "type": "Number",
 | 
			
		||||
@ -23,10 +25,12 @@ description: Variables in memory after executing bench.kcl
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  "connector": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  },
 | 
			
		||||
  "divider": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  },
 | 
			
		||||
  "dividerThickness": {
 | 
			
		||||
    "type": "Number",
 | 
			
		||||
@ -42,6 +46,7 @@ description: Variables in memory after executing bench.kcl
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  "seatSlats": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -197,7 +197,8 @@ description: Variables in memory after executing color-cube.kcl
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  "sketchRectangle": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  },
 | 
			
		||||
  "tealPlane": {
 | 
			
		||||
    "type": "Plane",
 | 
			
		||||
 | 
			
		||||
@ -4,6 +4,7 @@ description: Variables in memory after executing cycloidal-gear.kcl
 | 
			
		||||
---
 | 
			
		||||
{
 | 
			
		||||
  "cycloidalGear": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -4,13 +4,16 @@ description: Variables in memory after executing dodecahedron.kcl
 | 
			
		||||
---
 | 
			
		||||
{
 | 
			
		||||
  "calculateArrayLength": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  },
 | 
			
		||||
  "createFaceTemplate": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  },
 | 
			
		||||
  "createIntersection": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  },
 | 
			
		||||
  "dihedral": {
 | 
			
		||||
    "type": "Number",
 | 
			
		||||
 | 
			
		||||
@ -1352,7 +1352,8 @@ description: Variables in memory after executing enclosure.kcl
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  "function001": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  },
 | 
			
		||||
  "height": {
 | 
			
		||||
    "type": "Number",
 | 
			
		||||
 | 
			
		||||
@ -774,7 +774,8 @@ description: Variables in memory after executing exhaust-manifold.kcl
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  "primaryTube": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  },
 | 
			
		||||
  "primaryTubeDiameter": {
 | 
			
		||||
    "type": "Number",
 | 
			
		||||
 | 
			
		||||
@ -673,7 +673,8 @@ description: Variables in memory after executing focusrite-scarlett-mounting-bra
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  "bracketSketch": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  },
 | 
			
		||||
  "bs": {
 | 
			
		||||
    "type": "Sketch",
 | 
			
		||||
 | 
			
		||||
@ -2832,7 +2832,8 @@ description: Variables in memory after executing food-service-spatula.kcl
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  "slot": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  },
 | 
			
		||||
  "slotProfile000": {
 | 
			
		||||
    "type": "Sketch",
 | 
			
		||||
 | 
			
		||||
@ -19763,7 +19763,8 @@ description: Variables in memory after executing gear-rack.kcl
 | 
			
		||||
    ]
 | 
			
		||||
  },
 | 
			
		||||
  "tooth": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  },
 | 
			
		||||
  "width": {
 | 
			
		||||
    "type": "Number",
 | 
			
		||||
 | 
			
		||||
@ -2201,7 +2201,8 @@ description: Variables in memory after executing gear.kcl
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  "leftInvolute": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  },
 | 
			
		||||
  "module": {
 | 
			
		||||
    "type": "Number",
 | 
			
		||||
@ -2256,7 +2257,8 @@ description: Variables in memory after executing gear.kcl
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  "rightInvolute": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  },
 | 
			
		||||
  "rs": {
 | 
			
		||||
    "type": "MixedArray",
 | 
			
		||||
 | 
			
		||||
@ -10850,7 +10850,8 @@ description: Variables in memory after executing gridfinity-baseplate-magnets.kc
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  "face": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  },
 | 
			
		||||
  "firstStep": {
 | 
			
		||||
    "type": "Number",
 | 
			
		||||
@ -10938,10 +10939,12 @@ description: Variables in memory after executing gridfinity-baseplate-magnets.kc
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  "magnetBase": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  },
 | 
			
		||||
  "magnetCenterCutout": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  },
 | 
			
		||||
  "magnetCutoutExtrude": {
 | 
			
		||||
    "type": "Solid",
 | 
			
		||||
 | 
			
		||||
@ -10850,7 +10850,8 @@ description: Variables in memory after executing gridfinity-baseplate.kcl
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  "face": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  },
 | 
			
		||||
  "firstStep": {
 | 
			
		||||
    "type": "Number",
 | 
			
		||||
 | 
			
		||||
@ -14220,7 +14220,8 @@ description: Variables in memory after executing gridfinity-bins-stacking-lip.kc
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  "face": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  },
 | 
			
		||||
  "firstStep": {
 | 
			
		||||
    "type": "Number",
 | 
			
		||||
@ -15659,7 +15660,8 @@ description: Variables in memory after executing gridfinity-bins-stacking-lip.kc
 | 
			
		||||
    ]
 | 
			
		||||
  },
 | 
			
		||||
  "lipFace": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  },
 | 
			
		||||
  "lipHeight": {
 | 
			
		||||
    "type": "Number",
 | 
			
		||||
 | 
			
		||||
@ -14148,7 +14148,8 @@ description: Variables in memory after executing gridfinity-bins.kcl
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  "face": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  },
 | 
			
		||||
  "firstStep": {
 | 
			
		||||
    "type": "Number",
 | 
			
		||||
 | 
			
		||||
@ -17,7 +17,8 @@ description: Variables in memory after executing hex-nut.kcl
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  "hexNut": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  },
 | 
			
		||||
  "thickness": {
 | 
			
		||||
    "type": "Number",
 | 
			
		||||
 | 
			
		||||
@ -29,7 +29,8 @@ description: Variables in memory after executing keyboard.kcl
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  "keyFn": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  },
 | 
			
		||||
  "keyHeight": {
 | 
			
		||||
    "type": "Number",
 | 
			
		||||
@ -45,7 +46,8 @@ description: Variables in memory after executing keyboard.kcl
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  "o": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  },
 | 
			
		||||
  "plane001": {
 | 
			
		||||
    "type": "Object",
 | 
			
		||||
@ -2885,6 +2887,7 @@ description: Variables in memory after executing keyboard.kcl
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  "z": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -6262,7 +6262,8 @@ description: Variables in memory after executing kitt.kcl
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  "kitEar": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  },
 | 
			
		||||
  "kitEarDepth": {
 | 
			
		||||
    "type": "Number",
 | 
			
		||||
@ -13308,7 +13309,8 @@ description: Variables in memory after executing kitt.kcl
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  "kitLeg": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  },
 | 
			
		||||
  "kitLegOffset": {
 | 
			
		||||
    "type": "Number",
 | 
			
		||||
@ -22321,7 +22323,8 @@ description: Variables in memory after executing kitt.kcl
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  "pixelBox": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  },
 | 
			
		||||
  "seg01": {
 | 
			
		||||
    "type": "TagIdentifier",
 | 
			
		||||
 | 
			
		||||
@ -43,7 +43,8 @@ description: Variables in memory after executing makeup-mirror.kcl
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  "armFn": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  },
 | 
			
		||||
  "armLength": {
 | 
			
		||||
    "type": "Number",
 | 
			
		||||
@ -288,7 +289,8 @@ description: Variables in memory after executing makeup-mirror.kcl
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  "hingeFn": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  },
 | 
			
		||||
  "hingeGap": {
 | 
			
		||||
    "type": "Number",
 | 
			
		||||
@ -1194,7 +1196,8 @@ description: Variables in memory after executing makeup-mirror.kcl
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  "mirrorFn": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  },
 | 
			
		||||
  "mirrorRadius": {
 | 
			
		||||
    "type": "Number",
 | 
			
		||||
 | 
			
		||||
@ -435,7 +435,8 @@ description: Variables in memory after executing mounting-plate.kcl
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  "rectShape": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  },
 | 
			
		||||
  "rs": {
 | 
			
		||||
    "type": "Sketch",
 | 
			
		||||
 | 
			
		||||
@ -4,7 +4,8 @@ description: Variables in memory after executing pipe-flange-assembly.kcl
 | 
			
		||||
---
 | 
			
		||||
{
 | 
			
		||||
  "bolt": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  },
 | 
			
		||||
  "boltDiameter": {
 | 
			
		||||
    "type": "Number",
 | 
			
		||||
@ -103,7 +104,8 @@ description: Variables in memory after executing pipe-flange-assembly.kcl
 | 
			
		||||
    "value": "filletEdge"
 | 
			
		||||
  },
 | 
			
		||||
  "flange": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  },
 | 
			
		||||
  "flangeBackDiameter": {
 | 
			
		||||
    "type": "Number",
 | 
			
		||||
@ -240,7 +242,8 @@ description: Variables in memory after executing pipe-flange-assembly.kcl
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  "hexNut": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  },
 | 
			
		||||
  "hexNutDiameter": {
 | 
			
		||||
    "type": "Number",
 | 
			
		||||
@ -321,7 +324,8 @@ description: Variables in memory after executing pipe-flange-assembly.kcl
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  "pipe": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  },
 | 
			
		||||
  "pipeDiameter": {
 | 
			
		||||
    "type": "Number",
 | 
			
		||||
@ -376,7 +380,8 @@ description: Variables in memory after executing pipe-flange-assembly.kcl
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  "washer": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  },
 | 
			
		||||
  "washerInnerDia": {
 | 
			
		||||
    "type": "Number",
 | 
			
		||||
 | 
			
		||||
@ -77,7 +77,8 @@ description: Variables in memory after executing walkie-talkie.kcl
 | 
			
		||||
    "value": 2
 | 
			
		||||
  },
 | 
			
		||||
  "button": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  },
 | 
			
		||||
  "buttonHeight": {
 | 
			
		||||
    "type": "Number",
 | 
			
		||||
 | 
			
		||||
@ -4,10 +4,12 @@ description: Variables in memory after executing kw_fn.kcl
 | 
			
		||||
---
 | 
			
		||||
{
 | 
			
		||||
  "add": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  },
 | 
			
		||||
  "increment": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  },
 | 
			
		||||
  "three": {
 | 
			
		||||
    "type": "Number",
 | 
			
		||||
 | 
			
		||||
@ -4,7 +4,8 @@ description: Variables in memory after executing kw_fn_with_defaults.kcl
 | 
			
		||||
---
 | 
			
		||||
{
 | 
			
		||||
  "increment": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  },
 | 
			
		||||
  "twentyOne": {
 | 
			
		||||
    "type": "Number",
 | 
			
		||||
 | 
			
		||||
@ -17,7 +17,8 @@ description: Variables in memory after executing loop_tag.kcl
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  "calculatePoint": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  },
 | 
			
		||||
  "closedSketch": {
 | 
			
		||||
    "type": "Sketch",
 | 
			
		||||
 | 
			
		||||
@ -4,6 +4,7 @@ description: Variables in memory after executing multi_transform.kcl
 | 
			
		||||
---
 | 
			
		||||
{
 | 
			
		||||
  "transform": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -4,6 +4,7 @@ description: Variables in memory after executing pattern_circular_in_module.kcl
 | 
			
		||||
---
 | 
			
		||||
{
 | 
			
		||||
  "thing": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -4,6 +4,7 @@ description: Variables in memory after executing pattern_linear_in_module.kcl
 | 
			
		||||
---
 | 
			
		||||
{
 | 
			
		||||
  "thing": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -652,7 +652,8 @@ description: Variables in memory after executing pentagon_fillet_sugar.kcl
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  "circl": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  },
 | 
			
		||||
  "p": {
 | 
			
		||||
    "type": "Solid",
 | 
			
		||||
 | 
			
		||||
@ -4,10 +4,12 @@ description: Variables in memory after executing pipe_as_arg.kcl
 | 
			
		||||
---
 | 
			
		||||
{
 | 
			
		||||
  "cube": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  },
 | 
			
		||||
  "double": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  },
 | 
			
		||||
  "myCube": {
 | 
			
		||||
    "type": "Number",
 | 
			
		||||
@ -23,6 +25,7 @@ description: Variables in memory after executing pipe_as_arg.kcl
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  "width": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -223,7 +223,8 @@ description: Variables in memory after executing riddle_small.kcl
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  "t": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  },
 | 
			
		||||
  "xs": {
 | 
			
		||||
    "type": "Number",
 | 
			
		||||
 | 
			
		||||
@ -4,7 +4,8 @@ description: Variables in memory after executing rotate_after_fillet.kcl
 | 
			
		||||
---
 | 
			
		||||
{
 | 
			
		||||
  "bolt": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  },
 | 
			
		||||
  "boltDiameter": {
 | 
			
		||||
    "type": "Number",
 | 
			
		||||
 | 
			
		||||
@ -4,7 +4,8 @@ description: Variables in memory after executing scale_after_fillet.kcl
 | 
			
		||||
---
 | 
			
		||||
{
 | 
			
		||||
  "bolt": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  },
 | 
			
		||||
  "boltDiameter": {
 | 
			
		||||
    "type": "Number",
 | 
			
		||||
 | 
			
		||||
@ -4,10 +4,12 @@ description: Variables in memory after executing sketch_in_object.kcl
 | 
			
		||||
---
 | 
			
		||||
{
 | 
			
		||||
  "test": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  },
 | 
			
		||||
  "test2": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  },
 | 
			
		||||
  "x": {
 | 
			
		||||
    "type": "Sketch",
 | 
			
		||||
 | 
			
		||||
@ -4,7 +4,8 @@ description: Variables in memory after executing sketch_on_face_circle_tagged.kc
 | 
			
		||||
---
 | 
			
		||||
{
 | 
			
		||||
  "cube": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  },
 | 
			
		||||
  "myCircle": {
 | 
			
		||||
    "type": "TagIdentifier",
 | 
			
		||||
 | 
			
		||||
@ -4,7 +4,8 @@ description: Variables in memory after executing sketch_on_face_end.kcl
 | 
			
		||||
---
 | 
			
		||||
{
 | 
			
		||||
  "cube": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  },
 | 
			
		||||
  "part001": {
 | 
			
		||||
    "type": "Solid",
 | 
			
		||||
 | 
			
		||||
@ -4,7 +4,8 @@ description: Variables in memory after executing sketch_on_face_end_negative_ext
 | 
			
		||||
---
 | 
			
		||||
{
 | 
			
		||||
  "cube": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  },
 | 
			
		||||
  "part001": {
 | 
			
		||||
    "type": "Solid",
 | 
			
		||||
 | 
			
		||||
@ -4,7 +4,8 @@ description: Variables in memory after executing sketch_on_face_start.kcl
 | 
			
		||||
---
 | 
			
		||||
{
 | 
			
		||||
  "cube": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  },
 | 
			
		||||
  "foo": {
 | 
			
		||||
    "type": "Solid",
 | 
			
		||||
 | 
			
		||||
@ -4,7 +4,8 @@ description: Variables in memory after executing subtract_cylinder_from_cube.kcl
 | 
			
		||||
---
 | 
			
		||||
{
 | 
			
		||||
  "cube": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  },
 | 
			
		||||
  "fullPart": {
 | 
			
		||||
    "type": "Solid",
 | 
			
		||||
 | 
			
		||||
@ -4,7 +4,8 @@ description: Variables in memory after executing subtract_doesnt_need_brackets.k
 | 
			
		||||
---
 | 
			
		||||
{
 | 
			
		||||
  "cube": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  },
 | 
			
		||||
  "part001": {
 | 
			
		||||
    "type": "Solid",
 | 
			
		||||
 | 
			
		||||
@ -4,7 +4,8 @@ description: Variables in memory after executing translate_after_fillet.kcl
 | 
			
		||||
---
 | 
			
		||||
{
 | 
			
		||||
  "bolt": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  },
 | 
			
		||||
  "boltDiameter": {
 | 
			
		||||
    "type": "Number",
 | 
			
		||||
 | 
			
		||||
@ -4,7 +4,8 @@ description: Variables in memory after executing union_cubes.kcl
 | 
			
		||||
---
 | 
			
		||||
{
 | 
			
		||||
  "cube": {
 | 
			
		||||
    "type": "Function"
 | 
			
		||||
    "type": "Function",
 | 
			
		||||
    "value": null
 | 
			
		||||
  },
 | 
			
		||||
  "fullPart": {
 | 
			
		||||
    "type": "Solid",
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,11 @@
 | 
			
		||||
//! Wasm bindings for `kcl`.
 | 
			
		||||
 | 
			
		||||
use gloo_utils::format::JsValueSerdeExt;
 | 
			
		||||
use kcl_lib::{pretty::NumericSuffix, CoreDump, Program, SourceRange};
 | 
			
		||||
use kcl_lib::{
 | 
			
		||||
    exec::{NumericType, UnitAngle, UnitLen, UnitType},
 | 
			
		||||
    pretty::NumericSuffix,
 | 
			
		||||
    CoreDump, Program, SourceRange,
 | 
			
		||||
};
 | 
			
		||||
use wasm_bindgen::prelude::*;
 | 
			
		||||
 | 
			
		||||
// wasm_bindgen wrapper for lint
 | 
			
		||||
@ -50,11 +54,34 @@ pub fn recast_wasm(json_str: &str) -> Result<JsValue, JsError> {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[wasm_bindgen]
 | 
			
		||||
pub fn format_number(value: f64, suffix_json: &str) -> Result<String, JsError> {
 | 
			
		||||
pub fn format_number_literal(value: f64, suffix_json: &str) -> Result<String, JsError> {
 | 
			
		||||
    console_error_panic_hook::set_once();
 | 
			
		||||
 | 
			
		||||
    let suffix: NumericSuffix = serde_json::from_str(suffix_json).map_err(JsError::from)?;
 | 
			
		||||
    Ok(kcl_lib::pretty::format_number(value, suffix))
 | 
			
		||||
    kcl_lib::pretty::format_number_literal(value, suffix).map_err(JsError::from)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[wasm_bindgen]
 | 
			
		||||
pub fn human_display_number(value: f64, ty_json: &str) -> Result<String, String> {
 | 
			
		||||
    console_error_panic_hook::set_once();
 | 
			
		||||
 | 
			
		||||
    // ts-rs can't handle tuple types, so it mashes all of these types together.
 | 
			
		||||
    if let Ok(ty) = serde_json::from_str::<NumericType>(ty_json) {
 | 
			
		||||
        return Ok(kcl_lib::pretty::human_display_number(value, ty));
 | 
			
		||||
    }
 | 
			
		||||
    if let Ok(unit_type) = serde_json::from_str::<UnitType>(ty_json) {
 | 
			
		||||
        let ty = NumericType::Known(unit_type);
 | 
			
		||||
        return Ok(kcl_lib::pretty::human_display_number(value, ty));
 | 
			
		||||
    }
 | 
			
		||||
    if let Ok(unit_len) = serde_json::from_str::<UnitLen>(ty_json) {
 | 
			
		||||
        let ty = NumericType::Known(UnitType::Length(unit_len));
 | 
			
		||||
        return Ok(kcl_lib::pretty::human_display_number(value, ty));
 | 
			
		||||
    }
 | 
			
		||||
    if let Ok(unit_angle) = serde_json::from_str::<UnitAngle>(ty_json) {
 | 
			
		||||
        let ty = NumericType::Known(UnitType::Angle(unit_angle));
 | 
			
		||||
        return Ok(kcl_lib::pretty::human_display_number(value, ty));
 | 
			
		||||
    }
 | 
			
		||||
    Err(format!("Invalid type: {ty_json}"))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[wasm_bindgen]
 | 
			
		||||
 | 
			
		||||
@ -16,6 +16,13 @@ describe('processMemory', () => {
 | 
			
		||||
    return a - 2
 | 
			
		||||
  }
 | 
			
		||||
  otherVar = myFn(5)
 | 
			
		||||
  nFeet = 2ft
 | 
			
		||||
  nInches = 2in
 | 
			
		||||
  nMm = 2mm
 | 
			
		||||
  nDegrees = 2deg
 | 
			
		||||
  nRadians = 2rad
 | 
			
		||||
  nCount = 2_
 | 
			
		||||
  nUnknown = 1rad * PI
 | 
			
		||||
 | 
			
		||||
  theExtrude = startSketchOn(XY)
 | 
			
		||||
    |> startProfile(at = [0, 0])
 | 
			
		||||
@ -32,8 +39,17 @@ describe('processMemory', () => {
 | 
			
		||||
    const ast = assertParse(code)
 | 
			
		||||
    const execState = await enginelessExecutor(ast)
 | 
			
		||||
    const output = processMemory(execState.variables)
 | 
			
		||||
    expect(output.myVar).toEqual(5)
 | 
			
		||||
    expect(output.otherVar).toEqual(3)
 | 
			
		||||
    expect(output.nFeet).toEqual('2: number(ft)')
 | 
			
		||||
    expect(output.nInches).toEqual('2: number(in)')
 | 
			
		||||
    expect(output.nMm).toEqual('2: number(mm)')
 | 
			
		||||
    expect(output.nDegrees).toEqual('2: number(deg)')
 | 
			
		||||
    expect(output.nRadians).toEqual('2: number(rad)')
 | 
			
		||||
    expect(output.nCount).toEqual('2: number(Count)')
 | 
			
		||||
    expect(output.nUnknown).toEqual(
 | 
			
		||||
      '3.141592653589793 (number with unknown units)'
 | 
			
		||||
    )
 | 
			
		||||
    expect(output.myVar).toEqual('5 (no units, defaulting to mm or deg)')
 | 
			
		||||
    expect(output.otherVar).toEqual('3 (no units, defaulting to mm or deg)')
 | 
			
		||||
    expect(output.myFn).toEqual('__function__')
 | 
			
		||||
    expect(output.theExtrude).toEqual([
 | 
			
		||||
      {
 | 
			
		||||
 | 
			
		||||
@ -11,7 +11,7 @@ import { useModelingContext } from '@src/hooks/useModelingContext'
 | 
			
		||||
import { useResolvedTheme } from '@src/hooks/useResolvedTheme'
 | 
			
		||||
import { useKclContext } from '@src/lang/KclProvider'
 | 
			
		||||
import type { VariableMap } from '@src/lang/wasm'
 | 
			
		||||
import { sketchFromKclValueOptional } from '@src/lang/wasm'
 | 
			
		||||
import { humanDisplayNumber, sketchFromKclValueOptional } from '@src/lang/wasm'
 | 
			
		||||
import { Reason, trap } from '@src/lib/trap'
 | 
			
		||||
 | 
			
		||||
export const MemoryPaneMenu = () => {
 | 
			
		||||
@ -81,31 +81,29 @@ export const MemoryPane = () => {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const processMemory = (variables: VariableMap) => {
 | 
			
		||||
  const processedMemory: any = {}
 | 
			
		||||
  const processedMemory: Record<
 | 
			
		||||
    string,
 | 
			
		||||
    string | number | boolean | object | undefined
 | 
			
		||||
  > = {}
 | 
			
		||||
  for (const [key, val] of Object.entries(variables)) {
 | 
			
		||||
    if (val === undefined) continue
 | 
			
		||||
    if (
 | 
			
		||||
      val.type === 'Sketch' ||
 | 
			
		||||
      // @ts-ignore
 | 
			
		||||
      val.type !== 'Function'
 | 
			
		||||
    ) {
 | 
			
		||||
      const sk = sketchFromKclValueOptional(val, key)
 | 
			
		||||
      if (val.type === 'Solid') {
 | 
			
		||||
        processedMemory[key] = val.value.value.map(
 | 
			
		||||
          ({ ...rest }: ExtrudeSurface) => {
 | 
			
		||||
            return rest
 | 
			
		||||
          }
 | 
			
		||||
        )
 | 
			
		||||
      } else if (!(sk instanceof Reason)) {
 | 
			
		||||
        processedMemory[key] = sk.paths.map(({ __geoMeta, ...rest }: Path) => {
 | 
			
		||||
    const sk = sketchFromKclValueOptional(val, key)
 | 
			
		||||
    if (val.type === 'Solid') {
 | 
			
		||||
      processedMemory[key] = val.value.value.map(
 | 
			
		||||
        ({ ...rest }: ExtrudeSurface) => {
 | 
			
		||||
          return rest
 | 
			
		||||
        })
 | 
			
		||||
      } else {
 | 
			
		||||
        processedMemory[key] = val.value
 | 
			
		||||
      }
 | 
			
		||||
      //@ts-ignore
 | 
			
		||||
        }
 | 
			
		||||
      )
 | 
			
		||||
    } else if (!(sk instanceof Reason)) {
 | 
			
		||||
      processedMemory[key] = sk.paths.map(({ __geoMeta, ...rest }: Path) => {
 | 
			
		||||
        return rest
 | 
			
		||||
      })
 | 
			
		||||
    } else if (val.type === 'Function') {
 | 
			
		||||
      processedMemory[key] = `__function__`
 | 
			
		||||
      processedMemory[key] = '__function__'
 | 
			
		||||
    } else if (val.type === 'Number') {
 | 
			
		||||
      processedMemory[key] = humanDisplayNumber(val.value, val.ty)
 | 
			
		||||
    } else {
 | 
			
		||||
      processedMemory[key] = val.value
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return processedMemory
 | 
			
		||||
 | 
			
		||||
@ -31,28 +31,48 @@ import type {
 | 
			
		||||
  VariableDeclaration,
 | 
			
		||||
  VariableDeclarator,
 | 
			
		||||
} from '@src/lang/wasm'
 | 
			
		||||
import { formatNumber } from '@src/lang/wasm'
 | 
			
		||||
import { formatNumberLiteral } from '@src/lang/wasm'
 | 
			
		||||
import { err } from '@src/lib/trap'
 | 
			
		||||
 | 
			
		||||
export function createLiteral(value: number | string | boolean): Node<Literal> {
 | 
			
		||||
  // TODO: Should we handle string escape sequences?
 | 
			
		||||
  return {
 | 
			
		||||
    type: 'Literal',
 | 
			
		||||
    start: 0,
 | 
			
		||||
    end: 0,
 | 
			
		||||
    moduleId: 0,
 | 
			
		||||
    value: typeof value === 'number' ? { value, suffix: 'None' } : value,
 | 
			
		||||
    raw: `${value}`,
 | 
			
		||||
    outerAttrs: [],
 | 
			
		||||
    preComments: [],
 | 
			
		||||
    commentStart: 0,
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Note: This depends on WASM, but it's not async.  Callers are responsible for
 | 
			
		||||
 * awaiting init of the WASM module.
 | 
			
		||||
 */
 | 
			
		||||
export function createLiteral(value: LiteralValue | number): Node<Literal> {
 | 
			
		||||
  if (typeof value === 'number') {
 | 
			
		||||
    value = { value, suffix: 'None' }
 | 
			
		||||
export function createLiteralMaybeSuffix(
 | 
			
		||||
  value: LiteralValue
 | 
			
		||||
): Node<Literal> | Error {
 | 
			
		||||
  if (typeof value === 'string' || typeof value === 'boolean') {
 | 
			
		||||
    return createLiteral(value)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  let raw: string
 | 
			
		||||
  if (typeof value === 'string') {
 | 
			
		||||
    // TODO: Should we handle escape sequences?
 | 
			
		||||
    raw = `${value}`
 | 
			
		||||
  } else if (typeof value === 'boolean') {
 | 
			
		||||
    raw = `${value}`
 | 
			
		||||
  } else if (typeof value.value === 'number' && value.suffix === 'None') {
 | 
			
		||||
  if (typeof value.value === 'number' && value.suffix === 'None') {
 | 
			
		||||
    // Fast path for numbers when there are no units.
 | 
			
		||||
    raw = `${value.value}`
 | 
			
		||||
  } else {
 | 
			
		||||
    raw = formatNumber(value.value, value.suffix)
 | 
			
		||||
    const formatted = formatNumberLiteral(value.value, value.suffix)
 | 
			
		||||
    if (err(formatted)) {
 | 
			
		||||
      return new Error(
 | 
			
		||||
        `Invalid number literal: value=${value.value}, suffix=${value.suffix}`,
 | 
			
		||||
        { cause: formatted }
 | 
			
		||||
      )
 | 
			
		||||
    }
 | 
			
		||||
    raw = formatted
 | 
			
		||||
  }
 | 
			
		||||
  return {
 | 
			
		||||
    type: 'Literal',
 | 
			
		||||
 | 
			
		||||
@ -4,6 +4,7 @@ import {
 | 
			
		||||
  createArrayExpression,
 | 
			
		||||
  createIdentifier,
 | 
			
		||||
  createLiteral,
 | 
			
		||||
  createLiteralMaybeSuffix,
 | 
			
		||||
  createObjectExpression,
 | 
			
		||||
  createPipeExpression,
 | 
			
		||||
  createPipeSubstitution,
 | 
			
		||||
@ -35,6 +36,7 @@ import { initPromise } from '@src/lang/wasmUtils'
 | 
			
		||||
import { enginelessExecutor } from '@src/lib/testHelpers'
 | 
			
		||||
import { err } from '@src/lib/trap'
 | 
			
		||||
import { deleteFromSelection } from '@src/lang/modifyAst/deleteFromSelection'
 | 
			
		||||
import { assertNotErr } from '@src/unitTestUtils'
 | 
			
		||||
 | 
			
		||||
beforeAll(async () => {
 | 
			
		||||
  await initPromise
 | 
			
		||||
@ -50,7 +52,8 @@ describe('Testing createLiteral', () => {
 | 
			
		||||
  })
 | 
			
		||||
  it('should create a literal number with units', () => {
 | 
			
		||||
    const lit: LiteralValue = { value: 5, suffix: 'Mm' }
 | 
			
		||||
    const result = createLiteral(lit)
 | 
			
		||||
    const result = createLiteralMaybeSuffix(lit)
 | 
			
		||||
    assertNotErr(result)
 | 
			
		||||
    expect(result.type).toBe('Literal')
 | 
			
		||||
    expect((result as any).value.value).toBe(5)
 | 
			
		||||
    expect((result as any).value.suffix).toBe('Mm')
 | 
			
		||||
 | 
			
		||||
@ -3,7 +3,7 @@ import type { Program } from '@rust/kcl-lib/bindings/Program'
 | 
			
		||||
 | 
			
		||||
import type { ParseResult } from '@src/lang/wasm'
 | 
			
		||||
import {
 | 
			
		||||
  formatNumber,
 | 
			
		||||
  formatNumberLiteral,
 | 
			
		||||
  parse,
 | 
			
		||||
  errFromErrWithOutputs,
 | 
			
		||||
  rustImplPathToNode,
 | 
			
		||||
@ -33,12 +33,15 @@ it('can execute parsed AST', async () => {
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
it('formats numbers with units', () => {
 | 
			
		||||
  expect(formatNumber(1, 'None')).toEqual('1')
 | 
			
		||||
  expect(formatNumber(1, 'Count')).toEqual('1_')
 | 
			
		||||
  expect(formatNumber(1, 'Mm')).toEqual('1mm')
 | 
			
		||||
  expect(formatNumber(1, 'Inch')).toEqual('1in')
 | 
			
		||||
  expect(formatNumber(0.5, 'Mm')).toEqual('0.5mm')
 | 
			
		||||
  expect(formatNumber(-0.5, 'Mm')).toEqual('-0.5mm')
 | 
			
		||||
  expect(formatNumberLiteral(1, 'None')).toEqual('1')
 | 
			
		||||
  expect(formatNumberLiteral(1, 'Count')).toEqual('1_')
 | 
			
		||||
  expect(formatNumberLiteral(1, 'Mm')).toEqual('1mm')
 | 
			
		||||
  expect(formatNumberLiteral(1, 'Inch')).toEqual('1in')
 | 
			
		||||
  expect(formatNumberLiteral(0.5, 'Mm')).toEqual('0.5mm')
 | 
			
		||||
  expect(formatNumberLiteral(-0.5, 'Mm')).toEqual('-0.5mm')
 | 
			
		||||
  expect(formatNumberLiteral(1, 'Unknown')).toEqual(
 | 
			
		||||
    new Error('Error formatting number literal: value=1, suffix=Unknown')
 | 
			
		||||
  )
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
describe('test errFromErrWithOutputs', () => {
 | 
			
		||||
 | 
			
		||||
@ -47,9 +47,10 @@ import {
 | 
			
		||||
  coredump,
 | 
			
		||||
  default_app_settings,
 | 
			
		||||
  default_project_settings,
 | 
			
		||||
  format_number,
 | 
			
		||||
  format_number_literal,
 | 
			
		||||
  get_kcl_version,
 | 
			
		||||
  get_tangential_arc_to_info,
 | 
			
		||||
  human_display_number,
 | 
			
		||||
  is_kcl_empty_or_only_settings,
 | 
			
		||||
  is_points_ccw,
 | 
			
		||||
  kcl_lint,
 | 
			
		||||
@ -67,6 +68,7 @@ import {
 | 
			
		||||
  LABELED_ARG_FIELD,
 | 
			
		||||
  UNLABELED_ARG,
 | 
			
		||||
} from '@src/lang/queryAstConstants'
 | 
			
		||||
import type { NumericType } from '@rust/kcl-lib/bindings/NumericType'
 | 
			
		||||
 | 
			
		||||
export type { ArrayExpression } from '@rust/kcl-lib/bindings/ArrayExpression'
 | 
			
		||||
export type {
 | 
			
		||||
@ -440,8 +442,35 @@ export const recast = (ast: Program): string | Error => {
 | 
			
		||||
/**
 | 
			
		||||
 * Format a number with suffix as KCL.
 | 
			
		||||
 */
 | 
			
		||||
export function formatNumber(value: number, suffix: NumericSuffix): string {
 | 
			
		||||
  return format_number(value, JSON.stringify(suffix))
 | 
			
		||||
export function formatNumberLiteral(
 | 
			
		||||
  value: number,
 | 
			
		||||
  suffix: NumericSuffix
 | 
			
		||||
): string | Error {
 | 
			
		||||
  try {
 | 
			
		||||
    return format_number_literal(value, JSON.stringify(suffix))
 | 
			
		||||
  } catch (e) {
 | 
			
		||||
    return new Error(
 | 
			
		||||
      `Error formatting number literal: value=${value}, suffix=${suffix}`,
 | 
			
		||||
      { cause: e }
 | 
			
		||||
    )
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Debug display a number with suffix, for human consumption only.
 | 
			
		||||
 */
 | 
			
		||||
export function humanDisplayNumber(
 | 
			
		||||
  value: number,
 | 
			
		||||
  ty: NumericType
 | 
			
		||||
): string | Error {
 | 
			
		||||
  try {
 | 
			
		||||
    return human_display_number(value, JSON.stringify(ty))
 | 
			
		||||
  } catch (e) {
 | 
			
		||||
    return new Error(
 | 
			
		||||
      `Error formatting number for human display: value=${value}, ty=${JSON.stringify(ty)}`,
 | 
			
		||||
      { cause: e }
 | 
			
		||||
    )
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function isPointsCCW(points: Coords2d[]): number {
 | 
			
		||||
 | 
			
		||||
@ -12,7 +12,8 @@ import type {
 | 
			
		||||
  coredump as CoreDump,
 | 
			
		||||
  default_app_settings as DefaultAppSettings,
 | 
			
		||||
  default_project_settings as DefaultProjectSettings,
 | 
			
		||||
  format_number as FormatNumber,
 | 
			
		||||
  format_number_literal as FormatNumberLiteral,
 | 
			
		||||
  human_display_number as HumanDisplayNumber,
 | 
			
		||||
  get_kcl_version as GetKclVersion,
 | 
			
		||||
  get_tangential_arc_to_info as GetTangentialArcToInfo,
 | 
			
		||||
  import_file_extensions as ImportFileExtensions,
 | 
			
		||||
@ -55,8 +56,11 @@ export const parse_wasm: typeof ParseWasm = (...args) => {
 | 
			
		||||
export const recast_wasm: typeof RecastWasm = (...args) => {
 | 
			
		||||
  return getModule().recast_wasm(...args)
 | 
			
		||||
}
 | 
			
		||||
export const format_number: typeof FormatNumber = (...args) => {
 | 
			
		||||
  return getModule().format_number(...args)
 | 
			
		||||
export const format_number_literal: typeof FormatNumberLiteral = (...args) => {
 | 
			
		||||
  return getModule().format_number_literal(...args)
 | 
			
		||||
}
 | 
			
		||||
export const human_display_number: typeof HumanDisplayNumber = (...args) => {
 | 
			
		||||
  return getModule().human_display_number(...args)
 | 
			
		||||
}
 | 
			
		||||
export const kcl_lint: typeof KclLint = (...args) => {
 | 
			
		||||
  return getModule().kcl_lint(...args)
 | 
			
		||||
 | 
			
		||||
@ -10,6 +10,16 @@ import { createArrayExpression } from '@src/lang/create'
 | 
			
		||||
import { findKwArg, findKwArgAny } from '@src/lang/util'
 | 
			
		||||
import type { CallExpressionKw, Expr } from '@src/lang/wasm'
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Throw x if it's an Error. Only use this in tests.
 | 
			
		||||
 */
 | 
			
		||||
export function assertNotErr<T>(x: T): asserts x is Exclude<T, Error> {
 | 
			
		||||
  if (x instanceof Error) {
 | 
			
		||||
    // eslint-disable-next-line suggest-no-throw/suggest-no-throw
 | 
			
		||||
    throw x
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
Find the angle and some sort of length parameter from an angledLine-ish call.
 | 
			
		||||
E.g. finds the (angle, length) in angledLine or the (angle, endAbsoluteX) in angledLineToX
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user