Support types in the standard library (#5651)

* Parse an unparse type decls (and refactor impl attributes slightly)

Signed-off-by: Nick Cameron <nrc@ncameron.org>

* Remove special treatment of geometric types from parser and executor

Signed-off-by: Nick Cameron <nrc@ncameron.org>

* Generate docs for std types

Signed-off-by: Nick Cameron <nrc@ncameron.org>

* Hover tool-tips for types and fixup the frontend

Signed-off-by: Nick Cameron <nrc@ncameron.org>

* Fixes

Signed-off-by: Nick Cameron <nrc@ncameron.org>

---------

Signed-off-by: Nick Cameron <nrc@ncameron.org>
This commit is contained in:
Nick Cameron
2025-03-08 03:53:34 +13:00
committed by GitHub
parent 79d37d360a
commit 5d25f4a0e5
175 changed files with 2023 additions and 1647 deletions

View File

@ -5,8 +5,8 @@ use crate::parsing::ast::types::{
CallExpression, CallExpressionKw, DefaultParamVal, ElseIf, Expr, ExpressionStatement, FunctionExpression,
Identifier, IfExpression, ImportItem, ImportSelector, ImportStatement, ItemVisibility, KclNone, LabelledExpression,
Literal, LiteralIdentifier, LiteralValue, MemberExpression, MemberObject, ObjectExpression, ObjectProperty,
Parameter, PipeExpression, PipeSubstitution, Program, ReturnStatement, TagDeclarator, Type, UnaryExpression,
VariableDeclaration, VariableDeclarator, VariableKind,
Parameter, PipeExpression, PipeSubstitution, PrimitiveType, Program, ReturnStatement, TagDeclarator, Type,
TypeDeclaration, UnaryExpression, VariableDeclaration, VariableDeclarator, VariableKind,
};
/// Position-independent digest of the AST node.
@ -113,6 +113,7 @@ impl BodyItem {
BodyItem::ImportStatement(s) => s.compute_digest(),
BodyItem::ExpressionStatement(es) => es.compute_digest(),
BodyItem::VariableDeclaration(vs) => vs.compute_digest(),
BodyItem::TypeDeclaration(t) => t.compute_digest(),
BodyItem::ReturnStatement(rs) => rs.compute_digest(),
});
@ -191,11 +192,11 @@ impl Type {
match self {
Type::Primitive(prim) => {
hasher.update(b"FnArgType::Primitive");
hasher.update(prim.digestable_id())
hasher.update(prim.compute_digest())
}
Type::Array(prim) => {
hasher.update(b"FnArgType::Array");
hasher.update(prim.digestable_id())
hasher.update(prim.compute_digest())
}
Type::Object { properties } => {
hasher.update(b"FnArgType::Object");
@ -210,6 +211,21 @@ impl Type {
}
}
impl PrimitiveType {
pub fn compute_digest(&mut self) -> Digest {
let mut hasher = Sha256::new();
match self {
PrimitiveType::Named(id) => hasher.update(id.compute_digest()),
PrimitiveType::String => hasher.update(b"string"),
PrimitiveType::Number(suffix) => hasher.update(suffix.digestable_id()),
PrimitiveType::Boolean => hasher.update(b"bool"),
PrimitiveType::Tag => hasher.update(b"tag"),
}
hasher.finalize().into()
}
}
impl Parameter {
compute_digest!(|slf, hasher| {
hasher.update(slf.identifier.compute_digest());
@ -275,6 +291,18 @@ impl VariableDeclaration {
});
}
impl TypeDeclaration {
compute_digest!(|slf, hasher| {
hasher.update(slf.name.compute_digest());
if let Some(args) = &mut slf.args {
hasher.update([1]);
for a in args {
hasher.update(a.compute_digest());
}
}
});
}
impl VariableKind {
fn digestable_id(&self) -> [u8; 1] {
match self {

View File

@ -13,6 +13,7 @@ impl BodyItem {
BodyItem::ImportStatement(stmt) => stmt.module_id,
BodyItem::ExpressionStatement(expression_statement) => expression_statement.module_id,
BodyItem::VariableDeclaration(variable_declaration) => variable_declaration.module_id,
BodyItem::TypeDeclaration(ty_declaration) => ty_declaration.module_id,
BodyItem::ReturnStatement(return_statement) => return_statement.module_id,
}
}

View File

@ -55,6 +55,9 @@ pub async fn modify_ast_for_sketch(
let constraint_level = match ast_sketch {
super::types::Definition::Variable(var) => var.get_constraint_level(),
super::types::Definition::Import(import) => import.get_constraint_level(),
super::types::Definition::Type(_) => ConstraintLevel::Ignore {
source_ranges: Vec::new(),
},
};
match &constraint_level {
ConstraintLevel::None { source_ranges: _ } => {}

View File

@ -38,6 +38,7 @@ mod none;
pub enum Definition<'a> {
Variable(&'a VariableDeclarator),
Import(NodeRef<'a, ImportStatement>),
Type(NodeRef<'a, TypeDeclaration>),
}
#[derive(Debug, Default, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS)]
@ -54,18 +55,6 @@ pub struct Node<T> {
pub outer_attrs: NodeList<Annotation>,
}
impl<T> Node<T> {
pub fn metadata(&self) -> Metadata {
Metadata {
source_range: SourceRange::new(self.start, self.end, self.module_id),
}
}
pub fn contains(&self, pos: usize) -> bool {
self.start <= pos && pos <= self.end
}
}
impl<T: JsonSchema> schemars::JsonSchema for Node<T> {
fn schema_name() -> String {
T::schema_name()
@ -126,6 +115,26 @@ impl<T> Node<T> {
pub fn as_source_ranges(&self) -> Vec<SourceRange> {
vec![self.as_source_range()]
}
pub fn metadata(&self) -> Metadata {
Metadata {
source_range: SourceRange::new(self.start, self.end, self.module_id),
}
}
pub fn contains(&self, pos: usize) -> bool {
self.start <= pos && pos <= self.end
}
pub fn map<U>(self, f: fn(T) -> U) -> Node<U> {
Node {
inner: f(self.inner),
start: self.start,
end: self.end,
module_id: self.module_id,
outer_attrs: self.outer_attrs,
}
}
}
impl<T> Deref for Node<T> {
@ -352,7 +361,7 @@ impl Program {
// Recurse over the item.
match item {
BodyItem::ImportStatement(_) => None,
BodyItem::ImportStatement(_) | BodyItem::TypeDeclaration(_) => None,
BodyItem::ExpressionStatement(expression_statement) => Some(&expression_statement.expression),
BodyItem::VariableDeclaration(variable_declaration) => variable_declaration.get_expr_for_position(pos),
BodyItem::ReturnStatement(return_statement) => Some(&return_statement.argument),
@ -373,6 +382,7 @@ impl Program {
Some(BodyItem::VariableDeclaration(variable_declaration)) => {
variable_declaration.get_expr_for_position(pos)
}
Some(BodyItem::TypeDeclaration(_)) => None,
Some(BodyItem::ReturnStatement(return_statement)) => Some(&return_statement.argument),
None => return false,
};
@ -395,7 +405,7 @@ impl Program {
// We only care about the top level things in the program.
for item in &self.body {
match item {
BodyItem::ImportStatement(_) => continue,
BodyItem::ImportStatement(_) | BodyItem::TypeDeclaration(_) => continue,
BodyItem::ExpressionStatement(expression_statement) => {
if let Some(folding_range) = expression_statement.expression.get_lsp_folding_range() {
ranges.push(folding_range)
@ -425,16 +435,13 @@ impl Program {
break;
}
}
BodyItem::ExpressionStatement(_expression_statement) => {
continue;
}
BodyItem::VariableDeclaration(ref mut variable_declaration) => {
if let Some(var_old_name) = variable_declaration.rename_symbol(new_name, pos) {
old_name = Some(var_old_name);
break;
}
}
BodyItem::ReturnStatement(_return_statement) => continue,
_ => {}
}
}
@ -458,6 +465,7 @@ impl Program {
BodyItem::VariableDeclaration(ref mut variable_declaration) => {
variable_declaration.get_mut_expr_for_position(pos)
}
BodyItem::TypeDeclaration(_) => None,
BodyItem::ReturnStatement(ref mut return_statement) => Some(&mut return_statement.argument),
};
@ -483,16 +491,17 @@ impl Program {
fn rename_identifiers(&mut self, old_name: &str, new_name: &str) {
for item in &mut self.body {
match item {
BodyItem::ImportStatement(ref mut stmt) => {
BodyItem::ImportStatement(stmt) => {
stmt.rename_identifiers(old_name, new_name);
}
BodyItem::ExpressionStatement(ref mut expression_statement) => {
BodyItem::ExpressionStatement(expression_statement) => {
expression_statement.expression.rename_identifiers(old_name, new_name);
}
BodyItem::VariableDeclaration(ref mut variable_declaration) => {
BodyItem::VariableDeclaration(variable_declaration) => {
variable_declaration.rename_identifiers(old_name, new_name);
}
BodyItem::ReturnStatement(ref mut return_statement) => {
BodyItem::TypeDeclaration(_) => {}
BodyItem::ReturnStatement(return_statement) => {
return_statement.argument.rename_identifiers(old_name, new_name);
}
}
@ -506,7 +515,7 @@ impl Program {
BodyItem::ImportStatement(_) => {
continue;
}
BodyItem::ExpressionStatement(_expression_statement) => {
BodyItem::ExpressionStatement(_) => {
continue;
}
BodyItem::VariableDeclaration(ref mut variable_declaration) => {
@ -515,7 +524,10 @@ impl Program {
return;
}
}
BodyItem::ReturnStatement(_return_statement) => continue,
BodyItem::TypeDeclaration(_) => {
continue;
}
BodyItem::ReturnStatement(_) => continue,
}
}
}
@ -531,6 +543,7 @@ impl Program {
BodyItem::VariableDeclaration(ref mut variable_declaration) => {
variable_declaration.replace_value(source_range, new_value.clone())
}
BodyItem::TypeDeclaration(_) => {}
BodyItem::ReturnStatement(ref mut return_statement) => {
return_statement.argument.replace_value(source_range, new_value.clone())
}
@ -555,6 +568,11 @@ impl Program {
return Some(Definition::Variable(&variable_declaration.declaration));
}
}
BodyItem::TypeDeclaration(ty_declaration) => {
if ty_declaration.name.name == name {
return Some(Definition::Type(ty_declaration));
}
}
BodyItem::ReturnStatement(_return_statement) => continue,
}
}
@ -588,6 +606,7 @@ pub enum BodyItem {
ImportStatement(BoxNode<ImportStatement>),
ExpressionStatement(Node<ExpressionStatement>),
VariableDeclaration(BoxNode<VariableDeclaration>),
TypeDeclaration(BoxNode<TypeDeclaration>),
ReturnStatement(Node<ReturnStatement>),
}
@ -597,6 +616,7 @@ impl BodyItem {
BodyItem::ImportStatement(stmt) => stmt.start,
BodyItem::ExpressionStatement(expression_statement) => expression_statement.start,
BodyItem::VariableDeclaration(variable_declaration) => variable_declaration.start,
BodyItem::TypeDeclaration(ty_declaration) => ty_declaration.start,
BodyItem::ReturnStatement(return_statement) => return_statement.start,
}
}
@ -606,6 +626,7 @@ impl BodyItem {
BodyItem::ImportStatement(stmt) => stmt.end,
BodyItem::ExpressionStatement(expression_statement) => expression_statement.end,
BodyItem::VariableDeclaration(variable_declaration) => variable_declaration.end,
BodyItem::TypeDeclaration(ty_declaration) => ty_declaration.end,
BodyItem::ReturnStatement(return_statement) => return_statement.end,
}
}
@ -615,6 +636,7 @@ impl BodyItem {
BodyItem::ImportStatement(node) => node.outer_attrs = attr,
BodyItem::ExpressionStatement(node) => node.outer_attrs = attr,
BodyItem::VariableDeclaration(node) => node.outer_attrs = attr,
BodyItem::TypeDeclaration(ty_declaration) => ty_declaration.outer_attrs = attr,
BodyItem::ReturnStatement(node) => node.outer_attrs = attr,
}
}
@ -624,6 +646,7 @@ impl BodyItem {
BodyItem::ImportStatement(node) => &node.outer_attrs,
BodyItem::ExpressionStatement(node) => &node.outer_attrs,
BodyItem::VariableDeclaration(node) => &node.outer_attrs,
BodyItem::TypeDeclaration(ty_declaration) => &ty_declaration.outer_attrs,
BodyItem::ReturnStatement(node) => &node.outer_attrs,
}
}
@ -633,6 +656,7 @@ impl BodyItem {
BodyItem::ImportStatement(node) => &mut node.outer_attrs,
BodyItem::ExpressionStatement(node) => &mut node.outer_attrs,
BodyItem::VariableDeclaration(node) => &mut node.outer_attrs,
BodyItem::TypeDeclaration(ty_declaration) => &mut ty_declaration.outer_attrs,
BodyItem::ReturnStatement(node) => &mut node.outer_attrs,
}
}
@ -1765,6 +1789,20 @@ impl ItemVisibility {
}
}
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
#[ts(export)]
#[serde(tag = "type")]
pub struct TypeDeclaration {
pub name: Node<Identifier>,
pub args: Option<NodeList<Identifier>>,
#[serde(default, skip_serializing_if = "ItemVisibility::is_default")]
pub visibility: ItemVisibility,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[ts(optional)]
pub digest: Option<Digest>,
}
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
#[ts(export)]
#[serde(tag = "type")]
@ -2832,7 +2870,8 @@ impl PipeExpression {
}
}
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq, ts_rs::TS, JsonSchema)]
#[allow(clippy::large_enum_variant)]
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
#[ts(export)]
#[serde(tag = "type")]
pub enum PrimitiveType {
@ -2845,39 +2884,16 @@ pub enum PrimitiveType {
Boolean,
/// A tag.
Tag,
/// A sketch type.
Sketch,
/// A sketch surface type.
SketchSurface,
/// An solid type.
Solid,
/// A plane.
Plane,
/// An identifier used as a type (not really a primitive type, but whatever).
Named(Node<Identifier>),
}
impl PrimitiveType {
pub fn digestable_id(&self) -> &[u8] {
match self {
PrimitiveType::String => b"string",
PrimitiveType::Number(suffix) => suffix.digestable_id(),
PrimitiveType::Boolean => b"bool",
PrimitiveType::Tag => b"tag",
PrimitiveType::Sketch => b"Sketch",
PrimitiveType::SketchSurface => b"SketchSurface",
PrimitiveType::Solid => b"Solid",
PrimitiveType::Plane => b"Plane",
}
}
pub fn from_str(s: &str, suffix: Option<NumericSuffix>) -> Option<Self> {
pub fn primitive_from_str(s: &str, suffix: Option<NumericSuffix>) -> Option<Self> {
match (s, suffix) {
("string", None) => Some(PrimitiveType::String),
("bool", None) => Some(PrimitiveType::Boolean),
("tag", None) => Some(PrimitiveType::Tag),
("Sketch", None) => Some(PrimitiveType::Sketch),
("SketchSurface", None) => Some(PrimitiveType::SketchSurface),
("Solid", None) => Some(PrimitiveType::Solid),
("Plane", None) => Some(PrimitiveType::Plane),
("number", None) => Some(PrimitiveType::Number(NumericSuffix::None)),
("number", Some(s)) => Some(PrimitiveType::Number(s)),
_ => None,
@ -2898,10 +2914,7 @@ impl fmt::Display for PrimitiveType {
PrimitiveType::String => write!(f, "string"),
PrimitiveType::Boolean => write!(f, "bool"),
PrimitiveType::Tag => write!(f, "tag"),
PrimitiveType::Sketch => write!(f, "Sketch"),
PrimitiveType::SketchSurface => write!(f, "SketchSurface"),
PrimitiveType::Solid => write!(f, "Solid"),
PrimitiveType::Plane => write!(f, "Plane"),
PrimitiveType::Named(n) => write!(f, "{}", n.name),
}
}
}