Skip to main content

SourceMap

Struct SourceMap 

Source
pub struct SourceMap { /* private fields */ }
Expand description

The source view’s styling, as non-overlapping runs in ascending order.

Gaps between runs are Role::Body — the map stores only what differs from plain text, so an ordinary prose document is a handful of runs rather than one per byte.

Built by build and cached on the Doc against its revision; a frontend reads it through SourceMap::style_at for a one-shot question, or SourceMap::edges_in when it is already walking lines in order and wants to know where the styling changes.

Implementations§

Source§

impl SourceMap

Source

pub fn runs(&self) -> &[StyledRun]

The styled runs, ascending and non-overlapping. Bytes between them are Style::default.

Examples found in repository?
examples/bench.rs (line 81)
33fn main() {
34    for kb in [10usize, 100, 1000] {
35        let src = body(kb * 1024);
36        println!("=== {} KB ===", src.len() / 1024);
37
38        let mut ed = Editor::new_str(&src, Format::Markdown).unwrap();
39        let nodes = ed.nodes().unwrap();
40        let map = wysiwyg::build(
41            &nodes,
42            &src,
43            None,
44            false,
45            &wysiwyg::Surface::default(),
46            None,
47        );
48        println!("  ({} AST nodes, {} map rows)", nodes.len(), map.rows.len());
49
50        println!("  -- per edit (unavoidable today) --");
51        time("twig edit_range (reparse)", 5, || {
52            ed.edit_range(src.len() / 2, src.len() / 2, "x").is_ok()
53        });
54        // That loop left five `x`s in the editor, and every block below measures
55        // `ed` against `src` — spans from a document five bytes longer than the
56        // string they index. Re-parse so the two are the same document again.
57        //
58        // Untimed on purpose: this is the bench putting its fixture back, not a
59        // cost leaf pays. Skipping it used to end every run in a slice panic
60        // (`push_escaped_text`, walking a span past the end of a shorter source)
61        // and would otherwise have quietly measured a build over a mismatch.
62        let mut ed = Editor::new_str(&src, Format::Markdown).unwrap();
63        time("twig nodes() FFI marshal", 5, || ed.nodes().unwrap().len());
64        time("wysiwyg::build", 5, || {
65            wysiwyg::build(
66                &nodes,
67                &src,
68                None,
69                false,
70                &wysiwyg::Surface::default(),
71                None,
72            )
73            .rows
74            .len()
75        });
76        // The source view's whole per-edit cost, next to the WYSIWYG view's, so
77        // the "cheaper view" claim in `source::build`'s docs is a measured one.
78        // It has no incremental path: this plus the marshal above is what a
79        // keystroke in `View::Source` pays.
80        time("source::build", 5, || {
81            source::build(&nodes, &src).runs().len()
82        });
83        {
84            // The incremental path with a warm cache and nothing changed: the
85            // floor cost the block cache adds even on a pure repaint — hash every
86            // block, clone every reused row, recollect stops. No subtree is
87            // marshalled (every block hits). The real keystroke win shows up in
88            // "Doc::insert + rebuild" below, which re-marshals only the edited
89            // block and reuses the rest.
90            let mut cache = wysiwyg::BlockCache::default();
91            let top = ed.child_spans(None).unwrap();
92            let _ = wysiwyg::build_cached(
93                &top,
94                &src,
95                None,
96                false,
97                &wysiwyg::Surface::default(),
98                None,
99                &mut cache,
100                |id| ed.subtree(twig::NodeId(id)).unwrap_or_default(),
101            );
102            time("wysiwyg::build_cached (all reused)", 5, || {
103                let top = ed.child_spans(None).unwrap();
104                wysiwyg::build_cached(
105                    &top,
106                    &src,
107                    None,
108                    false,
109                    &wysiwyg::Surface::default(),
110                    None,
111                    &mut cache,
112                    |id| ed.subtree(twig::NodeId(id)).unwrap_or_default(),
113                )
114                .rows
115                .len()
116            });
117        }
118
119        println!("  -- claimed hot, actually noise --");
120        time("twig source_str() (full copy)", 5, || {
121            ed.source_str().unwrap().len()
122        });
123        let clean = src.clone();
124        time("dirty compare (full cmp)", 5, || src == clean);
125
126        println!("  -- what the GUI adds on a cache miss --");
127        time("clone every row's glyphs", 5, || {
128            map.rows
129                .iter()
130                .map(|r| r.glyphs.clone())
131                .collect::<Vec<_>>()
132                .len()
133        });
134        time("hash every glyph (cache key?)", 5, || {
135            let mut n = 0u64;
136            for r in &map.rows {
137                let mut h = std::collections::hash_map::DefaultHasher::new();
138                for g in &r.glyphs {
139                    g.ch.hash(&mut h);
140                }
141                n ^= h.finish();
142            }
143            n
144        });
145
146        println!("  -- the whole path, as a frontend calls it --");
147        let mut p = std::env::temp_dir();
148        p.push(format!("leaf_bench_{kb}.md"));
149        std::fs::write(&p, &src).unwrap();
150        let mut d = Doc::open(p).unwrap();
151        d.view = View::Wysiwyg;
152        d.place_caret(src.len() / 2, false);
153        d.build_visual_unwrapped();
154        time("build_visual (cached: a repaint)", 200, || {
155            d.build_visual_unwrapped()
156        });
157        time("Doc::insert + rebuild (a keystroke)", 5, || {
158            d.insert("x");
159            d.build_visual_unwrapped();
160        });
161        println!();
162    }
163}
Source

pub fn is_empty(&self) -> bool

Whether the map styles nothing — a document with no markup in it, or one that has not been built yet.

Source

pub fn style_at(&self, offset: usize) -> Style

The style covering source byte offset, or Style::default where no run does.

A binary search, for a caller asking about one offset. A painter walking the document in order should use edges_in instead and ask once per run rather than once per byte.

Source

pub fn edges_in(&self, range: Range<usize>, out: &mut Vec<usize>)

Append every styling boundary strictly inside range to out, in ascending order — the offsets where a painter has to break a span because the style changes there.

Both edges of every overlapping run, since a run that starts inside the range and one that ends inside it are equally a place the color changes. The range’s own ends are left to the caller, which already has them.

Trait Implementations§

Source§

impl Clone for SourceMap

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for SourceMap

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for SourceMap

Source§

fn default() -> Self

Returns the “default value” for a type. Read more
Source§

impl Eq for SourceMap

Source§

impl PartialEq for SourceMap

Source§

fn eq(&self, other: &Self) -> bool

Equality operator ==. Read more
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Inequality operator !=. Read more
Source§

impl StructuralPartialEq for SourceMap

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.