1use accesskit::{
7 Color, Node as NodeData, NodeId, Point, Rect, Role, TextAlign, TextDecoration, TextDirection,
8 TextPosition as WeakPosition, TextSelection, VerticalOffset,
9};
10use alloc::{string::String, vec::Vec};
11use core::{cmp::Ordering, fmt, iter::FusedIterator};
12
13use crate::{FilterResult, Node, TreeState};
14
15#[derive(Clone, Copy, Debug)]
16pub(crate) struct InnerPosition<'a> {
17 pub(crate) node: Node<'a>,
18 pub(crate) character_index: usize,
19}
20
21impl<'a> InnerPosition<'a> {
22 fn upgrade(tree_state: &'a TreeState, weak: WeakPosition) -> Option<Self> {
23 let node = tree_state.node_by_id(weak.node)?;
24 if node.role() != Role::TextRun {
25 return None;
26 }
27 let character_index = weak.character_index;
28 if character_index > node.data().character_lengths().len() {
29 return None;
30 }
31 Some(Self {
32 node,
33 character_index,
34 })
35 }
36
37 fn clamped_upgrade(tree_state: &'a TreeState, weak: WeakPosition) -> Option<Self> {
38 let node = tree_state.node_by_id(weak.node)?;
39 if node.role() != Role::TextRun {
40 return None;
41 }
42 let character_index = weak
43 .character_index
44 .min(node.data().character_lengths().len());
45 Some(Self {
46 node,
47 character_index,
48 })
49 }
50
51 fn is_run_start(&self) -> bool {
52 self.character_index == 0
53 }
54
55 fn is_line_start(&self) -> bool {
56 self.is_run_start() && self.node.data().previous_on_line().is_none()
57 }
58
59 fn is_run_end(&self) -> bool {
60 self.character_index == self.node.data().character_lengths().len()
61 }
62
63 fn is_line_end(&self) -> bool {
64 self.is_run_end() && self.node.data().next_on_line().is_none()
65 }
66
67 fn is_paragraph_end(&self) -> bool {
68 self.is_line_end() && self.node.data().value().unwrap().ends_with('\n')
69 }
70
71 fn is_document_start(&self, root_node: &Node) -> bool {
72 self.is_run_start() && self.node.preceding_text_runs(root_node).next().is_none()
73 }
74
75 fn is_document_end(&self, root_node: &Node) -> bool {
76 self.is_run_end() && self.node.following_text_runs(root_node).next().is_none()
77 }
78
79 fn biased_to_start(&self, root_node: &Node) -> Self {
80 if self.is_run_end() {
81 if let Some(node) = self.node.following_text_runs(root_node).next() {
82 return Self {
83 node,
84 character_index: 0,
85 };
86 }
87 }
88 *self
89 }
90
91 fn biased_to_end(&self, root_node: &Node) -> Self {
92 if self.is_run_start() {
93 if let Some(node) = self.node.preceding_text_runs(root_node).next() {
94 return Self {
95 node,
96 character_index: node.data().character_lengths().len(),
97 };
98 }
99 }
100 *self
101 }
102
103 fn comparable(&self, root_node: &Node) -> (Vec<usize>, usize) {
104 let normalized = self.biased_to_start(root_node);
105 (
106 normalized.node.relative_index_path(root_node.id()),
107 normalized.character_index,
108 )
109 }
110
111 fn line_start(&self) -> Self {
112 let mut node = self.node;
113 while let Some(id) = node.data().previous_on_line() {
114 node = node.tree_state.node_by_id(id).unwrap();
115 }
116 Self {
117 node,
118 character_index: 0,
119 }
120 }
121
122 fn line_end(&self) -> Self {
123 let mut node = self.node;
124 while let Some(id) = node.data().next_on_line() {
125 node = node.tree_state.node_by_id(id).unwrap();
126 }
127 Self {
128 node,
129 character_index: node.data().character_lengths().len(),
130 }
131 }
132
133 pub(crate) fn downgrade(&self) -> WeakPosition {
134 WeakPosition {
135 node: self.node.id(),
136 character_index: self.character_index,
137 }
138 }
139}
140
141impl PartialEq for InnerPosition<'_> {
142 fn eq(&self, other: &Self) -> bool {
143 self.node.id() == other.node.id() && self.character_index == other.character_index
144 }
145}
146
147impl Eq for InnerPosition<'_> {}
148
149#[derive(Clone, Copy, Debug)]
150pub struct Position<'a> {
151 root_node: Node<'a>,
152 pub(crate) inner: InnerPosition<'a>,
153}
154
155impl<'a> Position<'a> {
156 pub fn to_raw(self) -> WeakPosition {
157 self.inner.downgrade()
158 }
159
160 pub fn inner_node(&self) -> &Node<'a> {
161 &self.inner.node
162 }
163
164 pub fn is_format_start(&self) -> bool {
165 self.is_document_start()
166 || (self.inner.character_index == 0
167 && self.inner.node.text_attributes_differ(
168 &self
169 .inner
170 .node
171 .preceding_text_runs(&self.root_node)
172 .next()
173 .unwrap(),
174 ))
175 }
176
177 pub fn is_word_start(&self) -> bool {
178 self.is_paragraph_start()
179 || self
180 .inner
181 .node
182 .data()
183 .word_starts()
184 .binary_search(&(self.inner.character_index as u8))
185 .is_ok()
186 }
187
188 pub fn is_line_start(&self) -> bool {
189 self.inner.is_line_start()
190 }
191
192 pub fn is_line_end(&self) -> bool {
193 self.inner.is_line_end()
194 }
195
196 pub fn is_paragraph_start(&self) -> bool {
197 self.is_document_start()
198 || (self.is_line_start()
199 && self.inner.biased_to_end(&self.root_node).is_paragraph_end())
200 }
201
202 pub fn is_paragraph_end(&self) -> bool {
203 self.is_document_end() || self.inner.is_paragraph_end()
204 }
205
206 pub fn is_paragraph_separator(&self) -> bool {
207 if self.is_document_end() {
208 return false;
209 }
210 let next = self.forward_to_character_end();
211 !next.is_document_end() && next.is_paragraph_end()
212 }
213
214 pub fn is_page_start(&self) -> bool {
215 self.is_document_start()
216 }
217
218 pub fn is_document_start(&self) -> bool {
219 self.inner.is_document_start(&self.root_node)
220 }
221
222 pub fn is_document_end(&self) -> bool {
223 self.inner.is_document_end(&self.root_node)
224 }
225
226 pub fn to_degenerate_range(&self) -> Range<'a> {
227 Range::new(self.root_node, self.inner, self.inner)
228 }
229
230 pub fn to_global_usv_index(&self) -> usize {
231 let mut total_length = 0usize;
232 for node in self.root_node.text_runs() {
233 let node_text = node.data().value().unwrap();
234 if node.id() == self.inner.node.id() {
235 let character_lengths = node.data().character_lengths();
236 let slice_end = character_lengths[..self.inner.character_index]
237 .iter()
238 .copied()
239 .map(usize::from)
240 .sum::<usize>();
241 return total_length + node_text[..slice_end].chars().count();
242 }
243 total_length += node_text.chars().count();
244 }
245 panic!("invalid position")
246 }
247
248 pub fn to_global_utf16_index(&self) -> usize {
249 let mut total_length = 0usize;
250 for node in self.root_node.text_runs() {
251 let node_text = node.data().value().unwrap();
252 if node.id() == self.inner.node.id() {
253 let character_lengths = node.data().character_lengths();
254 let slice_end = character_lengths[..self.inner.character_index]
255 .iter()
256 .copied()
257 .map(usize::from)
258 .sum::<usize>();
259 return total_length
260 + node_text[..slice_end]
261 .chars()
262 .map(char::len_utf16)
263 .sum::<usize>();
264 }
265 total_length += node_text.chars().map(char::len_utf16).sum::<usize>();
266 }
267 panic!("invalid position")
268 }
269
270 pub fn to_line_index(&self) -> usize {
271 let mut pos = *self;
272 if !pos.is_line_start() {
273 pos = pos.backward_to_line_start();
274 }
275 let mut lines_before_current = 0usize;
276 while !pos.is_document_start() {
277 pos = pos.backward_to_line_start();
278 lines_before_current += 1;
279 }
280 lines_before_current
281 }
282
283 pub fn biased_to_start(&self) -> Self {
284 Self {
285 root_node: self.root_node,
286 inner: self.inner.biased_to_start(&self.root_node),
287 }
288 }
289
290 pub fn biased_to_end(&self) -> Self {
291 Self {
292 root_node: self.root_node,
293 inner: self.inner.biased_to_end(&self.root_node),
294 }
295 }
296
297 pub fn forward_to_character_start(&self) -> Self {
298 let pos = self.inner.biased_to_start(&self.root_node);
299 Self {
300 root_node: self.root_node,
301 inner: InnerPosition {
302 node: pos.node,
303 character_index: pos.character_index + 1,
304 }
305 .biased_to_start(&self.root_node),
306 }
307 }
308
309 pub fn forward_to_character_end(&self) -> Self {
310 let pos = self.inner.biased_to_start(&self.root_node);
311 Self {
312 root_node: self.root_node,
313 inner: InnerPosition {
314 node: pos.node,
315 character_index: pos.character_index + 1,
316 },
317 }
318 }
319
320 pub fn backward_to_character_start(&self) -> Self {
321 let pos = self.inner.biased_to_end(&self.root_node);
322 Self {
323 root_node: self.root_node,
324 inner: InnerPosition {
325 node: pos.node,
326 character_index: pos.character_index - 1,
327 }
328 .biased_to_start(&self.root_node),
329 }
330 }
331
332 pub fn forward_to_format_start(&self) -> Self {
333 for node in self.inner.node.following_text_runs(&self.root_node) {
334 if self.inner.node.text_attributes_differ(&node) {
335 return Self {
336 root_node: self.root_node,
337 inner: InnerPosition {
338 node,
339 character_index: 0,
340 },
341 };
342 }
343 }
344 self.document_end()
345 }
346
347 pub fn forward_to_format_end(&self) -> Self {
348 self.forward_to_format_start().biased_to_end()
349 }
350
351 pub fn backward_to_format_start(&self) -> Self {
352 if self.inner.character_index != 0 {
353 let test_pos = Self {
354 root_node: self.root_node,
355 inner: InnerPosition {
356 node: self.inner.node,
357 character_index: 0,
358 },
359 };
360 if test_pos.is_format_start() {
361 return test_pos;
362 }
363 }
364 for node in self.inner.node.preceding_text_runs(&self.root_node) {
365 let test_pos = Self {
366 root_node: self.root_node,
367 inner: InnerPosition {
368 node,
369 character_index: 0,
370 },
371 };
372 if test_pos.is_format_start() {
373 return test_pos;
374 }
375 }
376 self.document_start()
377 }
378
379 pub fn forward_to_word_start(&self) -> Self {
380 {
383 let word_starts = self.inner.node.data().word_starts();
384 let index = match word_starts.binary_search(&(self.inner.character_index as u8)) {
385 Ok(index) => index + 1,
386 Err(index) => index,
387 };
388 if let Some(start) = word_starts.get(index) {
389 return Self {
390 root_node: self.root_node,
391 inner: InnerPosition {
392 node: self.inner.node,
393 character_index: *start as usize,
394 },
395 };
396 }
397 }
398 for node in self.inner.node.following_text_runs(&self.root_node) {
399 let start_pos = Self {
400 root_node: self.root_node,
401 inner: InnerPosition {
402 node,
403 character_index: 0,
404 },
405 };
406 if start_pos.is_paragraph_start() {
407 return start_pos;
408 }
409 if let Some(start) = node.data().word_starts().first() {
410 return Self {
411 root_node: self.root_node,
412 inner: InnerPosition {
413 node,
414 character_index: *start as usize,
415 },
416 };
417 }
418 }
419 self.document_end()
420 }
421
422 pub fn forward_to_word_end(&self) -> Self {
423 self.forward_to_word_start().biased_to_end()
424 }
425
426 pub fn backward_to_word_start(&self) -> Self {
427 {
430 let word_starts = self.inner.node.data().word_starts();
431 let index = match word_starts.binary_search(&(self.inner.character_index as u8)) {
432 Ok(index) => index,
433 Err(index) => index,
434 };
435 if let Some(index) = index.checked_sub(1) {
436 return Self {
437 root_node: self.root_node,
438 inner: InnerPosition {
439 node: self.inner.node,
440 character_index: word_starts[index] as usize,
441 },
442 };
443 }
444 }
445 if self.inner.character_index != 0 {
446 let start_pos = Self {
447 root_node: self.root_node,
448 inner: InnerPosition {
449 node: self.inner.node,
450 character_index: 0,
451 },
452 };
453 if start_pos.is_paragraph_start() {
454 return start_pos;
455 }
456 }
457 for node in self.inner.node.preceding_text_runs(&self.root_node) {
458 if let Some(start) = node.data().word_starts().last() {
459 return Self {
460 root_node: self.root_node,
461 inner: InnerPosition {
462 node,
463 character_index: *start as usize,
464 },
465 };
466 }
467 let start_pos = Self {
468 root_node: self.root_node,
469 inner: InnerPosition {
470 node,
471 character_index: 0,
472 },
473 };
474 if start_pos.is_paragraph_start() {
475 return start_pos;
476 }
477 }
478 self.document_start()
479 }
480
481 pub fn forward_to_line_start(&self) -> Self {
482 let pos = self.inner.biased_to_start(&self.root_node);
483 Self {
484 root_node: self.root_node,
485 inner: pos.line_end().biased_to_start(&self.root_node),
486 }
487 }
488
489 pub fn forward_to_line_end(&self) -> Self {
490 let pos = self.inner.biased_to_start(&self.root_node);
491 Self {
492 root_node: self.root_node,
493 inner: pos.line_end(),
494 }
495 }
496
497 pub fn backward_to_line_start(&self) -> Self {
498 let pos = self.inner.biased_to_end(&self.root_node);
499 Self {
500 root_node: self.root_node,
501 inner: pos.line_start().biased_to_start(&self.root_node),
502 }
503 }
504
505 pub fn forward_to_paragraph_start(&self) -> Self {
506 let mut current = *self;
507 loop {
508 current = current.forward_to_line_start();
509 if current.is_document_end()
510 || current
511 .inner
512 .biased_to_end(&self.root_node)
513 .is_paragraph_end()
514 {
515 break;
516 }
517 }
518 current
519 }
520
521 pub fn forward_to_paragraph_end(&self) -> Self {
522 let mut current = *self;
523 loop {
524 current = current.forward_to_line_end();
525 if current.is_document_end() || current.inner.is_paragraph_end() {
526 break;
527 }
528 }
529 current
530 }
531
532 pub fn backward_to_paragraph_start(&self) -> Self {
533 let mut current = *self;
534 loop {
535 current = current.backward_to_line_start();
536 if current.is_paragraph_start() {
537 break;
538 }
539 }
540 current
541 }
542
543 pub fn forward_to_page_start(&self) -> Self {
544 self.document_end()
545 }
546
547 pub fn forward_to_page_end(&self) -> Self {
548 self.document_end()
549 }
550
551 pub fn backward_to_page_start(&self) -> Self {
552 self.document_start()
553 }
554
555 pub fn document_end(&self) -> Self {
556 self.root_node.document_end()
557 }
558
559 pub fn document_start(&self) -> Self {
560 self.root_node.document_start()
561 }
562}
563
564impl PartialEq for Position<'_> {
565 fn eq(&self, other: &Self) -> bool {
566 self.root_node.id() == other.root_node.id() && self.inner == other.inner
567 }
568}
569
570impl Eq for Position<'_> {}
571
572impl PartialOrd for Position<'_> {
573 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
574 if self.root_node.id() != other.root_node.id() {
575 return None;
576 }
577 let self_comparable = self.inner.comparable(&self.root_node);
578 let other_comparable = other.inner.comparable(&self.root_node);
579 Some(self_comparable.cmp(&other_comparable))
580 }
581}
582
583#[derive(Debug, PartialEq)]
584pub enum RangePropertyValue<T: alloc::fmt::Debug + PartialEq> {
585 Single(T),
586 Mixed,
587}
588
589impl<T: alloc::fmt::Debug + PartialEq> RangePropertyValue<Option<T>> {
590 pub fn map<U: alloc::fmt::Debug + PartialEq>(
591 self,
592 f: impl FnOnce(T) -> U,
593 ) -> RangePropertyValue<Option<U>> {
594 match self {
595 Self::Single(value) => RangePropertyValue::Single(value.map(f)),
596 Self::Mixed => RangePropertyValue::Mixed,
597 }
598 }
599}
600
601#[derive(Clone, Copy)]
602pub struct Range<'a> {
603 pub(crate) node: Node<'a>,
604 pub(crate) start: InnerPosition<'a>,
605 pub(crate) end: InnerPosition<'a>,
606}
607
608impl<'a> Range<'a> {
609 fn new(node: Node<'a>, mut start: InnerPosition<'a>, mut end: InnerPosition<'a>) -> Self {
610 if start.comparable(&node) > end.comparable(&node) {
611 core::mem::swap(&mut start, &mut end);
612 }
613 Self { node, start, end }
614 }
615
616 pub fn node(&self) -> &Node<'a> {
617 &self.node
618 }
619
620 pub fn start(&self) -> Position<'a> {
621 Position {
622 root_node: self.node,
623 inner: self.start,
624 }
625 }
626
627 pub fn end(&self) -> Position<'a> {
628 Position {
629 root_node: self.node,
630 inner: self.end,
631 }
632 }
633
634 pub fn is_degenerate(&self) -> bool {
635 self.start.comparable(&self.node) == self.end.comparable(&self.node)
636 }
637
638 fn walk<F, T>(&self, mut f: F) -> Option<T>
639 where
640 F: FnMut(&Node<'a>) -> Option<T>,
641 {
642 let (start, end) = if self.is_degenerate() {
646 (self.start, self.start)
647 } else {
648 let start = self.start.biased_to_start(&self.node);
649 let end = self.end.biased_to_end(&self.node);
650 (start, end)
651 };
652 if let Some(result) = f(&start.node) {
653 return Some(result);
654 }
655 if start.node.id() == end.node.id() {
656 return None;
657 }
658 for node in start.node.following_text_runs(&self.node) {
659 if let Some(result) = f(&node) {
660 return Some(result);
661 }
662 if node.id() == end.node.id() {
663 break;
664 }
665 }
666 None
667 }
668
669 pub fn text(&self) -> String {
670 let mut result = String::new();
671 self.write_text(&mut result).unwrap();
672 result
673 }
674
675 pub fn write_text<W: fmt::Write>(&self, mut writer: W) -> fmt::Result {
676 if let Some(err) = self.walk(|node| {
677 let character_lengths = node.data().character_lengths();
678 let start_index = if node.id() == self.start.node.id() {
679 self.start.character_index
680 } else {
681 0
682 };
683 let end_index = if node.id() == self.end.node.id() {
684 self.end.character_index
685 } else {
686 character_lengths.len()
687 };
688 let value = node.data().value().unwrap();
689 let s = if start_index == end_index {
690 ""
691 } else if start_index == 0 && end_index == character_lengths.len() {
692 value
693 } else {
694 let slice_start = character_lengths[..start_index]
695 .iter()
696 .copied()
697 .map(usize::from)
698 .sum::<usize>();
699 let slice_end = slice_start
700 + character_lengths[start_index..end_index]
701 .iter()
702 .copied()
703 .map(usize::from)
704 .sum::<usize>();
705 &value[slice_start..slice_end]
706 };
707 writer.write_str(s).err()
708 }) {
709 Err(err)
710 } else {
711 Ok(())
712 }
713 }
714
715 pub fn bounding_boxes(&self) -> Vec<Rect> {
723 let mut result = Vec::new();
724 self.walk(|node| {
725 let mut rect = match node.data().bounds() {
726 Some(rect) => rect,
727 None => {
728 return Some(Vec::new());
729 }
730 };
731 let positions = match node.data().character_positions() {
732 Some(positions) => positions,
733 None => {
734 return Some(Vec::new());
735 }
736 };
737 let widths = match node.data().character_widths() {
738 Some(widths) => widths,
739 None => {
740 return Some(Vec::new());
741 }
742 };
743 let direction = match node.text_direction() {
744 Some(direction) => direction,
745 None => {
746 return Some(Vec::new());
747 }
748 };
749 let character_lengths = node.data().character_lengths();
750 let start_index = if node.id() == self.start.node.id() {
751 self.start.character_index
752 } else {
753 0
754 };
755 let end_index = if node.id() == self.end.node.id() {
756 self.end.character_index
757 } else {
758 character_lengths.len()
759 };
760 if start_index != 0 || end_index != character_lengths.len() {
761 let pixel_start = if start_index < character_lengths.len() {
762 positions[start_index]
763 } else {
764 positions[start_index - 1] + widths[start_index - 1]
765 };
766 let pixel_end = if end_index == start_index {
767 pixel_start
768 } else {
769 positions[end_index - 1] + widths[end_index - 1]
770 };
771 let pixel_start = f64::from(pixel_start);
772 let pixel_end = f64::from(pixel_end);
773 match direction {
774 TextDirection::LeftToRight => {
775 let orig_left = rect.x0;
776 rect.x0 = orig_left + pixel_start;
777 rect.x1 = orig_left + pixel_end;
778 }
779 TextDirection::RightToLeft => {
780 let orig_right = rect.x1;
781 rect.x1 = orig_right - pixel_start;
782 rect.x0 = orig_right - pixel_end;
783 }
784 TextDirection::TopToBottom => {
788 let orig_top = rect.y0;
789 rect.y0 = orig_top + pixel_start;
790 rect.y1 = orig_top + pixel_end;
791 }
792 TextDirection::BottomToTop => {
793 let orig_bottom = rect.y1;
794 rect.y1 = orig_bottom - pixel_start;
795 rect.y0 = orig_bottom - pixel_end;
796 }
797 }
798 }
799 result.push(node.transform().transform_rect_bbox(rect));
800 None
801 })
802 .unwrap_or(result)
803 }
804
805 fn fetch_property<T: alloc::fmt::Debug + PartialEq>(
806 &self,
807 getter: fn(&Node<'a>) -> T,
808 ) -> RangePropertyValue<T> {
809 let mut value = None;
810 self.walk(|node| {
811 let current = getter(node);
812 if let Some(value) = &value {
813 if *value != current {
814 return Some(RangePropertyValue::Mixed);
815 }
816 } else {
817 value = Some(current);
818 }
819 None
820 })
821 .unwrap_or_else(|| RangePropertyValue::Single(value.unwrap()))
822 }
823
824 fn fix_start_bias(&mut self) {
825 if !self.is_degenerate() {
826 self.start = self.start.biased_to_start(&self.node);
827 }
828 }
829
830 pub fn set_start(&mut self, pos: Position<'a>) {
831 assert_eq!(pos.root_node.id(), self.node.id());
832 self.start = pos.inner;
833 if self.start.comparable(&self.node) >= self.end.comparable(&self.node) {
836 self.end = self.start;
837 }
838 self.fix_start_bias();
839 }
840
841 pub fn set_end(&mut self, pos: Position<'a>) {
842 assert_eq!(pos.root_node.id(), self.node.id());
843 self.end = pos.inner;
844 if self.start.comparable(&self.node) >= self.end.comparable(&self.node) {
847 self.start = self.end;
848 }
849 self.fix_start_bias();
850 }
851
852 pub fn to_text_selection(&self) -> TextSelection {
853 TextSelection {
854 anchor: self.start.downgrade(),
855 focus: self.end.downgrade(),
856 }
857 }
858
859 pub fn downgrade(&self) -> WeakRange {
860 WeakRange {
861 node_id: self.node.id(),
862 start: self.start.downgrade(),
863 end: self.end.downgrade(),
864 start_comparable: self.start.comparable(&self.node),
865 end_comparable: self.end.comparable(&self.node),
866 }
867 }
868}
869
870impl PartialEq for Range<'_> {
871 fn eq(&self, other: &Self) -> bool {
872 self.node.id() == other.node.id() && self.start == other.start && self.end == other.end
873 }
874}
875
876impl Eq for Range<'_> {}
877
878#[derive(Clone, Debug, PartialEq, Eq)]
879pub struct WeakRange {
880 node_id: NodeId,
881 start: WeakPosition,
882 end: WeakPosition,
883 start_comparable: (Vec<usize>, usize),
884 end_comparable: (Vec<usize>, usize),
885}
886
887impl WeakRange {
888 pub fn node_id(&self) -> NodeId {
889 self.node_id
890 }
891
892 pub fn start_comparable(&self) -> &(Vec<usize>, usize) {
893 &self.start_comparable
894 }
895
896 pub fn end_comparable(&self) -> &(Vec<usize>, usize) {
897 &self.end_comparable
898 }
899
900 pub fn upgrade_node<'a>(&self, tree_state: &'a TreeState) -> Option<Node<'a>> {
901 tree_state
902 .node_by_id(self.node_id)
903 .filter(Node::supports_text_ranges)
904 }
905
906 pub fn upgrade<'a>(&self, tree_state: &'a TreeState) -> Option<Range<'a>> {
907 let node = self.upgrade_node(tree_state)?;
908 let start = InnerPosition::upgrade(tree_state, self.start)?;
909 let end = InnerPosition::upgrade(tree_state, self.end)?;
910 Some(Range { node, start, end })
911 }
912}
913
914fn text_node_filter(root_id: NodeId, node: &Node) -> FilterResult {
915 if node.id() == root_id || node.role() == Role::TextRun {
916 FilterResult::Include
917 } else {
918 FilterResult::ExcludeNode
919 }
920}
921
922fn character_index_at_point(node: &Node, point: Point) -> usize {
923 let rect = node.data().bounds().unwrap();
926 let character_lengths = node.data().character_lengths();
927 let positions = match node.data().character_positions() {
928 Some(positions) => positions,
929 None => {
930 return 0;
931 }
932 };
933 let widths = match node.data().character_widths() {
934 Some(widths) => widths,
935 None => {
936 return 0;
937 }
938 };
939 let direction = match node.text_direction() {
940 Some(direction) => direction,
941 None => {
942 return 0;
943 }
944 };
945 for (i, (position, width)) in positions.iter().zip(widths.iter()).enumerate().rev() {
946 let relative_pos = match direction {
947 TextDirection::LeftToRight => point.x - rect.x0,
948 TextDirection::RightToLeft => rect.x1 - point.x,
949 TextDirection::TopToBottom => point.y - rect.y0,
953 TextDirection::BottomToTop => rect.y1 - point.y,
954 };
955 if relative_pos >= f64::from(*position) && relative_pos < f64::from(*position + *width) {
956 return i;
957 }
958 }
959 character_lengths.len()
960}
961
962macro_rules! inherited_properties {
963 ($(($getter:ident, $type:ty, $setter:ident, $test_value_1:expr, $test_value_2:expr)),+) => {
964 impl<'a> Node<'a> {
965 $(pub fn $getter(&self) -> Option<$type> {
966 self.fetch_inherited_property(NodeData::$getter)
967 })*
968 }
969 impl<'a> Position<'a> {
970 $(pub fn $getter(&self) -> Option<$type> {
971 self.inner.node.$getter()
972 })*
973 }
974 impl<'a> Range<'a> {
975 $(pub fn $getter(&self) -> RangePropertyValue<Option<$type>> {
976 self.fetch_property(Node::$getter)
977 })*
978 }
979 $(#[cfg(test)]
980 mod $getter {
981 use accesskit::{Node, NodeId, Role, Tree, TreeUpdate};
982 use alloc::vec;
983 use super::RangePropertyValue;
984 #[test]
985 fn directly_set() {
986 let update = TreeUpdate {
987 nodes: vec![
988 (NodeId(0), {
989 let mut node = Node::new(Role::TextInput);
990 node.set_children(vec![NodeId(1)]);
991 node
992 }),
993 (NodeId(1), {
994 let mut node = Node::new(Role::TextRun);
995 node.set_value("text");
996 node.set_character_lengths([1, 1, 1, 1]);
997 node.$setter($test_value_1);
998 node
999 }),
1000 ],
1001 tree: Some(Tree::new(NodeId(0))),
1002 focus: NodeId(0),
1003 };
1004 let tree = crate::Tree::new(update, false);
1005 let state = tree.state();
1006 let node = state.node_by_id(NodeId(0)).unwrap();
1007 let pos = node.document_start();
1008 assert_eq!(pos.$getter(), Some($test_value_1));
1009 let range = node.document_range();
1010 assert_eq!(range.$getter(), RangePropertyValue::Single(Some($test_value_1)));
1011 }
1012 #[test]
1013 fn set_on_parent() {
1014 let update = TreeUpdate {
1015 nodes: vec![
1016 (NodeId(0), {
1017 let mut node = Node::new(Role::TextInput);
1018 node.set_children(vec![NodeId(1)]);
1019 node.$setter($test_value_1);
1020 node
1021 }),
1022 (NodeId(1), {
1023 let mut node = Node::new(Role::TextRun);
1024 node.set_value("text");
1025 node.set_character_lengths([1, 1, 1, 1]);
1026 node
1027 }),
1028 ],
1029 tree: Some(Tree::new(NodeId(0))),
1030 focus: NodeId(0),
1031 };
1032 let tree = crate::Tree::new(update, false);
1033 let state = tree.state();
1034 let node = state.node_by_id(NodeId(0)).unwrap();
1035 let pos = node.document_start();
1036 assert_eq!(pos.$getter(), Some($test_value_1));
1037 let range = node.document_range();
1038 assert_eq!(range.$getter(), RangePropertyValue::Single(Some($test_value_1)));
1039 }
1040 #[test]
1041 fn only_child_overrides_parent() {
1042 let update = TreeUpdate {
1043 nodes: vec![
1044 (NodeId(0), {
1045 let mut node = Node::new(Role::TextInput);
1046 node.set_children(vec![NodeId(1)]);
1047 node.$setter($test_value_1);
1048 node
1049 }),
1050 (NodeId(1), {
1051 let mut node = Node::new(Role::TextRun);
1052 node.set_value("text");
1053 node.set_character_lengths([1, 1, 1, 1]);
1054 node.$setter($test_value_2);
1055 node
1056 }),
1057 ],
1058 tree: Some(Tree::new(NodeId(0))),
1059 focus: NodeId(0),
1060 };
1061 let tree = crate::Tree::new(update, false);
1062 let state = tree.state();
1063 let node = state.node_by_id(NodeId(0)).unwrap();
1064 assert_eq!(node.$getter(), Some($test_value_1));
1065 let pos = node.document_start();
1066 assert_eq!(pos.$getter(), Some($test_value_2));
1067 let range = node.document_range();
1068 assert_eq!(range.$getter(), RangePropertyValue::Single(Some($test_value_2)));
1069 }
1070 #[test]
1071 fn unset() {
1072 let update = TreeUpdate {
1073 nodes: vec![
1074 (NodeId(0), {
1075 let mut node = Node::new(Role::TextInput);
1076 node.set_children(vec![NodeId(1)]);
1077 node
1078 }),
1079 (NodeId(1), {
1080 let mut node = Node::new(Role::TextRun);
1081 node.set_value("text");
1082 node.set_character_lengths([1, 1, 1, 1]);
1083 node
1084 }),
1085 ],
1086 tree: Some(Tree::new(NodeId(0))),
1087 focus: NodeId(0),
1088 };
1089 let tree = crate::Tree::new(update, false);
1090 let state = tree.state();
1091 let node = state.node_by_id(NodeId(0)).unwrap();
1092 let pos = node.document_start();
1093 assert_eq!(pos.$getter(), None);
1094 let range = node.document_range();
1095 assert_eq!(range.$getter(), RangePropertyValue::Single(None));
1096 }
1097 #[test]
1098 fn mixed_some_and_none() {
1099 let update = TreeUpdate {
1100 nodes: vec![
1101 (NodeId(0), {
1102 let mut node = Node::new(Role::TextInput);
1103 node.set_children(vec![NodeId(1), NodeId(2)]);
1104 node
1105 }),
1106 (NodeId(1), {
1107 let mut node = Node::new(Role::TextRun);
1108 node.set_value("text 1\n");
1109 node.set_character_lengths([1, 1, 1, 1, 1, 1, 1]);
1110 node.$setter($test_value_1);
1111 node
1112 }),
1113 (NodeId(2), {
1114 let mut node = Node::new(Role::TextRun);
1115 node.set_value("text 2");
1116 node.set_character_lengths([1, 1, 1, 1, 1, 1]);
1117 node
1118 }),
1119 ],
1120 tree: Some(Tree::new(NodeId(0))),
1121 focus: NodeId(0),
1122 };
1123 let tree = crate::Tree::new(update, false);
1124 let state = tree.state();
1125 let node = state.node_by_id(NodeId(0)).unwrap();
1126 let range = node.document_range();
1127 assert_eq!(range.$getter(), RangePropertyValue::Mixed);
1128 }
1129 #[test]
1130 fn mixed_one_child_overrides_parent() {
1131 let update = TreeUpdate {
1132 nodes: vec![
1133 (NodeId(0), {
1134 let mut node = Node::new(Role::TextInput);
1135 node.set_children(vec![NodeId(1), NodeId(2)]);
1136 node.$setter($test_value_1);
1137 node
1138 }),
1139 (NodeId(1), {
1140 let mut node = Node::new(Role::TextRun);
1141 node.set_value("text 1\n");
1142 node.set_character_lengths([1, 1, 1, 1, 1, 1, 1]);
1143 node.$setter($test_value_2);
1144 node
1145 }),
1146 (NodeId(2), {
1147 let mut node = Node::new(Role::TextRun);
1148 node.set_value("text 2");
1149 node.set_character_lengths([1, 1, 1, 1, 1, 1]);
1150 node
1151 }),
1152 ],
1153 tree: Some(Tree::new(NodeId(0))),
1154 focus: NodeId(0),
1155 };
1156 let tree = crate::Tree::new(update, false);
1157 let state = tree.state();
1158 let node = state.node_by_id(NodeId(0)).unwrap();
1159 assert_eq!(node.$getter(), Some($test_value_1));
1160 let start = node.document_start();
1161 assert_eq!(start.$getter(), Some($test_value_2));
1162 let start_range = start.to_degenerate_range();
1163 assert_eq!(start_range.$getter(), RangePropertyValue::Single(Some($test_value_2)));
1164 let end = node.document_end();
1165 assert_eq!(end.$getter(), Some($test_value_1));
1166 let end_range = end.to_degenerate_range();
1167 assert_eq!(end_range.$getter(), RangePropertyValue::Single(Some($test_value_1)));
1168 let range = node.document_range();
1169 assert_eq!(range.$getter(), RangePropertyValue::Mixed);
1170 }
1171 })*
1172 }
1173}
1174
1175inherited_properties! {
1176 (text_direction, TextDirection, set_text_direction, accesskit::TextDirection::LeftToRight, accesskit::TextDirection::RightToLeft),
1177 (font_family, &'a str, set_font_family, "Noto", "Inconsolata"),
1178 (language, &'a str, set_language, "en", "fr"),
1179 (font_size, f32, set_font_size, 12.0, 24.0),
1180 (font_weight, f32, set_font_weight, 400.0, 700.0),
1181 (background_color, Color, set_background_color, accesskit::Color { red: 255, green: 255, blue: 255, alpha: 255 }, accesskit::Color { red: 255, green: 0, blue: 0, alpha: 255 }),
1182 (foreground_color, Color, set_foreground_color, accesskit::Color { red: 0, green: 0, blue: 0, alpha: 255 }, accesskit::Color { red: 0, green: 0, blue: 255, alpha: 255 }),
1183 (overline, TextDecoration, set_overline, accesskit::TextDecoration::Solid, accesskit::TextDecoration::Dotted),
1184 (strikethrough, TextDecoration, set_strikethrough, accesskit::TextDecoration::Dotted, accesskit::TextDecoration::Dashed),
1185 (underline, TextDecoration, set_underline, accesskit::TextDecoration::Dashed, accesskit::TextDecoration::Double),
1186 (text_align, TextAlign, set_text_align, accesskit::TextAlign::Left, accesskit::TextAlign::Justify),
1187 (vertical_offset, VerticalOffset, set_vertical_offset, accesskit::VerticalOffset::Subscript, accesskit::VerticalOffset::Superscript)
1188}
1189
1190macro_rules! inherited_flags {
1191 ($(($getter:ident, $setter:ident)),+) => {
1192 impl<'a> Node<'a> {
1193 $(pub fn $getter(&self) -> bool {
1194 self.fetch_inherited_flag(NodeData::$getter)
1195 })*
1196 }
1197 impl<'a> Position<'a> {
1198 $(pub fn $getter(&self) -> bool {
1199 self.inner.node.$getter()
1200 })*
1201 }
1202 impl<'a> Range<'a> {
1203 $(pub fn $getter(&self) -> RangePropertyValue<bool> {
1204 self.fetch_property(Node::$getter)
1205 })*
1206 }
1207 $(#[cfg(test)]
1208 mod $getter {
1209 use accesskit::{Node, NodeId, Role, Tree, TreeUpdate};
1210 use alloc::vec;
1211 use super::RangePropertyValue;
1212 #[test]
1213 fn directly_set() {
1214 let update = TreeUpdate {
1215 nodes: vec![
1216 (NodeId(0), {
1217 let mut node = Node::new(Role::TextInput);
1218 node.set_children(vec![NodeId(1)]);
1219 node
1220 }),
1221 (NodeId(1), {
1222 let mut node = Node::new(Role::TextRun);
1223 node.set_value("text");
1224 node.set_character_lengths([1, 1, 1, 1]);
1225 node.$setter();
1226 node
1227 }),
1228 ],
1229 tree: Some(Tree::new(NodeId(0))),
1230 focus: NodeId(0),
1231 };
1232 let tree = crate::Tree::new(update, false);
1233 let state = tree.state();
1234 let node = state.node_by_id(NodeId(0)).unwrap();
1235 let pos = node.document_start();
1236 assert!(pos.$getter());
1237 let range = node.document_range();
1238 assert_eq!(range.$getter(), RangePropertyValue::Single(true));
1239 }
1240 #[test]
1241 fn set_on_parent() {
1242 let update = TreeUpdate {
1243 nodes: vec![
1244 (NodeId(0), {
1245 let mut node = Node::new(Role::TextInput);
1246 node.set_children(vec![NodeId(1)]);
1247 node.$setter();
1248 node
1249 }),
1250 (NodeId(1), {
1251 let mut node = Node::new(Role::TextRun);
1252 node.set_value("text");
1253 node.set_character_lengths([1, 1, 1, 1]);
1254 node
1255 }),
1256 ],
1257 tree: Some(Tree::new(NodeId(0))),
1258 focus: NodeId(0),
1259 };
1260 let tree = crate::Tree::new(update, false);
1261 let state = tree.state();
1262 let node = state.node_by_id(NodeId(0)).unwrap();
1263 let pos = node.document_start();
1264 assert!(pos.$getter());
1265 let range = node.document_range();
1266 assert_eq!(range.$getter(), RangePropertyValue::Single(true));
1267 }
1268 #[test]
1269 fn unset() {
1270 let update = TreeUpdate {
1271 nodes: vec![
1272 (NodeId(0), {
1273 let mut node = Node::new(Role::TextInput);
1274 node.set_children(vec![NodeId(1)]);
1275 node
1276 }),
1277 (NodeId(1), {
1278 let mut node = Node::new(Role::TextRun);
1279 node.set_value("text");
1280 node.set_character_lengths([1, 1, 1, 1]);
1281 node
1282 }),
1283 ],
1284 tree: Some(Tree::new(NodeId(0))),
1285 focus: NodeId(0),
1286 };
1287 let tree = crate::Tree::new(update, false);
1288 let state = tree.state();
1289 let node = state.node_by_id(NodeId(0)).unwrap();
1290 let pos = node.document_start();
1291 assert!(!pos.$getter());
1292 let range = node.document_range();
1293 assert_eq!(range.$getter(), RangePropertyValue::Single(false));
1294 }
1295 #[test]
1296 fn mixed() {
1297 let update = TreeUpdate {
1298 nodes: vec![
1299 (NodeId(0), {
1300 let mut node = Node::new(Role::TextInput);
1301 node.set_children(vec![NodeId(1), NodeId(2)]);
1302 node
1303 }),
1304 (NodeId(1), {
1305 let mut node = Node::new(Role::TextRun);
1306 node.set_value("text 1\n");
1307 node.set_character_lengths([1, 1, 1, 1, 1, 1, 1]);
1308 node.$setter();
1309 node
1310 }),
1311 (NodeId(2), {
1312 let mut node = Node::new(Role::TextRun);
1313 node.set_value("text 2");
1314 node.set_character_lengths([1, 1, 1, 1, 1, 1]);
1315 node
1316 }),
1317 ],
1318 tree: Some(Tree::new(NodeId(0))),
1319 focus: NodeId(0),
1320 };
1321 let tree = crate::Tree::new(update, false);
1322 let state = tree.state();
1323 let node = state.node_by_id(NodeId(0)).unwrap();
1324 let range = node.document_range();
1325 assert_eq!(range.$getter(), RangePropertyValue::Mixed);
1326 }
1327 })*
1328 }
1329}
1330
1331inherited_flags! {
1332 (is_italic, set_italic)
1333}
1334
1335impl<'a> Node<'a> {
1336 fn text_attributes_differ(&self, other: &Self) -> bool {
1337 self.font_family() != other.font_family()
1338 || self.language() != other.language()
1339 || self.font_size() != other.font_size()
1340 || self.font_weight() != other.font_weight()
1341 || self.background_color() != other.background_color()
1342 || self.foreground_color() != other.foreground_color()
1343 || self.overline() != other.overline()
1344 || self.strikethrough() != other.strikethrough()
1345 || self.underline() != other.underline()
1346 || self.text_align() != other.text_align()
1347 || self.vertical_offset() != other.vertical_offset()
1348 }
1350
1351 pub(crate) fn text_runs(
1352 &self,
1353 ) -> impl DoubleEndedIterator<Item = Node<'a>> + FusedIterator<Item = Node<'a>> + 'a {
1354 let id = self.id();
1355 self.filtered_children(move |node| text_node_filter(id, node))
1356 }
1357
1358 fn following_text_runs(
1359 &self,
1360 root_node: &Node,
1361 ) -> impl DoubleEndedIterator<Item = Node<'a>> + FusedIterator<Item = Node<'a>> + 'a {
1362 let id = root_node.id();
1363 self.following_filtered_siblings(move |node| text_node_filter(id, node))
1364 }
1365
1366 fn preceding_text_runs(
1367 &self,
1368 root_node: &Node,
1369 ) -> impl DoubleEndedIterator<Item = Node<'a>> + FusedIterator<Item = Node<'a>> + 'a {
1370 let id = root_node.id();
1371 self.preceding_filtered_siblings(move |node| text_node_filter(id, node))
1372 }
1373
1374 pub fn supports_text_ranges(&self) -> bool {
1375 (self.is_text_input()
1376 || matches!(self.role(), Role::Label | Role::Document | Role::Terminal))
1377 && self.text_runs().next().is_some()
1378 }
1379
1380 fn document_start_inner(&self) -> InnerPosition<'a> {
1381 let node = self.text_runs().next().unwrap();
1382 InnerPosition {
1383 node,
1384 character_index: 0,
1385 }
1386 }
1387
1388 pub fn document_start(&self) -> Position<'a> {
1389 Position {
1390 root_node: *self,
1391 inner: self.document_start_inner(),
1392 }
1393 }
1394
1395 fn document_end_inner(&self) -> InnerPosition<'a> {
1396 let node = self.text_runs().next_back().unwrap();
1397 InnerPosition {
1398 node,
1399 character_index: node.data().character_lengths().len(),
1400 }
1401 }
1402
1403 pub fn document_end(&self) -> Position<'a> {
1404 Position {
1405 root_node: *self,
1406 inner: self.document_end_inner(),
1407 }
1408 }
1409
1410 pub fn document_range(&self) -> Range<'_> {
1411 let start = self.document_start_inner();
1412 let end = self.document_end_inner();
1413 Range::new(*self, start, end)
1414 }
1415
1416 pub fn has_text_selection(&self) -> bool {
1417 self.data().text_selection().is_some()
1418 }
1419
1420 pub fn text_selection(&self) -> Option<Range<'_>> {
1421 self.data().text_selection().map(|selection| {
1422 let anchor = InnerPosition::clamped_upgrade(self.tree_state, selection.anchor).unwrap();
1423 let focus = InnerPosition::clamped_upgrade(self.tree_state, selection.focus).unwrap();
1424 Range::new(*self, anchor, focus)
1425 })
1426 }
1427
1428 pub fn text_selection_anchor(&self) -> Option<Position<'_>> {
1429 self.data().text_selection().map(|selection| {
1430 let anchor = InnerPosition::clamped_upgrade(self.tree_state, selection.anchor).unwrap();
1431 Position {
1432 root_node: *self,
1433 inner: anchor,
1434 }
1435 })
1436 }
1437
1438 pub fn text_selection_focus(&self) -> Option<Position<'_>> {
1439 self.data().text_selection().map(|selection| {
1440 let focus = InnerPosition::clamped_upgrade(self.tree_state, selection.focus).unwrap();
1441 Position {
1442 root_node: *self,
1443 inner: focus,
1444 }
1445 })
1446 }
1447
1448 pub fn text_position_at_point(&self, point: Point) -> Position<'_> {
1451 let id = self.id();
1452 if let Some((node, point)) = self.hit_test(point, &move |node| text_node_filter(id, node)) {
1453 if node.role() == Role::TextRun {
1454 let pos = InnerPosition {
1455 node,
1456 character_index: character_index_at_point(&node, point),
1457 };
1458 return Position {
1459 root_node: *self,
1460 inner: pos,
1461 };
1462 }
1463 }
1464
1465 if let Some(node) = self.text_runs().next() {
1469 if let Some(rect) = node.bounding_box_in_coordinate_space(self) {
1470 let origin = rect.origin();
1471 if point.x < origin.x || point.y < origin.y {
1472 return self.document_start();
1473 }
1474 }
1475 }
1476
1477 for node in self.text_runs().rev() {
1478 if let Some(rect) = node.bounding_box_in_coordinate_space(self) {
1479 if let Some(direction) = node.text_direction() {
1480 let is_past_end = match direction {
1481 TextDirection::LeftToRight => {
1482 point.y >= rect.y0 && point.y < rect.y1 && point.x >= rect.x1
1483 }
1484 TextDirection::RightToLeft => {
1485 point.y >= rect.y0 && point.y < rect.y1 && point.x < rect.x0
1486 }
1487 TextDirection::TopToBottom => {
1491 point.x >= rect.x0 && point.x < rect.x1 && point.y >= rect.y1
1492 }
1493 TextDirection::BottomToTop => {
1494 point.x >= rect.x0 && point.x < rect.x1 && point.y < rect.y0
1495 }
1496 };
1497 if is_past_end {
1498 return Position {
1499 root_node: *self,
1500 inner: InnerPosition {
1501 node,
1502 character_index: node.data().character_lengths().len(),
1503 },
1504 };
1505 }
1506 }
1507 }
1508 }
1509
1510 self.document_end()
1511 }
1512
1513 pub fn line_range_from_index(&self, line_index: usize) -> Option<Range<'_>> {
1514 let mut pos = self.document_start();
1515
1516 if line_index > 0 {
1517 if pos.is_document_end() || pos.forward_to_line_end().is_document_end() {
1518 return None;
1519 }
1520 for _ in 0..line_index {
1521 if pos.is_document_end() {
1522 return None;
1523 }
1524 pos = pos.forward_to_line_start();
1525 }
1526 }
1527
1528 let end = if pos.is_document_end() {
1529 pos
1530 } else {
1531 pos.forward_to_line_end()
1532 };
1533 Some(Range::new(*self, pos.inner, end.inner))
1534 }
1535
1536 pub fn text_position_from_global_usv_index(&self, index: usize) -> Option<Position<'_>> {
1537 let mut total_length = 0usize;
1538 for node in self.text_runs() {
1539 let node_text = node.data().value().unwrap();
1540 let node_text_length = node_text.chars().count();
1541 let new_total_length = total_length + node_text_length;
1542 if index >= total_length && index < new_total_length {
1543 let index = index - total_length;
1544 let mut utf8_length = 0usize;
1545 let mut usv_length = 0usize;
1546 for (character_index, utf8_char_length) in
1547 node.data().character_lengths().iter().enumerate()
1548 {
1549 let new_utf8_length = utf8_length + (*utf8_char_length as usize);
1550 let char_str = &node_text[utf8_length..new_utf8_length];
1551 let usv_char_length = char_str.chars().count();
1552 let new_usv_length = usv_length + usv_char_length;
1553 if index >= usv_length && index < new_usv_length {
1554 return Some(Position {
1555 root_node: *self,
1556 inner: InnerPosition {
1557 node,
1558 character_index,
1559 },
1560 });
1561 }
1562 utf8_length = new_utf8_length;
1563 usv_length = new_usv_length;
1564 }
1565 panic!("index out of range");
1566 }
1567 total_length = new_total_length;
1568 }
1569 if index == total_length {
1570 return Some(self.document_end());
1571 }
1572 None
1573 }
1574
1575 pub fn text_position_from_global_utf16_index(&self, index: usize) -> Option<Position<'_>> {
1576 let mut total_length = 0usize;
1577 for node in self.text_runs() {
1578 let node_text = node.data().value().unwrap();
1579 let node_text_length = node_text.chars().map(char::len_utf16).sum::<usize>();
1580 let new_total_length = total_length + node_text_length;
1581 if index >= total_length && index < new_total_length {
1582 let index = index - total_length;
1583 let mut utf8_length = 0usize;
1584 let mut utf16_length = 0usize;
1585 for (character_index, utf8_char_length) in
1586 node.data().character_lengths().iter().enumerate()
1587 {
1588 let new_utf8_length = utf8_length + (*utf8_char_length as usize);
1589 let char_str = &node_text[utf8_length..new_utf8_length];
1590 let utf16_char_length = char_str.chars().map(char::len_utf16).sum::<usize>();
1591 let new_utf16_length = utf16_length + utf16_char_length;
1592 if index >= utf16_length && index < new_utf16_length {
1593 return Some(Position {
1594 root_node: *self,
1595 inner: InnerPosition {
1596 node,
1597 character_index,
1598 },
1599 });
1600 }
1601 utf8_length = new_utf8_length;
1602 utf16_length = new_utf16_length;
1603 }
1604 panic!("index out of range");
1605 }
1606 total_length = new_total_length;
1607 }
1608 if index == total_length {
1609 return Some(self.document_end());
1610 }
1611 None
1612 }
1613}
1614
1615#[cfg(test)]
1616mod tests {
1617 use accesskit::{NodeId, Point, Rect, TextDecoration, TextSelection};
1618 use alloc::vec;
1619
1620 fn main_multiline_tree(selection: Option<TextSelection>) -> crate::Tree {
1623 use accesskit::{Action, Affine, Node, Role, TextDirection, Tree, TreeUpdate};
1624
1625 let update = TreeUpdate {
1626 nodes: vec![
1627 (NodeId(0), {
1628 let mut node = Node::new(Role::Window);
1629 node.set_transform(Affine::scale(1.5));
1630 node.set_children(vec![NodeId(1)]);
1631 node
1632 }),
1633 (NodeId(1), {
1634 let mut node = Node::new(Role::MultilineTextInput);
1635 node.set_bounds(Rect {
1636 x0: 8.0,
1637 y0: 31.666664123535156,
1638 x1: 296.0,
1639 y1: 123.66666412353516,
1640 });
1641 node.set_children(vec![
1642 NodeId(2),
1643 NodeId(3),
1644 NodeId(4),
1645 NodeId(5),
1646 NodeId(6),
1647 NodeId(7),
1648 NodeId(8),
1649 NodeId(9),
1650 ]);
1651 node.add_action(Action::Focus);
1652 node.set_text_direction(TextDirection::LeftToRight);
1653 if let Some(selection) = selection {
1654 node.set_text_selection(selection);
1655 }
1656 node
1657 }),
1658 (NodeId(2), {
1659 let mut node = Node::new(Role::TextRun);
1660 node.set_bounds(Rect {
1661 x0: 12.0,
1662 y0: 33.666664123535156,
1663 x1: 290.9189147949219,
1664 y1: 48.33333206176758,
1665 });
1666 node.set_value("This paragraph is\u{a0}long enough to wrap ");
1671 node.set_character_lengths([
1672 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,
1673 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1674 ]);
1675 node.set_character_positions([
1676 0.0, 7.3333335, 14.666667, 22.0, 29.333334, 36.666668, 44.0, 51.333332,
1677 58.666668, 66.0, 73.333336, 80.666664, 88.0, 95.333336, 102.666664, 110.0,
1678 117.333336, 124.666664, 132.0, 139.33333, 146.66667, 154.0, 161.33333,
1679 168.66667, 176.0, 183.33333, 190.66667, 198.0, 205.33333, 212.66667, 220.0,
1680 227.33333, 234.66667, 242.0, 249.33333, 256.66666, 264.0, 271.33334,
1681 ]);
1682 node.set_character_widths([
1683 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557,
1684 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557,
1685 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557,
1686 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557,
1687 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557,
1688 ]);
1689 node.set_word_starts([5, 15, 18, 23, 30, 33]);
1690 node
1691 }),
1692 (NodeId(3), {
1693 let mut node = Node::new(Role::TextRun);
1694 node.set_bounds(Rect {
1695 x0: 12.0,
1696 y0: 48.33333206176758,
1697 x1: 34.252257,
1698 y1: 63.0,
1699 });
1700 node.set_value("to ");
1701 node.set_character_lengths([1, 1, 1]);
1702 node.set_character_positions([0.0, 7.3333435, 14.666687]);
1703 node.set_character_widths([7.58557, 7.58557, 7.58557]);
1704 node.set_word_starts([0]);
1705 node.set_next_on_line(NodeId(4));
1706 node
1707 }),
1708 (NodeId(4), {
1709 let mut node = Node::new(Role::TextRun);
1710 node.set_bounds(Rect {
1711 x0: 34.0,
1712 y0: 48.33333206176758,
1713 x1: 85.58557,
1714 y1: 63.0,
1715 });
1716 node.set_value("another");
1717 node.set_character_lengths([1, 1, 1, 1, 1, 1, 1]);
1718 node.set_character_positions([
1719 0.0, 7.333344, 14.666687, 22.0, 29.333344, 36.666687, 44.0,
1720 ]);
1721 node.set_character_widths([
1722 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557,
1723 ]);
1724 node.set_word_starts([0]);
1725 node.set_underline(TextDecoration::Solid);
1726 node.set_previous_on_line(NodeId(3));
1727 node.set_next_on_line(NodeId(5));
1728 node
1729 }),
1730 (NodeId(5), {
1731 let mut node = Node::new(Role::TextRun);
1732 node.set_bounds(Rect {
1733 x0: 85.33334,
1734 y0: 48.33333206176758,
1735 x1: 129.5855712890625,
1736 y1: 63.0,
1737 });
1738 node.set_value(" line.\n");
1739 node.set_character_lengths([1, 1, 1, 1, 1, 1, 1]);
1740 node.set_character_positions([
1741 0.0, 7.333344, 14.666687, 22.0, 29.333344, 36.666687, 44.25226,
1742 ]);
1743 node.set_character_widths([
1744 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 0.0,
1745 ]);
1746 node.set_word_starts([1]);
1747 node.set_previous_on_line(NodeId(4));
1748 node
1749 }),
1750 (NodeId(6), {
1751 let mut node = Node::new(Role::TextRun);
1752 node.set_bounds(Rect {
1753 x0: 12.0,
1754 y0: 63.0,
1755 x1: 144.25222778320313,
1756 y1: 77.66666412353516,
1757 });
1758 node.set_value("Another paragraph.\n");
1759 node.set_character_lengths([
1760 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1761 ]);
1762 node.set_character_positions([
1763 0.0, 7.3333335, 14.666667, 22.0, 29.333334, 36.666668, 44.0, 51.333332,
1764 58.666668, 66.0, 73.333336, 80.666664, 88.0, 95.333336, 102.666664, 110.0,
1765 117.333336, 124.666664, 132.25223,
1766 ]);
1767 node.set_character_widths([
1768 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557,
1769 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557,
1770 7.58557, 7.58557, 0.0,
1771 ]);
1772 node.set_word_starts([8]);
1773 node
1774 }),
1775 (NodeId(7), {
1776 let mut node = Node::new(Role::TextRun);
1777 node.set_bounds(Rect {
1778 x0: 12.0,
1779 y0: 77.66666412353516,
1780 x1: 12.0,
1781 y1: 92.33332824707031,
1782 });
1783 node.set_value("\n");
1784 node.set_character_lengths([1]);
1785 node.set_character_positions([0.0]);
1786 node.set_character_widths([0.0]);
1787 node
1788 }),
1789 (NodeId(8), {
1790 let mut node = Node::new(Role::TextRun);
1791 node.set_bounds(Rect {
1792 x0: 12.0,
1793 y0: 92.33332824707031,
1794 x1: 158.9188995361328,
1795 y1: 107.0,
1796 });
1797 node.set_value("Last non-blank line\u{1f44d}\u{1f3fb}\n");
1802 node.set_character_lengths([
1803 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 8, 1,
1804 ]);
1805 node.set_character_positions([
1806 0.0, 7.3333335, 14.666667, 22.0, 29.333334, 36.666668, 44.0, 51.333332,
1807 58.666668, 66.0, 73.333336, 80.666664, 88.0, 95.333336, 102.666664, 110.0,
1808 117.333336, 124.666664, 132.0, 139.33333, 146.9189,
1809 ]);
1810 node.set_character_widths([
1811 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557,
1812 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557, 7.58557,
1813 7.58557, 7.58557, 7.58557, 7.58557, 0.0,
1814 ]);
1815 node.set_word_starts([5, 9, 15]);
1816 node
1817 }),
1818 (NodeId(9), {
1819 let mut node = Node::new(Role::TextRun);
1820 node.set_bounds(Rect {
1821 x0: 12.0,
1822 y0: 107.0,
1823 x1: 12.0,
1824 y1: 121.66666412353516,
1825 });
1826 node.set_value("");
1827 node.set_character_lengths([]);
1828 node.set_character_positions([]);
1829 node.set_character_widths([]);
1830 node
1831 }),
1832 ],
1833 tree: Some(Tree::new(NodeId(0))),
1834 focus: NodeId(1),
1835 };
1836
1837 crate::Tree::new(update, true)
1838 }
1839
1840 fn multiline_end_selection() -> TextSelection {
1841 use accesskit::TextPosition;
1842
1843 TextSelection {
1844 anchor: TextPosition {
1845 node: NodeId(9),
1846 character_index: 0,
1847 },
1848 focus: TextPosition {
1849 node: NodeId(9),
1850 character_index: 0,
1851 },
1852 }
1853 }
1854
1855 fn multiline_past_end_selection() -> TextSelection {
1856 use accesskit::TextPosition;
1857
1858 TextSelection {
1859 anchor: TextPosition {
1860 node: NodeId(9),
1861 character_index: 3,
1862 },
1863 focus: TextPosition {
1864 node: NodeId(9),
1865 character_index: 3,
1866 },
1867 }
1868 }
1869
1870 fn multiline_wrapped_line_end_selection() -> TextSelection {
1871 use accesskit::TextPosition;
1872
1873 TextSelection {
1874 anchor: TextPosition {
1875 node: NodeId(2),
1876 character_index: 38,
1877 },
1878 focus: TextPosition {
1879 node: NodeId(2),
1880 character_index: 38,
1881 },
1882 }
1883 }
1884
1885 fn multiline_first_line_middle_selection() -> TextSelection {
1886 use accesskit::TextPosition;
1887
1888 TextSelection {
1889 anchor: TextPosition {
1890 node: NodeId(2),
1891 character_index: 5,
1892 },
1893 focus: TextPosition {
1894 node: NodeId(2),
1895 character_index: 5,
1896 },
1897 }
1898 }
1899
1900 fn multiline_second_line_middle_selection() -> TextSelection {
1901 use accesskit::TextPosition;
1902
1903 TextSelection {
1904 anchor: TextPosition {
1905 node: NodeId(4),
1906 character_index: 3,
1907 },
1908 focus: TextPosition {
1909 node: NodeId(4),
1910 character_index: 3,
1911 },
1912 }
1913 }
1914
1915 #[test]
1916 fn supports_text_ranges() {
1917 let tree = main_multiline_tree(None);
1918 let state = tree.state();
1919 assert!(!state.node_by_id(NodeId(0)).unwrap().supports_text_ranges());
1920 assert!(state.node_by_id(NodeId(1)).unwrap().supports_text_ranges());
1921 }
1922
1923 #[test]
1924 fn multiline_document_range() {
1925 let tree = main_multiline_tree(None);
1926 let state = tree.state();
1927 let node = state.node_by_id(NodeId(1)).unwrap();
1928 let range = node.document_range();
1929 let start = range.start();
1930 assert!(start.is_word_start());
1931 assert!(start.is_line_start());
1932 assert!(!start.is_line_end());
1933 assert!(start.is_paragraph_start());
1934 assert!(start.is_document_start());
1935 assert!(!start.is_document_end());
1936 let end = range.end();
1937 assert!(start < end);
1938 assert!(end.is_word_start());
1939 assert!(end.is_line_start());
1940 assert!(end.is_line_end());
1941 assert!(end.is_paragraph_start());
1942 assert!(!end.is_document_start());
1943 assert!(end.is_document_end());
1944 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");
1945 assert_eq!(
1946 range.bounding_boxes(),
1947 vec![
1948 Rect {
1949 x0: 18.0,
1950 y0: 50.499996185302734,
1951 x1: 436.3783721923828,
1952 y1: 72.49999809265137
1953 },
1954 Rect {
1955 x0: 18.0,
1956 y0: 72.49999809265137,
1957 x1: 51.3783855,
1958 y1: 94.5
1959 },
1960 Rect {
1961 x0: 51.0,
1962 y0: 72.49999809265137,
1963 x1: 128.378355,
1964 y1: 94.5
1965 },
1966 Rect {
1967 x0: 128.00001,
1968 y0: 72.49999809265137,
1969 x1: 194.37835693359375,
1970 y1: 94.5
1971 },
1972 Rect {
1973 x0: 18.0,
1974 y0: 94.5,
1975 x1: 216.3783416748047,
1976 y1: 116.49999618530273
1977 },
1978 Rect {
1979 x0: 18.0,
1980 y0: 116.49999618530273,
1981 x1: 18.0,
1982 y1: 138.49999237060547
1983 },
1984 Rect {
1985 x0: 18.0,
1986 y0: 138.49999237060547,
1987 x1: 238.37834930419922,
1988 y1: 160.5
1989 }
1990 ]
1991 );
1992 }
1993
1994 #[test]
1995 fn multiline_document_range_to_first_format_change() {
1996 let tree = main_multiline_tree(None);
1997 let state = tree.state();
1998 let node = state.node_by_id(NodeId(1)).unwrap();
1999 let mut range = node.document_range();
2000 range.set_end(range.start().forward_to_format_end());
2001 assert_eq!(
2002 range.text(),
2003 "This paragraph is\u{a0}long enough to wrap to "
2004 );
2005 assert_eq!(
2006 range.bounding_boxes(),
2007 vec![
2008 Rect {
2009 x0: 18.0,
2010 y0: 50.499996185302734,
2011 x1: 436.3783721923828,
2012 y1: 72.49999809265137
2013 },
2014 Rect {
2015 x0: 18.0,
2016 y0: 72.49999809265137,
2017 x1: 51.3783855,
2018 y1: 94.5
2019 }
2020 ]
2021 );
2022 }
2023
2024 #[test]
2025 fn multiline_document_range_from_last_format_change() {
2026 let tree = main_multiline_tree(None);
2027 let state = tree.state();
2028 let node = state.node_by_id(NodeId(1)).unwrap();
2029 let mut range = node.document_range();
2030 range.set_start(range.end().backward_to_format_start());
2031 assert_eq!(
2032 range.text(),
2033 " line.\nAnother paragraph.\n\nLast non-blank line\u{1f44d}\u{1f3fb}\n"
2034 );
2035 assert_eq!(
2036 range.bounding_boxes(),
2037 vec![
2038 Rect {
2039 x0: 128.00001,
2040 y0: 72.49999809265137,
2041 x1: 194.37835693359375,
2042 y1: 94.5
2043 },
2044 Rect {
2045 x0: 18.0,
2046 y0: 94.5,
2047 x1: 216.3783416748047,
2048 y1: 116.49999618530273
2049 },
2050 Rect {
2051 x0: 18.0,
2052 y0: 116.49999618530273,
2053 x1: 18.0,
2054 y1: 138.49999237060547
2055 },
2056 Rect {
2057 x0: 18.0,
2058 y0: 138.49999237060547,
2059 x1: 238.37834930419922,
2060 y1: 160.5
2061 }
2062 ]
2063 );
2064 }
2065
2066 #[test]
2067 fn multiline_end_degenerate_range() {
2068 let tree = main_multiline_tree(Some(multiline_end_selection()));
2069 let state = tree.state();
2070 let node = state.node_by_id(NodeId(1)).unwrap();
2071 let range = node.text_selection().unwrap();
2072 assert!(range.is_degenerate());
2073 let pos = range.start();
2074 assert!(pos.is_word_start());
2075 assert!(pos.is_line_start());
2076 assert!(pos.is_line_end());
2077 assert!(pos.is_paragraph_start());
2078 assert!(!pos.is_document_start());
2079 assert!(pos.is_document_end());
2080 assert_eq!(range.text(), "");
2081 assert_eq!(
2082 range.bounding_boxes(),
2083 vec![Rect {
2084 x0: 18.0,
2085 y0: 160.5,
2086 x1: 18.0,
2087 y1: 182.49999618530273,
2088 }]
2089 );
2090 }
2091
2092 #[test]
2093 fn multiline_wrapped_line_end_range() {
2094 let tree = main_multiline_tree(Some(multiline_wrapped_line_end_selection()));
2095 let state = tree.state();
2096 let node = state.node_by_id(NodeId(1)).unwrap();
2097 let range = node.text_selection().unwrap();
2098 assert!(range.is_degenerate());
2099 let pos = range.start();
2100 assert!(!pos.is_word_start());
2101 assert!(!pos.is_line_start());
2102 assert!(pos.is_line_end());
2103 assert!(!pos.is_paragraph_start());
2104 assert!(!pos.is_document_start());
2105 assert!(!pos.is_document_end());
2106 assert_eq!(range.text(), "");
2107 assert_eq!(
2108 range.bounding_boxes(),
2109 vec![Rect {
2110 x0: 436.3783721923828,
2111 y0: 50.499996185302734,
2112 x1: 436.3783721923828,
2113 y1: 72.49999809265137
2114 }]
2115 );
2116 let char_end_pos = pos.forward_to_character_end();
2117 let mut line_start_range = range;
2118 line_start_range.set_end(char_end_pos);
2119 assert!(!line_start_range.is_degenerate());
2120 assert!(line_start_range.start().is_line_start());
2121 assert_eq!(line_start_range.text(), "t");
2122 assert_eq!(
2123 line_start_range.bounding_boxes(),
2124 vec![Rect {
2125 x0: 18.0,
2126 y0: 72.49999809265137,
2127 x1: 29.378354787826538,
2128 y1: 94.5
2129 }]
2130 );
2131 let prev_char_pos = pos.backward_to_character_start();
2132 let mut prev_char_range = range;
2133 prev_char_range.set_start(prev_char_pos);
2134 assert!(!prev_char_range.is_degenerate());
2135 assert!(prev_char_range.end().is_line_end());
2136 assert_eq!(prev_char_range.text(), " ");
2137 assert_eq!(
2138 prev_char_range.bounding_boxes(),
2139 vec![Rect {
2140 x0: 425.00001525878906,
2141 y0: 50.499996185302734,
2142 x1: 436.3783721923828,
2143 y1: 72.49999809265137
2144 }]
2145 );
2146 assert!(prev_char_pos.forward_to_character_end().is_line_end());
2147 assert!(prev_char_pos.forward_to_word_end().is_line_end());
2148 assert!(prev_char_pos.forward_to_line_end().is_line_end());
2149 assert!(prev_char_pos.forward_to_character_start().is_line_start());
2150 assert!(prev_char_pos.forward_to_word_start().is_line_start());
2151 assert!(prev_char_pos.forward_to_line_start().is_line_start());
2152 }
2153
2154 #[test]
2155 fn multiline_find_line_ends_from_middle() {
2156 let tree = main_multiline_tree(Some(multiline_second_line_middle_selection()));
2157 let state = tree.state();
2158 let node = state.node_by_id(NodeId(1)).unwrap();
2159 let mut range = node.text_selection().unwrap();
2160 assert!(range.is_degenerate());
2161 let pos = range.start();
2162 assert!(!pos.is_line_start());
2163 assert!(!pos.is_line_end());
2164 assert!(!pos.is_document_start());
2165 assert!(!pos.is_document_end());
2166 let line_start = pos.backward_to_line_start();
2167 range.set_start(line_start);
2168 let line_end = line_start.forward_to_line_end();
2169 range.set_end(line_end);
2170 assert!(!range.is_degenerate());
2171 assert!(range.start().is_line_start());
2172 assert!(range.end().is_line_end());
2173 assert_eq!(range.text(), "to another line.\n");
2174 assert_eq!(
2175 range.bounding_boxes(),
2176 vec![
2177 Rect {
2178 x0: 18.0,
2179 y0: 72.49999809265137,
2180 x1: 51.3783855,
2181 y1: 94.5
2182 },
2183 Rect {
2184 x0: 51.0,
2185 y0: 72.49999809265137,
2186 x1: 128.378355,
2187 y1: 94.5
2188 },
2189 Rect {
2190 x0: 128.00001,
2191 y0: 72.49999809265137,
2192 x1: 194.37835693359375,
2193 y1: 94.5
2194 },
2195 ]
2196 );
2197 assert!(line_start.forward_to_line_start().is_line_start());
2198 }
2199
2200 #[test]
2201 fn multiline_find_wrapped_line_ends_from_middle() {
2202 let tree = main_multiline_tree(Some(multiline_first_line_middle_selection()));
2203 let state = tree.state();
2204 let node = state.node_by_id(NodeId(1)).unwrap();
2205 let mut range = node.text_selection().unwrap();
2206 assert!(range.is_degenerate());
2207 let pos = range.start();
2208 assert!(!pos.is_line_start());
2209 assert!(!pos.is_line_end());
2210 assert!(!pos.is_document_start());
2211 assert!(!pos.is_document_end());
2212 let line_start = pos.backward_to_line_start();
2213 range.set_start(line_start);
2214 let line_end = line_start.forward_to_line_end();
2215 range.set_end(line_end);
2216 assert!(!range.is_degenerate());
2217 assert!(range.start().is_line_start());
2218 assert!(range.end().is_line_end());
2219 assert_eq!(range.text(), "This paragraph is\u{a0}long enough to wrap ");
2220 assert_eq!(
2221 range.bounding_boxes(),
2222 vec![Rect {
2223 x0: 18.0,
2224 y0: 50.499996185302734,
2225 x1: 436.3783721923828,
2226 y1: 72.49999809265137
2227 }]
2228 );
2229 assert!(line_start.forward_to_line_start().is_line_start());
2230 }
2231
2232 #[test]
2233 fn multiline_find_paragraph_ends_from_middle() {
2234 let tree = main_multiline_tree(Some(multiline_second_line_middle_selection()));
2235 let state = tree.state();
2236 let node = state.node_by_id(NodeId(1)).unwrap();
2237 let mut range = node.text_selection().unwrap();
2238 assert!(range.is_degenerate());
2239 let pos = range.start();
2240 assert!(!pos.is_paragraph_start());
2241 assert!(!pos.is_document_start());
2242 assert!(!pos.is_document_end());
2243 let paragraph_start = pos.backward_to_paragraph_start();
2244 range.set_start(paragraph_start);
2245 let paragraph_end = paragraph_start.forward_to_paragraph_end();
2246 range.set_end(paragraph_end);
2247 assert!(!range.is_degenerate());
2248 assert!(range.start().is_paragraph_start());
2249 assert!(range.end().is_paragraph_end());
2250 assert_eq!(
2251 range.text(),
2252 "This paragraph is\u{a0}long enough to wrap to another line.\n"
2253 );
2254 assert_eq!(
2255 range.bounding_boxes(),
2256 vec![
2257 Rect {
2258 x0: 18.0,
2259 y0: 50.499996185302734,
2260 x1: 436.3783721923828,
2261 y1: 72.49999809265137
2262 },
2263 Rect {
2264 x0: 18.0,
2265 y0: 72.49999809265137,
2266 x1: 51.3783855,
2267 y1: 94.5
2268 },
2269 Rect {
2270 x0: 51.0,
2271 y0: 72.49999809265137,
2272 x1: 128.378355,
2273 y1: 94.5
2274 },
2275 Rect {
2276 x0: 128.00001,
2277 y0: 72.49999809265137,
2278 x1: 194.37835693359375,
2279 y1: 94.5
2280 },
2281 ]
2282 );
2283 assert!(paragraph_start
2284 .forward_to_paragraph_start()
2285 .is_paragraph_start());
2286 }
2287
2288 #[test]
2289 fn multiline_find_format_ends_from_middle() {
2290 let tree = main_multiline_tree(Some(multiline_second_line_middle_selection()));
2291 let state = tree.state();
2292 let node = state.node_by_id(NodeId(1)).unwrap();
2293 let mut range = node.text_selection().unwrap();
2294 assert!(range.is_degenerate());
2295 let pos = range.start();
2296 assert!(!pos.is_format_start());
2297 assert!(!pos.is_document_start());
2298 assert!(!pos.is_document_end());
2299 let format_start = pos.backward_to_format_start();
2300 range.set_start(format_start);
2301 let format_end = pos.forward_to_format_end();
2302 range.set_end(format_end);
2303 assert!(!range.is_degenerate());
2304 assert_eq!(range.text(), "another");
2305 assert_eq!(
2306 range.bounding_boxes(),
2307 vec![Rect {
2308 x0: 51.0,
2309 y0: 72.49999809265137,
2310 x1: 128.378355,
2311 y1: 94.5
2312 }]
2313 );
2314 }
2315
2316 #[test]
2317 fn multiline_find_word_ends_from_middle() {
2318 let tree = main_multiline_tree(Some(multiline_second_line_middle_selection()));
2319 let state = tree.state();
2320 let node = state.node_by_id(NodeId(1)).unwrap();
2321 let mut range = node.text_selection().unwrap();
2322 assert!(range.is_degenerate());
2323 let pos = range.start();
2324 assert!(!pos.is_word_start());
2325 assert!(!pos.is_document_start());
2326 assert!(!pos.is_document_end());
2327 let word_start = pos.backward_to_word_start();
2328 range.set_start(word_start);
2329 let word_end = word_start.forward_to_word_end();
2330 let word_end2 = pos.forward_to_word_end();
2331 assert_eq!(word_end, word_end2);
2332 let word_start2 = word_end.backward_to_word_start();
2333 assert_eq!(word_start, word_start2);
2334 range.set_end(word_end);
2335 assert!(!range.is_degenerate());
2336 assert_eq!(range.text(), "another ");
2337 assert_eq!(
2338 range.bounding_boxes(),
2339 [
2340 Rect {
2341 x0: 51.0,
2342 y0: 72.49999809265137,
2343 x1: 128.378355,
2344 y1: 94.5
2345 },
2346 Rect {
2347 x0: 128.00001,
2348 y0: 72.49999809265137,
2349 x1: 139.37836478782654,
2350 y1: 94.5
2351 }
2352 ]
2353 );
2354 }
2355
2356 #[test]
2357 fn text_position_at_point() {
2358 let tree = main_multiline_tree(None);
2359 let state = tree.state();
2360 let node = state.node_by_id(NodeId(1)).unwrap();
2361
2362 {
2363 let pos = node.text_position_at_point(Point::new(8.0, 31.666664123535156));
2364 assert!(pos.is_document_start());
2365 }
2366
2367 {
2368 let pos = node.text_position_at_point(Point::new(12.0, 33.666664123535156));
2369 assert!(pos.is_document_start());
2370 }
2371
2372 {
2373 let pos = node.text_position_at_point(Point::new(16.0, 40.0));
2374 assert!(pos.is_document_start());
2375 }
2376
2377 {
2378 let pos = node.text_position_at_point(Point::new(144.0, 40.0));
2379 assert!(!pos.is_document_start());
2380 assert!(!pos.is_document_end());
2381 assert!(!pos.is_line_end());
2382 let mut range = pos.to_degenerate_range();
2383 range.set_end(pos.forward_to_character_end());
2384 assert_eq!(range.text(), "l");
2385 }
2386
2387 {
2388 let pos = node.text_position_at_point(Point::new(150.0, 40.0));
2389 assert!(!pos.is_document_start());
2390 assert!(!pos.is_document_end());
2391 assert!(!pos.is_line_end());
2392 let mut range = pos.to_degenerate_range();
2393 range.set_end(pos.forward_to_character_end());
2394 assert_eq!(range.text(), "l");
2395 }
2396
2397 {
2398 let pos = node.text_position_at_point(Point::new(291.0, 40.0));
2399 assert!(!pos.is_document_start());
2400 assert!(!pos.is_document_end());
2401 assert!(pos.is_line_end());
2402 let mut range = pos.to_degenerate_range();
2403 range.set_start(pos.backward_to_word_start());
2404 assert_eq!(range.text(), "wrap ");
2405 }
2406
2407 {
2408 let pos = node.text_position_at_point(Point::new(12.0, 50.0));
2409 assert!(!pos.is_document_start());
2410 assert!(pos.is_line_start());
2411 assert!(!pos.is_paragraph_start());
2412 let mut range = pos.to_degenerate_range();
2413 range.set_end(pos.forward_to_word_end());
2414 assert_eq!(range.text(), "to ");
2415 }
2416
2417 {
2418 let pos = node.text_position_at_point(Point::new(130.0, 50.0));
2419 assert!(!pos.is_document_start());
2420 assert!(!pos.is_document_end());
2421 assert!(pos.is_line_end());
2422 let mut range = pos.to_degenerate_range();
2423 range.set_start(pos.backward_to_word_start());
2424 assert_eq!(range.text(), "line.\n");
2425 }
2426
2427 {
2428 let pos = node.text_position_at_point(Point::new(12.0, 80.0));
2429 assert!(!pos.is_document_start());
2430 assert!(!pos.is_document_end());
2431 assert!(pos.is_line_end());
2432 let mut range = pos.to_degenerate_range();
2433 range.set_start(pos.backward_to_line_start());
2434 assert_eq!(range.text(), "\n");
2435 }
2436
2437 {
2438 let pos = node.text_position_at_point(Point::new(12.0, 120.0));
2439 assert!(pos.is_document_end());
2440 }
2441
2442 {
2443 let pos = node.text_position_at_point(Point::new(250.0, 122.0));
2444 assert!(pos.is_document_end());
2445 }
2446 }
2447
2448 #[test]
2449 fn to_global_usv_index() {
2450 let tree = main_multiline_tree(None);
2451 let state = tree.state();
2452 let node = state.node_by_id(NodeId(1)).unwrap();
2453
2454 {
2455 let range = node.document_range();
2456 assert_eq!(range.start().to_global_usv_index(), 0);
2457 assert_eq!(range.end().to_global_usv_index(), 97);
2458 }
2459
2460 {
2461 let range = node.document_range();
2462 let pos = range.start().forward_to_line_end();
2463 assert_eq!(pos.to_global_usv_index(), 38);
2464 let pos = range.start().forward_to_line_start();
2465 assert_eq!(pos.to_global_usv_index(), 38);
2466 let pos = pos.forward_to_character_start();
2467 assert_eq!(pos.to_global_usv_index(), 39);
2468 let pos = pos.forward_to_line_start();
2469 assert_eq!(pos.to_global_usv_index(), 55);
2470 }
2471 }
2472
2473 #[test]
2474 fn to_global_utf16_index() {
2475 let tree = main_multiline_tree(None);
2476 let state = tree.state();
2477 let node = state.node_by_id(NodeId(1)).unwrap();
2478
2479 {
2480 let range = node.document_range();
2481 assert_eq!(range.start().to_global_utf16_index(), 0);
2482 assert_eq!(range.end().to_global_utf16_index(), 99);
2483 }
2484
2485 {
2486 let range = node.document_range();
2487 let pos = range.start().forward_to_line_end();
2488 assert_eq!(pos.to_global_utf16_index(), 38);
2489 let pos = range.start().forward_to_line_start();
2490 assert_eq!(pos.to_global_utf16_index(), 38);
2491 let pos = pos.forward_to_character_start();
2492 assert_eq!(pos.to_global_utf16_index(), 39);
2493 let pos = pos.forward_to_line_start();
2494 assert_eq!(pos.to_global_utf16_index(), 55);
2495 }
2496 }
2497
2498 #[test]
2499 fn to_line_index() {
2500 let tree = main_multiline_tree(None);
2501 let state = tree.state();
2502 let node = state.node_by_id(NodeId(1)).unwrap();
2503
2504 {
2505 let range = node.document_range();
2506 assert_eq!(range.start().to_line_index(), 0);
2507 assert_eq!(range.end().to_line_index(), 5);
2508 }
2509
2510 {
2511 let range = node.document_range();
2512 let pos = range.start().forward_to_line_end();
2513 assert_eq!(pos.to_line_index(), 0);
2514 let pos = range.start().forward_to_line_start();
2515 assert_eq!(pos.to_line_index(), 1);
2516 let pos = pos.forward_to_character_start();
2517 assert_eq!(pos.to_line_index(), 1);
2518 assert_eq!(pos.forward_to_line_end().to_line_index(), 1);
2519 let pos = pos.forward_to_line_start();
2520 assert_eq!(pos.to_line_index(), 2);
2521 }
2522 }
2523
2524 #[test]
2525 fn line_range_from_index() {
2526 let tree = main_multiline_tree(None);
2527 let state = tree.state();
2528 let node = state.node_by_id(NodeId(1)).unwrap();
2529
2530 {
2531 let range = node.line_range_from_index(0).unwrap();
2532 assert_eq!(range.text(), "This paragraph is\u{a0}long enough to wrap ");
2533 }
2534
2535 {
2536 let range = node.line_range_from_index(1).unwrap();
2537 assert_eq!(range.text(), "to another line.\n");
2538 }
2539
2540 {
2541 let range = node.line_range_from_index(2).unwrap();
2542 assert_eq!(range.text(), "Another paragraph.\n");
2543 }
2544
2545 {
2546 let range = node.line_range_from_index(3).unwrap();
2547 assert_eq!(range.text(), "\n");
2548 }
2549
2550 {
2551 let range = node.line_range_from_index(4).unwrap();
2552 assert_eq!(range.text(), "Last non-blank line\u{1f44d}\u{1f3fb}\n");
2553 }
2554
2555 {
2556 let range = node.line_range_from_index(5).unwrap();
2557 assert_eq!(range.text(), "");
2558 }
2559
2560 assert!(node.line_range_from_index(6).is_none());
2561 }
2562
2563 #[test]
2564 fn text_position_from_global_usv_index() {
2565 let tree = main_multiline_tree(None);
2566 let state = tree.state();
2567 let node = state.node_by_id(NodeId(1)).unwrap();
2568
2569 {
2570 let pos = node.text_position_from_global_usv_index(0).unwrap();
2571 assert!(pos.is_document_start());
2572 }
2573
2574 {
2575 let pos = node.text_position_from_global_usv_index(17).unwrap();
2576 let mut range = pos.to_degenerate_range();
2577 range.set_end(pos.forward_to_character_end());
2578 assert_eq!(range.text(), "\u{a0}");
2579 }
2580
2581 {
2582 let pos = node.text_position_from_global_usv_index(18).unwrap();
2583 let mut range = pos.to_degenerate_range();
2584 range.set_end(pos.forward_to_character_end());
2585 assert_eq!(range.text(), "l");
2586 }
2587
2588 {
2589 let pos = node.text_position_from_global_usv_index(37).unwrap();
2590 let mut range = pos.to_degenerate_range();
2591 range.set_end(pos.forward_to_character_end());
2592 assert_eq!(range.text(), " ");
2593 }
2594
2595 {
2596 let pos = node.text_position_from_global_usv_index(38).unwrap();
2597 assert!(!pos.is_paragraph_start());
2598 assert!(pos.is_line_start());
2599 let mut range = pos.to_degenerate_range();
2600 range.set_end(pos.forward_to_character_end());
2601 assert_eq!(range.text(), "t");
2602 }
2603
2604 {
2605 let pos = node.text_position_from_global_usv_index(54).unwrap();
2606 let mut range = pos.to_degenerate_range();
2607 range.set_end(pos.forward_to_character_end());
2608 assert_eq!(range.text(), "\n");
2609 }
2610
2611 {
2612 let pos = node.text_position_from_global_usv_index(55).unwrap();
2613 assert!(pos.is_paragraph_start());
2614 assert!(pos.is_line_start());
2615 let mut range = pos.to_degenerate_range();
2616 range.set_end(pos.forward_to_character_end());
2617 assert_eq!(range.text(), "A");
2618 }
2619
2620 for i in 94..=95 {
2621 let pos = node.text_position_from_global_usv_index(i).unwrap();
2622 let mut range = pos.to_degenerate_range();
2623 range.set_end(pos.forward_to_character_end());
2624 assert_eq!(range.text(), "\u{1f44d}\u{1f3fb}");
2625 }
2626
2627 {
2628 let pos = node.text_position_from_global_usv_index(96).unwrap();
2629 let mut range = pos.to_degenerate_range();
2630 range.set_end(pos.forward_to_character_end());
2631 assert_eq!(range.text(), "\n");
2632 }
2633
2634 {
2635 let pos = node.text_position_from_global_usv_index(97).unwrap();
2636 assert!(pos.is_document_end());
2637 }
2638
2639 assert!(node.text_position_from_global_usv_index(98).is_none());
2640 }
2641
2642 #[test]
2643 fn text_position_from_global_utf16_index() {
2644 let tree = main_multiline_tree(None);
2645 let state = tree.state();
2646 let node = state.node_by_id(NodeId(1)).unwrap();
2647
2648 {
2649 let pos = node.text_position_from_global_utf16_index(0).unwrap();
2650 assert!(pos.is_document_start());
2651 }
2652
2653 {
2654 let pos = node.text_position_from_global_utf16_index(17).unwrap();
2655 let mut range = pos.to_degenerate_range();
2656 range.set_end(pos.forward_to_character_end());
2657 assert_eq!(range.text(), "\u{a0}");
2658 }
2659
2660 {
2661 let pos = node.text_position_from_global_utf16_index(18).unwrap();
2662 let mut range = pos.to_degenerate_range();
2663 range.set_end(pos.forward_to_character_end());
2664 assert_eq!(range.text(), "l");
2665 }
2666
2667 {
2668 let pos = node.text_position_from_global_utf16_index(37).unwrap();
2669 let mut range = pos.to_degenerate_range();
2670 range.set_end(pos.forward_to_character_end());
2671 assert_eq!(range.text(), " ");
2672 }
2673
2674 {
2675 let pos = node.text_position_from_global_utf16_index(38).unwrap();
2676 assert!(!pos.is_paragraph_start());
2677 assert!(pos.is_line_start());
2678 let mut range = pos.to_degenerate_range();
2679 range.set_end(pos.forward_to_character_end());
2680 assert_eq!(range.text(), "t");
2681 }
2682
2683 {
2684 let pos = node.text_position_from_global_utf16_index(54).unwrap();
2685 let mut range = pos.to_degenerate_range();
2686 range.set_end(pos.forward_to_character_end());
2687 assert_eq!(range.text(), "\n");
2688 }
2689
2690 {
2691 let pos = node.text_position_from_global_utf16_index(55).unwrap();
2692 assert!(pos.is_paragraph_start());
2693 assert!(pos.is_line_start());
2694 let mut range = pos.to_degenerate_range();
2695 range.set_end(pos.forward_to_character_end());
2696 assert_eq!(range.text(), "A");
2697 }
2698
2699 for i in 94..=97 {
2700 let pos = node.text_position_from_global_utf16_index(i).unwrap();
2701 let mut range = pos.to_degenerate_range();
2702 range.set_end(pos.forward_to_character_end());
2703 assert_eq!(range.text(), "\u{1f44d}\u{1f3fb}");
2704 }
2705
2706 {
2707 let pos = node.text_position_from_global_utf16_index(98).unwrap();
2708 let mut range = pos.to_degenerate_range();
2709 range.set_end(pos.forward_to_character_end());
2710 assert_eq!(range.text(), "\n");
2711 }
2712
2713 {
2714 let pos = node.text_position_from_global_utf16_index(99).unwrap();
2715 assert!(pos.is_document_end());
2716 }
2717
2718 assert!(node.text_position_from_global_utf16_index(100).is_none());
2719 }
2720
2721 #[test]
2722 fn multiline_selection_clamping() {
2723 let tree = main_multiline_tree(Some(multiline_past_end_selection()));
2724 let state = tree.state();
2725 let node = state.node_by_id(NodeId(1)).unwrap();
2726 let _ = node.text_selection().unwrap();
2727 }
2728
2729 #[test]
2730 fn range_property_value_map() {
2731 use super::RangePropertyValue;
2732 assert_eq!(
2733 RangePropertyValue::Single(Some(0)).map(|x| x + 1),
2734 RangePropertyValue::Single(Some(1))
2735 );
2736 assert_eq!(
2737 RangePropertyValue::<Option<usize>>::Single(None).map(|x| x + 1),
2738 RangePropertyValue::Single(None)
2739 );
2740 assert_eq!(
2741 RangePropertyValue::<Option<usize>>::Mixed.map(|x| x + 1),
2742 RangePropertyValue::Mixed
2743 );
2744 }
2745}