@@ 124,6 124,7 @@ impl highlight::Highlighting for Highlig
}
}
+#[derive(Clone)]
pub struct Highlight {
state: (ParseState, SyntectHighlightState),
theme: Signal<SyntectTheme>
@@ 167,6 168,22 @@ impl highlight::Highlight for Highlight
.collect()
)
}
+
+ fn parse(&mut self, line: &str) {
+ let state = &mut self.state;
+ self.theme.sample_with(|theme|
+ HighlightIterator::new(
+ &mut state.1,
+ state.0.parse_line(line).as_slice(),
+ line,
+ &SyntectHighlighter::new(&theme)
+ ).count()
+ );
+ }
+
+ fn clone_dyn(&self) -> Box<dyn highlight::Highlight> {
+ Box::new(self.clone())
+ }
}
fn mod_color_back(target: &Option<ColorStyle>, back: &SyntectColor, palette: &Palette) -> ColorStyle {
@@ 1,5 1,10 @@
-use cursive::utils::markup::StyledIndexedSpan;
-use cursive::theme::{Style, Effect};
+use cursive::{
+ utils::markup::StyledIndexedSpan,
+ theme::{
+ Style,
+ Effect
+ }
+};
pub trait Highlighting {
fn start(&self) -> Box<dyn Highlight>;
@@ 11,10 16,16 @@ pub trait Highlighting {
fn cursor_line(&self, _: &mut Style) {}
}
-/// *Stateful*: highlights lines based on its state.
+/// **Stateful**: highlights lines based on its state.
pub trait Highlight {
/// Highlight the next line.
fn highlight(&mut self, line: &str) -> Vec<StyledIndexedSpan>;
+
+ /// Only parses the next line to advance internal state.
+ /// This is equivalent to `highlight()` without output.
+ fn parse(&mut self, line: &str);
+
+ fn clone_dyn(&self) -> Box<dyn Highlight>;
}
impl Highlighting for () {
@@ 27,4 38,10 @@ impl Highlight for () {
fn highlight(&mut self, line: &str) -> Vec<StyledIndexedSpan> {
vec![StyledIndexedSpan::simple(line, Style::none())]
}
+
+ fn parse(&mut self, _: &str) {}
+
+ fn clone_dyn(&self) -> Box<dyn Highlight> {
+ Box::new(())
+ }
}
@@ 1,4 1,3 @@
-use frappe::{Sink, Stream, Signal};
use super::unicode_width::UnicodeWidthStr;
use {
frappe::{
@@ 9,11 8,18 @@ use {
std::{
fmt,
ops::Range,
+ cmp::{
+ Ord,
+ Ordering
+ },
time::{
Instant,
Duration
},
- collections::BTreeSet
+ collections::{
+ BTreeSet,
+ BinaryHeap
+ }
},
cursive::{
Printer,
@@ 58,7 64,10 @@ use {
},
command::CursorCmd
},
- highlight::Highlighting
+ highlight::{
+ Highlighting,
+ Highlight
+ }
};
/// An open buffer with a cursor and history.
@@ 81,6 90,7 @@ pub struct EditorView {
// rendering
lines: LineCache<String>,
spans: LineCache<Vec<StyledIndexedSpan>>,
+ highlight_heap: BinaryHeap<LineHighlightState>,
lines_to_highlight: BTreeSet<usize>,
/// Draw this instead of `spans` if a cursor is on the line.
@@ 98,6 108,31 @@ pub struct EditorView {
type KeyMapper = TrieState<Event, Cmd>;
+struct LineHighlightState {
+ line_idx: usize,
+ highlight: Box<dyn Highlight>
+}
+
+impl PartialEq for LineHighlightState {
+ fn eq(&self, other: &Self) -> bool {
+ self.line_idx == other.line_idx
+ }
+}
+
+impl Eq for LineHighlightState {}
+
+impl PartialOrd for LineHighlightState {
+ fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+ self.line_idx.partial_cmp(&other.line_idx)
+ }
+}
+
+impl Ord for LineHighlightState {
+ fn cmp(&self, other: &Self) -> Ordering {
+ self.line_idx.cmp(&other.line_idx)
+ }
+}
+
#[derive(Clone)]
pub enum Cmd {
Cur(CursorCmd),
@@ 231,6 266,7 @@ impl Builder {
lines: LineCache::new(),
spans: LineCache::new(),
+ highlight_heap: BinaryHeap::new(),
lines_to_highlight: BTreeSet::new(),
line_highlight: {
let mut style = ColorStyle::primary().into();
@@ 358,15 394,40 @@ impl View for EditorView {
}
}
- // Cache the line styles.
- let mut highlighter = self.highlighting.start(); // FIXME missing context from lines before `line_idc`
- for line_idx in line_idc.clone() {
- if let None = self.spans.get(line_idx) {
- let line = self.lines.get(line_idx).expect("line not allocated").as_str();
- self.spans.cache(line_idx,
- // highlight syntax
- highlighter.highlight(line)
- );
+ { // Cache the line styles.
+ let mut current_highlight = None;
+ for line_idx in line_idc.clone() {
+ if let None = self.spans.get(line_idx) {
+ let mut highlight = current_highlight.take()
+ .unwrap_or_else(|| {
+ let (start_line_idx, mut highlight) = self.highlight_heap.peek()
+ .map(|state| (state.line_idx + 1, state.highlight.clone_dyn()))
+ .unwrap_or_else(|| (0, self.highlighting.start()));
+
+ for line_idx in start_line_idx..line_idx {
+ highlight.parse(
+ self.lines.get(line_idx).expect("line not allocated").as_str()
+ );
+ }
+
+ highlight
+ });
+
+ let line = self.lines.get(line_idx).expect("line not allocated").as_str();
+ self.spans.cache(line_idx,
+ // highlight syntax
+ highlight.highlight(line)
+ );
+
+ current_highlight = Some(highlight);
+ }
+ }
+
+ if let Some(highlight) = current_highlight {
+ self.highlight_heap.push(LineHighlightState {
+ line_idx: line_idc.end - 1,
+ highlight
+ });
}
}
@@ 844,6 905,12 @@ impl View for EditorView {
self.lines .invalidate_lines_from(first_damage..);
self.spans .invalidate_lines_from(first_damage..);
self.spans_highlight.invalidate_lines_from(first_damage..);
+ while self.highlight_heap.peek()
+ .map(|x| x.line_idx >= first_damage)
+ .unwrap_or(false)
+ {
+ self.highlight_heap.pop();
+ }
}
if tx_buf.has_changed() {