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

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() -> SourceMap

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

impl Eq for SourceMap

Source§

impl PartialEq for SourceMap

Source§

fn eq(&self, other: &SourceMap) -> 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.