mirror of
https://github.com/helix-editor/helix.git
synced 2025-04-06 20:37:44 +03:00
Extract the merge "operator" into helix-core
This commit is contained in:
parent
ae58d813b2
commit
1b102d5532
2 changed files with 157 additions and 162 deletions
|
@ -1652,6 +1652,125 @@ fn shrink_and_clear<T>(vec: &mut Vec<T>, capacity: usize) {
|
||||||
vec.clear();
|
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]
|
#[test]
|
||||||
fn test_parser() {
|
fn test_parser() {
|
||||||
let highlight_names: Vec<String> = [
|
let highlight_names: Vec<String> = [
|
||||||
|
|
|
@ -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 {
|
impl EditorView {
|
||||||
pub fn new(keymaps: Keymaps) -> Self {
|
pub fn new(keymaps: Keymaps) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
@ -290,27 +172,16 @@ impl EditorView {
|
||||||
.find_scope_index("ui.selection.primary")
|
.find_scope_index("ui.selection.primary")
|
||||||
.unwrap_or(selection_scope);
|
.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 primary_cursor_scope = theme
|
|
||||||
.find_scope_index("ui.cursor.primary")
|
|
||||||
.unwrap_or(cursor_scope);
|
|
||||||
|
|
||||||
let highlights: Box<dyn Iterator<Item = HighlightEvent>> = if is_focused {
|
let highlights: Box<dyn Iterator<Item = HighlightEvent>> = if is_focused {
|
||||||
// inject selections as highlight scopes
|
// inject selections as highlight scopes
|
||||||
let mut spans_: Vec<(usize, std::ops::Range<usize>)> = Vec::new();
|
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);
|
||||||
|
|
||||||
for (i, range) in selections.iter().enumerate() {
|
for (i, range) in selections.iter().enumerate() {
|
||||||
let (cursor_scope, selection_scope) = if i == primary_idx {
|
let (cursor_scope, selection_scope) = if i == primary_idx {
|
||||||
|
@ -320,39 +191,40 @@ impl EditorView {
|
||||||
};
|
};
|
||||||
|
|
||||||
if range.head == range.anchor {
|
if range.head == range.anchor {
|
||||||
spans_.push((cursor_scope, range.head..range.head + 1));
|
spans.push((cursor_scope, range.head..range.head + 1));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let reverse = range.head < range.anchor;
|
let reverse = range.head < range.anchor;
|
||||||
|
|
||||||
if reverse {
|
if reverse {
|
||||||
spans_.push((cursor_scope, range.head..range.head + 1));
|
spans.push((cursor_scope, range.head..range.head + 1));
|
||||||
spans_.push((selection_scope, range.head + 1..range.anchor + 1));
|
spans.push((selection_scope, range.head + 1..range.anchor + 1));
|
||||||
} else {
|
} else {
|
||||||
spans_.push((selection_scope, range.anchor..range.head));
|
spans.push((selection_scope, range.anchor..range.head));
|
||||||
spans_.push((cursor_scope, range.head..range.head + 1));
|
spans.push((cursor_scope, range.head..range.head + 1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Box::new(merge(highlights, spans_))
|
Box::new(syntax::merge(highlights, spans))
|
||||||
} else {
|
} else {
|
||||||
Box::new(highlights)
|
Box::new(highlights)
|
||||||
};
|
};
|
||||||
|
|
||||||
// diagnostic injection
|
// diagnostic injection
|
||||||
let diagnostic_scope = theme.find_scope_index("diagnostic").unwrap_or(cursor_scope);
|
let diagnostic_scope = theme.find_scope_index("diagnostic").unwrap_or(cursor_scope);
|
||||||
let spans_ = doc
|
let highlights = Box::new(syntax::merge(
|
||||||
.diagnostics()
|
highlights,
|
||||||
.iter()
|
doc.diagnostics()
|
||||||
.map(|diagnostic| {
|
.iter()
|
||||||
(
|
.map(|diagnostic| {
|
||||||
diagnostic_scope,
|
(
|
||||||
diagnostic.range.start..diagnostic.range.end,
|
diagnostic_scope,
|
||||||
)
|
diagnostic.range.start..diagnostic.range.end,
|
||||||
})
|
)
|
||||||
.collect();
|
})
|
||||||
let highlights = Box::new(merge(highlights, spans_));
|
.collect(),
|
||||||
|
));
|
||||||
|
|
||||||
'outer: for event in highlights {
|
'outer: for event in highlights {
|
||||||
match event {
|
match event {
|
||||||
|
@ -403,16 +275,20 @@ impl EditorView {
|
||||||
break 'outer;
|
break 'outer;
|
||||||
}
|
}
|
||||||
} else if grapheme == "\t" {
|
} else if grapheme == "\t" {
|
||||||
if !out_of_bounds {
|
if out_of_bounds {
|
||||||
// we still want to render an empty cell with the style
|
// if we're offscreen just keep going until we hit a new line
|
||||||
surface.set_string(
|
visual_x = visual_x.saturating_add(tab_width as u16);
|
||||||
viewport.x + visual_x - view.first_col as u16,
|
continue;
|
||||||
viewport.y + line,
|
|
||||||
" ".repeat(tab_width),
|
|
||||||
style,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// we still want to render an empty cell with the style
|
||||||
|
surface.set_string(
|
||||||
|
viewport.x + visual_x - view.first_col as u16,
|
||||||
|
viewport.y + line,
|
||||||
|
" ".repeat(tab_width),
|
||||||
|
style,
|
||||||
|
);
|
||||||
|
|
||||||
visual_x = visual_x.saturating_add(tab_width as u16);
|
visual_x = visual_x.saturating_add(tab_width as u16);
|
||||||
} else {
|
} else {
|
||||||
// Cow will prevent allocations if span contained in a single slice
|
// Cow will prevent allocations if span contained in a single slice
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue