accesskit_consumer/
text.rs

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