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