xi_core_lib/
layers.rs

1// Copyright 2017 The xi-editor Authors.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Handles syntax highlighting and other styling.
16//!
17//! Plugins provide syntax highlighting information in the form of 'scopes'.
18//! Scope information originating from any number of plugins can be resolved
19//! into styles using a theme, augmented with additional style definitions.
20
21use std::collections::{BTreeMap, HashMap, HashSet};
22use syntect::highlighting::StyleModifier;
23use syntect::parsing::Scope;
24
25use xi_rope::spans::{Spans, SpansBuilder};
26use xi_rope::{Interval, RopeDelta};
27use xi_trace::trace_block;
28
29use crate::plugins::PluginPid;
30use crate::styles::{Style, ThemeStyleMap};
31
32/// A collection of layers containing scope information.
33#[derive(Default)]
34pub struct Layers {
35    layers: BTreeMap<PluginPid, ScopeLayer>,
36    deleted: HashSet<PluginPid>,
37    merged: Spans<Style>,
38}
39
40/// A collection of scope spans from a single source.
41pub struct ScopeLayer {
42    stack_lookup: Vec<Vec<Scope>>,
43    style_lookup: Vec<Style>,
44    // TODO: this might be efficient (in memory at least) if we use
45    // a prefix tree.
46    /// style state of existing scope spans, so we can more efficiently
47    /// compute styles of child spans.
48    style_cache: HashMap<Vec<Scope>, StyleModifier>,
49    /// Human readable scope names, for debugging
50    scope_spans: Spans<u32>,
51    style_spans: Spans<Style>,
52}
53
54impl Layers {
55    pub fn get_merged(&self) -> &Spans<Style> {
56        &self.merged
57    }
58
59    /// Adds the provided scopes to the layer's lookup table.
60    pub fn add_scopes(
61        &mut self,
62        layer: PluginPid,
63        scopes: Vec<Vec<String>>,
64        style_map: &ThemeStyleMap,
65    ) {
66        let _t = trace_block("Layers::AddScopes", &["core"]);
67        if self.create_if_missing(layer).is_err() {
68            return;
69        }
70        self.layers.get_mut(&layer).unwrap().add_scopes(scopes, style_map);
71    }
72
73    /// Applies the delta to all layers, inserting empty intervals
74    /// for any regions inserted in the delta.
75    ///
76    /// This is useful for clearing spans, and for updating spans
77    /// as edits occur.
78    pub fn update_all(&mut self, delta: &RopeDelta) {
79        self.merged.apply_shape(delta);
80
81        for layer in self.layers.values_mut() {
82            layer.blank_scopes(delta);
83        }
84        let (iv, _len) = delta.summary();
85        self.resolve_styles(iv);
86    }
87
88    /// Updates the scope spans for a given layer.
89    pub fn update_layer(&mut self, layer: PluginPid, iv: Interval, spans: Spans<u32>) {
90        if self.create_if_missing(layer).is_err() {
91            return;
92        }
93        self.layers.get_mut(&layer).unwrap().update_scopes(iv, &spans);
94        self.resolve_styles(iv);
95    }
96
97    /// Removes a given layer. This will remove all styles derived from
98    /// that layer's scopes.
99    pub fn remove_layer(&mut self, layer: PluginPid) -> Option<ScopeLayer> {
100        self.deleted.insert(layer);
101        let layer = self.layers.remove(&layer);
102        if layer.is_some() {
103            let iv_all = Interval::new(0, self.merged.len());
104            //TODO: should Spans<T> have a clear() method?
105            self.merged = SpansBuilder::new(self.merged.len()).build();
106            self.resolve_styles(iv_all);
107        }
108        layer
109    }
110
111    pub fn theme_changed(&mut self, style_map: &ThemeStyleMap) {
112        for layer in self.layers.values_mut() {
113            layer.theme_changed(style_map);
114        }
115        self.merged = SpansBuilder::new(self.merged.len()).build();
116        let iv_all = Interval::new(0, self.merged.len());
117        self.resolve_styles(iv_all);
118    }
119
120    /// Resolves styles from all layers for the given interval, updating
121    /// the master style spans.
122    fn resolve_styles(&mut self, iv: Interval) {
123        if self.layers.is_empty() {
124            return;
125        }
126        let mut layer_iter = self.layers.values();
127        let mut resolved = layer_iter.next().unwrap().style_spans.subseq(iv);
128
129        for other in layer_iter {
130            let spans = other.style_spans.subseq(iv);
131            assert_eq!(resolved.len(), spans.len());
132            resolved = resolved.merge(&spans, |a, b| match b {
133                Some(b) => a.merge(b),
134                None => a.to_owned(),
135            });
136        }
137        self.merged.edit(iv, resolved);
138    }
139
140    /// Prints scopes and style information for the given `Interval`.
141    pub fn debug_print_spans(&self, iv: Interval) {
142        for (id, layer) in &self.layers {
143            let spans = layer.scope_spans.subseq(iv);
144            let styles = layer.style_spans.subseq(iv);
145            if spans.iter().next().is_some() {
146                info!("scopes for layer {:?}:", id);
147                for (iv, val) in spans.iter() {
148                    info!("{}: {:?}", iv, layer.stack_lookup[*val as usize]);
149                }
150                info!("styles:");
151                for (iv, val) in styles.iter() {
152                    info!("{}: {:?}", iv, val);
153                }
154            }
155        }
156    }
157
158    /// Returns an `Err` if this layer has been deleted; the caller should return.
159    fn create_if_missing(&mut self, layer_id: PluginPid) -> Result<(), ()> {
160        if self.deleted.contains(&layer_id) {
161            return Err(());
162        }
163        if !self.layers.contains_key(&layer_id) {
164            self.layers.insert(layer_id, ScopeLayer::new(self.merged.len()));
165        }
166        Ok(())
167    }
168}
169
170impl Default for ScopeLayer {
171    fn default() -> Self {
172        ScopeLayer {
173            stack_lookup: Vec::new(),
174            style_lookup: Vec::new(),
175            style_cache: HashMap::new(),
176            scope_spans: Spans::default(),
177            style_spans: Spans::default(),
178        }
179    }
180}
181
182impl ScopeLayer {
183    pub fn new(len: usize) -> Self {
184        ScopeLayer {
185            stack_lookup: Vec::new(),
186            style_lookup: Vec::new(),
187            style_cache: HashMap::new(),
188            scope_spans: SpansBuilder::new(len).build(),
189            style_spans: SpansBuilder::new(len).build(),
190        }
191    }
192
193    fn theme_changed(&mut self, style_map: &ThemeStyleMap) {
194        // recompute styles with the new theme
195        let cur_stacks = self.stack_lookup.clone();
196        self.style_lookup = self.styles_for_stacks(&cur_stacks, style_map);
197        let iv_all = Interval::new(0, self.style_spans.len());
198        self.style_spans = SpansBuilder::new(self.style_spans.len()).build();
199        // this feels unnecessary but we can't pass in a reference to self
200        // and I don't want to get fancy unless there's an actual perf problem
201        let scopes = self.scope_spans.clone();
202        self.update_styles(iv_all, &scopes)
203    }
204
205    fn add_scopes(&mut self, scopes: Vec<Vec<String>>, style_map: &ThemeStyleMap) {
206        let mut stacks = Vec::with_capacity(scopes.len());
207        for stack in scopes {
208            let scopes = stack
209                .iter()
210                .map(|s| Scope::new(&s))
211                .filter(|result| match *result {
212                    Err(ref err) => {
213                        warn!("failed to resolve scope {}\nErr: {:?}", &stack.join(" "), err);
214                        false
215                    }
216                    _ => true,
217                })
218                .map(|s| s.unwrap())
219                .collect::<Vec<_>>();
220            stacks.push(scopes);
221        }
222
223        let mut new_styles = self.styles_for_stacks(stacks.as_slice(), style_map);
224        self.stack_lookup.append(&mut stacks);
225        self.style_lookup.append(&mut new_styles);
226    }
227
228    fn styles_for_stacks(
229        &mut self,
230        stacks: &[Vec<Scope>],
231        style_map: &ThemeStyleMap,
232    ) -> Vec<Style> {
233        //let style_map = style_map.borrow();
234        let highlighter = style_map.get_highlighter();
235        let mut new_styles = Vec::new();
236
237        for stack in stacks {
238            let mut last_style: Option<StyleModifier> = None;
239            let mut upper_bound_of_last = stack.len() as usize;
240
241            // walk backwards through stack to see if we have an existing
242            // style for any child stacks.
243            for i in 0..stack.len() - 1 {
244                let prev_range = 0..stack.len() - (i + 1);
245                if let Some(s) = self.style_cache.get(&stack[prev_range]) {
246                    last_style = Some(*s);
247                    upper_bound_of_last = stack.len() - (i + 1);
248                    break;
249                }
250            }
251            let mut base_style_mod = last_style.unwrap_or_default();
252
253            // apply the stack, generating children as needed.
254            for i in upper_bound_of_last..stack.len() {
255                let style_mod = highlighter.style_mod_for_stack(&stack[0..=i]);
256                base_style_mod = base_style_mod.apply(style_mod);
257            }
258
259            let style = Style::from_syntect_style_mod(&base_style_mod);
260            self.style_cache.insert(stack.clone(), base_style_mod);
261
262            new_styles.push(style);
263        }
264        new_styles
265    }
266
267    fn update_scopes(&mut self, iv: Interval, spans: &Spans<u32>) {
268        self.scope_spans.edit(iv, spans.to_owned());
269        self.update_styles(iv, spans);
270    }
271
272    /// Applies `delta`, which is presumed to contain empty spans.
273    /// This is only used when we receive an edit, to adjust current span
274    /// positions.
275    fn blank_scopes(&mut self, delta: &RopeDelta) {
276        self.style_spans.apply_shape(delta);
277        self.scope_spans.apply_shape(delta);
278    }
279
280    /// Updates `self.style_spans`, mapping scopes to styles and combining
281    /// adjacent and equal spans.
282    fn update_styles(&mut self, iv: Interval, spans: &Spans<u32>) {
283        // NOTE: This is a tradeoff. Keeping both u32 and Style spans for each
284        // layer makes debugging simpler and reduces the total number of spans
285        // on the wire (because we combine spans that resolve to the same style)
286        // but it does require additional computation + memory up front.
287        let mut sb = SpansBuilder::new(spans.len());
288        let mut spans_iter = spans.iter();
289        let mut prev = spans_iter.next();
290        {
291            // distinct adjacent scopes can often resolve to the same style,
292            // so we combine them when building the styles.
293            let style_eq = |i1: &u32, i2: &u32| {
294                self.style_lookup[*i1 as usize] == self.style_lookup[*i2 as usize]
295            };
296
297            while let Some((p_iv, p_val)) = prev {
298                match spans_iter.next() {
299                    Some((n_iv, n_val)) if n_iv.start() == p_iv.end() && style_eq(p_val, n_val) => {
300                        prev = Some((p_iv.union(n_iv), p_val));
301                    }
302                    other => {
303                        sb.add_span(p_iv, self.style_lookup[*p_val as usize].to_owned());
304                        prev = other;
305                    }
306                }
307            }
308        }
309        self.style_spans.edit(iv, sb.build());
310    }
311}