1use accesskit::{
7 NodeId, Point, Rect, Role, TextDirection, TextPosition as WeakPosition, TextSelection,
8};
9use std::{cmp::Ordering, iter::FusedIterator};
10
11use crate::{FilterResult, Node, TreeState};
12
13#[derive(Clone, Copy)]
14pub(crate) struct InnerPosition<'a> {
15 pub(crate) node: Node<'a>,
16 pub(crate) character_index: usize,
17}
18
19impl<'a> InnerPosition<'a> {
20 fn upgrade(tree_state: &'a TreeState, weak: WeakPosition) -> Option<Self> {
21 let node = tree_state.node_by_id(weak.node)?;
22 if node.role() != Role::InlineTextBox {
23 return None;
24 }
25 let character_index = weak.character_index;
26 if character_index > node.data().character_lengths().len() {
27 return None;
28 }
29 Some(Self {
30 node,
31 character_index,
32 })
33 }
34
35 fn clamped_upgrade(tree_state: &'a TreeState, weak: WeakPosition) -> Option<Self> {
36 let node = tree_state.node_by_id(weak.node)?;
37 if node.role() != Role::InlineTextBox {
38 return None;
39 }
40 let character_index = weak
41 .character_index
42 .min(node.data().character_lengths().len());
43 Some(Self {
44 node,
45 character_index,
46 })
47 }
48
49 fn is_word_start(&self) -> bool {
50 let mut total_length = 0usize;
51 for length in self.node.data().word_lengths().iter() {
52 if total_length == self.character_index {
53 return true;
54 }
55 total_length += *length as usize;
56 }
57 false
58 }
59
60 fn is_box_start(&self) -> bool {
61 self.character_index == 0
62 }
63
64 fn is_line_start(&self) -> bool {
65 self.is_box_start() && self.node.data().previous_on_line().is_none()
66 }
67
68 fn is_box_end(&self) -> bool {
69 self.character_index == self.node.data().character_lengths().len()
70 }
71
72 fn is_line_end(&self) -> bool {
73 self.is_box_end() && self.node.data().next_on_line().is_none()
74 }
75
76 fn is_paragraph_end(&self) -> bool {
77 self.is_line_end() && self.node.data().value().unwrap().ends_with('\n')
78 }
79
80 fn is_document_start(&self, root_node: &Node) -> bool {
81 self.is_box_start()
82 && self
83 .node
84 .preceding_inline_text_boxes(root_node)
85 .next()
86 .is_none()
87 }
88
89 fn is_document_end(&self, root_node: &Node) -> bool {
90 self.is_box_end()
91 && self
92 .node
93 .following_inline_text_boxes(root_node)
94 .next()
95 .is_none()
96 }
97
98 fn biased_to_start(&self, root_node: &Node) -> Self {
99 if self.is_box_end() {
100 if let Some(node) = self.node.following_inline_text_boxes(root_node).next() {
101 return Self {
102 node,
103 character_index: 0,
104 };
105 }
106 }
107 *self
108 }
109
110 fn biased_to_end(&self, root_node: &Node) -> Self {
111 if self.is_box_start() {
112 if let Some(node) = self.node.preceding_inline_text_boxes(root_node).next() {
113 return Self {
114 node,
115 character_index: node.data().character_lengths().len(),
116 };
117 }
118 }
119 *self
120 }
121
122 fn comparable(&self, root_node: &Node) -> (Vec<usize>, usize) {
123 let normalized = self.biased_to_start(root_node);
124 (
125 normalized.node.relative_index_path(root_node.id()),
126 normalized.character_index,
127 )
128 }
129
130 fn previous_word_start(&self) -> Self {
131 let mut total_length_before = 0usize;
132 for length in self.node.data().word_lengths().iter() {
133 let new_total_length = total_length_before + (*length as usize);
134 if new_total_length >= self.character_index {
135 break;
136 }
137 total_length_before = new_total_length;
138 }
139 Self {
140 node: self.node,
141 character_index: total_length_before,
142 }
143 }
144
145 fn word_end(&self) -> Self {
146 let mut total_length = 0usize;
147 for length in self.node.data().word_lengths().iter() {
148 total_length += *length as usize;
149 if total_length > self.character_index {
150 break;
151 }
152 }
153 Self {
154 node: self.node,
155 character_index: total_length,
156 }
157 }
158
159 fn line_start(&self) -> Self {
160 let mut node = self.node;
161 while let Some(id) = node.data().previous_on_line() {
162 node = node.tree_state.node_by_id(id).unwrap();
163 }
164 Self {
165 node,
166 character_index: 0,
167 }
168 }
169
170 fn line_end(&self) -> Self {
171 let mut node = self.node;
172 while let Some(id) = node.data().next_on_line() {
173 node = node.tree_state.node_by_id(id).unwrap();
174 }
175 Self {
176 node,
177 character_index: node.data().character_lengths().len(),
178 }
179 }
180
181 pub(crate) fn downgrade(&self) -> WeakPosition {
182 WeakPosition {
183 node: self.node.id(),
184 character_index: self.character_index,
185 }
186 }
187}
188
189impl<'a> PartialEq for InnerPosition<'a> {
190 fn eq(&self, other: &Self) -> bool {
191 self.node.id() == other.node.id() && self.character_index == other.character_index
192 }
193}
194
195impl<'a> Eq for InnerPosition<'a> {}
196
197#[derive(Clone, Copy)]
198pub struct Position<'a> {
199 root_node: Node<'a>,
200 pub(crate) inner: InnerPosition<'a>,
201}
202
203impl<'a> Position<'a> {
204 pub fn inner_node(&self) -> &Node {
205 &self.inner.node
206 }
207
208 pub fn is_format_start(&self) -> bool {
209 self.is_document_start()
211 }
212
213 pub fn is_word_start(&self) -> bool {
214 self.inner.is_word_start()
215 }
216
217 pub fn is_line_start(&self) -> bool {
218 self.inner.is_line_start()
219 }
220
221 pub fn is_line_end(&self) -> bool {
222 self.inner.is_line_end()
223 }
224
225 pub fn is_paragraph_start(&self) -> bool {
226 self.is_document_start()
227 || (self.is_line_start()
228 && self.inner.biased_to_end(&self.root_node).is_paragraph_end())
229 }
230
231 pub fn is_paragraph_end(&self) -> bool {
232 self.is_document_end() || self.inner.is_paragraph_end()
233 }
234
235 pub fn is_page_start(&self) -> bool {
236 self.is_document_start()
237 }
238
239 pub fn is_document_start(&self) -> bool {
240 self.inner.is_document_start(&self.root_node)
241 }
242
243 pub fn is_document_end(&self) -> bool {
244 self.inner.is_document_end(&self.root_node)
245 }
246
247 pub fn to_degenerate_range(&self) -> Range<'a> {
248 Range::new(self.root_node, self.inner, self.inner)
249 }
250
251 pub fn to_global_usv_index(&self) -> usize {
252 let mut total_length = 0usize;
253 for node in self.root_node.inline_text_boxes() {
254 let node_text = node.data().value().unwrap();
255 if node.id() == self.inner.node.id() {
256 let character_lengths = node.data().character_lengths();
257 let slice_end = character_lengths[..self.inner.character_index]
258 .iter()
259 .copied()
260 .map(usize::from)
261 .sum::<usize>();
262 return total_length + node_text[..slice_end].chars().count();
263 }
264 total_length += node_text.chars().count();
265 }
266 panic!("invalid position")
267 }
268
269 pub fn to_global_utf16_index(&self) -> usize {
270 let mut total_length = 0usize;
271 for node in self.root_node.inline_text_boxes() {
272 let node_text = node.data().value().unwrap();
273 if node.id() == self.inner.node.id() {
274 let character_lengths = node.data().character_lengths();
275 let slice_end = character_lengths[..self.inner.character_index]
276 .iter()
277 .copied()
278 .map(usize::from)
279 .sum::<usize>();
280 return total_length
281 + node_text[..slice_end]
282 .chars()
283 .map(char::len_utf16)
284 .sum::<usize>();
285 }
286 total_length += node_text.chars().map(char::len_utf16).sum::<usize>();
287 }
288 panic!("invalid position")
289 }
290
291 pub fn to_line_index(&self) -> usize {
292 let mut pos = *self;
293 if !pos.is_line_start() {
294 pos = pos.backward_to_line_start();
295 }
296 let mut lines_before_current = 0usize;
297 while !pos.is_document_start() {
298 pos = pos.backward_to_line_start();
299 lines_before_current += 1;
300 }
301 lines_before_current
302 }
303
304 pub fn forward_to_character_start(&self) -> Self {
305 let pos = self.inner.biased_to_start(&self.root_node);
306 Self {
307 root_node: self.root_node,
308 inner: InnerPosition {
309 node: pos.node,
310 character_index: pos.character_index + 1,
311 }
312 .biased_to_start(&self.root_node),
313 }
314 }
315
316 pub fn forward_to_character_end(&self) -> Self {
317 let pos = self.inner.biased_to_start(&self.root_node);
318 Self {
319 root_node: self.root_node,
320 inner: InnerPosition {
321 node: pos.node,
322 character_index: pos.character_index + 1,
323 },
324 }
325 }
326
327 pub fn backward_to_character_start(&self) -> Self {
328 let pos = self.inner.biased_to_end(&self.root_node);
329 Self {
330 root_node: self.root_node,
331 inner: InnerPosition {
332 node: pos.node,
333 character_index: pos.character_index - 1,
334 }
335 .biased_to_start(&self.root_node),
336 }
337 }
338
339 pub fn forward_to_format_start(&self) -> Self {
340 self.document_end()
342 }
343
344 pub fn forward_to_format_end(&self) -> Self {
345 self.document_end()
347 }
348
349 pub fn backward_to_format_start(&self) -> Self {
350 self.document_start()
352 }
353
354 pub fn forward_to_word_start(&self) -> Self {
355 let pos = self.inner.biased_to_start(&self.root_node);
356 Self {
357 root_node: self.root_node,
358 inner: pos.word_end().biased_to_start(&self.root_node),
359 }
360 }
361
362 pub fn forward_to_word_end(&self) -> Self {
363 let pos = self.inner.biased_to_start(&self.root_node);
364 Self {
365 root_node: self.root_node,
366 inner: pos.word_end(),
367 }
368 }
369
370 pub fn backward_to_word_start(&self) -> Self {
371 let pos = self.inner.biased_to_end(&self.root_node);
372 Self {
373 root_node: self.root_node,
374 inner: pos.previous_word_start().biased_to_start(&self.root_node),
375 }
376 }
377
378 pub fn forward_to_line_start(&self) -> Self {
379 let pos = self.inner.biased_to_start(&self.root_node);
380 Self {
381 root_node: self.root_node,
382 inner: pos.line_end().biased_to_start(&self.root_node),
383 }
384 }
385
386 pub fn forward_to_line_end(&self) -> Self {
387 let pos = self.inner.biased_to_start(&self.root_node);
388 Self {
389 root_node: self.root_node,
390 inner: pos.line_end(),
391 }
392 }
393
394 pub fn backward_to_line_start(&self) -> Self {
395 let pos = self.inner.biased_to_end(&self.root_node);
396 Self {
397 root_node: self.root_node,
398 inner: pos.line_start().biased_to_start(&self.root_node),
399 }
400 }
401
402 pub fn forward_to_paragraph_start(&self) -> Self {
403 let mut current = *self;
404 loop {
405 current = current.forward_to_line_start();
406 if current.is_document_end()
407 || current
408 .inner
409 .biased_to_end(&self.root_node)
410 .is_paragraph_end()
411 {
412 break;
413 }
414 }
415 current
416 }
417
418 pub fn forward_to_paragraph_end(&self) -> Self {
419 let mut current = *self;
420 loop {
421 current = current.forward_to_line_end();
422 if current.is_document_end() || current.inner.is_paragraph_end() {
423 break;
424 }
425 }
426 current
427 }
428
429 pub fn backward_to_paragraph_start(&self) -> Self {
430 let mut current = *self;
431 loop {
432 current = current.backward_to_line_start();
433 if current.is_paragraph_start() {
434 break;
435 }
436 }
437 current
438 }
439
440 pub fn forward_to_page_start(&self) -> Self {
441 self.document_end()
442 }
443
444 pub fn forward_to_page_end(&self) -> Self {
445 self.document_end()
446 }
447
448 pub fn backward_to_page_start(&self) -> Self {
449 self.document_start()
450 }
451
452 pub fn document_end(&self) -> Self {
453 Self {
454 root_node: self.root_node,
455 inner: self.root_node.document_end(),
456 }
457 }
458
459 pub fn document_start(&self) -> Self {
460 Self {
461 root_node: self.root_node,
462 inner: self.root_node.document_start(),
463 }
464 }
465}
466
467impl<'a> PartialEq for Position<'a> {
468 fn eq(&self, other: &Self) -> bool {
469 self.root_node.id() == other.root_node.id() && self.inner == other.inner
470 }
471}
472
473impl<'a> Eq for Position<'a> {}
474
475impl<'a> PartialOrd for Position<'a> {
476 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
477 if self.root_node.id() != other.root_node.id() {
478 return None;
479 }
480 let self_comparable = self.inner.comparable(&self.root_node);
481 let other_comparable = other.inner.comparable(&self.root_node);
482 Some(self_comparable.cmp(&other_comparable))
483 }
484}
485
486pub enum AttributeValue<T> {
487 Single(T),
488 Mixed,
489}
490
491#[derive(Clone, Copy)]
492pub struct Range<'a> {
493 pub(crate) node: Node<'a>,
494 pub(crate) start: InnerPosition<'a>,
495 pub(crate) end: InnerPosition<'a>,
496}
497
498impl<'a> Range<'a> {
499 fn new(node: Node<'a>, mut start: InnerPosition<'a>, mut end: InnerPosition<'a>) -> Self {
500 if start.comparable(&node) > end.comparable(&node) {
501 std::mem::swap(&mut start, &mut end);
502 }
503 Self { node, start, end }
504 }
505
506 pub fn node(&self) -> &Node {
507 &self.node
508 }
509
510 pub fn start(&self) -> Position<'a> {
511 Position {
512 root_node: self.node,
513 inner: self.start,
514 }
515 }
516
517 pub fn end(&self) -> Position<'a> {
518 Position {
519 root_node: self.node,
520 inner: self.end,
521 }
522 }
523
524 pub fn is_degenerate(&self) -> bool {
525 self.start.comparable(&self.node) == self.end.comparable(&self.node)
526 }
527
528 fn walk<F, T>(&self, mut f: F) -> Option<T>
529 where
530 F: FnMut(&Node) -> Option<T>,
531 {
532 let (start, end) = if self.is_degenerate() {
536 (self.start, self.start)
537 } else {
538 let start = self.start.biased_to_start(&self.node);
539 let end = self.end.biased_to_end(&self.node);
540 (start, end)
541 };
542 if let Some(result) = f(&start.node) {
543 return Some(result);
544 }
545 if start.node.id() == end.node.id() {
546 return None;
547 }
548 for node in start.node.following_inline_text_boxes(&self.node) {
549 if let Some(result) = f(&node) {
550 return Some(result);
551 }
552 if node.id() == end.node.id() {
553 break;
554 }
555 }
556 None
557 }
558
559 pub fn text(&self) -> String {
560 let mut result = String::new();
561 self.walk::<_, ()>(|node| {
562 let character_lengths = node.data().character_lengths();
563 let start_index = if node.id() == self.start.node.id() {
564 self.start.character_index
565 } else {
566 0
567 };
568 let end_index = if node.id() == self.end.node.id() {
569 self.end.character_index
570 } else {
571 character_lengths.len()
572 };
573 let value = node.data().value().unwrap();
574 let s = if start_index == end_index {
575 ""
576 } else if start_index == 0 && end_index == character_lengths.len() {
577 value
578 } else {
579 let slice_start = character_lengths[..start_index]
580 .iter()
581 .copied()
582 .map(usize::from)
583 .sum::<usize>();
584 let slice_end = slice_start
585 + character_lengths[start_index..end_index]
586 .iter()
587 .copied()
588 .map(usize::from)
589 .sum::<usize>();
590 &value[slice_start..slice_end]
591 };
592 result.push_str(s);
593 None
594 });
595 result
596 }
597
598 pub fn bounding_boxes(&self) -> Vec<Rect> {
606 let mut result = Vec::new();
607 self.walk(|node| {
608 let mut rect = match node.data().bounds() {
609 Some(rect) => rect,
610 None => {
611 return Some(Vec::new());
612 }
613 };
614 let positions = match node.data().character_positions() {
615 Some(positions) => positions,
616 None => {
617 return Some(Vec::new());
618 }
619 };
620 let widths = match node.data().character_widths() {
621 Some(widths) => widths,
622 None => {
623 return Some(Vec::new());
624 }
625 };
626 let direction = match node.data().text_direction() {
627 Some(direction) => direction,
628 None => {
629 return Some(Vec::new());
630 }
631 };
632 let character_lengths = node.data().character_lengths();
633 let start_index = if node.id() == self.start.node.id() {
634 self.start.character_index
635 } else {
636 0
637 };
638 let end_index = if node.id() == self.end.node.id() {
639 self.end.character_index
640 } else {
641 character_lengths.len()
642 };
643 if start_index != 0 || end_index != character_lengths.len() {
644 let pixel_start = if start_index < character_lengths.len() {
645 positions[start_index]
646 } else {
647 positions[start_index - 1] + widths[start_index - 1]
648 };
649 let pixel_end = if end_index == start_index {
650 pixel_start
651 } else {
652 positions[end_index - 1] + widths[end_index - 1]
653 };
654 let pixel_start = f64::from(pixel_start);
655 let pixel_end = f64::from(pixel_end);
656 match direction {
657 TextDirection::LeftToRight => {
658 let orig_left = rect.x0;
659 rect.x0 = orig_left + pixel_start;
660 rect.x1 = orig_left + pixel_end;
661 }
662 TextDirection::RightToLeft => {
663 let orig_right = rect.x1;
664 rect.x1 = orig_right - pixel_start;
665 rect.x0 = orig_right - pixel_end;
666 }
667 TextDirection::TopToBottom => {
671 let orig_top = rect.y0;
672 rect.y0 = orig_top + pixel_start;
673 rect.y1 = orig_top + pixel_end;
674 }
675 TextDirection::BottomToTop => {
676 let orig_bottom = rect.y1;
677 rect.y1 = orig_bottom - pixel_start;
678 rect.y0 = orig_bottom - pixel_end;
679 }
680 }
681 }
682 result.push(node.transform().transform_rect_bbox(rect));
683 None
684 })
685 .unwrap_or(result)
686 }
687
688 pub fn attribute<F, T>(&self, f: F) -> AttributeValue<T>
689 where
690 F: Fn(&Node) -> T,
691 T: PartialEq,
692 {
693 let mut value = None;
694 self.walk(|node| {
695 let current = f(node);
696 if let Some(value) = &value {
697 if *value != current {
698 return Some(AttributeValue::Mixed);
699 }
700 } else {
701 value = Some(current);
702 }
703 None
704 })
705 .unwrap_or_else(|| AttributeValue::Single(value.unwrap()))
706 }
707
708 fn fix_start_bias(&mut self) {
709 if !self.is_degenerate() {
710 self.start = self.start.biased_to_start(&self.node);
711 }
712 }
713
714 pub fn set_start(&mut self, pos: Position<'a>) {
715 assert_eq!(pos.root_node.id(), self.node.id());
716 self.start = pos.inner;
717 if self.start.comparable(&self.node) >= self.end.comparable(&self.node) {
720 self.end = self.start;
721 }
722 self.fix_start_bias();
723 }
724
725 pub fn set_end(&mut self, pos: Position<'a>) {
726 assert_eq!(pos.root_node.id(), self.node.id());
727 self.end = pos.inner;
728 if self.start.comparable(&self.node) >= self.end.comparable(&self.node) {
731 self.start = self.end;
732 }
733 self.fix_start_bias();
734 }
735
736 pub fn to_text_selection(&self) -> TextSelection {
737 TextSelection {
738 anchor: self.start.downgrade(),
739 focus: self.end.downgrade(),
740 }
741 }
742
743 pub fn downgrade(&self) -> WeakRange {
744 WeakRange {
745 node_id: self.node.id(),
746 start: self.start.downgrade(),
747 end: self.end.downgrade(),
748 start_comparable: self.start.comparable(&self.node),
749 end_comparable: self.end.comparable(&self.node),
750 }
751 }
752}
753
754impl<'a> PartialEq for Range<'a> {
755 fn eq(&self, other: &Self) -> bool {
756 self.node.id() == other.node.id() && self.start == other.start && self.end == other.end
757 }
758}
759
760impl<'a> Eq for Range<'a> {}
761
762#[derive(Clone, Debug, PartialEq, Eq)]
763pub struct WeakRange {
764 node_id: NodeId,
765 start: WeakPosition,
766 end: WeakPosition,
767 start_comparable: (Vec<usize>, usize),
768 end_comparable: (Vec<usize>, usize),
769}
770
771impl WeakRange {
772 pub fn node_id(&self) -> NodeId {
773 self.node_id
774 }
775
776 pub fn start_comparable(&self) -> &(Vec<usize>, usize) {
777 &self.start_comparable
778 }
779
780 pub fn end_comparable(&self) -> &(Vec<usize>, usize) {
781 &self.end_comparable
782 }
783
784 pub fn upgrade_node<'a>(&self, tree_state: &'a TreeState) -> Option<Node<'a>> {
785 tree_state
786 .node_by_id(self.node_id)
787 .filter(Node::supports_text_ranges)
788 }
789
790 pub fn upgrade<'a>(&self, tree_state: &'a TreeState) -> Option<Range<'a>> {
791 let node = self.upgrade_node(tree_state)?;
792 let start = InnerPosition::upgrade(tree_state, self.start)?;
793 let end = InnerPosition::upgrade(tree_state, self.end)?;
794 Some(Range { node, start, end })
795 }
796}
797
798fn text_node_filter(root_id: NodeId, node: &Node) -> FilterResult {
799 if node.id() == root_id || node.role() == Role::InlineTextBox {
800 FilterResult::Include
801 } else {
802 FilterResult::ExcludeNode
803 }
804}
805
806fn character_index_at_point(node: &Node, point: Point) -> usize {
807 let rect = node.data().bounds().unwrap();
810 let character_lengths = node.data().character_lengths();
811 let positions = match node.data().character_positions() {
812 Some(positions) => positions,
813 None => {
814 return 0;
815 }
816 };
817 let widths = match node.data().character_widths() {
818 Some(widths) => widths,
819 None => {
820 return 0;
821 }
822 };
823 let direction = match node.data().text_direction() {
824 Some(direction) => direction,
825 None => {
826 return 0;
827 }
828 };
829 for (i, (position, width)) in positions.iter().zip(widths.iter()).enumerate().rev() {
830 let relative_pos = match direction {
831 TextDirection::LeftToRight => point.x - rect.x0,
832 TextDirection::RightToLeft => rect.x1 - point.x,
833 TextDirection::TopToBottom => point.y - rect.y0,
837 TextDirection::BottomToTop => rect.y1 - point.y,
838 };
839 if relative_pos >= f64::from(*position) && relative_pos < f64::from(*position + *width) {
840 return i;
841 }
842 }
843 character_lengths.len()
844}
845
846impl<'a> Node<'a> {
847 fn inline_text_boxes(
848 &self,
849 ) -> impl DoubleEndedIterator<Item = Node<'a>> + FusedIterator<Item = Node<'a>> + 'a {
850 let id = self.id();
851 self.filtered_children(move |node| text_node_filter(id, node))
852 }
853
854 fn following_inline_text_boxes(
855 &self,
856 root_node: &Node,
857 ) -> impl DoubleEndedIterator<Item = Node<'a>> + FusedIterator<Item = Node<'a>> + 'a {
858 let id = root_node.id();
859 self.following_filtered_siblings(move |node| text_node_filter(id, node))
860 }
861
862 fn preceding_inline_text_boxes(
863 &self,
864 root_node: &Node,
865 ) -> impl DoubleEndedIterator<Item = Node<'a>> + FusedIterator<Item = Node<'a>> + 'a {
866 let id = root_node.id();
867 self.preceding_filtered_siblings(move |node| text_node_filter(id, node))
868 }
869
870 pub fn supports_text_ranges(&self) -> bool {
871 (self.is_text_input()
872 || matches!(self.role(), Role::Label | Role::Document | Role::Terminal))
873 && self.inline_text_boxes().next().is_some()
874 }
875
876 fn document_start(&self) -> InnerPosition<'a> {
877 let node = self.inline_text_boxes().next().unwrap();
878 InnerPosition {
879 node,
880 character_index: 0,
881 }
882 }
883
884 fn document_end(&self) -> InnerPosition<'a> {
885 let node = self.inline_text_boxes().next_back().unwrap();
886 InnerPosition {
887 node,
888 character_index: node.data().character_lengths().len(),
889 }
890 }
891
892 pub fn document_range(&self) -> Range {
893 let start = self.document_start();
894 let end = self.document_end();
895 Range::new(*self, start, end)
896 }
897
898 pub fn has_text_selection(&self) -> bool {
899 self.data().text_selection().is_some()
900 }
901
902 pub fn text_selection(&self) -> Option<Range> {
903 self.data().text_selection().map(|selection| {
904 let anchor = InnerPosition::clamped_upgrade(self.tree_state, selection.anchor).unwrap();
905 let focus = InnerPosition::clamped_upgrade(self.tree_state, selection.focus).unwrap();
906 Range::new(*self, anchor, focus)
907 })
908 }
909
910 pub fn text_selection_focus(&self) -> Option<Position> {
911 self.data().text_selection().map(|selection| {
912 let focus = InnerPosition::clamped_upgrade(self.tree_state, selection.focus).unwrap();
913 Position {
914 root_node: *self,
915 inner: focus,
916 }
917 })
918 }
919
920 pub fn text_position_at_point(&self, point: Point) -> Position {
923 let id = self.id();
924 if let Some((node, point)) = self.hit_test(point, &move |node| text_node_filter(id, node)) {
925 if node.role() == Role::InlineTextBox {
926 let pos = InnerPosition {
927 node,
928 character_index: character_index_at_point(&node, point),
929 };
930 return Position {
931 root_node: *self,
932 inner: pos,
933 };
934 }
935 }
936
937 if let Some(node) = self.inline_text_boxes().next() {
941 if let Some(rect) = node.bounding_box_in_coordinate_space(self) {
942 let origin = rect.origin();
943 if point.x < origin.x || point.y < origin.y {
944 return Position {
945 root_node: *self,
946 inner: self.document_start(),
947 };
948 }
949 }
950 }
951
952 for node in self.inline_text_boxes().rev() {
953 if let Some(rect) = node.bounding_box_in_coordinate_space(self) {
954 if let Some(direction) = node.data().text_direction() {
955 let is_past_end = match direction {
956 TextDirection::LeftToRight => {
957 point.y >= rect.y0 && point.y < rect.y1 && point.x >= rect.x1
958 }
959 TextDirection::RightToLeft => {
960 point.y >= rect.y0 && point.y < rect.y1 && point.x < rect.x0
961 }
962 TextDirection::TopToBottom => {
966 point.x >= rect.x0 && point.x < rect.x1 && point.y >= rect.y1
967 }
968 TextDirection::BottomToTop => {
969 point.x >= rect.x0 && point.x < rect.x1 && point.y < rect.y0
970 }
971 };
972 if is_past_end {
973 return Position {
974 root_node: *self,
975 inner: InnerPosition {
976 node,
977 character_index: node.data().character_lengths().len(),
978 },
979 };
980 }
981 }
982 }
983 }
984
985 Position {
986 root_node: *self,
987 inner: self.document_end(),
988 }
989 }
990
991 pub fn line_range_from_index(&self, line_index: usize) -> Option<Range> {
992 let mut pos = self.document_range().start();
993
994 if line_index > 0 {
995 if pos.is_document_end() || pos.forward_to_line_end().is_document_end() {
996 return None;
997 }
998 for _ in 0..line_index {
999 if pos.is_document_end() {
1000 return None;
1001 }
1002 pos = pos.forward_to_line_start();
1003 }
1004 }
1005
1006 let end = if pos.is_document_end() {
1007 pos
1008 } else {
1009 pos.forward_to_line_end()
1010 };
1011 Some(Range::new(*self, pos.inner, end.inner))
1012 }
1013
1014 pub fn text_position_from_global_usv_index(&self, index: usize) -> Option<Position> {
1015 let mut total_length = 0usize;
1016 for node in self.inline_text_boxes() {
1017 let node_text = node.data().value().unwrap();
1018 let node_text_length = node_text.chars().count();
1019 let new_total_length = total_length + node_text_length;
1020 if index >= total_length && index < new_total_length {
1021 let index = index - total_length;
1022 let mut utf8_length = 0usize;
1023 let mut usv_length = 0usize;
1024 for (character_index, utf8_char_length) in
1025 node.data().character_lengths().iter().enumerate()
1026 {
1027 let new_utf8_length = utf8_length + (*utf8_char_length as usize);
1028 let char_str = &node_text[utf8_length..new_utf8_length];
1029 let usv_char_length = char_str.chars().count();
1030 let new_usv_length = usv_length + usv_char_length;
1031 if index >= usv_length && index < new_usv_length {
1032 return Some(Position {
1033 root_node: *self,
1034 inner: InnerPosition {
1035 node,
1036 character_index,
1037 },
1038 });
1039 }
1040 utf8_length = new_utf8_length;
1041 usv_length = new_usv_length;
1042 }
1043 panic!("index out of range");
1044 }
1045 total_length = new_total_length;
1046 }
1047 if index == total_length {
1048 return Some(Position {
1049 root_node: *self,
1050 inner: self.document_end(),
1051 });
1052 }
1053 None
1054 }
1055
1056 pub fn text_position_from_global_utf16_index(&self, index: usize) -> Option<Position> {
1057 let mut total_length = 0usize;
1058 for node in self.inline_text_boxes() {
1059 let node_text = node.data().value().unwrap();
1060 let node_text_length = node_text.chars().map(char::len_utf16).sum::<usize>();
1061 let new_total_length = total_length + node_text_length;
1062 if index >= total_length && index < new_total_length {
1063 let index = index - total_length;
1064 let mut utf8_length = 0usize;
1065 let mut utf16_length = 0usize;
1066 for (character_index, utf8_char_length) in
1067 node.data().character_lengths().iter().enumerate()
1068 {
1069 let new_utf8_length = utf8_length + (*utf8_char_length as usize);
1070 let char_str = &node_text[utf8_length..new_utf8_length];
1071 let utf16_char_length = char_str.chars().map(char::len_utf16).sum::<usize>();
1072 let new_utf16_length = utf16_length + utf16_char_length;
1073 if index >= utf16_length && index < new_utf16_length {
1074 return Some(Position {
1075 root_node: *self,
1076 inner: InnerPosition {
1077 node,
1078 character_index,
1079 },
1080 });
1081 }
1082 utf8_length = new_utf8_length;
1083 utf16_length = new_utf16_length;
1084 }
1085 panic!("index out of range");
1086 }
1087 total_length = new_total_length;
1088 }
1089 if index == total_length {
1090 return Some(Position {
1091 root_node: *self,
1092 inner: self.document_end(),
1093 });
1094 }
1095 None
1096 }
1097}
1098
1099#[cfg(test)]
1100mod tests {
1101 use accesskit::{NodeId, Point, Rect, TextSelection};
1102
1103 fn main_multiline_tree(selection: Option<TextSelection>) -> crate::Tree {
1105 use accesskit::{Action, Affine, NodeBuilder, Role, TextDirection, Tree, TreeUpdate};
1106
1107 let update = TreeUpdate {
1108 nodes: vec![
1109 (NodeId(0), {
1110 let mut builder = NodeBuilder::new(Role::Window);
1111 builder.set_transform(Affine::scale(1.5));
1112 builder.set_children(vec![NodeId(1)]);
1113 builder.build()
1114 }),
1115 (NodeId(1), {
1116 let mut builder = NodeBuilder::new(Role::MultilineTextInput);
1117 builder.set_bounds(Rect {
1118 x0: 8.0,
1119 y0: 31.666664123535156,
1120 x1: 296.0,
1121 y1: 123.66666412353516,
1122 });
1123 builder.set_children(vec![
1124 NodeId(2),
1125 NodeId(3),
1126 NodeId(4),
1127 NodeId(5),
1128 NodeId(6),
1129 NodeId(7),
1130 ]);
1131 builder.add_action(Action::Focus);
1132 if let Some(selection) = selection {
1133 builder.set_text_selection(selection);
1134 }
1135 builder.build()
1136 }),
1137 (NodeId(2), {
1138 let mut builder = NodeBuilder::new(Role::InlineTextBox);
1139 builder.set_bounds(Rect {
1140 x0: 12.0,
1141 y0: 33.666664123535156,
1142 x1: 290.9189147949219,
1143 y1: 48.33333206176758,
1144 });
1145 builder.set_value("This paragraph is\u{a0}long enough to wrap ");
1150 builder.set_text_direction(TextDirection::LeftToRight);
1151 builder.set_character_lengths([
1152 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1,
1153 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1154 ]);
1155 builder.set_character_positions([
1156 0.0, 7.3333335, 14.666667, 22.0, 29.333334, 36.666668, 44.0, 51.333332,
1157 58.666668, 66.0, 73.333336, 80.666664, 88.0, 95.333336, 102.666664, 110.0,
1158 117.333336, 124.666664, 132.0, 139.33333, 146.66667, 154.0, 161.33333,
1159 168.66667, 176.0, 183.33333, 190.66667, 198.0, 205.33333, 212.66667, 220.0,
1160 227.33333, 234.66667, 242.0, 249.33333, 256.66666, 264.0, 271.33334,
1161 ]);
1162 builder.set_character_widths([
1163 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557,
1164 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557,
1165 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557,
1166 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557,
1167 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557,
1168 ]);
1169 builder.set_word_lengths([5, 10, 3, 5, 7, 3, 5]);
1170 builder.build()
1171 }),
1172 (NodeId(3), {
1173 let mut builder = NodeBuilder::new(Role::InlineTextBox);
1174 builder.set_bounds(Rect {
1175 x0: 12.0,
1176 y0: 48.33333206176758,
1177 x1: 129.5855712890625,
1178 y1: 63.0,
1179 });
1180 builder.set_value("to another line.\n");
1181 builder.set_text_direction(TextDirection::LeftToRight);
1182 builder
1183 .set_character_lengths([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]);
1184 builder.set_character_positions([
1185 0.0, 7.3333435, 14.666687, 22.0, 29.333344, 36.666687, 44.0, 51.333344,
1186 58.666687, 66.0, 73.33334, 80.66669, 88.0, 95.33334, 102.66669, 110.0,
1187 117.58557,
1188 ]);
1189 builder.set_character_widths([
1190 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557,
1191 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557,
1192 0.0,
1193 ]);
1194 builder.set_word_lengths([3, 8, 6]);
1195 builder.build()
1196 }),
1197 (NodeId(4), {
1198 let mut builder = NodeBuilder::new(Role::InlineTextBox);
1199 builder.set_bounds(Rect {
1200 x0: 12.0,
1201 y0: 63.0,
1202 x1: 144.25222778320313,
1203 y1: 77.66666412353516,
1204 });
1205 builder.set_value("Another paragraph.\n");
1206 builder.set_text_direction(TextDirection::LeftToRight);
1207 builder.set_character_lengths([
1208 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1209 ]);
1210 builder.set_character_positions([
1211 0.0, 7.3333335, 14.666667, 22.0, 29.333334, 36.666668, 44.0, 51.333332,
1212 58.666668, 66.0, 73.333336, 80.666664, 88.0, 95.333336, 102.666664, 110.0,
1213 117.333336, 124.666664, 132.25223,
1214 ]);
1215 builder.set_character_widths([
1216 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557,
1217 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557,
1218 7.58557, 7.58557, 0.0,
1219 ]);
1220 builder.set_word_lengths([8, 11]);
1221 builder.build()
1222 }),
1223 (NodeId(5), {
1224 let mut builder = NodeBuilder::new(Role::InlineTextBox);
1225 builder.set_bounds(Rect {
1226 x0: 12.0,
1227 y0: 77.66666412353516,
1228 x1: 12.0,
1229 y1: 92.33332824707031,
1230 });
1231 builder.set_value("\n");
1232 builder.set_text_direction(TextDirection::LeftToRight);
1233 builder.set_character_lengths([1]);
1234 builder.set_character_positions([0.0]);
1235 builder.set_character_widths([0.0]);
1236 builder.set_word_lengths([1]);
1237 builder.build()
1238 }),
1239 (NodeId(6), {
1240 let mut builder = NodeBuilder::new(Role::InlineTextBox);
1241 builder.set_bounds(Rect {
1242 x0: 12.0,
1243 y0: 92.33332824707031,
1244 x1: 158.9188995361328,
1245 y1: 107.0,
1246 });
1247 builder.set_value("Last non-blank line\u{1f44d}\u{1f3fb}\n");
1252 builder.set_text_direction(TextDirection::LeftToRight);
1253 builder.set_character_lengths([
1254 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 8, 1,
1255 ]);
1256 builder.set_character_positions([
1257 0.0, 7.3333335, 14.666667, 22.0, 29.333334, 36.666668, 44.0, 51.333332,
1258 58.666668, 66.0, 73.333336, 80.666664, 88.0, 95.333336, 102.666664, 110.0,
1259 117.333336, 124.666664, 132.0, 139.33333, 146.9189,
1260 ]);
1261 builder.set_character_widths([
1262 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557,
1263 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557,
1264 7.58557, 7.58557, 7.58557, 7.58557, 0.0,
1265 ]);
1266 builder.set_word_lengths([5, 4, 6, 6]);
1267 builder.build()
1268 }),
1269 (NodeId(7), {
1270 let mut builder = NodeBuilder::new(Role::InlineTextBox);
1271 builder.set_bounds(Rect {
1272 x0: 12.0,
1273 y0: 107.0,
1274 x1: 12.0,
1275 y1: 121.66666412353516,
1276 });
1277 builder.set_value("");
1278 builder.set_text_direction(TextDirection::LeftToRight);
1279 builder.set_character_lengths([]);
1280 builder.set_character_positions([]);
1281 builder.set_character_widths([]);
1282 builder.set_word_lengths([0]);
1283 builder.build()
1284 }),
1285 ],
1286 tree: Some(Tree::new(NodeId(0))),
1287 focus: NodeId(1),
1288 };
1289
1290 crate::Tree::new(update, true)
1291 }
1292
1293 fn multiline_end_selection() -> TextSelection {
1294 use accesskit::TextPosition;
1295
1296 TextSelection {
1297 anchor: TextPosition {
1298 node: NodeId(7),
1299 character_index: 0,
1300 },
1301 focus: TextPosition {
1302 node: NodeId(7),
1303 character_index: 0,
1304 },
1305 }
1306 }
1307
1308 fn multiline_past_end_selection() -> TextSelection {
1309 use accesskit::TextPosition;
1310
1311 TextSelection {
1312 anchor: TextPosition {
1313 node: NodeId(7),
1314 character_index: 3,
1315 },
1316 focus: TextPosition {
1317 node: NodeId(7),
1318 character_index: 3,
1319 },
1320 }
1321 }
1322
1323 fn multiline_wrapped_line_end_selection() -> TextSelection {
1324 use accesskit::TextPosition;
1325
1326 TextSelection {
1327 anchor: TextPosition {
1328 node: NodeId(2),
1329 character_index: 38,
1330 },
1331 focus: TextPosition {
1332 node: NodeId(2),
1333 character_index: 38,
1334 },
1335 }
1336 }
1337
1338 fn multiline_first_line_middle_selection() -> TextSelection {
1339 use accesskit::TextPosition;
1340
1341 TextSelection {
1342 anchor: TextPosition {
1343 node: NodeId(2),
1344 character_index: 5,
1345 },
1346 focus: TextPosition {
1347 node: NodeId(2),
1348 character_index: 5,
1349 },
1350 }
1351 }
1352
1353 fn multiline_second_line_middle_selection() -> TextSelection {
1354 use accesskit::TextPosition;
1355
1356 TextSelection {
1357 anchor: TextPosition {
1358 node: NodeId(3),
1359 character_index: 5,
1360 },
1361 focus: TextPosition {
1362 node: NodeId(3),
1363 character_index: 5,
1364 },
1365 }
1366 }
1367
1368 #[test]
1369 fn supports_text_ranges() {
1370 let tree = main_multiline_tree(None);
1371 let state = tree.state();
1372 assert!(!state.node_by_id(NodeId(0)).unwrap().supports_text_ranges());
1373 assert!(state.node_by_id(NodeId(1)).unwrap().supports_text_ranges());
1374 }
1375
1376 #[test]
1377 fn multiline_document_range() {
1378 let tree = main_multiline_tree(None);
1379 let state = tree.state();
1380 let node = state.node_by_id(NodeId(1)).unwrap();
1381 let range = node.document_range();
1382 let start = range.start();
1383 assert!(start.is_word_start());
1384 assert!(start.is_line_start());
1385 assert!(!start.is_line_end());
1386 assert!(start.is_paragraph_start());
1387 assert!(start.is_document_start());
1388 assert!(!start.is_document_end());
1389 let end = range.end();
1390 assert!(start < end);
1391 assert!(end.is_word_start());
1392 assert!(end.is_line_start());
1393 assert!(end.is_line_end());
1394 assert!(end.is_paragraph_start());
1395 assert!(!end.is_document_start());
1396 assert!(end.is_document_end());
1397 assert_eq!(range.text(), "This paragraph is\u{a0}long enough to wrap to another line.\nAnother paragraph.\n\nLast non-blank line\u{1f44d}\u{1f3fb}\n");
1398 assert_eq!(
1399 range.bounding_boxes(),
1400 vec![
1401 Rect {
1402 x0: 18.0,
1403 y0: 50.499996185302734,
1404 x1: 436.3783721923828,
1405 y1: 72.49999809265137
1406 },
1407 Rect {
1408 x0: 18.0,
1409 y0: 72.49999809265137,
1410 x1: 194.37835693359375,
1411 y1: 94.5
1412 },
1413 Rect {
1414 x0: 18.0,
1415 y0: 94.5,
1416 x1: 216.3783416748047,
1417 y1: 116.49999618530273
1418 },
1419 Rect {
1420 x0: 18.0,
1421 y0: 116.49999618530273,
1422 x1: 18.0,
1423 y1: 138.49999237060547
1424 },
1425 Rect {
1426 x0: 18.0,
1427 y0: 138.49999237060547,
1428 x1: 238.37834930419922,
1429 y1: 160.5
1430 }
1431 ]
1432 );
1433 }
1434
1435 #[test]
1436 fn multiline_end_degenerate_range() {
1437 let tree = main_multiline_tree(Some(multiline_end_selection()));
1438 let state = tree.state();
1439 let node = state.node_by_id(NodeId(1)).unwrap();
1440 let range = node.text_selection().unwrap();
1441 assert!(range.is_degenerate());
1442 let pos = range.start();
1443 assert!(pos.is_word_start());
1444 assert!(pos.is_line_start());
1445 assert!(pos.is_line_end());
1446 assert!(pos.is_paragraph_start());
1447 assert!(!pos.is_document_start());
1448 assert!(pos.is_document_end());
1449 assert_eq!(range.text(), "");
1450 assert_eq!(
1451 range.bounding_boxes(),
1452 vec![Rect {
1453 x0: 18.0,
1454 y0: 160.5,
1455 x1: 18.0,
1456 y1: 182.49999618530273,
1457 }]
1458 );
1459 }
1460
1461 #[test]
1462 fn multiline_wrapped_line_end_range() {
1463 let tree = main_multiline_tree(Some(multiline_wrapped_line_end_selection()));
1464 let state = tree.state();
1465 let node = state.node_by_id(NodeId(1)).unwrap();
1466 let range = node.text_selection().unwrap();
1467 assert!(range.is_degenerate());
1468 let pos = range.start();
1469 assert!(!pos.is_word_start());
1470 assert!(!pos.is_line_start());
1471 assert!(pos.is_line_end());
1472 assert!(!pos.is_paragraph_start());
1473 assert!(!pos.is_document_start());
1474 assert!(!pos.is_document_end());
1475 assert_eq!(range.text(), "");
1476 assert_eq!(
1477 range.bounding_boxes(),
1478 vec![Rect {
1479 x0: 436.3783721923828,
1480 y0: 50.499996185302734,
1481 x1: 436.3783721923828,
1482 y1: 72.49999809265137
1483 }]
1484 );
1485 let char_end_pos = pos.forward_to_character_end();
1486 let mut line_start_range = range;
1487 line_start_range.set_end(char_end_pos);
1488 assert!(!line_start_range.is_degenerate());
1489 assert!(line_start_range.start().is_line_start());
1490 assert_eq!(line_start_range.text(), "t");
1491 assert_eq!(
1492 line_start_range.bounding_boxes(),
1493 vec![Rect {
1494 x0: 18.0,
1495 y0: 72.49999809265137,
1496 x1: 29.378354787826538,
1497 y1: 94.5
1498 }]
1499 );
1500 let prev_char_pos = pos.backward_to_character_start();
1501 let mut prev_char_range = range;
1502 prev_char_range.set_start(prev_char_pos);
1503 assert!(!prev_char_range.is_degenerate());
1504 assert!(prev_char_range.end().is_line_end());
1505 assert_eq!(prev_char_range.text(), " ");
1506 assert_eq!(
1507 prev_char_range.bounding_boxes(),
1508 vec![Rect {
1509 x0: 425.00001525878906,
1510 y0: 50.499996185302734,
1511 x1: 436.3783721923828,
1512 y1: 72.49999809265137
1513 }]
1514 );
1515 assert!(prev_char_pos.forward_to_character_end().is_line_end());
1516 assert!(prev_char_pos.forward_to_word_end().is_line_end());
1517 assert!(prev_char_pos.forward_to_line_end().is_line_end());
1518 assert!(prev_char_pos.forward_to_character_start().is_line_start());
1519 assert!(prev_char_pos.forward_to_word_start().is_line_start());
1520 assert!(prev_char_pos.forward_to_line_start().is_line_start());
1521 }
1522
1523 #[test]
1524 fn multiline_find_line_ends_from_middle() {
1525 let tree = main_multiline_tree(Some(multiline_second_line_middle_selection()));
1526 let state = tree.state();
1527 let node = state.node_by_id(NodeId(1)).unwrap();
1528 let mut range = node.text_selection().unwrap();
1529 assert!(range.is_degenerate());
1530 let pos = range.start();
1531 assert!(!pos.is_line_start());
1532 assert!(!pos.is_line_end());
1533 assert!(!pos.is_document_start());
1534 assert!(!pos.is_document_end());
1535 let line_start = pos.backward_to_line_start();
1536 range.set_start(line_start);
1537 let line_end = line_start.forward_to_line_end();
1538 range.set_end(line_end);
1539 assert!(!range.is_degenerate());
1540 assert!(range.start().is_line_start());
1541 assert!(range.end().is_line_end());
1542 assert_eq!(range.text(), "to another line.\n");
1543 assert_eq!(
1544 range.bounding_boxes(),
1545 vec![Rect {
1546 x0: 18.0,
1547 y0: 72.49999809265137,
1548 x1: 194.37835693359375,
1549 y1: 94.5
1550 },]
1551 );
1552 assert!(line_start.forward_to_line_start().is_line_start());
1553 }
1554
1555 #[test]
1556 fn multiline_find_wrapped_line_ends_from_middle() {
1557 let tree = main_multiline_tree(Some(multiline_first_line_middle_selection()));
1558 let state = tree.state();
1559 let node = state.node_by_id(NodeId(1)).unwrap();
1560 let mut range = node.text_selection().unwrap();
1561 assert!(range.is_degenerate());
1562 let pos = range.start();
1563 assert!(!pos.is_line_start());
1564 assert!(!pos.is_line_end());
1565 assert!(!pos.is_document_start());
1566 assert!(!pos.is_document_end());
1567 let line_start = pos.backward_to_line_start();
1568 range.set_start(line_start);
1569 let line_end = line_start.forward_to_line_end();
1570 range.set_end(line_end);
1571 assert!(!range.is_degenerate());
1572 assert!(range.start().is_line_start());
1573 assert!(range.end().is_line_end());
1574 assert_eq!(range.text(), "This paragraph is\u{a0}long enough to wrap ");
1575 assert_eq!(
1576 range.bounding_boxes(),
1577 vec![Rect {
1578 x0: 18.0,
1579 y0: 50.499996185302734,
1580 x1: 436.3783721923828,
1581 y1: 72.49999809265137
1582 }]
1583 );
1584 assert!(line_start.forward_to_line_start().is_line_start());
1585 }
1586
1587 #[test]
1588 fn multiline_find_paragraph_ends_from_middle() {
1589 let tree = main_multiline_tree(Some(multiline_second_line_middle_selection()));
1590 let state = tree.state();
1591 let node = state.node_by_id(NodeId(1)).unwrap();
1592 let mut range = node.text_selection().unwrap();
1593 assert!(range.is_degenerate());
1594 let pos = range.start();
1595 assert!(!pos.is_paragraph_start());
1596 assert!(!pos.is_document_start());
1597 assert!(!pos.is_document_end());
1598 let paragraph_start = pos.backward_to_paragraph_start();
1599 range.set_start(paragraph_start);
1600 let paragraph_end = paragraph_start.forward_to_paragraph_end();
1601 range.set_end(paragraph_end);
1602 assert!(!range.is_degenerate());
1603 assert!(range.start().is_paragraph_start());
1604 assert!(range.end().is_paragraph_end());
1605 assert_eq!(
1606 range.text(),
1607 "This paragraph is\u{a0}long enough to wrap to another line.\n"
1608 );
1609 assert_eq!(
1610 range.bounding_boxes(),
1611 vec![
1612 Rect {
1613 x0: 18.0,
1614 y0: 50.499996185302734,
1615 x1: 436.3783721923828,
1616 y1: 72.49999809265137
1617 },
1618 Rect {
1619 x0: 18.0,
1620 y0: 72.49999809265137,
1621 x1: 194.37835693359375,
1622 y1: 94.5
1623 },
1624 ]
1625 );
1626 assert!(paragraph_start
1627 .forward_to_paragraph_start()
1628 .is_paragraph_start());
1629 }
1630
1631 #[test]
1632 fn multiline_find_word_ends_from_middle() {
1633 let tree = main_multiline_tree(Some(multiline_second_line_middle_selection()));
1634 let state = tree.state();
1635 let node = state.node_by_id(NodeId(1)).unwrap();
1636 let mut range = node.text_selection().unwrap();
1637 assert!(range.is_degenerate());
1638 let pos = range.start();
1639 assert!(!pos.is_word_start());
1640 assert!(!pos.is_document_start());
1641 assert!(!pos.is_document_end());
1642 let word_start = pos.backward_to_word_start();
1643 range.set_start(word_start);
1644 let word_end = word_start.forward_to_word_end();
1645 range.set_end(word_end);
1646 assert!(!range.is_degenerate());
1647 assert_eq!(range.text(), "another ");
1648 assert_eq!(
1649 range.bounding_boxes(),
1650 vec![Rect {
1651 x0: 51.0,
1652 y0: 72.49999809265137,
1653 x1: 139.3783721923828,
1654 y1: 94.5
1655 }]
1656 );
1657 }
1658
1659 #[test]
1660 fn text_position_at_point() {
1661 let tree = main_multiline_tree(None);
1662 let state = tree.state();
1663 let node = state.node_by_id(NodeId(1)).unwrap();
1664
1665 {
1666 let pos = node.text_position_at_point(Point::new(8.0, 31.666664123535156));
1667 assert!(pos.is_document_start());
1668 }
1669
1670 {
1671 let pos = node.text_position_at_point(Point::new(12.0, 33.666664123535156));
1672 assert!(pos.is_document_start());
1673 }
1674
1675 {
1676 let pos = node.text_position_at_point(Point::new(16.0, 40.0));
1677 assert!(pos.is_document_start());
1678 }
1679
1680 {
1681 let pos = node.text_position_at_point(Point::new(144.0, 40.0));
1682 assert!(!pos.is_document_start());
1683 assert!(!pos.is_document_end());
1684 assert!(!pos.is_line_end());
1685 let mut range = pos.to_degenerate_range();
1686 range.set_end(pos.forward_to_character_end());
1687 assert_eq!(range.text(), "l");
1688 }
1689
1690 {
1691 let pos = node.text_position_at_point(Point::new(150.0, 40.0));
1692 assert!(!pos.is_document_start());
1693 assert!(!pos.is_document_end());
1694 assert!(!pos.is_line_end());
1695 let mut range = pos.to_degenerate_range();
1696 range.set_end(pos.forward_to_character_end());
1697 assert_eq!(range.text(), "l");
1698 }
1699
1700 {
1701 let pos = node.text_position_at_point(Point::new(291.0, 40.0));
1702 assert!(!pos.is_document_start());
1703 assert!(!pos.is_document_end());
1704 assert!(pos.is_line_end());
1705 let mut range = pos.to_degenerate_range();
1706 range.set_start(pos.backward_to_word_start());
1707 assert_eq!(range.text(), "wrap ");
1708 }
1709
1710 {
1711 let pos = node.text_position_at_point(Point::new(12.0, 50.0));
1712 assert!(!pos.is_document_start());
1713 assert!(pos.is_line_start());
1714 assert!(!pos.is_paragraph_start());
1715 let mut range = pos.to_degenerate_range();
1716 range.set_end(pos.forward_to_word_end());
1717 assert_eq!(range.text(), "to ");
1718 }
1719
1720 {
1721 let pos = node.text_position_at_point(Point::new(130.0, 50.0));
1722 assert!(!pos.is_document_start());
1723 assert!(!pos.is_document_end());
1724 assert!(pos.is_line_end());
1725 let mut range = pos.to_degenerate_range();
1726 range.set_start(pos.backward_to_word_start());
1727 assert_eq!(range.text(), "line.\n");
1728 }
1729
1730 {
1731 let pos = node.text_position_at_point(Point::new(12.0, 80.0));
1732 assert!(!pos.is_document_start());
1733 assert!(!pos.is_document_end());
1734 assert!(pos.is_line_end());
1735 let mut range = pos.to_degenerate_range();
1736 range.set_start(pos.backward_to_line_start());
1737 assert_eq!(range.text(), "\n");
1738 }
1739
1740 {
1741 let pos = node.text_position_at_point(Point::new(12.0, 120.0));
1742 assert!(pos.is_document_end());
1743 }
1744
1745 {
1746 let pos = node.text_position_at_point(Point::new(250.0, 122.0));
1747 assert!(pos.is_document_end());
1748 }
1749 }
1750
1751 #[test]
1752 fn to_global_usv_index() {
1753 let tree = main_multiline_tree(None);
1754 let state = tree.state();
1755 let node = state.node_by_id(NodeId(1)).unwrap();
1756
1757 {
1758 let range = node.document_range();
1759 assert_eq!(range.start().to_global_usv_index(), 0);
1760 assert_eq!(range.end().to_global_usv_index(), 97);
1761 }
1762
1763 {
1764 let range = node.document_range();
1765 let pos = range.start().forward_to_line_end();
1766 assert_eq!(pos.to_global_usv_index(), 38);
1767 let pos = range.start().forward_to_line_start();
1768 assert_eq!(pos.to_global_usv_index(), 38);
1769 let pos = pos.forward_to_character_start();
1770 assert_eq!(pos.to_global_usv_index(), 39);
1771 let pos = pos.forward_to_line_start();
1772 assert_eq!(pos.to_global_usv_index(), 55);
1773 }
1774 }
1775
1776 #[test]
1777 fn to_global_utf16_index() {
1778 let tree = main_multiline_tree(None);
1779 let state = tree.state();
1780 let node = state.node_by_id(NodeId(1)).unwrap();
1781
1782 {
1783 let range = node.document_range();
1784 assert_eq!(range.start().to_global_utf16_index(), 0);
1785 assert_eq!(range.end().to_global_utf16_index(), 99);
1786 }
1787
1788 {
1789 let range = node.document_range();
1790 let pos = range.start().forward_to_line_end();
1791 assert_eq!(pos.to_global_utf16_index(), 38);
1792 let pos = range.start().forward_to_line_start();
1793 assert_eq!(pos.to_global_utf16_index(), 38);
1794 let pos = pos.forward_to_character_start();
1795 assert_eq!(pos.to_global_utf16_index(), 39);
1796 let pos = pos.forward_to_line_start();
1797 assert_eq!(pos.to_global_utf16_index(), 55);
1798 }
1799 }
1800
1801 #[test]
1802 fn to_line_index() {
1803 let tree = main_multiline_tree(None);
1804 let state = tree.state();
1805 let node = state.node_by_id(NodeId(1)).unwrap();
1806
1807 {
1808 let range = node.document_range();
1809 assert_eq!(range.start().to_line_index(), 0);
1810 assert_eq!(range.end().to_line_index(), 5);
1811 }
1812
1813 {
1814 let range = node.document_range();
1815 let pos = range.start().forward_to_line_end();
1816 assert_eq!(pos.to_line_index(), 0);
1817 let pos = range.start().forward_to_line_start();
1818 assert_eq!(pos.to_line_index(), 1);
1819 let pos = pos.forward_to_character_start();
1820 assert_eq!(pos.to_line_index(), 1);
1821 assert_eq!(pos.forward_to_line_end().to_line_index(), 1);
1822 let pos = pos.forward_to_line_start();
1823 assert_eq!(pos.to_line_index(), 2);
1824 }
1825 }
1826
1827 #[test]
1828 fn line_range_from_index() {
1829 let tree = main_multiline_tree(None);
1830 let state = tree.state();
1831 let node = state.node_by_id(NodeId(1)).unwrap();
1832
1833 {
1834 let range = node.line_range_from_index(0).unwrap();
1835 assert_eq!(range.text(), "This paragraph is\u{a0}long enough to wrap ");
1836 }
1837
1838 {
1839 let range = node.line_range_from_index(1).unwrap();
1840 assert_eq!(range.text(), "to another line.\n");
1841 }
1842
1843 {
1844 let range = node.line_range_from_index(2).unwrap();
1845 assert_eq!(range.text(), "Another paragraph.\n");
1846 }
1847
1848 {
1849 let range = node.line_range_from_index(3).unwrap();
1850 assert_eq!(range.text(), "\n");
1851 }
1852
1853 {
1854 let range = node.line_range_from_index(4).unwrap();
1855 assert_eq!(range.text(), "Last non-blank line\u{1f44d}\u{1f3fb}\n");
1856 }
1857
1858 {
1859 let range = node.line_range_from_index(5).unwrap();
1860 assert_eq!(range.text(), "");
1861 }
1862
1863 assert!(node.line_range_from_index(6).is_none());
1864 }
1865
1866 #[test]
1867 fn text_position_from_global_usv_index() {
1868 let tree = main_multiline_tree(None);
1869 let state = tree.state();
1870 let node = state.node_by_id(NodeId(1)).unwrap();
1871
1872 {
1873 let pos = node.text_position_from_global_usv_index(0).unwrap();
1874 assert!(pos.is_document_start());
1875 }
1876
1877 {
1878 let pos = node.text_position_from_global_usv_index(17).unwrap();
1879 let mut range = pos.to_degenerate_range();
1880 range.set_end(pos.forward_to_character_end());
1881 assert_eq!(range.text(), "\u{a0}");
1882 }
1883
1884 {
1885 let pos = node.text_position_from_global_usv_index(18).unwrap();
1886 let mut range = pos.to_degenerate_range();
1887 range.set_end(pos.forward_to_character_end());
1888 assert_eq!(range.text(), "l");
1889 }
1890
1891 {
1892 let pos = node.text_position_from_global_usv_index(37).unwrap();
1893 let mut range = pos.to_degenerate_range();
1894 range.set_end(pos.forward_to_character_end());
1895 assert_eq!(range.text(), " ");
1896 }
1897
1898 {
1899 let pos = node.text_position_from_global_usv_index(38).unwrap();
1900 assert!(!pos.is_paragraph_start());
1901 assert!(pos.is_line_start());
1902 let mut range = pos.to_degenerate_range();
1903 range.set_end(pos.forward_to_character_end());
1904 assert_eq!(range.text(), "t");
1905 }
1906
1907 {
1908 let pos = node.text_position_from_global_usv_index(54).unwrap();
1909 let mut range = pos.to_degenerate_range();
1910 range.set_end(pos.forward_to_character_end());
1911 assert_eq!(range.text(), "\n");
1912 }
1913
1914 {
1915 let pos = node.text_position_from_global_usv_index(55).unwrap();
1916 assert!(pos.is_paragraph_start());
1917 assert!(pos.is_line_start());
1918 let mut range = pos.to_degenerate_range();
1919 range.set_end(pos.forward_to_character_end());
1920 assert_eq!(range.text(), "A");
1921 }
1922
1923 for i in 94..=95 {
1924 let pos = node.text_position_from_global_usv_index(i).unwrap();
1925 let mut range = pos.to_degenerate_range();
1926 range.set_end(pos.forward_to_character_end());
1927 assert_eq!(range.text(), "\u{1f44d}\u{1f3fb}");
1928 }
1929
1930 {
1931 let pos = node.text_position_from_global_usv_index(96).unwrap();
1932 let mut range = pos.to_degenerate_range();
1933 range.set_end(pos.forward_to_character_end());
1934 assert_eq!(range.text(), "\n");
1935 }
1936
1937 {
1938 let pos = node.text_position_from_global_usv_index(97).unwrap();
1939 assert!(pos.is_document_end());
1940 }
1941
1942 assert!(node.text_position_from_global_usv_index(98).is_none());
1943 }
1944
1945 #[test]
1946 fn text_position_from_global_utf16_index() {
1947 let tree = main_multiline_tree(None);
1948 let state = tree.state();
1949 let node = state.node_by_id(NodeId(1)).unwrap();
1950
1951 {
1952 let pos = node.text_position_from_global_utf16_index(0).unwrap();
1953 assert!(pos.is_document_start());
1954 }
1955
1956 {
1957 let pos = node.text_position_from_global_utf16_index(17).unwrap();
1958 let mut range = pos.to_degenerate_range();
1959 range.set_end(pos.forward_to_character_end());
1960 assert_eq!(range.text(), "\u{a0}");
1961 }
1962
1963 {
1964 let pos = node.text_position_from_global_utf16_index(18).unwrap();
1965 let mut range = pos.to_degenerate_range();
1966 range.set_end(pos.forward_to_character_end());
1967 assert_eq!(range.text(), "l");
1968 }
1969
1970 {
1971 let pos = node.text_position_from_global_utf16_index(37).unwrap();
1972 let mut range = pos.to_degenerate_range();
1973 range.set_end(pos.forward_to_character_end());
1974 assert_eq!(range.text(), " ");
1975 }
1976
1977 {
1978 let pos = node.text_position_from_global_utf16_index(38).unwrap();
1979 assert!(!pos.is_paragraph_start());
1980 assert!(pos.is_line_start());
1981 let mut range = pos.to_degenerate_range();
1982 range.set_end(pos.forward_to_character_end());
1983 assert_eq!(range.text(), "t");
1984 }
1985
1986 {
1987 let pos = node.text_position_from_global_utf16_index(54).unwrap();
1988 let mut range = pos.to_degenerate_range();
1989 range.set_end(pos.forward_to_character_end());
1990 assert_eq!(range.text(), "\n");
1991 }
1992
1993 {
1994 let pos = node.text_position_from_global_utf16_index(55).unwrap();
1995 assert!(pos.is_paragraph_start());
1996 assert!(pos.is_line_start());
1997 let mut range = pos.to_degenerate_range();
1998 range.set_end(pos.forward_to_character_end());
1999 assert_eq!(range.text(), "A");
2000 }
2001
2002 for i in 94..=97 {
2003 let pos = node.text_position_from_global_utf16_index(i).unwrap();
2004 let mut range = pos.to_degenerate_range();
2005 range.set_end(pos.forward_to_character_end());
2006 assert_eq!(range.text(), "\u{1f44d}\u{1f3fb}");
2007 }
2008
2009 {
2010 let pos = node.text_position_from_global_utf16_index(98).unwrap();
2011 let mut range = pos.to_degenerate_range();
2012 range.set_end(pos.forward_to_character_end());
2013 assert_eq!(range.text(), "\n");
2014 }
2015
2016 {
2017 let pos = node.text_position_from_global_utf16_index(99).unwrap();
2018 assert!(pos.is_document_end());
2019 }
2020
2021 assert!(node.text_position_from_global_utf16_index(100).is_none());
2022 }
2023
2024 #[test]
2025 fn multiline_selection_clamping() {
2026 let tree = main_multiline_tree(Some(multiline_past_end_selection()));
2027 let state = tree.state();
2028 let node = state.node_by_id(NodeId(1)).unwrap();
2029 let _ = node.text_selection().unwrap();
2030 }
2031}