@@ -124,7 +124,15 @@ impl EditorView {
124 Box::new(highlights)
124 Box::new(highlights)
127− Self::render_text_highlights(doc, view.offset, inner, surface, theme, highlights);
127+ Self::render_text_highlights(
134+ &editor.config().whitespace,
128 Self::render_gutter(editor, doc, view, view.area, surface, theme, is_focused);
136 Self::render_gutter(editor, doc, view, view.area, surface, theme, is_focused);
129 Self::render_rulers(editor, doc, view, inner, surface, theme);
137 Self::render_rulers(editor, doc, view, inner, surface, theme);
@@ -344,7 +352,10 @@ impl EditorView {
344 surface: &mut Surface,
352 surface: &mut Surface,
345 theme: &Theme,
353 theme: &Theme,
346 highlights: H,
354 highlights: H,
355+ whitespace: &helix_view::editor::WhitespaceConfig,
357+ use helix_view::editor::WhitespaceRenderValue;
348 // It's slightly more efficient to produce a full RopeSlice from the Rope, then slice that a bunch
359 // It's slightly more efficient to produce a full RopeSlice from the Rope, then slice that a bunch
349 // of times than it is to always call Rope::slice/get_slice (it will internally always hit RSEnum::Light).
360 // of times than it is to always call Rope::slice/get_slice (it will internally always hit RSEnum::Light).
350 let text = doc.text().slice(..);
361 let text = doc.text().slice(..);
@@ -353,9 +364,20 @@ impl EditorView {
353 let mut visual_x = 0u16;
364 let mut visual_x = 0u16;
354 let mut line = 0u16;
365 let mut line = 0u16;
355 let tab_width = doc.tab_width();
366 let tab_width = doc.tab_width();
367+ let tab = if whitespace.render.tab() == WhitespaceRenderValue::All {
368+ (1..tab_width).fold(whitespace.characters.tab.to_string(), |s, _| s + " ")
356− let tab = " ".repeat(tab_width);
370+ " ".repeat(tab_width)
372+ let space = whitespace.characters.space.to_string();
373+ let newline = if whitespace.render.newline() == WhitespaceRenderValue::All {
374+ whitespace.characters.newline.to_string()
358 let text_style = theme.get("ui.text");
379 let text_style = theme.get("ui.text");
380+ let whitespace_style = theme.get("ui.virtual.whitespace");
360 'outer: for event in highlights {
382 'outer: for event in highlights {
361 match event {
383 match event {
@@ -374,6 +396,14 @@ impl EditorView {
375 .fold(text_style, |acc, span| acc.patch(theme.highlight(span.0)));
397 .fold(text_style, |acc, span| acc.patch(theme.highlight(span.0)));
399+ let space = if whitespace.render.space() == WhitespaceRenderValue::All
400+ && text.len_chars() < end
377 use helix_core::graphemes::{grapheme_width, RopeGraphemes};
407 use helix_core::graphemes::{grapheme_width, RopeGraphemes};
379 for grapheme in RopeGraphemes::new(text) {
409 for grapheme in RopeGraphemes::new(text) {
@@ -386,8 +416,8 @@ impl EditorView {
386 surface.set_string(
416 surface.set_string(
387 viewport.x + visual_x - offset.col as u16,
417 viewport.x + visual_x - offset.col as u16,
388 viewport.y + line,
418 viewport.y + line,
390− style,
420+ style.patch(whitespace_style),
@@ -400,12 +430,21 @@ impl EditorView {
402 let grapheme = Cow::from(grapheme);
432 let grapheme = Cow::from(grapheme);
404 let (grapheme, width) = if grapheme == "\t" {
435 let (grapheme, width) = if grapheme == "\t" {
436+ is_whitespace = true;
405 // make sure we display tab as appropriate amount of spaces
437 // make sure we display tab as appropriate amount of spaces
406 let visual_tab_width = tab_width - (visual_x as usize % tab_width);
438 let visual_tab_width = tab_width - (visual_x as usize % tab_width);
439+ let grapheme_tab_width =
440+ ropey::str_utils::char_to_byte_idx(&tab, visual_tab_width);
407− (&tab[..visual_tab_width], visual_tab_width)
442+ (&tab[..grapheme_tab_width], visual_tab_width)
443+ } else if grapheme == " " {
444+ is_whitespace = true;
447+ is_whitespace = false;
409 // Cow will prevent allocations if span contained in a single slice
448 // Cow will prevent allocations if span contained in a single slice
410 // which should really be the majority case
449 // which should really be the majority case
411 let width = grapheme_width(&grapheme);
450 let width = grapheme_width(&grapheme);
@@ -418,7 +457,11 @@ impl EditorView {
418 viewport.x + visual_x - offset.col as u16,
457 viewport.x + visual_x - offset.col as u16,
419 viewport.y + line,
458 viewport.y + line,
420 grapheme,
459 grapheme,
421− style,
460+ if is_whitespace {
461+ style.patch(whitespace_style)