logicaffeine_web/
composer.rs1use logicaffeine_language::symbol_dict::notation;
9use std::sync::LazyLock;
10
11#[derive(Debug, Clone, PartialEq)]
13pub struct SymbolButton {
14 pub symbol: &'static str,
16 pub name: &'static str,
18 pub meaning: String,
20}
21
22impl SymbolButton {
23 pub fn label(&self) -> String {
25 format!("Insert {}, {}", self.symbol, self.name)
26 }
27}
28
29const PAD: &[(&str, &str, Option<&str>)] = &[
32 ("∀", "for every", None),
33 ("∃", "there is", None),
34 ("¬", "not", None),
35 ("∧", "and", None),
36 ("∨", "or", None),
37 ("→", "if then", None),
38 ("↔", "if and only if", None),
39 ("□", "necessarily", None),
40 ("◇", "possibly", None),
41 ("=", "is the same as", None),
42 ("(", "open", Some("starts a group, or a list of arguments")),
43 (")", "close", Some("ends a group, or a list of arguments")),
44 (",", "comma", Some("separates the arguments of a predicate")),
45];
46
47static BUTTONS: LazyLock<Vec<SymbolButton>> = LazyLock::new(|| {
48 let dictionary = notation();
49 PAD.iter()
50 .map(|&(symbol, name, own)| {
51 let meaning = match own {
52 Some(meaning) => meaning.to_string(),
53 None => dictionary
54 .iter()
55 .find(|entry| entry.symbol == symbol)
56 .map(|entry| entry.description.clone())
57 .unwrap_or_else(|| panic!("the symbol dictionary has no meaning for {symbol}")),
58 };
59 SymbolButton { symbol, name, meaning }
60 })
61 .collect()
62});
63
64pub fn symbol_buttons() -> &'static [SymbolButton] {
66 &BUTTONS
67}
68
69fn byte_at(text: &str, units: usize) -> usize {
72 let mut counted = 0;
73 for (at, c) in text.char_indices() {
74 if counted >= units {
75 return at;
76 }
77 counted += c.len_utf16();
78 }
79 text.len()
80}
81
82pub fn insert_at(text: &str, start: usize, end: usize, insert: &str) -> (String, usize) {
86 let (from, to) = (byte_at(text, start.min(end)), byte_at(text, start.max(end)));
87 let mut out = String::with_capacity(text.len() + insert.len());
88 out.push_str(&text[..from]);
89 out.push_str(insert);
90 let caret = out.encode_utf16().count();
91 out.push_str(&text[to..]);
92 (out, caret)
93}
94
95#[cfg(test)]
96#[path = "../../../crates/logicaffeine_tests/unit/logicaffeine_web/composer/tests.rs"]
97mod tests;