mirror of https://github.com/helix-editor/helix
feat: implement basic highlighting (does not handle newlines yet)
parent
f4827c8abf
commit
fa2de3222f
|
@ -1,3 +1,11 @@
|
||||||
|
/// ```helix
|
||||||
|
/// #(|hello world)#
|
||||||
|
/// #(|hello world)#
|
||||||
|
/// #[hello world|]#
|
||||||
|
/// #(|hello world)#
|
||||||
|
/// ```
|
||||||
|
fn a() {}
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
commands::{self, OnKeyCallback, OnKeyCallbackKind},
|
commands::{self, OnKeyCallback, OnKeyCallbackKind},
|
||||||
compositor::{Component, Context, Event, EventResult},
|
compositor::{Component, Context, Event, EventResult},
|
||||||
|
|
|
@ -5,13 +5,14 @@ use tui::{
|
||||||
text::{Span, Spans, Text},
|
text::{Span, Spans, Text},
|
||||||
};
|
};
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::{cmp::Ordering, collections::HashSet, sync::Arc};
|
||||||
|
|
||||||
use pulldown_cmark::{CodeBlockKind, Event, HeadingLevel, Options, Parser, Tag, TagEnd};
|
use pulldown_cmark::{CodeBlockKind, Event, HeadingLevel, Options, Parser, Tag, TagEnd};
|
||||||
|
|
||||||
use helix_core::{
|
use helix_core::{
|
||||||
syntax::{self, HighlightEvent, InjectionLanguageMarker, Syntax},
|
syntax::{self, HighlightEvent, InjectionLanguageMarker, Syntax},
|
||||||
RopeSlice,
|
test::print,
|
||||||
|
Rope, RopeSlice,
|
||||||
};
|
};
|
||||||
use helix_view::{
|
use helix_view::{
|
||||||
graphics::{Margin, Rect, Style},
|
graphics::{Margin, Rect, Style},
|
||||||
|
@ -71,42 +72,89 @@ pub fn highlighted_code_block<'a>(
|
||||||
Box::new(highlight_iter)
|
Box::new(highlight_iter)
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut highlights = Vec::new();
|
if language == "helix" {
|
||||||
for event in highlight_iter {
|
let (text, selections) = print(text);
|
||||||
match event {
|
// let text = Rope::from(text).slice(..);
|
||||||
HighlightEvent::HighlightStart(span) => {
|
|
||||||
highlights.push(span);
|
|
||||||
}
|
|
||||||
HighlightEvent::HighlightEnd => {
|
|
||||||
highlights.pop();
|
|
||||||
}
|
|
||||||
HighlightEvent::Source { start, end } => {
|
|
||||||
let style = highlights
|
|
||||||
.iter()
|
|
||||||
.fold(text_style, |acc, span| acc.patch(theme.highlight(span.0)));
|
|
||||||
|
|
||||||
let mut slice = &text[start..end];
|
let style_cursor = get_theme("ui.cursor");
|
||||||
// TODO: do we need to handle all unicode line endings
|
let style_cursor_primary = get_theme("ui.cursor.primary");
|
||||||
// here, or is just '\n' okay?
|
let style_selection = get_theme("ui.selection");
|
||||||
while let Some(end) = slice.find('\n') {
|
let style_selection_primary = get_theme("ui.selection.primary");
|
||||||
// emit span up to newline
|
let style_text = get_theme("ui.text");
|
||||||
let text = &slice[..end];
|
|
||||||
let text = text.replace('\t', " "); // replace tabs
|
|
||||||
let span = Span::styled(text, style);
|
|
||||||
spans.push(span);
|
|
||||||
|
|
||||||
// truncate slice to after newline
|
let mut ranges2 = HashSet::new();
|
||||||
slice = &slice[end + 1..];
|
let mut cursors = HashSet::new();
|
||||||
|
let primary_idx = selections.primary_index();
|
||||||
|
|
||||||
// make a new line
|
for range in selections.iter() {
|
||||||
let spans = std::mem::take(&mut spans);
|
ranges2.extend(range.from()..range.to());
|
||||||
lines.push(Spans::from(spans));
|
cursors.insert(if range.head > range.anchor {
|
||||||
|
range.head.saturating_sub(1)
|
||||||
|
} else {
|
||||||
|
range.head
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
for (idx, ch) in text.chars().enumerate() {
|
||||||
|
let is_cursor = cursors.contains(&idx);
|
||||||
|
let is_selection = ranges2.contains(&idx);
|
||||||
|
|
||||||
|
let style = if is_cursor {
|
||||||
|
if idx == primary_idx {
|
||||||
|
style_cursor_primary
|
||||||
|
} else {
|
||||||
|
style_cursor
|
||||||
}
|
}
|
||||||
|
} else if is_selection {
|
||||||
|
if idx == primary_idx {
|
||||||
|
style_selection_primary
|
||||||
|
} else {
|
||||||
|
style_selection
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
style_text
|
||||||
|
};
|
||||||
|
|
||||||
// if there's anything left, emit it too
|
spans.push(Span::styled(ch.to_string(), style));
|
||||||
if !slice.is_empty() {
|
}
|
||||||
let span = Span::styled(slice.replace('\t', " "), style);
|
} else {
|
||||||
spans.push(span);
|
let mut highlights = Vec::new();
|
||||||
|
for event in highlight_iter {
|
||||||
|
match event {
|
||||||
|
HighlightEvent::HighlightStart(span) => {
|
||||||
|
highlights.push(span);
|
||||||
|
}
|
||||||
|
HighlightEvent::HighlightEnd => {
|
||||||
|
highlights.pop();
|
||||||
|
}
|
||||||
|
HighlightEvent::Source { start, end } => {
|
||||||
|
let style = highlights
|
||||||
|
.iter()
|
||||||
|
.fold(text_style, |acc, span| acc.patch(theme.highlight(span.0)));
|
||||||
|
|
||||||
|
let mut slice = &text[start..end];
|
||||||
|
// TODO: do we need to handle all unicode line endings
|
||||||
|
// here, or is just '\n' okay?
|
||||||
|
while let Some(end) = slice.find('\n') {
|
||||||
|
// emit span up to newline
|
||||||
|
let text = &slice[..end];
|
||||||
|
let text = text.replace('\t', " "); // replace tabs
|
||||||
|
let span = Span::styled(text, style);
|
||||||
|
spans.push(span);
|
||||||
|
|
||||||
|
// truncate slice to after newline
|
||||||
|
slice = &slice[end + 1..];
|
||||||
|
|
||||||
|
// make a new line
|
||||||
|
let spans = std::mem::take(&mut spans);
|
||||||
|
lines.push(Spans::from(spans));
|
||||||
|
}
|
||||||
|
|
||||||
|
// if there's anything left, emit it too
|
||||||
|
if !slice.is_empty() {
|
||||||
|
let span = Span::styled(slice.replace('\t', " "), style);
|
||||||
|
spans.push(span);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue