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