kcl_lib/ast/types/
condition.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
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};

// 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
type IfBlock = crate::ast::types::Program;

#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, Bake)]
#[databake(path = kcl_lib::ast::types)]
#[ts(export)]
#[serde(tag = "type")]
pub struct IfExpression {
    pub cond: Box<Expr>,
    pub then_val: BoxNode<IfBlock>,
    pub else_ifs: NodeList<ElseIf>,
    pub final_else: BoxNode<IfBlock>,

    pub digest: Option<Digest>,
}

#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, Bake)]
#[databake(path = kcl_lib::ast::types)]
#[ts(export)]
#[serde(tag = "type")]
pub struct ElseIf {
    pub cond: Expr,
    pub then_val: BoxNode<IfBlock>,

    pub digest: Option<Digest>,
}

// Source code metadata

impl Node<IfExpression> {
    fn source_ranges(&self) -> Vec<SourceRange> {
        vec![SourceRange::from(self)]
    }
}

impl Node<ElseIf> {
    #[allow(dead_code)]
    fn source_ranges(&self) -> Vec<SourceRange> {
        vec![SourceRange([self.start, self.end, self.module_id.as_usize()])]
    }
}

// IDE support and refactors

impl Node<IfExpression> {
    /// Get the constraint level.
    pub fn get_constraint_level(&self) -> ConstraintLevel {
        ConstraintLevel::Full {
            source_ranges: self.source_ranges(),
        }
    }
}

impl IfExpression {
    pub fn get_hover_value_for_position(&self, pos: usize, code: &str) -> Option<Hover> {
        self.cond
            .get_hover_value_for_position(pos, code)
            .or_else(|| self.then_val.get_hover_value_for_position(pos, code))
            .or_else(|| {
                self.else_ifs
                    .iter()
                    .find_map(|else_if| else_if.get_hover_value_for_position(pos, code))
            })
            .or_else(|| self.final_else.get_hover_value_for_position(pos, code))
    }

    /// Rename all identifiers that have the old name to the new given name.
    pub fn rename_identifiers(&mut self, old_name: &str, new_name: &str) {
        self.cond.rename_identifiers(old_name, new_name);
        self.then_val.rename_identifiers(old_name, new_name);
        for else_if in &mut self.else_ifs {
            else_if.rename_identifiers(old_name, new_name);
        }
        self.final_else.rename_identifiers(old_name, new_name);
    }

    pub fn replace_value(&mut self, source_range: SourceRange, new_value: Expr) {
        self.cond.replace_value(source_range, new_value.clone());
        for else_if in &mut self.else_ifs {
            else_if.cond.replace_value(source_range, new_value.clone());
        }
    }
}

impl ElseIf {
    fn get_hover_value_for_position(&self, pos: usize, code: &str) -> Option<Hover> {
        self.cond
            .get_hover_value_for_position(pos, code)
            .or_else(|| self.then_val.get_hover_value_for_position(pos, code))
    }
    /// Rename all identifiers that have the old name to the new given name.
    fn rename_identifiers(&mut self, old_name: &str, new_name: &str) {
        self.cond.rename_identifiers(old_name, new_name);
        self.then_val.rename_identifiers(old_name, new_name);
    }
}