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