1use std::collections::{HashMap, HashSet};
17use std::sync::atomic::{AtomicU64, Ordering};
18
19use slotmap::SecondaryMap;
20use texform_interface::syntax_node::{self, SyntaxNode};
21
22pub use crate::ast::NodeKind;
23use crate::ast::{
24 Argument, ArgumentKind, ArgumentSlot, ArgumentValue, Ast, ContentMode, Delimiter, GroupKind,
25 Node, NodeId as RawNodeId, Slot,
26};
27use crate::parse::Span;
28use crate::serialize::{SerializeError, SerializeOptions, serialize, serialize_with};
29
30#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
32pub struct DocumentId(u64);
33
34static NEXT_DOCUMENT_ID: AtomicU64 = AtomicU64::new(1);
35
36fn next_document_id() -> DocumentId {
37 DocumentId(NEXT_DOCUMENT_ID.fetch_add(1, Ordering::Relaxed))
38}
39
40#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
46pub struct NodeId {
47 document: DocumentId,
48 raw: RawNodeId,
49}
50
51impl NodeId {
52 fn new(document: DocumentId, raw: RawNodeId) -> Self {
53 Self { document, raw }
54 }
55
56 fn raw(self) -> RawNodeId {
57 self.raw
58 }
59
60 fn document(self) -> DocumentId {
61 self.document
62 }
63}
64
65#[derive(Clone, Debug, PartialEq, Eq)]
67#[non_exhaustive]
68pub enum FromSyntaxError {
69 NotARoot,
71 InvalidPrimeCount,
73 PrimeInTextMode,
75}
76
77impl std::fmt::Display for FromSyntaxError {
78 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
79 match self {
80 FromSyntaxError::NotARoot => f.write_str("from_syntax expects a SyntaxNode::Root"),
81 FromSyntaxError::InvalidPrimeCount => {
82 f.write_str("Prime count must be greater than zero")
83 }
84 FromSyntaxError::PrimeInTextMode => {
85 f.write_str("Prime nodes are only valid in math mode")
86 }
87 }
88 }
89}
90
91impl std::error::Error for FromSyntaxError {}
92
93#[derive(Clone, Debug, PartialEq, Eq)]
95#[non_exhaustive]
96pub enum EditError {
97 NodeNotFound,
98 ReadOnlyDocument,
99 CannotEditRoot,
100 NotAContainer,
101 SlotShapeMismatch { expected: &'static str },
102 WouldCreateCycle,
103 IndexOutOfBounds,
104 DuplicateChild,
105 ExpectedDetachedRoot,
106 ForeignNode,
107}
108
109impl std::fmt::Display for EditError {
110 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
111 match self {
112 EditError::NodeNotFound => f.write_str("node not found"),
113 EditError::ReadOnlyDocument => f.write_str("document is read-only"),
114 EditError::CannotEditRoot => f.write_str("cannot edit the root node"),
115 EditError::NotAContainer => f.write_str("node is not a container"),
116 EditError::SlotShapeMismatch { expected } => {
117 write!(f, "slot shape mismatch: expected {expected}")
118 }
119 EditError::WouldCreateCycle => f.write_str("edit would create a cycle"),
120 EditError::IndexOutOfBounds => f.write_str("index out of bounds"),
121 EditError::DuplicateChild => f.write_str("node cannot appear more than once"),
122 EditError::ExpectedDetachedRoot => f.write_str("expected a detached root"),
123 EditError::ForeignNode => f.write_str("node belongs to a different document"),
124 }
125 }
126}
127
128impl std::error::Error for EditError {}
129
130#[derive(Clone, Debug, PartialEq, Eq)]
132pub enum ArgValue {
133 Math(NodeId),
134 Text(NodeId),
135 Delimiter(DelimiterValue),
136 CSName(String),
137 Dimension(String),
138 Integer(String),
139 KeyVal(String),
140 Column(String),
141 Boolean(bool),
142}
143
144impl ArgValue {
145 pub fn math(id: NodeId) -> Self {
146 Self::Math(id)
147 }
148
149 pub fn text(id: NodeId) -> Self {
150 Self::Text(id)
151 }
152
153 pub fn delimiter(delimiter: DelimiterValue) -> Self {
154 Self::Delimiter(delimiter)
155 }
156
157 pub fn cs_name(value: impl Into<String>) -> Self {
158 Self::CSName(value.into())
159 }
160
161 pub fn dimension(value: impl Into<String>) -> Self {
162 Self::Dimension(value.into())
163 }
164
165 pub fn integer(value: impl Into<String>) -> Self {
166 Self::Integer(value.into())
167 }
168
169 pub fn key_val(value: impl Into<String>) -> Self {
170 Self::KeyVal(value.into())
171 }
172
173 pub fn column(value: impl Into<String>) -> Self {
174 Self::Column(value.into())
175 }
176
177 pub fn boolean(value: bool) -> Self {
178 Self::Boolean(value)
179 }
180}
181
182#[derive(Clone, Debug, PartialEq, Eq)]
184pub enum DelimiterValue {
185 None,
186 Char(char),
187 Control(String),
188}
189
190impl DelimiterValue {
191 fn into_ast(self) -> Delimiter {
192 match self {
193 DelimiterValue::None => Delimiter::None,
194 DelimiterValue::Char(ch) => Delimiter::Char(ch),
195 DelimiterValue::Control(name) => Delimiter::Control(name),
196 }
197 }
198}
199
200pub struct Document {
202 ast: Ast,
203 spans: SecondaryMap<RawNodeId, Span>,
204 has_errors: bool,
205 id: DocumentId,
206}
207
208impl Document {
209 pub fn new() -> Self {
211 Self::with_mode(ContentMode::Math)
212 }
213
214 pub fn with_mode(mode: ContentMode) -> Self {
216 Document {
217 ast: Ast::with_root_mode(mode),
218 spans: SecondaryMap::new(),
219 has_errors: false,
220 id: next_document_id(),
221 }
222 }
223
224 pub fn from_syntax(node: &SyntaxNode) -> Result<Document, FromSyntaxError> {
226 Self::validate_syntax(node, None, true)?;
227 let ast = Ast::from_syntax_root(node);
228 let has_errors = ast
229 .find(ast.root(), |node| matches!(node, Node::Error { .. }))
230 .is_some();
231 Ok(Document {
232 ast,
233 spans: SecondaryMap::new(),
234 has_errors,
235 id: next_document_id(),
236 })
237 }
238
239 #[allow(dead_code)]
241 pub(crate) fn from_syntax_with_spans(
242 node: &SyntaxNode,
243 path_spans: &[(String, Span)],
244 ) -> Result<Document, FromSyntaxError> {
245 let mut doc = Document::from_syntax(node)?;
246 let lookup: HashMap<&str, &Span> = path_spans
247 .iter()
248 .map(|(path, span)| (path.as_str(), span))
249 .collect();
250 let mut spans = SecondaryMap::new();
251 Self::assign_spans(&doc.ast, node, doc.ast.root(), "root", &lookup, &mut spans);
252 doc.spans = spans;
253 Ok(doc)
254 }
255
256 pub fn id(&self) -> DocumentId {
258 self.id
259 }
260
261 pub fn root(&self) -> NodeRef<'_> {
263 NodeRef {
264 doc: self,
265 raw: self.ast.root(),
266 }
267 }
268
269 pub fn node(&self, id: NodeId) -> Result<NodeRef<'_>, EditError> {
271 let raw = self.check_node_owner(id)?;
272 Ok(NodeRef { doc: self, raw })
273 }
274
275 pub fn has_errors(&self) -> bool {
277 self.has_errors
278 }
279
280 pub fn errors(&self) -> impl Iterator<Item = NodeRef<'_>> + '_ {
282 self.ast
283 .find_all(self.ast.root(), |node| matches!(node, Node::Error { .. }))
284 .into_iter()
285 .map(move |raw| NodeRef { doc: self, raw })
286 }
287
288 pub fn is_read_only(&self) -> bool {
290 self.has_errors
291 }
292
293 pub fn find<'a>(
295 &'a self,
296 start: NodeRef<'a>,
297 pred: impl Fn(NodeRef<'a>) -> bool + 'a,
298 ) -> Option<NodeRef<'a>> {
299 if start.doc.id != self.id || !self.ast.contains(start.raw) {
300 return None;
301 }
302 self.ast
303 .find_all(start.raw, |_| true)
304 .into_iter()
305 .map(|raw| NodeRef { doc: self, raw })
306 .find(|node| pred(*node))
307 }
308
309 pub fn find_all<'a>(
311 &'a self,
312 start: NodeRef<'a>,
313 pred: impl Fn(NodeRef<'a>) -> bool + 'a,
314 ) -> impl Iterator<Item = NodeRef<'a>> + 'a {
315 let raws = if start.doc.id == self.id && self.ast.contains(start.raw) {
316 self.ast.find_all(start.raw, |_| true)
317 } else {
318 Vec::new()
319 };
320 raws.into_iter().filter_map(move |raw| {
321 let node = NodeRef { doc: self, raw };
322 pred(node).then_some(node)
323 })
324 }
325
326 pub fn find_commands<'a>(&'a self, name: &'a str) -> impl Iterator<Item = NodeRef<'a>> + 'a {
328 self.find_all(self.root(), move |node| node.is_command(name))
329 }
330
331 pub fn find_environments<'a>(
333 &'a self,
334 name: &'a str,
335 ) -> impl Iterator<Item = NodeRef<'a>> + 'a {
336 self.find_all(self.root(), move |node| node.env_name() == Some(name))
337 }
338
339 pub fn to_syntax(&self) -> SyntaxNode {
341 self.ast.to_syntax_root()
342 }
343
344 #[doc(hidden)]
349 pub fn __texform_engine_ast_mut(&mut self) -> &mut Ast {
350 &mut self.ast
351 }
352
353 pub fn to_latex(&self) -> Result<String, SerializeError> {
355 Ok(serialize(&self.ast))
356 }
357
358 pub fn to_latex_with(&self, options: &SerializeOptions) -> Result<String, SerializeError> {
360 Ok(serialize_with(&self.ast, options))
361 }
362
363 pub fn create_char(&mut self, c: char) -> Result<NodeId, EditError> {
365 self.create_node(Node::Char(c))
366 }
367
368 pub fn create_text(&mut self, s: impl Into<String>) -> Result<NodeId, EditError> {
370 self.create_node(Node::Text(s.into()))
371 }
372
373 pub fn create_active_space(&mut self) -> Result<NodeId, EditError> {
375 self.create_node(Node::ActiveSpace)
376 }
377
378 pub fn create_group(&mut self, mode: ContentMode) -> Result<NodeId, EditError> {
380 self.create_node(Node::Group {
381 children: Vec::new(),
382 kind: GroupKind::Explicit,
383 mode,
384 })
385 }
386
387 pub fn create_command(
389 &mut self,
390 name: impl Into<String>,
391 args: Vec<ArgValue>,
392 ) -> Result<NodeId, EditError> {
393 self.check_writable()?;
394 let args = self.build_arg_slots(args)?;
395 self.create_node(Node::Command {
396 name: name.into(),
397 args,
398 known: false,
399 })
400 }
401
402 pub fn create_declarative(
404 &mut self,
405 name: impl Into<String>,
406 args: Vec<ArgValue>,
407 ) -> Result<NodeId, EditError> {
408 self.check_writable()?;
409 let args = self.build_arg_slots(args)?;
410 self.create_node(Node::Declarative {
411 name: name.into(),
412 args,
413 })
414 }
415
416 pub fn create_environment(
418 &mut self,
419 name: impl Into<String>,
420 args: Vec<ArgValue>,
421 body: NodeId,
422 ) -> Result<NodeId, EditError> {
423 self.check_writable()?;
424 let body = self.check_node_owner(body)?;
425 self.check_detached(body)?;
426 if !matches!(self.ast.node_opt(body), Some(Node::Group { .. })) {
427 return Err(EditError::SlotShapeMismatch { expected: "group" });
428 }
429 let args = self.build_arg_slots(args)?;
430 self.create_node(Node::Environment {
431 name: name.into(),
432 args,
433 known: false,
434 body,
435 })
436 }
437
438 pub fn append_child(&mut self, parent: NodeId, child: NodeId) -> Result<(), EditError> {
440 self.check_writable()?;
441 self.insert_child_at(parent, self.child_len(parent)?, child)
442 }
443
444 pub fn insert_before(&mut self, anchor: NodeId, new: NodeId) -> Result<(), EditError> {
446 self.check_writable()?;
447 let (parent, index) = self.group_child_position(anchor)?;
448 self.insert_child_at(parent, index, new)
449 }
450
451 pub fn insert_after(&mut self, anchor: NodeId, new: NodeId) -> Result<(), EditError> {
453 self.check_writable()?;
454 let (parent, index) = self.group_child_position(anchor)?;
455 self.insert_child_at(parent, index + 1, new)
456 }
457
458 pub fn insert_child(
460 &mut self,
461 parent: NodeId,
462 index: usize,
463 child: NodeId,
464 ) -> Result<(), EditError> {
465 self.check_writable()?;
466 self.insert_child_at(parent, index, child)
467 }
468
469 pub fn extract(&mut self, id: NodeId) -> Result<NodeId, EditError> {
471 self.check_writable()?;
472 let raw = self.check_node_owner(id)?;
473 if raw == self.ast.root() {
474 return Err(EditError::CannotEditRoot);
475 }
476 if !matches!(self.ast.slot(raw), Some(Slot::GroupChild(_))) {
477 return Err(EditError::SlotShapeMismatch {
478 expected: "group child",
479 });
480 }
481 Ok(NodeId::new(self.id, self.ast.detach(raw)))
482 }
483
484 pub fn remove(&mut self, id: NodeId) -> Result<(), EditError> {
486 self.check_writable()?;
487 let raw = self.extract(id)?.raw();
488 self.ast.remove_detached(raw);
489 Ok(())
490 }
491
492 pub fn replace_with(&mut self, target: NodeId, replacement: NodeId) -> Result<(), EditError> {
494 self.check_writable()?;
495 let target = self.check_node_owner(target)?;
496 let replacement = self.check_node_owner(replacement)?;
497 if target == self.ast.root() {
498 return Err(EditError::CannotEditRoot);
499 }
500 self.check_detached(replacement)?;
501 self.check_no_cycle(replacement, target)?;
502 match self.ast.slot(target) {
503 Some(Slot::GroupChild(index)) => {
504 let parent = self.ast.parent_id(target).ok_or(EditError::NodeNotFound)?;
505 let detached = self.ast.detach(target);
506 self.ast.insert_child(parent, index, replacement);
507 self.ast.remove_detached(detached);
508 Ok(())
509 }
510 Some(_) => {
511 let slot = self.ast.slot(target).ok_or(EditError::NodeNotFound)?;
512 self.check_slot_shape(slot, replacement)?;
513 self.ast.replace_content_child(target, replacement);
514 if self.ast.contains(target)
515 && self.ast.parent_id(target).is_none()
516 && target != self.ast.root()
517 {
518 self.ast.remove_detached(target);
519 }
520 Ok(())
521 }
522 None => Err(EditError::NodeNotFound),
523 }
524 }
525
526 pub fn clear(&mut self, container: NodeId) -> Result<(), EditError> {
528 self.check_writable()?;
529 let raw = self.check_node_owner(container)?;
530 self.check_container(raw)?;
531 let len = self.ast.children(raw).len();
532 let removed = self.ast.detach_children_range(raw, 0..len);
533 for child in removed {
534 self.ast.remove_detached(child);
535 }
536 Ok(())
537 }
538
539 pub fn set_command_name(
541 &mut self,
542 id: NodeId,
543 name: impl Into<String>,
544 ) -> Result<(), EditError> {
545 self.check_writable()?;
546 let raw = self.check_node_owner(id)?;
547 match self.ast.node_opt_mut(raw) {
548 Some(Node::Command { name: current, .. })
549 | Some(Node::Infix { name: current, .. })
550 | Some(Node::Declarative { name: current, .. })
551 | Some(Node::Environment { name: current, .. }) => {
552 *current = name.into();
553 Ok(())
554 }
555 Some(_) => Err(EditError::SlotShapeMismatch {
556 expected: "command-like node",
557 }),
558 None => Err(EditError::NodeNotFound),
559 }
560 }
561
562 pub fn set_text(&mut self, id: NodeId, s: impl Into<String>) -> Result<(), EditError> {
564 self.check_writable()?;
565 let raw = self.check_node_owner(id)?;
566 match self.ast.node_opt_mut(raw) {
567 Some(Node::Text(text)) => {
568 *text = s.into();
569 Ok(())
570 }
571 Some(_) => Err(EditError::SlotShapeMismatch {
572 expected: "text node",
573 }),
574 None => Err(EditError::NodeNotFound),
575 }
576 }
577
578 pub fn set_char(&mut self, id: NodeId, c: char) -> Result<(), EditError> {
580 self.check_writable()?;
581 let raw = self.check_node_owner(id)?;
582 match self.ast.node_opt_mut(raw) {
583 Some(Node::Char(ch)) => {
584 *ch = c;
585 Ok(())
586 }
587 Some(_) => Err(EditError::SlotShapeMismatch {
588 expected: "char node",
589 }),
590 None => Err(EditError::NodeNotFound),
591 }
592 }
593
594 pub fn set_arg(&mut self, id: NodeId, index: usize, value: ArgValue) -> Result<(), EditError> {
596 self.check_writable()?;
597 let raw = self.check_node_owner(id)?;
598 let kind = match self.ast.node_opt(raw) {
599 Some(
600 Node::Command { args, .. }
601 | Node::Infix { args, .. }
602 | Node::Declarative { args, .. }
603 | Node::Environment { args, .. },
604 ) => {
605 let Some(slot) = args.get(index) else {
606 return Err(EditError::IndexOutOfBounds);
607 };
608 let Some(argument) = slot else {
609 return Err(EditError::SlotShapeMismatch {
610 expected: "filled argument slot",
611 });
612 };
613 argument.kind.clone()
614 }
615 Some(_) => {
616 return Err(EditError::SlotShapeMismatch {
617 expected: "command-like node",
618 });
619 }
620 None => return Err(EditError::NodeNotFound),
621 };
622 let value = self.build_arg_value_for_kind(value, &kind)?;
623 self.replace_single_arg_value(raw, index, value)
624 }
625
626 pub fn wrap(&mut self, target: NodeId, wrapper: NodeId) -> Result<NodeId, EditError> {
628 self.check_writable()?;
629 let target = self.check_node_owner(target)?;
630 let wrapper = self.check_node_owner(wrapper)?;
631 if target == self.ast.root() {
632 return Err(EditError::CannotEditRoot);
633 }
634 self.check_container(wrapper)?;
635 self.check_detached(wrapper)?;
636 let (parent, index) = match self.ast.slot(target) {
637 Some(Slot::GroupChild(index)) => (
638 self.ast.parent_id(target).ok_or(EditError::NodeNotFound)?,
639 index,
640 ),
641 Some(_) => {
642 return Err(EditError::SlotShapeMismatch {
643 expected: "group child",
644 });
645 }
646 None => return Err(EditError::NodeNotFound),
647 };
648 self.check_no_cycle(wrapper, parent)?;
649 let target = self.ast.detach(target);
650 self.ast.insert_child(parent, index, wrapper);
651 self.ast.append_child(wrapper, target);
652 Ok(NodeId::new(self.id, wrapper))
653 }
654
655 pub fn unwrap(&mut self, group: NodeId) -> Result<Vec<NodeId>, EditError> {
657 self.check_writable()?;
658 let group = self.check_node_owner(group)?;
659 if group == self.ast.root() {
660 return Err(EditError::CannotEditRoot);
661 }
662 if !matches!(self.ast.node_opt(group), Some(Node::Group { .. })) {
663 return Err(EditError::SlotShapeMismatch { expected: "group" });
664 }
665 let (parent, index) = match self.ast.slot(group) {
666 Some(Slot::GroupChild(index)) => (
667 self.ast.parent_id(group).ok_or(EditError::NodeNotFound)?,
668 index,
669 ),
670 Some(_) => {
671 return Err(EditError::SlotShapeMismatch {
672 expected: "group child",
673 });
674 }
675 None => return Err(EditError::NodeNotFound),
676 };
677 let count = self.ast.children(group).len();
678 let children = self.ast.detach_children_range(group, 0..count);
679 let detached_group = self.ast.detach(group);
680 self.ast.remove_detached(detached_group);
681 for (offset, child) in children.iter().copied().enumerate() {
682 self.ast.insert_child(parent, index + offset, child);
683 }
684 Ok(children
685 .into_iter()
686 .map(|raw| NodeId::new(self.id, raw))
687 .collect())
688 }
689
690 fn create_node(&mut self, node: Node) -> Result<NodeId, EditError> {
691 self.check_writable()?;
692 self.check_unique_direct_children(&node)?;
693 Ok(NodeId::new(self.id, self.ast.new_node(node)))
694 }
695
696 fn child_len(&self, parent: NodeId) -> Result<usize, EditError> {
697 let raw = self.check_node_owner(parent)?;
698 self.check_container(raw)?;
699 Ok(self.ast.children(raw).len())
700 }
701
702 fn check_writable(&self) -> Result<(), EditError> {
703 if self.has_errors {
704 Err(EditError::ReadOnlyDocument)
705 } else {
706 Ok(())
707 }
708 }
709
710 fn check_node_owner(&self, id: NodeId) -> Result<RawNodeId, EditError> {
711 if id.document() != self.id {
712 return Err(EditError::ForeignNode);
713 }
714 let raw = id.raw();
715 if self.ast.contains(raw) {
716 Ok(raw)
717 } else {
718 Err(EditError::NodeNotFound)
719 }
720 }
721
722 fn check_container(&self, id: RawNodeId) -> Result<(), EditError> {
723 match self.ast.node_opt(id) {
724 Some(Node::Root { .. }) | Some(Node::Group { .. }) => Ok(()),
725 Some(_) => Err(EditError::NotAContainer),
726 None => Err(EditError::NodeNotFound),
727 }
728 }
729
730 fn check_detached(&self, id: RawNodeId) -> Result<(), EditError> {
731 if !self.ast.contains(id) {
732 return Err(EditError::NodeNotFound);
733 }
734 if id == self.ast.root() {
735 return Err(EditError::CannotEditRoot);
736 }
737 if self.ast.parent_id(id).is_some() || !self.ast.is_detached_root(id) {
738 return Err(EditError::ExpectedDetachedRoot);
739 }
740 Ok(())
741 }
742
743 fn check_no_cycle(&self, child: RawNodeId, new_parent: RawNodeId) -> Result<(), EditError> {
744 if self.ast.subtree_contains_node(child, new_parent) {
745 Err(EditError::WouldCreateCycle)
746 } else {
747 Ok(())
748 }
749 }
750
751 fn check_slot_shape(&self, slot: Slot, child: RawNodeId) -> Result<(), EditError> {
752 if matches!(slot, Slot::EnvBody)
753 && !matches!(self.ast.node_opt(child), Some(Node::Group { .. }))
754 {
755 Err(EditError::SlotShapeMismatch { expected: "group" })
756 } else {
757 Ok(())
758 }
759 }
760
761 fn check_unique_direct_children(&self, node: &Node) -> Result<(), EditError> {
762 let mut seen = HashSet::new();
763 for child in Self::direct_children(node) {
764 if !seen.insert(child) {
765 return Err(EditError::DuplicateChild);
766 }
767 }
768 Ok(())
769 }
770
771 fn direct_children(node: &Node) -> Vec<RawNodeId> {
772 let mut children = Vec::new();
773 match node {
774 Node::Root {
775 children: group_children,
776 ..
777 }
778 | Node::Group {
779 children: group_children,
780 ..
781 } => children.extend(group_children.iter().copied()),
782 Node::Command { args, .. } | Node::Declarative { args, .. } => {
783 Self::push_arg_children(args, &mut children);
784 }
785 Node::Infix {
786 args, left, right, ..
787 } => {
788 children.push(*left);
789 Self::push_arg_children(args, &mut children);
790 children.push(*right);
791 }
792 Node::Environment { args, body, .. } => {
793 Self::push_arg_children(args, &mut children);
794 children.push(*body);
795 }
796 Node::Scripted {
797 base,
798 subscript,
799 superscript,
800 } => {
801 children.push(*base);
802 children.extend(*subscript);
803 children.extend(*superscript);
804 }
805 Node::Prime { .. }
806 | Node::Text(_)
807 | Node::Char(_)
808 | Node::ActiveSpace
809 | Node::Error { .. } => {}
810 }
811 children
812 }
813
814 fn validate_syntax(
815 node: &SyntaxNode,
816 current_mode: Option<ContentMode>,
817 is_top_level: bool,
818 ) -> Result<(), FromSyntaxError> {
819 if is_top_level && !matches!(node, SyntaxNode::Root { .. }) {
820 return Err(FromSyntaxError::NotARoot);
821 }
822
823 match node {
824 SyntaxNode::Root { mode, children } => {
825 if !is_top_level {
826 return Err(FromSyntaxError::NotARoot);
827 }
828 for child in children {
829 Self::validate_syntax(child, Some(*mode), false)?;
830 }
831 }
832 SyntaxNode::Group { mode, children, .. } => {
833 for child in children {
834 Self::validate_syntax(child, Some(*mode), false)?;
835 }
836 }
837 SyntaxNode::Command { args, .. } | SyntaxNode::Declarative { args, .. } => {
838 Self::validate_syntax_args(args)?;
839 }
840 SyntaxNode::Infix {
841 args, left, right, ..
842 } => {
843 Self::validate_syntax_args(args)?;
844 Self::validate_syntax(left, current_mode, false)?;
845 Self::validate_syntax(right, current_mode, false)?;
846 }
847 SyntaxNode::Environment { args, body, .. } => {
848 Self::validate_syntax_args(args)?;
849 Self::validate_syntax(body, None, false)?;
850 }
851 SyntaxNode::Scripted {
852 base,
853 subscript,
854 superscript,
855 } => {
856 Self::validate_syntax(base, current_mode, false)?;
857 if let Some(subscript) = subscript {
858 Self::validate_syntax(subscript, current_mode, false)?;
859 }
860 if let Some(superscript) = superscript {
861 Self::validate_syntax(superscript, current_mode, false)?;
862 }
863 }
864 SyntaxNode::Prime { count } => {
865 if *count == 0 {
866 return Err(FromSyntaxError::InvalidPrimeCount);
867 }
868 if current_mode != Some(ContentMode::Math) {
869 return Err(FromSyntaxError::PrimeInTextMode);
870 }
871 }
872 SyntaxNode::Error { .. }
873 | SyntaxNode::Text(_)
874 | SyntaxNode::Char(_)
875 | SyntaxNode::ActiveSpace => {}
876 }
877
878 Ok(())
879 }
880
881 fn validate_syntax_args(args: &[syntax_node::ArgumentSlot]) -> Result<(), FromSyntaxError> {
882 for arg in args.iter().flatten() {
883 match &arg.value {
884 syntax_node::ArgumentValue::MathContent(node) => {
885 Self::validate_syntax(node, Some(ContentMode::Math), false)?;
886 }
887 syntax_node::ArgumentValue::TextContent(node) => {
888 Self::validate_syntax(node, Some(ContentMode::Text), false)?;
889 }
890 _ => {}
891 }
892 }
893 Ok(())
894 }
895
896 fn push_arg_children(args: &[ArgumentSlot], out: &mut Vec<RawNodeId>) {
897 for arg in args.iter().flatten() {
898 match &arg.value {
899 ArgumentValue::MathContent(child) | ArgumentValue::TextContent(child) => {
900 out.push(*child);
901 }
902 _ => {}
903 }
904 }
905 }
906
907 fn group_child_position(&self, anchor: NodeId) -> Result<(NodeId, usize), EditError> {
908 let raw = self.check_node_owner(anchor)?;
909 match self.ast.slot(raw) {
910 Some(Slot::GroupChild(index)) => {
911 let parent = self.ast.parent_id(raw).ok_or(EditError::NodeNotFound)?;
912 Ok((NodeId::new(self.id, parent), index))
913 }
914 Some(_) => Err(EditError::SlotShapeMismatch {
915 expected: "group child",
916 }),
917 None => Err(EditError::NodeNotFound),
918 }
919 }
920
921 fn insert_child_at(
922 &mut self,
923 parent: NodeId,
924 index: usize,
925 child: NodeId,
926 ) -> Result<(), EditError> {
927 self.check_writable()?;
928 let parent = self.check_node_owner(parent)?;
929 let child = self.check_node_owner(child)?;
930 self.check_container(parent)?;
931 self.check_detached(child)?;
932 self.check_no_cycle(child, parent)?;
933 if index > self.ast.children(parent).len() {
934 return Err(EditError::IndexOutOfBounds);
935 }
936 self.ast.insert_child(parent, index, child);
937 Ok(())
938 }
939
940 fn build_arg_slots(&mut self, args: Vec<ArgValue>) -> Result<Vec<ArgumentSlot>, EditError> {
941 args.into_iter()
942 .map(|arg| self.build_arg_slot(arg))
943 .collect()
944 }
945
946 fn build_arg_slot(&mut self, value: ArgValue) -> Result<ArgumentSlot, EditError> {
947 let (kind, value) = match value {
948 ArgValue::Math(id) => {
949 let raw = self.check_node_owner(id)?;
950 self.check_detached(raw)?;
951 (ArgumentKind::Mandatory, ArgumentValue::MathContent(raw))
952 }
953 ArgValue::Text(id) => {
954 let raw = self.check_node_owner(id)?;
955 self.check_detached(raw)?;
956 (ArgumentKind::Mandatory, ArgumentValue::TextContent(raw))
957 }
958 ArgValue::Delimiter(d) => (
959 ArgumentKind::Mandatory,
960 ArgumentValue::Delimiter(d.into_ast()),
961 ),
962 ArgValue::CSName(s) => (ArgumentKind::Mandatory, ArgumentValue::CSName(s)),
963 ArgValue::Dimension(s) => (ArgumentKind::Mandatory, ArgumentValue::Dimension(s)),
964 ArgValue::Integer(s) => (ArgumentKind::Mandatory, ArgumentValue::Integer(s)),
965 ArgValue::KeyVal(s) => (ArgumentKind::Mandatory, ArgumentValue::KeyVal(s)),
966 ArgValue::Column(s) => (ArgumentKind::Mandatory, ArgumentValue::Column(s)),
967 ArgValue::Boolean(b) => (ArgumentKind::Star, ArgumentValue::Boolean(b)),
968 };
969 Ok(Some(Argument { kind, value }))
970 }
971
972 fn build_arg_value_for_kind(
973 &self,
974 value: ArgValue,
975 kind: &ArgumentKind,
976 ) -> Result<ArgumentValue, EditError> {
977 match (kind, value) {
978 (ArgumentKind::Star, ArgValue::Boolean(value)) => Ok(ArgumentValue::Boolean(value)),
979 (ArgumentKind::Star, _) => Err(EditError::SlotShapeMismatch {
980 expected: "boolean value",
981 }),
982 (_, ArgValue::Boolean(_)) => Err(EditError::SlotShapeMismatch {
983 expected: "non-boolean argument value",
984 }),
985 (_, ArgValue::Math(id)) => {
986 let raw = self.check_node_owner(id)?;
987 self.check_detached(raw)?;
988 Ok(ArgumentValue::MathContent(raw))
989 }
990 (_, ArgValue::Text(id)) => {
991 let raw = self.check_node_owner(id)?;
992 self.check_detached(raw)?;
993 Ok(ArgumentValue::TextContent(raw))
994 }
995 (_, ArgValue::Delimiter(delimiter)) => {
996 Ok(ArgumentValue::Delimiter(delimiter.into_ast()))
997 }
998 (_, ArgValue::CSName(value)) => Ok(ArgumentValue::CSName(value)),
999 (_, ArgValue::Dimension(value)) => Ok(ArgumentValue::Dimension(value)),
1000 (_, ArgValue::Integer(value)) => Ok(ArgumentValue::Integer(value)),
1001 (_, ArgValue::KeyVal(value)) => Ok(ArgumentValue::KeyVal(value)),
1002 (_, ArgValue::Column(value)) => Ok(ArgumentValue::Column(value)),
1003 }
1004 }
1005
1006 fn replace_single_arg_value(
1007 &mut self,
1008 id: RawNodeId,
1009 index: usize,
1010 value: ArgumentValue,
1011 ) -> Result<(), EditError> {
1012 let old_content = self
1013 .ast
1014 .arg_slots(id)
1015 .get(index)
1016 .and_then(|slot| slot.as_ref())
1017 .and_then(|arg| match &arg.value {
1018 ArgumentValue::MathContent(content) | ArgumentValue::TextContent(content) => {
1019 Some(*content)
1020 }
1021 _ => None,
1022 });
1023
1024 let mut node = self.ast.node(id).clone();
1025 match &mut node {
1026 Node::Command { args, .. }
1027 | Node::Infix { args, .. }
1028 | Node::Declarative { args, .. }
1029 | Node::Environment { args, .. } => {
1030 let Some(Some(argument)) = args.get_mut(index) else {
1031 return Err(EditError::SlotShapeMismatch {
1032 expected: "filled argument slot",
1033 });
1034 };
1035 argument.value = value;
1036 }
1037 _ => {
1038 return Err(EditError::SlotShapeMismatch {
1039 expected: "command-like node",
1040 });
1041 }
1042 }
1043 self.ast.replace_node(id, node);
1044
1045 if let Some(old) = old_content
1046 && self.ast.contains(old)
1047 && self.ast.parent_id(old).is_none()
1048 && old != self.ast.root()
1049 {
1050 self.ast.remove_detached(old);
1051 }
1052 Ok(())
1053 }
1054
1055 pub fn node_spans(&self) -> Vec<(String, Span)> {
1065 let mut out = Vec::new();
1066 self.collect_node_spans(self.ast.root(), "root", &mut out);
1067 out
1068 }
1069
1070 fn collect_node_spans(&self, id: RawNodeId, path: &str, out: &mut Vec<(String, Span)>) {
1071 if let Some(span) = self.spans.get(id) {
1072 out.push((path.to_string(), span.clone()));
1073 }
1074 match self.ast.node(id) {
1075 Node::Root { children, .. } | Node::Group { children, .. } => {
1076 for (index, child) in children.iter().enumerate() {
1077 self.collect_node_spans(*child, &format!("{path}.child.{index}"), out);
1078 }
1079 }
1080 Node::Command { args, .. } | Node::Declarative { args, .. } => {
1081 self.collect_arg_node_spans(args, path, out);
1082 }
1083 Node::Infix {
1084 args, left, right, ..
1085 } => {
1086 self.collect_node_spans(*left, &format!("{path}.left"), out);
1087 self.collect_arg_node_spans(args, path, out);
1088 self.collect_node_spans(*right, &format!("{path}.right"), out);
1089 }
1090 Node::Environment { args, body, .. } => {
1091 self.collect_arg_node_spans(args, path, out);
1092 self.collect_node_spans(*body, &format!("{path}.body"), out);
1093 }
1094 Node::Scripted {
1095 base,
1096 subscript,
1097 superscript,
1098 } => {
1099 self.collect_node_spans(*base, &format!("{path}.base"), out);
1100 if let Some(sub) = subscript {
1101 self.collect_node_spans(*sub, &format!("{path}.sub"), out);
1102 }
1103 if let Some(sup) = superscript {
1104 self.collect_node_spans(*sup, &format!("{path}.sup"), out);
1105 }
1106 }
1107 _ => {}
1108 }
1109 }
1110
1111 fn collect_arg_node_spans(
1112 &self,
1113 args: &[ArgumentSlot],
1114 path: &str,
1115 out: &mut Vec<(String, Span)>,
1116 ) {
1117 for (index, slot) in args.iter().enumerate() {
1118 let Some(arg) = slot else { continue };
1119 if let ArgumentValue::MathContent(id) | ArgumentValue::TextContent(id) = &arg.value {
1120 self.collect_node_spans(*id, &format!("{path}.arg.{index}.content"), out);
1121 }
1122 }
1123 }
1124
1125 #[allow(dead_code)]
1126 fn assign_spans(
1127 ast: &Ast,
1128 syntax: &SyntaxNode,
1129 id: RawNodeId,
1130 path: &str,
1131 lookup: &HashMap<&str, &Span>,
1132 out: &mut SecondaryMap<RawNodeId, Span>,
1133 ) {
1134 if let Some(span) = lookup.get(path) {
1135 out.insert(id, (*span).clone());
1136 }
1137
1138 match (syntax, ast.node(id)) {
1139 (
1140 SyntaxNode::Root {
1141 children: syntax_children,
1142 ..
1143 },
1144 Node::Root { children, .. },
1145 )
1146 | (
1147 SyntaxNode::Group {
1148 children: syntax_children,
1149 ..
1150 },
1151 Node::Group { children, .. },
1152 ) => {
1153 for (index, (syntax_child, ast_child)) in
1154 syntax_children.iter().zip(children.iter()).enumerate()
1155 {
1156 Self::assign_spans(
1157 ast,
1158 syntax_child,
1159 *ast_child,
1160 &format!("{path}.child.{index}"),
1161 lookup,
1162 out,
1163 );
1164 }
1165 }
1166 (
1167 SyntaxNode::Command {
1168 args: syntax_args, ..
1169 },
1170 Node::Command { args: ast_args, .. },
1171 )
1172 | (
1173 SyntaxNode::Declarative {
1174 args: syntax_args, ..
1175 },
1176 Node::Declarative { args: ast_args, .. },
1177 ) => Self::assign_arg_spans(ast, syntax_args, ast_args, path, lookup, out),
1178 (
1179 SyntaxNode::Infix {
1180 args: syntax_args,
1181 left: syntax_left,
1182 right: syntax_right,
1183 ..
1184 },
1185 Node::Infix {
1186 args: ast_args,
1187 left,
1188 right,
1189 ..
1190 },
1191 ) => {
1192 Self::assign_spans(
1193 ast,
1194 syntax_left,
1195 *left,
1196 &format!("{path}.left"),
1197 lookup,
1198 out,
1199 );
1200 Self::assign_arg_spans(ast, syntax_args, ast_args, path, lookup, out);
1201 Self::assign_spans(
1202 ast,
1203 syntax_right,
1204 *right,
1205 &format!("{path}.right"),
1206 lookup,
1207 out,
1208 );
1209 }
1210 (
1211 SyntaxNode::Environment {
1212 args: syntax_args,
1213 body: syntax_body,
1214 ..
1215 },
1216 Node::Environment {
1217 args: ast_args,
1218 body,
1219 ..
1220 },
1221 ) => {
1222 Self::assign_arg_spans(ast, syntax_args, ast_args, path, lookup, out);
1223 Self::assign_spans(
1224 ast,
1225 syntax_body,
1226 *body,
1227 &format!("{path}.body"),
1228 lookup,
1229 out,
1230 );
1231 }
1232 (
1233 SyntaxNode::Scripted {
1234 base: syntax_base,
1235 subscript: syntax_subscript,
1236 superscript: syntax_superscript,
1237 },
1238 Node::Scripted {
1239 base,
1240 subscript,
1241 superscript,
1242 },
1243 ) => {
1244 Self::assign_spans(
1245 ast,
1246 syntax_base,
1247 *base,
1248 &format!("{path}.base"),
1249 lookup,
1250 out,
1251 );
1252 if let (Some(syntax), Some(ast_id)) = (syntax_subscript, subscript) {
1253 Self::assign_spans(ast, syntax, *ast_id, &format!("{path}.sub"), lookup, out);
1254 }
1255 if let (Some(syntax), Some(ast_id)) = (syntax_superscript, superscript) {
1256 Self::assign_spans(ast, syntax, *ast_id, &format!("{path}.sup"), lookup, out);
1257 }
1258 }
1259 _ => {}
1260 }
1261 }
1262
1263 #[allow(dead_code)]
1264 fn assign_arg_spans(
1265 ast: &Ast,
1266 syntax_args: &[syntax_node::ArgumentSlot],
1267 ast_args: &[ArgumentSlot],
1268 path: &str,
1269 lookup: &HashMap<&str, &Span>,
1270 out: &mut SecondaryMap<RawNodeId, Span>,
1271 ) {
1272 for (index, (syntax_slot, ast_slot)) in syntax_args.iter().zip(ast_args.iter()).enumerate()
1273 {
1274 let (Some(syntax_arg), Some(ast_arg)) = (syntax_slot, ast_slot) else {
1275 continue;
1276 };
1277 let content_path = format!("{path}.arg.{index}.content");
1278 if let (
1279 syntax_node::ArgumentValue::MathContent(syntax_node)
1280 | syntax_node::ArgumentValue::TextContent(syntax_node),
1281 ArgumentValue::MathContent(ast_id) | ArgumentValue::TextContent(ast_id),
1282 ) = (&syntax_arg.value, &ast_arg.value)
1283 {
1284 Self::assign_spans(ast, syntax_node, *ast_id, &content_path, lookup, out);
1285 }
1286 }
1287 }
1288}
1289
1290impl Default for Document {
1291 fn default() -> Self {
1292 Self::new()
1293 }
1294}
1295
1296impl Clone for Document {
1297 fn clone(&self) -> Self {
1298 Document {
1299 ast: self.ast.clone(),
1300 spans: self.spans.clone(),
1301 has_errors: self.has_errors,
1302 id: next_document_id(),
1303 }
1304 }
1305}
1306
1307impl std::fmt::Debug for Document {
1308 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1309 f.debug_struct("Document")
1310 .field("id", &self.id)
1311 .field("root", &self.ast.root())
1312 .field("has_errors", &self.has_errors)
1313 .finish()
1314 }
1315}
1316
1317impl std::fmt::Display for Document {
1318 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1319 let latex = self.to_latex().map_err(|_| std::fmt::Error)?;
1320 f.write_str(&latex)
1321 }
1322}
1323
1324#[derive(Clone, Copy)]
1326pub struct NodeRef<'a> {
1327 doc: &'a Document,
1328 raw: RawNodeId,
1329}
1330
1331impl std::fmt::Debug for NodeRef<'_> {
1332 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1333 f.debug_struct("NodeRef").field("id", &self.id()).finish()
1334 }
1335}
1336
1337impl<'a> NodeRef<'a> {
1338 pub fn id(&self) -> NodeId {
1340 NodeId::new(self.doc.id, self.raw)
1341 }
1342
1343 pub fn kind(&self) -> NodeKind {
1345 self.doc.ast.kind(self.raw)
1346 }
1347
1348 pub fn is_command(&self, name: &str) -> bool {
1350 matches!(self.node(), Node::Command { name: current, .. } if current == name)
1351 }
1352
1353 pub fn is_char(&self, c: char) -> bool {
1355 matches!(self.node(), Node::Char(ch) if *ch == c)
1356 }
1357
1358 pub fn is_error(&self) -> bool {
1360 matches!(self.node(), Node::Error { .. })
1361 }
1362
1363 pub fn parent(&self) -> Option<NodeRef<'a>> {
1365 self.doc
1366 .ast
1367 .parent_id(self.raw)
1368 .map(|raw| self.sibling(raw))
1369 }
1370
1371 pub fn children(&self) -> impl Iterator<Item = NodeRef<'a>> + 'a {
1373 let doc = self.doc;
1374 doc.ast
1375 .children(self.raw)
1376 .to_vec()
1377 .into_iter()
1378 .map(move |raw| NodeRef { doc, raw })
1379 }
1380
1381 pub fn next_sibling(&self) -> Option<NodeRef<'a>> {
1383 self.doc
1384 .ast
1385 .next_sibling(self.raw)
1386 .map(|raw| self.sibling(raw))
1387 }
1388
1389 pub fn prev_sibling(&self) -> Option<NodeRef<'a>> {
1391 self.doc
1392 .ast
1393 .prev_sibling(self.raw)
1394 .map(|raw| self.sibling(raw))
1395 }
1396
1397 pub fn ancestors(&self) -> impl Iterator<Item = NodeRef<'a>> + 'a {
1399 let doc = self.doc;
1400 let mut current = doc.ast.parent_id(self.raw);
1401 std::iter::from_fn(move || {
1402 let raw = current?;
1403 current = doc.ast.parent_id(raw);
1404 Some(NodeRef { doc, raw })
1405 })
1406 }
1407
1408 pub fn descendants(&self) -> impl Iterator<Item = NodeRef<'a>> + 'a {
1410 let doc = self.doc;
1411 let start = self.raw;
1412 doc.ast
1413 .find_all(start, |_| true)
1414 .into_iter()
1415 .filter(move |raw| *raw != start)
1416 .map(move |raw| NodeRef { doc, raw })
1417 }
1418
1419 pub fn command_name(&self) -> Option<&'a str> {
1421 match self.node() {
1422 Node::Command { name, .. }
1423 | Node::Infix { name, .. }
1424 | Node::Declarative { name, .. } => Some(name),
1425 _ => None,
1426 }
1427 }
1428
1429 pub fn env_name(&self) -> Option<&'a str> {
1431 match self.node() {
1432 Node::Environment { name, .. } => Some(name),
1433 _ => None,
1434 }
1435 }
1436
1437 pub fn text(&self) -> Option<&'a str> {
1439 match self.node() {
1440 Node::Text(text) => Some(text),
1441 _ => None,
1442 }
1443 }
1444
1445 pub fn char(&self) -> Option<char> {
1447 match self.node() {
1448 Node::Char(ch) => Some(*ch),
1449 _ => None,
1450 }
1451 }
1452
1453 pub fn prime_count(&self) -> Option<usize> {
1455 match self.node() {
1456 Node::Prime { count } => Some(*count),
1457 _ => None,
1458 }
1459 }
1460
1461 pub fn error_parts(&self) -> Option<(&'a str, &'a str)> {
1463 match self.node() {
1464 Node::Error { message, snippet } => Some((message, snippet)),
1465 _ => None,
1466 }
1467 }
1468
1469 pub fn content_mode(&self) -> Option<ContentMode> {
1471 match self.node() {
1472 Node::Root { mode, .. } | Node::Group { mode, .. } => Some(*mode),
1473 _ => None,
1474 }
1475 }
1476
1477 pub fn group_kind(&self) -> Option<GroupKindRef<'a>> {
1479 match self.node() {
1480 Node::Group { kind, .. } => Some(self.group_kind_ref(kind)),
1481 _ => None,
1482 }
1483 }
1484
1485 pub fn arg_count(&self) -> usize {
1487 self.doc.ast.arg_slots(self.raw).len()
1488 }
1489
1490 pub fn arg(&self, index: usize) -> Option<ArgRef<'a>> {
1492 let arg = self.doc.ast.arg_slots(self.raw).get(index)?.as_ref()?;
1493 Some(self.arg_ref(arg))
1494 }
1495
1496 pub fn arg_slots(&self) -> impl Iterator<Item = Option<ArgRef<'a>>> + 'a {
1498 let this = *self;
1499 (0..self.arg_count()).map(move |index| this.arg(index))
1500 }
1501
1502 pub fn script_base(&self) -> Option<NodeRef<'a>> {
1504 match self.node() {
1505 Node::Scripted { base, .. } => Some(self.sibling(*base)),
1506 _ => None,
1507 }
1508 }
1509
1510 pub fn subscript(&self) -> Option<NodeRef<'a>> {
1512 match self.node() {
1513 Node::Scripted { subscript, .. } => subscript.map(|raw| self.sibling(raw)),
1514 _ => None,
1515 }
1516 }
1517
1518 pub fn superscript(&self) -> Option<NodeRef<'a>> {
1520 match self.node() {
1521 Node::Scripted { superscript, .. } => superscript.map(|raw| self.sibling(raw)),
1522 _ => None,
1523 }
1524 }
1525
1526 pub fn infix_left(&self) -> Option<NodeRef<'a>> {
1528 match self.node() {
1529 Node::Infix { left, .. } => Some(self.sibling(*left)),
1530 _ => None,
1531 }
1532 }
1533
1534 pub fn infix_right(&self) -> Option<NodeRef<'a>> {
1536 match self.node() {
1537 Node::Infix { right, .. } => Some(self.sibling(*right)),
1538 _ => None,
1539 }
1540 }
1541
1542 pub fn env_body(&self) -> Option<NodeRef<'a>> {
1544 match self.node() {
1545 Node::Environment { body, .. } => Some(self.sibling(*body)),
1546 _ => None,
1547 }
1548 }
1549
1550 pub fn span(&self) -> Option<Span> {
1552 self.doc.spans.get(self.raw).cloned()
1553 }
1554
1555 fn node(&self) -> &'a Node {
1556 self.doc.ast.node(self.raw)
1557 }
1558
1559 fn sibling(&self, raw: RawNodeId) -> NodeRef<'a> {
1560 NodeRef { doc: self.doc, raw }
1561 }
1562
1563 fn arg_ref(&self, arg: &'a Argument) -> ArgRef<'a> {
1564 match &arg.value {
1565 ArgumentValue::MathContent(id) => ArgRef::Math(self.sibling(*id)),
1566 ArgumentValue::TextContent(id) => ArgRef::Text(self.sibling(*id)),
1567 ArgumentValue::Delimiter(delimiter) => {
1568 ArgRef::Delimiter(Self::delimiter_ref(delimiter))
1569 }
1570 ArgumentValue::CSName(value) => ArgRef::CSName(value),
1571 ArgumentValue::Dimension(value) => ArgRef::Dimension(value),
1572 ArgumentValue::Integer(value) => ArgRef::Integer(value),
1573 ArgumentValue::KeyVal(value) => ArgRef::KeyVal(value),
1574 ArgumentValue::Column(value) => ArgRef::Column(value),
1575 ArgumentValue::Boolean(value) => ArgRef::Boolean(*value),
1576 }
1577 }
1578
1579 fn group_kind_ref(&self, kind: &'a GroupKind) -> GroupKindRef<'a> {
1580 match kind {
1581 GroupKind::Explicit => GroupKindRef::Explicit,
1582 GroupKind::Implicit => GroupKindRef::Implicit,
1583 GroupKind::Delimited { left, right } => GroupKindRef::Delimited {
1584 left: Self::delimiter_ref(left),
1585 right: Self::delimiter_ref(right),
1586 },
1587 GroupKind::InlineMath => GroupKindRef::InlineMath,
1588 }
1589 }
1590
1591 fn delimiter_ref(delimiter: &'a Delimiter) -> DelimiterRef<'a> {
1592 match delimiter {
1593 Delimiter::None => DelimiterRef::None,
1594 Delimiter::Char(ch) => DelimiterRef::Char(*ch),
1595 Delimiter::Control(name) => DelimiterRef::Control(name),
1596 }
1597 }
1598}
1599
1600#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1601pub enum DelimiterRef<'a> {
1602 None,
1603 Char(char),
1604 Control(&'a str),
1605}
1606
1607#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1608pub enum GroupKindRef<'a> {
1609 Explicit,
1610 Implicit,
1611 Delimited {
1612 left: DelimiterRef<'a>,
1613 right: DelimiterRef<'a>,
1614 },
1615 InlineMath,
1616}
1617
1618#[derive(Clone, Copy, Debug)]
1620pub enum ArgRef<'a> {
1621 Math(NodeRef<'a>),
1622 Text(NodeRef<'a>),
1623 Delimiter(DelimiterRef<'a>),
1624 CSName(&'a str),
1625 Dimension(&'a str),
1626 Integer(&'a str),
1627 KeyVal(&'a str),
1628 Column(&'a str),
1629 Boolean(bool),
1630}
1631
1632impl<'a> ArgRef<'a> {
1633 pub fn as_node(self) -> Option<NodeRef<'a>> {
1634 match self {
1635 ArgRef::Math(node) | ArgRef::Text(node) => Some(node),
1636 _ => None,
1637 }
1638 }
1639}
1640
1641#[cfg(test)]
1642impl Document {
1643 fn from_ast_for_test(build: impl FnOnce(&mut Ast)) -> Self {
1644 let mut ast = Ast::new();
1645 build(&mut ast);
1646 ast.assert_invariants();
1647 Document {
1648 ast,
1649 spans: SecondaryMap::new(),
1650 has_errors: false,
1651 id: next_document_id(),
1652 }
1653 }
1654
1655 fn from_ast_with_errors_for_test(build: impl FnOnce(&mut Ast)) -> Self {
1656 let mut ast = Ast::new();
1657 build(&mut ast);
1658 ast.assert_invariants();
1659 let has_errors = ast
1660 .find(ast.root(), |node| matches!(node, Node::Error { .. }))
1661 .is_some();
1662 Document {
1663 ast,
1664 spans: SecondaryMap::new(),
1665 has_errors,
1666 id: next_document_id(),
1667 }
1668 }
1669}
1670
1671#[cfg(test)]
1672mod tests {
1673 use super::*;
1674
1675 #[test]
1676 fn new_document_is_empty_and_editable() {
1677 let doc = Document::new();
1678 assert_eq!(doc.root().kind(), NodeKind::Root);
1679 assert!(!doc.has_errors());
1680 assert!(!doc.is_read_only());
1681 assert_eq!(doc.errors().count(), 0);
1682 }
1683
1684 #[test]
1685 fn document_ids_are_unique_across_construction_and_clone() {
1686 let a = Document::new();
1687 let b = Document::new();
1688 assert_ne!(a.id(), b.id());
1689
1690 let c = a.clone();
1691 assert_ne!(a.id(), c.id());
1692 }
1693
1694 #[test]
1695 fn node_ref_reads_synthesized_chars() {
1696 let doc = Document::from_ast_for_test(|ast| {
1697 let a = ast.new_node(Node::Char('a'));
1698 let b = ast.new_node(Node::Char('b'));
1699 ast.append_child(ast.root(), a);
1700 ast.append_child(ast.root(), b);
1701 });
1702
1703 let mut children = doc.root().children();
1704 let first = children.next().unwrap();
1705 let second = children.next().unwrap();
1706 assert!(first.is_char('a'));
1707 assert!(second.is_char('b'));
1708 assert_eq!(first.next_sibling().map(|n| n.id()), Some(second.id()));
1709 assert_eq!(second.prev_sibling().map(|n| n.id()), Some(first.id()));
1710 assert_eq!(doc.root().children().count(), 2);
1711 }
1712
1713 #[test]
1714 fn find_all_collects_matching_nodes() {
1715 let doc = Document::from_ast_for_test(|ast| {
1716 let a = ast.new_node(Node::Char('a'));
1717 let group = ast.new_node(Node::Group {
1718 children: vec![a],
1719 kind: crate::ast::GroupKind::Explicit,
1720 mode: ContentMode::Math,
1721 });
1722 let b = ast.new_node(Node::Char('b'));
1723 ast.append_child(ast.root(), group);
1724 ast.append_child(ast.root(), b);
1725 });
1726
1727 let chars: Vec<_> = doc
1728 .find_all(doc.root(), |node| node.char().is_some())
1729 .filter_map(|node| node.char())
1730 .collect();
1731 assert_eq!(chars, vec!['a', 'b']);
1732 }
1733
1734 #[test]
1735 fn edit_error_implements_display_and_error() {
1736 fn assert_error<E: std::error::Error>() {}
1737 assert_error::<EditError>();
1738
1739 assert_eq!(
1740 EditError::CannotEditRoot.to_string(),
1741 "cannot edit the root node"
1742 );
1743 assert_eq!(
1744 EditError::SlotShapeMismatch { expected: "group" }.to_string(),
1745 "slot shape mismatch: expected group"
1746 );
1747 }
1748
1749 #[test]
1750 fn create_and_append_children() {
1751 let mut doc = Document::new();
1752 let a = doc.create_char('a').unwrap();
1753 let b = doc.create_char('b').unwrap();
1754 let root = doc.root().id();
1755
1756 doc.append_child(root, a).unwrap();
1757 doc.append_child(root, b).unwrap();
1758
1759 let chars: Vec<_> = doc
1760 .root()
1761 .children()
1762 .filter_map(|node| node.char())
1763 .collect();
1764 assert_eq!(chars, vec!['a', 'b']);
1765 }
1766
1767 #[test]
1768 fn insert_before_and_after() {
1769 let mut doc = Document::new();
1770 let a = doc.create_char('a').unwrap();
1771 let c = doc.create_char('c').unwrap();
1772 let b = doc.create_char('b').unwrap();
1773 let d = doc.create_char('d').unwrap();
1774 let root = doc.root().id();
1775 doc.append_child(root, a).unwrap();
1776 doc.append_child(root, c).unwrap();
1777
1778 doc.insert_before(c, b).unwrap();
1779 doc.insert_after(c, d).unwrap();
1780
1781 let chars: Vec<_> = doc
1782 .root()
1783 .children()
1784 .filter_map(|node| node.char())
1785 .collect();
1786 assert_eq!(chars, vec!['a', 'b', 'c', 'd']);
1787 }
1788
1789 #[test]
1790 fn remove_and_extract() {
1791 let mut doc = Document::new();
1792 let a = doc.create_char('a').unwrap();
1793 let b = doc.create_char('b').unwrap();
1794 let root = doc.root().id();
1795 doc.append_child(root, a).unwrap();
1796 doc.append_child(root, b).unwrap();
1797
1798 let extracted = doc.extract(a).unwrap();
1799 assert_eq!(extracted, a);
1800 doc.remove(b).unwrap();
1801
1802 assert_eq!(doc.root().children().count(), 0);
1803 doc.append_child(root, extracted).unwrap();
1804 assert_eq!(
1805 doc.root().children().next().and_then(|node| node.char()),
1806 Some('a')
1807 );
1808 }
1809
1810 #[test]
1811 fn replace_with_swaps_node() {
1812 let mut doc = Document::new();
1813 let a = doc.create_char('a').unwrap();
1814 let b = doc.create_char('b').unwrap();
1815 let root = doc.root().id();
1816 doc.append_child(root, a).unwrap();
1817
1818 doc.replace_with(a, b).unwrap();
1819
1820 assert_eq!(
1821 doc.root().children().next().and_then(|node| node.char()),
1822 Some('b')
1823 );
1824 }
1825
1826 #[test]
1827 fn set_text_and_char_and_command_name() {
1828 let mut doc = Document::new();
1829 let text = doc.create_text("old").unwrap();
1830 let ch = doc.create_char('a').unwrap();
1831 let cmd = doc.create_command("alpha", Vec::new()).unwrap();
1832
1833 doc.set_text(text, "new").unwrap();
1834 doc.set_char(ch, 'b').unwrap();
1835 doc.set_command_name(cmd, "beta").unwrap();
1836
1837 assert_eq!(doc.node(text).unwrap().text(), Some("new"));
1838 assert_eq!(doc.node(ch).unwrap().char(), Some('b'));
1839 assert_eq!(doc.node(cmd).unwrap().command_name(), Some("beta"));
1840 }
1841
1842 #[test]
1843 fn cannot_edit_root() {
1844 let mut doc = Document::new();
1845 let root = doc.root().id();
1846 assert_eq!(doc.remove(root), Err(EditError::CannotEditRoot));
1847 assert_eq!(doc.extract(root), Err(EditError::CannotEditRoot));
1848 }
1849
1850 #[test]
1851 fn append_to_non_container_fails() {
1852 let mut doc = Document::new();
1853 let parent = doc.create_char('a').unwrap();
1854 let child = doc.create_char('b').unwrap();
1855
1856 assert_eq!(
1857 doc.append_child(parent, child),
1858 Err(EditError::NotAContainer)
1859 );
1860 }
1861
1862 #[test]
1863 fn create_command_with_args_and_read_back() {
1864 let mut doc = Document::new();
1865 let numerator = doc.create_char('a').unwrap();
1866 let denominator = doc.create_char('b').unwrap();
1867 let frac = doc
1868 .create_command(
1869 "frac",
1870 vec![ArgValue::math(numerator), ArgValue::math(denominator)],
1871 )
1872 .unwrap();
1873
1874 let node = doc.node(frac).unwrap();
1875 assert_eq!(node.command_name(), Some("frac"));
1876 assert_eq!(node.arg_count(), 2);
1877 assert_eq!(
1878 node.arg(0)
1879 .and_then(|arg| arg.as_node())
1880 .and_then(|node| node.char()),
1881 Some('a')
1882 );
1883 assert_eq!(
1884 node.arg(1)
1885 .and_then(|arg| arg.as_node())
1886 .and_then(|node| node.char()),
1887 Some('b')
1888 );
1889 }
1890
1891 #[test]
1892 fn duplicate_child_is_rejected() {
1893 let mut doc = Document::new();
1894 let child = doc.create_char('x').unwrap();
1895
1896 assert_eq!(
1897 doc.create_command("dup", vec![ArgValue::math(child), ArgValue::math(child)]),
1898 Err(EditError::DuplicateChild)
1899 );
1900 }
1901
1902 #[test]
1903 fn set_arg_replaces_content() {
1904 let mut doc = Document::new();
1905 let old = doc.create_char('a').unwrap();
1906 let cmd = doc
1907 .create_command("sqrt", vec![ArgValue::math(old)])
1908 .unwrap();
1909 let new = doc.create_char('b').unwrap();
1910
1911 doc.set_arg(cmd, 0, ArgValue::math(new)).unwrap();
1912
1913 let node = doc.node(cmd).unwrap();
1914 assert_eq!(
1915 node.arg(0)
1916 .and_then(|arg| arg.as_node())
1917 .and_then(|node| node.char()),
1918 Some('b')
1919 );
1920 }
1921
1922 #[test]
1923 fn set_arg_preserves_optional_slot_kind() {
1924 use texform_interface::syntax_node::{
1925 Argument as SyntaxArgument, ArgumentKind as SyntaxArgumentKind,
1926 ArgumentValue as SyntaxArgumentValue, ContentMode as M, SyntaxNode,
1927 };
1928
1929 let syntax = SyntaxNode::Root {
1930 mode: M::Math,
1931 children: vec![SyntaxNode::Command {
1932 name: "sqrt".to_string(),
1933 args: vec![Some(SyntaxArgument {
1934 kind: SyntaxArgumentKind::Optional,
1935 value: SyntaxArgumentValue::MathContent(SyntaxNode::Char('a')),
1936 })],
1937 known: true,
1938 }],
1939 };
1940 let mut doc = Document::from_syntax(&syntax).unwrap();
1941 let command = doc.root().children().next().unwrap().id();
1942 let replacement = doc.create_char('b').unwrap();
1943
1944 doc.set_arg(command, 0, ArgValue::math(replacement))
1945 .unwrap();
1946
1947 assert_eq!(doc.to_latex().unwrap(), r"\sqrt [ b ]");
1948 }
1949
1950 #[test]
1951 fn set_arg_preserves_star_boolean_slot_kind() {
1952 use texform_interface::syntax_node::{
1953 Argument as SyntaxArgument, ArgumentKind as SyntaxArgumentKind,
1954 ArgumentValue as SyntaxArgumentValue, ContentMode as M, SyntaxNode,
1955 };
1956
1957 let syntax = SyntaxNode::Root {
1958 mode: M::Math,
1959 children: vec![SyntaxNode::Command {
1960 name: "operatorname".to_string(),
1961 args: vec![
1962 Some(SyntaxArgument {
1963 kind: SyntaxArgumentKind::Star,
1964 value: SyntaxArgumentValue::Boolean(false),
1965 }),
1966 Some(SyntaxArgument {
1967 kind: SyntaxArgumentKind::Mandatory,
1968 value: SyntaxArgumentValue::MathContent(SyntaxNode::Char('x')),
1969 }),
1970 ],
1971 known: true,
1972 }],
1973 };
1974 let mut doc = Document::from_syntax(&syntax).unwrap();
1975 let command = doc.root().children().next().unwrap().id();
1976
1977 doc.set_arg(command, 0, ArgValue::boolean(true)).unwrap();
1978
1979 assert_eq!(doc.to_latex().unwrap(), r"\operatorname* { x }");
1980 }
1981
1982 #[test]
1983 fn read_only_editing_checks_precede_invalid_node_checks() {
1984 let mut read_only = Document::from_ast_with_errors_for_test(|ast| {
1985 let err = ast.new_node(Node::Error {
1986 message: "bad".to_string(),
1987 snippet: "x".to_string(),
1988 });
1989 ast.append_child(ast.root(), err);
1990 });
1991 let mut other = Document::new();
1992 let foreign = other.create_char('x').unwrap();
1993
1994 assert_eq!(
1995 read_only.append_child(foreign, foreign),
1996 Err(EditError::ReadOnlyDocument)
1997 );
1998 assert_eq!(
1999 read_only.insert_before(foreign, foreign),
2000 Err(EditError::ReadOnlyDocument)
2001 );
2002 }
2003
2004 #[test]
2005 fn wrap_moves_target_inside_wrapper() {
2006 let mut doc = Document::new();
2007 let a = doc.create_char('a').unwrap();
2008 let wrapper = doc.create_group(ContentMode::Math).unwrap();
2009 let root = doc.root().id();
2010 doc.append_child(root, a).unwrap();
2011
2012 let wrapped = doc.wrap(a, wrapper).unwrap();
2013
2014 let group = doc.root().children().next().unwrap();
2015 assert_eq!(group.id(), wrapped);
2016 assert_eq!(
2017 group.children().next().and_then(|node| node.char()),
2018 Some('a')
2019 );
2020 }
2021
2022 #[test]
2023 fn unwrap_splices_group_children_into_parent() {
2024 let mut doc = Document::new();
2025 let group = doc.create_group(ContentMode::Math).unwrap();
2026 let a = doc.create_char('a').unwrap();
2027 let b = doc.create_char('b').unwrap();
2028 doc.append_child(group, a).unwrap();
2029 doc.append_child(group, b).unwrap();
2030 let root = doc.root().id();
2031 doc.append_child(root, group).unwrap();
2032
2033 let children = doc.unwrap(group).unwrap();
2034
2035 assert_eq!(children.len(), 2);
2036 let chars: Vec<_> = doc
2037 .root()
2038 .children()
2039 .filter_map(|node| node.char())
2040 .collect();
2041 assert_eq!(chars, vec!['a', 'b']);
2042 }
2043
2044 #[test]
2045 fn error_tree_is_read_only() {
2046 let doc = Document::from_ast_with_errors_for_test(|ast| {
2047 let err = ast.new_node(Node::Error {
2048 message: "bad".to_string(),
2049 snippet: "x".to_string(),
2050 });
2051 ast.append_child(ast.root(), err);
2052 });
2053 assert!(doc.has_errors());
2054 assert!(doc.is_read_only());
2055 assert_eq!(doc.errors().count(), 1);
2056
2057 let mut doc = doc;
2058 assert_eq!(doc.create_char('z'), Err(EditError::ReadOnlyDocument));
2059 }
2060
2061 #[test]
2062 fn from_syntax_round_trips_clean_tree() {
2063 use texform_interface::syntax_node::{ContentMode as M, SyntaxNode};
2064
2065 let syntax = SyntaxNode::Root {
2066 mode: M::Math,
2067 children: vec![SyntaxNode::Char('a'), SyntaxNode::Char('b')],
2068 };
2069 let doc = Document::from_syntax(&syntax).unwrap();
2070 assert!(!doc.has_errors());
2071
2072 assert_eq!(doc.to_syntax(), syntax);
2073 }
2074
2075 #[test]
2076 fn from_syntax_rejects_zero_count_prime() {
2077 use texform_interface::syntax_node::{ContentMode as M, SyntaxNode};
2078
2079 let syntax = SyntaxNode::Root {
2080 mode: M::Math,
2081 children: vec![SyntaxNode::Prime { count: 0 }],
2082 };
2083
2084 assert_eq!(
2085 Document::from_syntax(&syntax).expect_err("expected invalid prime count"),
2086 FromSyntaxError::InvalidPrimeCount
2087 );
2088 }
2089
2090 #[test]
2091 fn from_syntax_rejects_text_mode_prime() {
2092 use texform_interface::syntax_node::{ContentMode as M, SyntaxNode};
2093
2094 let syntax = SyntaxNode::Root {
2095 mode: M::Text,
2096 children: vec![SyntaxNode::Prime { count: 1 }],
2097 };
2098
2099 assert_eq!(
2100 Document::from_syntax(&syntax).expect_err("expected text-mode prime rejection"),
2101 FromSyntaxError::PrimeInTextMode
2102 );
2103 }
2104
2105 #[test]
2106 fn from_syntax_rejects_text_mode_scripted_prime() {
2107 use texform_interface::syntax_node::{ContentMode as M, SyntaxNode};
2108
2109 let syntax = SyntaxNode::Root {
2110 mode: M::Text,
2111 children: vec![SyntaxNode::Scripted {
2112 base: Box::new(SyntaxNode::Prime { count: 1 }),
2113 subscript: None,
2114 superscript: None,
2115 }],
2116 };
2117
2118 assert_eq!(
2119 Document::from_syntax(&syntax)
2120 .expect_err("expected text-mode scripted prime rejection"),
2121 FromSyntaxError::PrimeInTextMode
2122 );
2123 }
2124
2125 #[test]
2126 fn from_syntax_rejects_text_content_scripted_prime() {
2127 use texform_interface::syntax_node::{
2128 Argument, ArgumentKind, ArgumentValue, ContentMode as M, SyntaxNode,
2129 };
2130
2131 let syntax = SyntaxNode::Root {
2132 mode: M::Math,
2133 children: vec![SyntaxNode::Command {
2134 name: "text".to_string(),
2135 args: vec![Some(Argument {
2136 kind: ArgumentKind::Mandatory,
2137 value: ArgumentValue::TextContent(SyntaxNode::Scripted {
2138 base: Box::new(SyntaxNode::Prime { count: 1 }),
2139 subscript: None,
2140 superscript: None,
2141 }),
2142 })],
2143 known: true,
2144 }],
2145 };
2146
2147 assert_eq!(
2148 Document::from_syntax(&syntax)
2149 .expect_err("expected text-content scripted prime rejection"),
2150 FromSyntaxError::PrimeInTextMode
2151 );
2152 }
2153
2154 #[test]
2155 fn from_syntax_marks_error_tree_read_only() {
2156 use texform_interface::syntax_node::{ContentMode as M, SyntaxNode};
2157
2158 let syntax = SyntaxNode::Root {
2159 mode: M::Math,
2160 children: vec![SyntaxNode::Error {
2161 message: "bad".to_string(),
2162 snippet: "x".to_string(),
2163 }],
2164 };
2165 let doc = Document::from_syntax(&syntax).unwrap();
2166 assert!(doc.has_errors());
2167 assert!(doc.is_read_only());
2168 }
2169
2170 #[test]
2171 fn from_syntax_alone_has_no_spans() {
2172 use texform_interface::syntax_node::{ContentMode as M, SyntaxNode};
2173
2174 let syntax = SyntaxNode::Root {
2175 mode: M::Math,
2176 children: vec![SyntaxNode::Char('a')],
2177 };
2178 let doc = Document::from_syntax(&syntax).unwrap();
2179 assert_eq!(doc.root().children().next().unwrap().span(), None);
2180 }
2181
2182 #[test]
2183 fn span_mapping_aligns_paths_to_node_ids() {
2184 use texform_interface::syntax_node::{ContentMode as M, SyntaxNode};
2185
2186 let syntax = SyntaxNode::Root {
2187 mode: M::Math,
2188 children: vec![SyntaxNode::Char('a'), SyntaxNode::Char('b')],
2189 };
2190 let path_spans = vec![
2191 ("root".to_string(), Span { start: 0, end: 2 }),
2192 ("root.child.0".to_string(), Span { start: 0, end: 1 }),
2193 ("root.child.1".to_string(), Span { start: 1, end: 2 }),
2194 ];
2195
2196 let doc = Document::from_syntax_with_spans(&syntax, &path_spans).unwrap();
2197 let mut kids = doc.root().children();
2198 let a = kids.next().unwrap();
2199 let b = kids.next().unwrap();
2200 assert_eq!(a.span(), Some(Span { start: 0, end: 1 }));
2201 assert_eq!(b.span(), Some(Span { start: 1, end: 2 }));
2202 assert_eq!(doc.root().span(), Some(Span { start: 0, end: 2 }));
2203 }
2204
2205 #[test]
2206 fn to_latex_default_and_with_options() {
2207 use crate::serialize::SerializeOptions;
2208
2209 let mut doc = Document::new();
2210 let a = doc.create_char('a').unwrap();
2211 let b = doc.create_char('b').unwrap();
2212 let root = doc.root().id();
2213 doc.append_child(root, a).unwrap();
2214 doc.append_child(root, b).unwrap();
2215
2216 assert_eq!(doc.to_latex().unwrap(), "a b");
2217 assert_eq!(format!("{doc}"), "a b");
2218
2219 let opts = SerializeOptions::default();
2220 assert_eq!(doc.to_latex_with(&opts).unwrap(), "a b");
2221 }
2222}