aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--helix-core/src/syntax.rs119
-rw-r--r--helix-term/src/ui/editor.rs200
2 files changed, 157 insertions, 162 deletions
diff --git a/helix-core/src/syntax.rs b/helix-core/src/syntax.rs
index 42c275d3..d03ca6bf 100644
--- a/helix-core/src/syntax.rs
+++ b/helix-core/src/syntax.rs
@@ -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> = [
diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs
index f5d33694..1211fc65 100644
--- a/helix-term/src/ui/editor.rs
+++ b/helix-term/src/ui/editor.rs
@@ -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,27 +172,16 @@ 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 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();
+ 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() {
let (cursor_scope, selection_scope) = if i == primary_idx {
@@ -320,39 +191,40 @@ 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()
- .iter()
- .map(|diagnostic| {
- (
- diagnostic_scope,
- diagnostic.range.start..diagnostic.range.end,
- )
- })
- .collect();
- let highlights = Box::new(merge(highlights, spans_));
+ let highlights = Box::new(syntax::merge(
+ highlights,
+ doc.diagnostics()
+ .iter()
+ .map(|diagnostic| {
+ (
+ diagnostic_scope,
+ diagnostic.range.start..diagnostic.range.end,
+ )
+ })
+ .collect(),
+ ));
'outer: for event in highlights {
match event {
@@ -403,16 +275,20 @@ impl EditorView {
break 'outer;
}
} else if grapheme == "\t" {
- if !out_of_bounds {
- // 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,
- );
+ 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,
+ viewport.y + line,
+ " ".repeat(tab_width),
+ style,
+ );
+
visual_x = visual_x.saturating_add(tab_width as u16);
} else {
// Cow will prevent allocations if span contained in a single slice