From 316bbe945f64e002c61d366457c42af0cb394e6f Mon Sep 17 00:00:00 2001 From: A-Walrus Date: Mon, 10 Oct 2022 11:13:25 +0300 Subject: [PATCH 1/4] Fix rendering of lines longer than 2^16 Before things would be cast to u16 earlier than needed, which would cause problems for insanely long lines (longer than 2^16 ~ 65 thousand) --- helix-term/src/ui/editor.rs | 19 +++++++++---------- helix-view/src/editor.rs | 2 +- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs index 47fb7a4a5408..97372c47fe23 100644 --- a/helix-term/src/ui/editor.rs +++ b/helix-term/src/ui/editor.rs @@ -404,7 +404,7 @@ impl EditorView { let characters = &whitespace.characters; let mut spans = Vec::new(); - let mut visual_x = 0u16; + let mut visual_x = 0usize; let mut line = 0u16; let tab_width = doc.tab_width(); let tab = if whitespace.render.tab() == WhitespaceRenderValue::All { @@ -441,14 +441,13 @@ impl EditorView { return; } - let starting_indent = - (offset.col / tab_width) as u16 + config.indent_guides.skip_levels; + let starting_indent = (offset.col / tab_width) + config.indent_guides.skip_levels; // TODO: limit to a max indent level too. It doesn't cause visual artifacts but it would avoid some // extra loops if the code is deeply nested. - for i in starting_indent..(indent_level / tab_width as u16) { + for i in starting_indent..(indent_level / tab_width) { surface.set_string( - viewport.x + (i * tab_width as u16) - offset.col as u16, + (viewport.x as usize + (i * tab_width) - offset.col) as u16, viewport.y + line, &indent_guide_char, indent_guide_style, @@ -494,14 +493,14 @@ impl EditorView { use helix_core::graphemes::{grapheme_width, RopeGraphemes}; for grapheme in RopeGraphemes::new(text) { - let out_of_bounds = visual_x < offset.col as u16 - || visual_x >= viewport.width + offset.col as u16; + let out_of_bounds = offset.col > (visual_x as usize) + || (visual_x as usize) >= viewport.width as usize + offset.col; if LineEnding::from_rope_slice(&grapheme).is_some() { if !out_of_bounds { // we still want to render an empty cell with the style surface.set_string( - viewport.x + visual_x - offset.col as u16, + (viewport.x as usize + visual_x - offset.col) as u16, viewport.y + line, &newline, style.patch(whitespace_style), @@ -549,7 +548,7 @@ impl EditorView { if !out_of_bounds { // if we're offscreen just keep going until we hit a new line surface.set_string( - viewport.x + visual_x - offset.col as u16, + (viewport.x as usize + visual_x - offset.col) as u16, viewport.y + line, display_grapheme, if is_whitespace { @@ -582,7 +581,7 @@ impl EditorView { last_line_indent_level = visual_x; } - visual_x = visual_x.saturating_add(width as u16); + visual_x = visual_x.saturating_add(width); } } } diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index 60b3880c6e14..dd1e30f95b7e 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -558,7 +558,7 @@ impl Default for WhitespaceCharacters { pub struct IndentGuidesConfig { pub render: bool, pub character: char, - pub skip_levels: u16, + pub skip_levels: usize, } impl Default for IndentGuidesConfig { From 536c93963b48b671b66966a9410e3f3dd3a430af Mon Sep 17 00:00:00 2001 From: A-Walrus Date: Tue, 11 Oct 2022 10:00:41 +0300 Subject: [PATCH 2/4] Make skip_levels a u8 --- helix-term/src/ui/editor.rs | 3 ++- helix-view/src/editor.rs | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs index 97372c47fe23..0ce96e06f336 100644 --- a/helix-term/src/ui/editor.rs +++ b/helix-term/src/ui/editor.rs @@ -441,7 +441,8 @@ impl EditorView { return; } - let starting_indent = (offset.col / tab_width) + config.indent_guides.skip_levels; + let starting_indent = + (offset.col / tab_width) + config.indent_guides.skip_levels as usize; // TODO: limit to a max indent level too. It doesn't cause visual artifacts but it would avoid some // extra loops if the code is deeply nested. diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index dd1e30f95b7e..e9a3c6397017 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -558,7 +558,7 @@ impl Default for WhitespaceCharacters { pub struct IndentGuidesConfig { pub render: bool, pub character: char, - pub skip_levels: usize, + pub skip_levels: u8, } impl Default for IndentGuidesConfig { From cc35d5acad484d2f6413da8a9cef30bada7353a5 Mon Sep 17 00:00:00 2001 From: A-Walrus Date: Tue, 11 Oct 2022 11:37:57 +0300 Subject: [PATCH 3/4] Only draw indent guides within bounds Better performance, and otherwise very long lines with lots of tabs will wrap around the u16 and come back on the other side, messing up the beginning skip_levels. --- helix-term/src/ui/editor.rs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs index 0ce96e06f336..b511206c569c 100644 --- a/helix-term/src/ui/editor.rs +++ b/helix-term/src/ui/editor.rs @@ -443,16 +443,14 @@ impl EditorView { let starting_indent = (offset.col / tab_width) + config.indent_guides.skip_levels as usize; - // TODO: limit to a max indent level too. It doesn't cause visual artifacts but it would avoid some - // extra loops if the code is deeply nested. for i in starting_indent..(indent_level / tab_width) { - surface.set_string( - (viewport.x as usize + (i * tab_width) - offset.col) as u16, - viewport.y + line, - &indent_guide_char, - indent_guide_style, - ); + let x = (viewport.x as usize + (i * tab_width) - offset.col) as u16; + let y = viewport.y + line; + if !surface.in_bounds(x, y) { + break; + } + surface.set_string(x, y, &indent_guide_char, indent_guide_style); } }; From 84edf8e13f17d93468f30b10a8c9b0656964611c Mon Sep 17 00:00:00 2001 From: A-Walrus Date: Mon, 17 Oct 2022 14:23:50 +0300 Subject: [PATCH 4/4] Replace `in_bounds` with calculation of end_indent Instead of repeatedly checking if it is in_bounds, calculate the max_indent beforehand and just loop. I added a debug_assert to "prove" that it never tries drawing out of bounds. --- helix-term/src/ui/editor.rs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs index b511206c569c..813e803f57cb 100644 --- a/helix-term/src/ui/editor.rs +++ b/helix-term/src/ui/editor.rs @@ -23,7 +23,7 @@ use helix_view::{ keyboard::{KeyCode, KeyModifiers}, Document, Editor, Theme, View, }; -use std::{borrow::Cow, path::PathBuf}; +use std::{borrow::Cow, cmp::min, path::PathBuf}; use tui::buffer::Buffer as Surface; @@ -444,12 +444,18 @@ impl EditorView { let starting_indent = (offset.col / tab_width) + config.indent_guides.skip_levels as usize; - for i in starting_indent..(indent_level / tab_width) { + // Don't draw indent guides outside of view + let end_indent = min( + indent_level, + // Add tab_width - 1 to round up, since the first visible + // indent might be a bit after offset.col + offset.col + viewport.width as usize + (tab_width - 1), + ) / tab_width; + + for i in starting_indent..end_indent { let x = (viewport.x as usize + (i * tab_width) - offset.col) as u16; let y = viewport.y + line; - if !surface.in_bounds(x, y) { - break; - } + debug_assert!(surface.in_bounds(x, y)); surface.set_string(x, y, &indent_guide_char, indent_guide_style); } };