markdown_editor/
selection.rs1use super::{
2 document::EditorDocument,
3 id::BlockId,
4 position::{BlockPosition, PositionError},
5};
6
7#[derive(Clone, PartialEq, Debug)]
8pub enum BlockSelection {
9 Text {
12 anchor: BlockPosition,
13 head: BlockPosition,
14 },
15 Blocks { anchor: BlockId, head: BlockId },
17 Cells { anchor: BlockId, head: BlockId },
21}
22
23impl BlockSelection {
24 pub fn caret(position: BlockPosition) -> Self {
25 Self::Text {
26 anchor: position,
27 head: position,
28 }
29 }
30
31 pub fn is_collapsed(&self) -> bool {
32 matches!(self, Self::Text { anchor, head } if anchor == head)
33 }
34}
35
36#[non_exhaustive]
37#[derive(Clone, Copy, PartialEq, Eq, Debug)]
38pub enum SelectionError {
39 Position(PositionError),
40 UnknownBlock(BlockId),
41 NotSiblings,
42}
43
44impl From<PositionError> for SelectionError {
45 fn from(error: PositionError) -> Self {
46 Self::Position(error)
47 }
48}
49
50#[derive(Clone, Copy, PartialEq, Debug)]
52pub(crate) struct ResolvedSelection {
53 pub(crate) start: BlockPosition,
54 pub(crate) end: BlockPosition,
55}
56
57impl ResolvedSelection {
58 pub(crate) fn is_collapsed(&self) -> bool {
59 self.start.block == self.end.block && self.start.offset == self.end.offset
60 }
61}
62
63impl EditorDocument {
64 pub(crate) fn validate_selection(
65 &self,
66 selection: &BlockSelection,
67 ) -> Result<(), SelectionError> {
68 match selection {
69 BlockSelection::Text { anchor, head } => {
70 self.validate(*anchor)?;
71 self.validate(*head)?;
72 }
73 BlockSelection::Blocks { anchor, head } => {
74 let a = self
75 .get(*anchor)
76 .ok_or(SelectionError::UnknownBlock(*anchor))?;
77 let b = self.get(*head).ok_or(SelectionError::UnknownBlock(*head))?;
78 if a.parent != b.parent || a.parent.is_none() {
79 return Err(SelectionError::NotSiblings);
80 }
81 }
82 BlockSelection::Cells { anchor, head } => {
83 let table = |id: BlockId| super::table::table_of(self, id);
84 match (table(*anchor), table(*head)) {
85 (Some(a), Some(b)) if a == b => {}
86 (None, _) => return Err(SelectionError::UnknownBlock(*anchor)),
87 _ => return Err(SelectionError::NotSiblings),
88 }
89 }
90 }
91 Ok(())
92 }
93
94 pub(crate) fn resolve_selection(
97 &self,
98 selection: &BlockSelection,
99 ) -> Result<ResolvedSelection, SelectionError> {
100 self.validate_selection(selection)?;
101 let (a, b) = match selection {
102 BlockSelection::Text { anchor, head } => (*anchor, *head),
103 BlockSelection::Blocks { anchor, head } => {
104 let first = |id: BlockId| self.first_leaf_in(id);
105 let last = |id: BlockId| self.last_leaf_in(id);
106 let (lo, hi) = if self.cmp_positions(first(*anchor), first(*head))?.is_le() {
107 (*anchor, *head)
108 } else {
109 (*head, *anchor)
110 };
111 (first(lo), last(hi))
112 }
113 BlockSelection::Cells { anchor, head } => (
114 BlockPosition::new(*anchor, 0),
115 BlockPosition::new(*head, self.leaf_len(*head).unwrap_or(0)),
116 ),
117 };
118 if self.cmp_positions(a, b)?.is_le() {
119 Ok(ResolvedSelection { start: a, end: b })
120 } else {
121 Ok(ResolvedSelection { start: b, end: a })
122 }
123 }
124
125 pub(crate) fn first_leaf_in(&self, id: BlockId) -> BlockPosition {
126 let mut current = id;
127 while let Some(first) = self.children(current).first() {
128 current = *first;
129 }
130 BlockPosition::new(current, 0)
131 }
132
133 pub(crate) fn last_leaf_in(&self, id: BlockId) -> BlockPosition {
134 let mut current = id;
135 while let Some(last) = self.children(current).last() {
136 current = *last;
137 }
138 BlockPosition::new(current, self.leaf_len(current).unwrap_or(0))
139 }
140}