Extract the merge "operator" into helix-core

This commit is contained in:
Blaž Hrastnik 2021-06-27 19:10:48 +09:00
parent ae58d813b2
commit 1b102d5532
2 changed files with 157 additions and 162 deletions

View file

@ -1652,6 +1652,125 @@ fn shrink_and_clear<T>(vec: &mut Vec<T>, capacity: usize) {
vec.clear();
}
pub struct Merge<I> {
iter: I,
spans: Box<dyn Iterator<Item = (usize, std::ops::Range<usize>)>>,
next_event: Option<HighlightEvent>,
next_span: Option<(usize, std::ops::Range<usize>)>,
queue: Vec<HighlightEvent>,
}
/// Merge a list of spans into the highlight event stream.
pub fn merge<I: Iterator<Item = HighlightEvent>>(
iter: I,
spans: Vec<(usize, std::ops::Range<usize>)>,
) -> Merge<I> {
let spans = Box::new(spans.into_iter());
let mut merge = Merge {
iter,
spans,
next_event: None,
next_span: None,
queue: Vec::new(),
};
merge.next_event = merge.iter.next();
merge.next_span = merge.spans.next();
merge
}
impl<I: Iterator<Item = HighlightEvent>> Iterator for Merge<I> {
type Item = HighlightEvent;
fn next(&mut self) -> Option<Self::Item> {
use HighlightEvent::*;
if let Some(event) = self.queue.pop() {
return Some(event);
}
loop {
match (self.next_event, &self.next_span) {
// this happens when range is partially or fully offscreen
(Some(Source { start, end }), Some((span, range))) if start > range.start => {
if start > range.end {
self.next_span = self.spans.next();
} else {
self.next_span = Some((*span, start..range.end));
};
}
_ => break,
}
}
match (self.next_event, &self.next_span) {
(Some(HighlightStart(i)), _) => {
self.next_event = self.iter.next();
Some(HighlightStart(i))
}
(Some(HighlightEnd), _) => {
self.next_event = self.iter.next();
Some(HighlightEnd)
}
(Some(Source { start, end }), Some((span, range))) if start < range.start => {
let intersect = range.start.min(end);
let event = Source {
start,
end: intersect,
};
if end == intersect {
// the event is complete
self.next_event = self.iter.next();
} else {
// subslice the event
self.next_event = Some(Source {
start: intersect,
end,
});
};
Some(event)
}
(Some(Source { start, end }), Some((span, range))) if start == range.start => {
let intersect = range.end.min(end);
let event = HighlightStart(Highlight(*span));
// enqueue in reverse order
self.queue.push(HighlightEnd);
self.queue.push(Source {
start,
end: intersect,
});
if end == intersect {
// the event is complete
self.next_event = self.iter.next();
} else {
// subslice the event
self.next_event = Some(Source {
start: intersect,
end,
});
};
if intersect == range.end {
self.next_span = self.spans.next();
} else {
self.next_span = Some((*span, intersect..range.end));
}
Some(event)
}
(Some(event), None) => {
self.next_event = self.iter.next();
Some(event)
}
(None, None) => None,
e => unreachable!("{:?}", e),
}
}
}
#[test]
fn test_parser() {
let highlight_names: Vec<String> = [

View file

@ -44,124 +44,6 @@ impl Default for EditorView {
}
}
struct Merge<I> {
iter: I,
spans: Box<dyn Iterator<Item = (usize, std::ops::Range<usize>)>>,
next_event: Option<HighlightEvent>,
next_span: Option<(usize, std::ops::Range<usize>)>,
queue: Vec<HighlightEvent>,
}
fn merge<I: Iterator<Item = HighlightEvent>>(
iter: I,
spans: Vec<(usize, std::ops::Range<usize>)>,
) -> impl Iterator<Item = HighlightEvent> {
let spans = Box::new(spans.into_iter());
let mut merge = Merge {
iter,
spans,
next_event: None,
next_span: None,
queue: Vec::new(),
};
merge.next_event = merge.iter.next();
merge.next_span = merge.spans.next();
merge
}
impl<I: Iterator<Item = HighlightEvent>> Iterator for Merge<I> {
type Item = HighlightEvent;
fn next(&mut self) -> Option<Self::Item> {
use HighlightEvent::*;
if let Some(event) = self.queue.pop() {
return Some(event);
}
loop {
match (self.next_event, &self.next_span) {
// this happens when range is partially or fully offscreen
(Some(Source { start, end }), Some((span, range))) if start > range.start => {
if start > range.end {
self.next_span = self.spans.next();
} else {
self.next_span = Some((*span, start..range.end));
};
}
_ => break,
}
}
match (self.next_event, &self.next_span) {
(Some(HighlightStart(i)), _) => {
self.next_event = self.iter.next();
Some(HighlightStart(i))
}
(Some(HighlightEnd), _) => {
self.next_event = self.iter.next();
Some(HighlightEnd)
}
(Some(Source { start, end }), Some((span, range))) if start < range.start => {
let intersect = range.start.min(end);
let event = Source {
start,
end: intersect,
};
if end == intersect {
// the event is complete
self.next_event = self.iter.next();
} else {
// subslice the event
self.next_event = Some(Source {
start: intersect,
end,
});
};
Some(event)
}
(Some(Source { start, end }), Some((span, range))) if start == range.start => {
let intersect = range.end.min(end);
let event = HighlightStart(Highlight(*span));
// enqueue in reverse order
self.queue.push(HighlightEnd);
self.queue.push(Source {
start,
end: intersect,
});
if end == intersect {
// the event is complete
self.next_event = self.iter.next();
} else {
// subslice the event
self.next_event = Some(Source {
start: intersect,
end,
});
};
if intersect == range.end {
self.next_span = self.spans.next();
} else {
self.next_span = Some((*span, intersect..range.end));
}
Some(event)
}
(Some(event), None) => {
self.next_event = self.iter.next();
Some(event)
}
(None, None) => None,
e => unreachable!("{:?}", e),
}
}
}
impl EditorView {
pub fn new(keymaps: Keymaps) -> Self {
Self {
@ -290,28 +172,17 @@ impl EditorView {
.find_scope_index("ui.selection.primary")
.unwrap_or(selection_scope);
// TODO: primary + insert mode patching
// let primary_cursor_style = theme
// .try_get("ui.cursor.primary")
// .map(|style| {
// if mode != Mode::Normal {
// // we want to make sure that the insert and select highlights
// // also affect the primary cursor if set
// style.patch(cursor_style)
// } else {
// style
// }
// })
// .unwrap_or(cursor_style);
let highlights: Box<dyn Iterator<Item = HighlightEvent>> = if is_focused {
// inject selections as highlight scopes
let mut spans: Vec<(usize, std::ops::Range<usize>)> = Vec::new();
// TODO: primary + insert mode patching:
// (ui.cursor.primary).patch(mode).unwrap_or(cursor)
let primary_cursor_scope = theme
.find_scope_index("ui.cursor.primary")
.unwrap_or(cursor_scope);
let highlights: Box<dyn Iterator<Item = HighlightEvent>> = if is_focused {
// inject selections as highlight scopes
let mut spans_: Vec<(usize, std::ops::Range<usize>)> = Vec::new();
for (i, range) in selections.iter().enumerate() {
let (cursor_scope, selection_scope) = if i == primary_idx {
(primary_cursor_scope, primary_selection_scope)
@ -320,30 +191,31 @@ impl EditorView {
};
if range.head == range.anchor {
spans_.push((cursor_scope, range.head..range.head + 1));
spans.push((cursor_scope, range.head..range.head + 1));
continue;
}
let reverse = range.head < range.anchor;
if reverse {
spans_.push((cursor_scope, range.head..range.head + 1));
spans_.push((selection_scope, range.head + 1..range.anchor + 1));
spans.push((cursor_scope, range.head..range.head + 1));
spans.push((selection_scope, range.head + 1..range.anchor + 1));
} else {
spans_.push((selection_scope, range.anchor..range.head));
spans_.push((cursor_scope, range.head..range.head + 1));
spans.push((selection_scope, range.anchor..range.head));
spans.push((cursor_scope, range.head..range.head + 1));
}
}
Box::new(merge(highlights, spans_))
Box::new(syntax::merge(highlights, spans))
} else {
Box::new(highlights)
};
// diagnostic injection
let diagnostic_scope = theme.find_scope_index("diagnostic").unwrap_or(cursor_scope);
let spans_ = doc
.diagnostics()
let highlights = Box::new(syntax::merge(
highlights,
doc.diagnostics()
.iter()
.map(|diagnostic| {
(
@ -351,8 +223,8 @@ impl EditorView {
diagnostic.range.start..diagnostic.range.end,
)
})
.collect();
let highlights = Box::new(merge(highlights, spans_));
.collect(),
));
'outer: for event in highlights {
match event {
@ -403,7 +275,12 @@ impl EditorView {
break 'outer;
}
} else if grapheme == "\t" {
if !out_of_bounds {
if out_of_bounds {
// if we're offscreen just keep going until we hit a new line
visual_x = visual_x.saturating_add(tab_width as u16);
continue;
}
// we still want to render an empty cell with the style
surface.set_string(
viewport.x + visual_x - view.first_col as u16,
@ -411,7 +288,6 @@ impl EditorView {
" ".repeat(tab_width),
style,
);
}
visual_x = visual_x.saturating_add(tab_width as u16);
} else {