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