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