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 Left,
39 Right,
41 Cell {
43 row: usize,
45 col: usize,
47 },
48}
49
50#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
52pub struct Step {
53 pub node: usize,
55 pub slot: Slot,
57}
58
59#[derive(Debug, Clone, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
61pub struct CaretPath {
62 pub steps: Vec<Step>,
64 pub index: usize,
66}
67
68impl CaretPath {
69 pub fn root(index: usize) -> Self {
71 Self { steps: Vec::new(), index }
72 }
73}
74
75#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
77pub struct Selection {
78 pub anchor: CaretPath,
80 pub focus: CaretPath,
82}
83
84#[derive(Debug, Clone, Copy, PartialEq, Eq)]
86pub enum PathError {
87 NoNode {
89 depth: usize,
91 },
92 NoSlot {
94 depth: usize,
96 },
97 GapOutOfRange {
99 len: usize,
101 index: usize,
103 },
104 SplitSelection,
106}
107
108impl fmt::Display for PathError {
109 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
110 match self {
111 PathError::NoNode { depth } => write!(f, "step {depth} names a missing node"),
112 PathError::NoSlot { depth } => write!(f, "step {depth} names a missing slot"),
113 PathError::GapOutOfRange { len, index } => {
114 write!(f, "gap {index} is outside a sequence of length {len}")
115 }
116 PathError::SplitSelection => write!(f, "selection ends lie in different sequences"),
117 }
118 }
119}
120
121impl std::error::Error for PathError {}
122
123impl Tree {
124 pub(crate) fn slot_of(&self, node: NodeId, seq: SeqId) -> Option<Slot> {
126 let hit = |s: SeqId| s == seq;
127 Some(match self.kind(node)? {
128 Kind::Atom(_) | Kind::HostBox { .. } => return None,
129 Kind::Frac { num, den, .. } => {
130 if hit(*num) {
131 Slot::Numerator
132 } else if hit(*den) {
133 Slot::Denominator
134 } else {
135 return None;
136 }
137 }
138 Kind::Script { base, sub, sup } => {
139 if hit(*base) {
140 Slot::Base
141 } else if sub.is_some_and(hit) {
142 Slot::Sub
143 } else if sup.is_some_and(hit) {
144 Slot::Sup
145 } else {
146 return None;
147 }
148 }
149 Kind::BigOp { lower, upper, .. } => {
150 if hit(*lower) {
151 Slot::Lower
152 } else if hit(*upper) {
153 Slot::Upper
154 } else {
155 return None;
156 }
157 }
158 Kind::Sqrt { index, radicand } => {
159 if hit(*index) {
160 Slot::Index
161 } else if hit(*radicand) {
162 Slot::Radicand
163 } else {
164 return None;
165 }
166 }
167 Kind::Delim { body, .. } | Kind::Styled { content: body, .. } => {
168 if hit(*body) {
169 Slot::Body
170 } else {
171 return None;
172 }
173 }
174 Kind::Accent { base, .. } => {
175 if hit(*base) {
176 Slot::Base
177 } else {
178 return None;
179 }
180 }
181 Kind::UnderOver { base, over, under, .. } => {
182 if hit(*base) {
183 Slot::Base
184 } else if over.is_some_and(hit) {
185 Slot::Over
186 } else if under.is_some_and(hit) {
187 Slot::Under
188 } else {
189 return None;
190 }
191 }
192 Kind::Ratio { left, right } => {
193 if hit(*left) {
194 Slot::Left
195 } else if hit(*right) {
196 Slot::Right
197 } else {
198 return None;
199 }
200 }
201 Kind::EvalBar { body, lower, upper } => {
202 if hit(*body) {
203 Slot::Body
204 } else if hit(*lower) {
205 Slot::Lower
206 } else if hit(*upper) {
207 Slot::Upper
208 } else {
209 return None;
210 }
211 }
212 Kind::LabeledArrow { over, under, .. } => {
213 if hit(*over) {
214 Slot::Over
215 } else if hit(*under) {
216 Slot::Under
217 } else {
218 return None;
219 }
220 }
221 Kind::Matrix { rows, .. } => {
222 let (row, col) = rows
223 .iter()
224 .enumerate()
225 .find_map(|(r, cells)| cells.iter().position(|&c| c == seq).map(|c| (r, c)))?;
226 Slot::Cell { row, col }
227 }
228 })
229 }
230
231 pub(crate) fn slot_seq(&self, node: NodeId, slot: Slot) -> Option<SeqId> {
233 match (self.kind(node)?, slot) {
234 (Kind::Frac { num, .. }, Slot::Numerator) => Some(*num),
235 (Kind::Frac { den, .. }, Slot::Denominator) => Some(*den),
236 (Kind::Script { base, .. }, Slot::Base) => Some(*base),
237 (Kind::Script { sub, .. }, Slot::Sub) => *sub,
238 (Kind::Script { sup, .. }, Slot::Sup) => *sup,
239 (Kind::BigOp { lower, .. }, Slot::Lower) => Some(*lower),
240 (Kind::BigOp { upper, .. }, Slot::Upper) => Some(*upper),
241 (Kind::Sqrt { index, .. }, Slot::Index) => Some(*index),
242 (Kind::Sqrt { radicand, .. }, Slot::Radicand) => Some(*radicand),
243 (Kind::Delim { body, .. }, Slot::Body) => Some(*body),
244 (Kind::Styled { content, .. }, Slot::Body) => Some(*content),
245 (Kind::Accent { base, .. }, Slot::Base) => Some(*base),
246 (Kind::UnderOver { base, .. }, Slot::Base) => Some(*base),
247 (Kind::UnderOver { over, .. }, Slot::Over) => *over,
248 (Kind::UnderOver { under, .. }, Slot::Under) => *under,
249 (Kind::Matrix { rows, .. }, Slot::Cell { row, col }) => rows.get(row)?.get(col).copied(),
250 (Kind::Ratio { left, .. }, Slot::Left) => Some(*left),
251 (Kind::Ratio { right, .. }, Slot::Right) => Some(*right),
252 (Kind::EvalBar { body, .. }, Slot::Body) => Some(*body),
253 (Kind::EvalBar { lower, .. }, Slot::Lower) => Some(*lower),
254 (Kind::EvalBar { upper, .. }, Slot::Upper) => Some(*upper),
255 (Kind::LabeledArrow { over, .. }, Slot::Over) => Some(*over),
256 (Kind::LabeledArrow { under, .. }, Slot::Under) => Some(*under),
257 _ => None,
258 }
259 }
260
261 pub(crate) fn seq_steps(&self, seq: SeqId) -> Vec<Step> {
263 let mut steps = Vec::new();
264 let mut cur = seq;
265 while let Some(node) = self.seq_parent(cur) {
266 let (Some((pseq, idx)), Some(slot)) = (self.index_in_parent(node), self.slot_of(node, cur)) else {
267 break;
268 };
269 steps.push(Step { node: idx, slot });
270 cur = pseq;
271 }
272 steps.reverse();
273 steps
274 }
275
276 pub(crate) fn path_of(&self, at: Cursor) -> CaretPath {
277 CaretPath { steps: self.seq_steps(at.seq), index: at.index }
278 }
279
280 pub(crate) fn resolve_steps(&self, steps: &[Step]) -> Result<SeqId, PathError> {
281 let mut seq = self.root();
282 for (depth, step) in steps.iter().enumerate() {
283 let node = *self.items(seq).get(step.node).ok_or(PathError::NoNode { depth })?;
284 seq = self.slot_seq(node, step.slot).ok_or(PathError::NoSlot { depth })?;
285 }
286 Ok(seq)
287 }
288
289 pub(crate) fn resolve(&self, path: &CaretPath) -> Result<Cursor, PathError> {
290 let seq = self.resolve_steps(&path.steps)?;
291 let len = self.len(seq);
292 if path.index > len {
293 return Err(PathError::GapOutOfRange { len, index: path.index });
294 }
295 Ok(Cursor { seq, index: path.index })
296 }
297}