1use std::fmt;
4
5use serde::{Deserialize, Serialize};
6
7use crate::model::{Cursor, Kind, NodeId, SeqId, Tree};
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
11#[serde(rename_all = "snake_case")]
12pub enum Slot {
13 Numerator,
15 Denominator,
17 Base,
19 Sub,
21 Sup,
23 Lower,
25 Upper,
27 Index,
29 Radicand,
31 Body,
33 Over,
35 Under,
37 Cell {
39 row: usize,
41 col: usize,
43 },
44}
45
46#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
48pub struct Step {
49 pub node: usize,
51 pub slot: Slot,
53}
54
55#[derive(Debug, Clone, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
57pub struct CaretPath {
58 pub steps: Vec<Step>,
60 pub index: usize,
62}
63
64impl CaretPath {
65 pub fn root(index: usize) -> Self {
67 Self { steps: Vec::new(), index }
68 }
69}
70
71#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
73pub struct Selection {
74 pub anchor: CaretPath,
76 pub focus: CaretPath,
78}
79
80#[derive(Debug, Clone, Copy, PartialEq, Eq)]
82pub enum PathError {
83 NoNode {
85 depth: usize,
87 },
88 NoSlot {
90 depth: usize,
92 },
93 GapOutOfRange {
95 len: usize,
97 index: usize,
99 },
100 SplitSelection,
102}
103
104impl fmt::Display for PathError {
105 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
106 match self {
107 PathError::NoNode { depth } => write!(f, "step {depth} names a missing node"),
108 PathError::NoSlot { depth } => write!(f, "step {depth} names a missing slot"),
109 PathError::GapOutOfRange { len, index } => {
110 write!(f, "gap {index} is outside a sequence of length {len}")
111 }
112 PathError::SplitSelection => write!(f, "selection ends lie in different sequences"),
113 }
114 }
115}
116
117impl std::error::Error for PathError {}
118
119impl Tree {
120 pub(crate) fn slot_of(&self, node: NodeId, seq: SeqId) -> Option<Slot> {
122 let hit = |s: SeqId| s == seq;
123 Some(match self.kind(node)? {
124 Kind::Atom(_) | Kind::HostBox { .. } => return None,
125 Kind::Frac { num, den, .. } => {
126 if hit(*num) {
127 Slot::Numerator
128 } else if hit(*den) {
129 Slot::Denominator
130 } else {
131 return None;
132 }
133 }
134 Kind::Script { base, sub, sup } => {
135 if hit(*base) {
136 Slot::Base
137 } else if sub.is_some_and(hit) {
138 Slot::Sub
139 } else if sup.is_some_and(hit) {
140 Slot::Sup
141 } else {
142 return None;
143 }
144 }
145 Kind::BigOp { lower, upper, .. } => {
146 if hit(*lower) {
147 Slot::Lower
148 } else if hit(*upper) {
149 Slot::Upper
150 } else {
151 return None;
152 }
153 }
154 Kind::Sqrt { index, radicand } => {
155 if hit(*index) {
156 Slot::Index
157 } else if hit(*radicand) {
158 Slot::Radicand
159 } else {
160 return None;
161 }
162 }
163 Kind::Delim { body, .. } | Kind::Styled { content: body, .. } => {
164 if hit(*body) {
165 Slot::Body
166 } else {
167 return None;
168 }
169 }
170 Kind::Accent { base, .. } => {
171 if hit(*base) {
172 Slot::Base
173 } else {
174 return None;
175 }
176 }
177 Kind::UnderOver { base, over, under, .. } => {
178 if hit(*base) {
179 Slot::Base
180 } else if over.is_some_and(hit) {
181 Slot::Over
182 } else if under.is_some_and(hit) {
183 Slot::Under
184 } else {
185 return None;
186 }
187 }
188 Kind::Matrix { rows, .. } => {
189 let (row, col) = rows
190 .iter()
191 .enumerate()
192 .find_map(|(r, cells)| cells.iter().position(|&c| c == seq).map(|c| (r, c)))?;
193 Slot::Cell { row, col }
194 }
195 })
196 }
197
198 pub(crate) fn slot_seq(&self, node: NodeId, slot: Slot) -> Option<SeqId> {
200 match (self.kind(node)?, slot) {
201 (Kind::Frac { num, .. }, Slot::Numerator) => Some(*num),
202 (Kind::Frac { den, .. }, Slot::Denominator) => Some(*den),
203 (Kind::Script { base, .. }, Slot::Base) => Some(*base),
204 (Kind::Script { sub, .. }, Slot::Sub) => *sub,
205 (Kind::Script { sup, .. }, Slot::Sup) => *sup,
206 (Kind::BigOp { lower, .. }, Slot::Lower) => Some(*lower),
207 (Kind::BigOp { upper, .. }, Slot::Upper) => Some(*upper),
208 (Kind::Sqrt { index, .. }, Slot::Index) => Some(*index),
209 (Kind::Sqrt { radicand, .. }, Slot::Radicand) => Some(*radicand),
210 (Kind::Delim { body, .. }, Slot::Body) => Some(*body),
211 (Kind::Styled { content, .. }, Slot::Body) => Some(*content),
212 (Kind::Accent { base, .. }, Slot::Base) => Some(*base),
213 (Kind::UnderOver { base, .. }, Slot::Base) => Some(*base),
214 (Kind::UnderOver { over, .. }, Slot::Over) => *over,
215 (Kind::UnderOver { under, .. }, Slot::Under) => *under,
216 (Kind::Matrix { rows, .. }, Slot::Cell { row, col }) => rows.get(row)?.get(col).copied(),
217 _ => None,
218 }
219 }
220
221 pub(crate) fn seq_steps(&self, seq: SeqId) -> Vec<Step> {
223 let mut steps = Vec::new();
224 let mut cur = seq;
225 while let Some(node) = self.seq_parent(cur) {
226 let (Some((pseq, idx)), Some(slot)) = (self.index_in_parent(node), self.slot_of(node, cur)) else {
227 break;
228 };
229 steps.push(Step { node: idx, slot });
230 cur = pseq;
231 }
232 steps.reverse();
233 steps
234 }
235
236 pub(crate) fn path_of(&self, at: Cursor) -> CaretPath {
237 CaretPath { steps: self.seq_steps(at.seq), index: at.index }
238 }
239
240 pub(crate) fn resolve_steps(&self, steps: &[Step]) -> Result<SeqId, PathError> {
241 let mut seq = self.root();
242 for (depth, step) in steps.iter().enumerate() {
243 let node = *self.items(seq).get(step.node).ok_or(PathError::NoNode { depth })?;
244 seq = self.slot_seq(node, step.slot).ok_or(PathError::NoSlot { depth })?;
245 }
246 Ok(seq)
247 }
248
249 pub(crate) fn resolve(&self, path: &CaretPath) -> Result<Cursor, PathError> {
250 let seq = self.resolve_steps(&path.steps)?;
251 let len = self.len(seq);
252 if path.index > len {
253 return Err(PathError::GapOutOfRange { len, index: path.index });
254 }
255 Ok(Cursor { seq, index: path.index })
256 }
257}