From 0b5a4c0d951622e60c318ecaa66f15ab2c80eeaa Mon Sep 17 00:00:00 2001 From: Reilly Wood <26268125+rgwood@users.noreply.github.com> Date: Thu, 23 May 2024 06:51:39 -0700 Subject: [PATCH] `explore` refactoring+clarification (#12940) Another very boring PR cleaning up and documenting some of `explore`'s innards. Mostly renaming things that I found confusing or vague when reading through the code, also adding some comments. --- ...coloredtextw.rs => colored_text_widget.rs} | 13 ++-- crates/nu-explore/src/views/mod.rs | 2 +- crates/nu-explore/src/views/preview.rs | 11 +-- crates/nu-explore/src/views/record/mod.rs | 12 ++-- .../record/{tablew.rs => table_widget.rs} | 68 +++++++++---------- 5 files changed, 55 insertions(+), 51 deletions(-) rename crates/nu-explore/src/views/{coloredtextw.rs => colored_text_widget.rs} (89%) rename crates/nu-explore/src/views/record/{tablew.rs => table_widget.rs} (92%) diff --git a/crates/nu-explore/src/views/coloredtextw.rs b/crates/nu-explore/src/views/colored_text_widget.rs similarity index 89% rename from crates/nu-explore/src/views/coloredtextw.rs rename to crates/nu-explore/src/views/colored_text_widget.rs index 93ef68f38f..4b1398e713 100644 --- a/crates/nu-explore/src/views/coloredtextw.rs +++ b/crates/nu-explore/src/views/colored_text_widget.rs @@ -8,24 +8,27 @@ use ratatui::{ widgets::Widget, }; -pub struct ColoredTextW<'a> { +/// A widget that represents a single line of text with ANSI styles. +pub struct ColoredTextWidget<'a> { text: &'a str, + /// Column to start rendering from col: usize, } -impl<'a> ColoredTextW<'a> { +impl<'a> ColoredTextWidget<'a> { pub fn new(text: &'a str, col: usize) -> Self { Self { text, col } } - pub fn what(&self, area: Rect) -> String { - cut_string(self.text, self.col, area.width as usize) + /// Return a window of the text that fits into the given width, with ANSI styles stripped. + pub fn get_plain_text(&self, max_width: usize) -> String { + cut_string(self.text, self.col, max_width) .ansi_strip() .into_owned() } } -impl Widget for ColoredTextW<'_> { +impl Widget for ColoredTextWidget<'_> { fn render(self, area: Rect, buf: &mut ratatui::buffer::Buffer) { let text = cut_string(self.text, self.col, area.width as usize); diff --git a/crates/nu-explore/src/views/mod.rs b/crates/nu-explore/src/views/mod.rs index 3226f546d2..dd0a4a3339 100644 --- a/crates/nu-explore/src/views/mod.rs +++ b/crates/nu-explore/src/views/mod.rs @@ -1,5 +1,5 @@ mod binary; -mod coloredtextw; +mod colored_text_widget; mod cursor; mod interactive; mod preview; diff --git a/crates/nu-explore/src/views/preview.rs b/crates/nu-explore/src/views/preview.rs index 2bd495144b..360cfbd226 100644 --- a/crates/nu-explore/src/views/preview.rs +++ b/crates/nu-explore/src/views/preview.rs @@ -1,4 +1,4 @@ -use super::{coloredtextw::ColoredTextW, cursor::XYCursor, Layout, View, ViewConfig}; +use super::{colored_text_widget::ColoredTextWidget, cursor::XYCursor, Layout, View, ViewConfig}; use crate::{ nu_common::{NuSpan, NuText}, pager::{report::Report, Frame, Transition, ViewInfo}, @@ -43,13 +43,14 @@ impl View for Preview { let lines = &self.lines[self.cursor.row_starts_at()..]; for (i, line) in lines.iter().enumerate().take(area.height as usize) { - let text = ColoredTextW::new(line, self.cursor.column()); - let s = text.what(area); + let text_widget = ColoredTextWidget::new(line, self.cursor.column()); + let plain_text = text_widget.get_plain_text(area.width as usize); let area = Rect::new(area.x, area.y + i as u16, area.width, 1); - f.render_widget(text, area); + f.render_widget(text_widget, area); - layout.push(&s, area.x, area.y, area.width, area.height); + // push the plain text to layout so it can be searched + layout.push(&plain_text, area.x, area.y, area.width, area.height); } } diff --git a/crates/nu-explore/src/views/record/mod.rs b/crates/nu-explore/src/views/record/mod.rs index 2ad3140bf4..5bd74c2329 100644 --- a/crates/nu-explore/src/views/record/mod.rs +++ b/crates/nu-explore/src/views/record/mod.rs @@ -1,6 +1,6 @@ -mod tablew; +mod table_widget; -use self::tablew::{TableStyle, TableW, TableWState}; +use self::table_widget::{TableStyle, TableWidget, TableWidgetState}; use super::{ cursor::XYCursor, util::{make_styled_string, nu_style_to_tui}, @@ -25,7 +25,7 @@ use nu_protocol::{ use ratatui::{layout::Rect, widgets::Block}; use std::{borrow::Cow, collections::HashMap}; -pub use self::tablew::Orientation; +pub use self::table_widget::Orientation; #[derive(Debug, Clone)] pub struct RecordView<'a> { @@ -175,7 +175,7 @@ impl<'a> RecordView<'a> { } } - fn create_tablew(&'a self, cfg: ViewConfig<'a>) -> TableW<'a> { + fn create_tablew(&'a self, cfg: ViewConfig<'a>) -> TableWidget<'a> { let layer = self.get_layer_last(); let mut data = convert_records_to_string(&layer.records, cfg.nu_config, cfg.style_computer); @@ -185,7 +185,7 @@ impl<'a> RecordView<'a> { let style_computer = cfg.style_computer; let (row, column) = self.get_current_offset(); - TableW::new( + TableWidget::new( headers, data, style_computer, @@ -225,7 +225,7 @@ impl<'a> RecordView<'a> { impl View for RecordView<'_> { fn draw(&mut self, f: &mut Frame, area: Rect, cfg: ViewConfig<'_>, layout: &mut Layout) { - let mut table_layout = TableWState::default(); + let mut table_layout = TableWidgetState::default(); let table = self.create_tablew(cfg); f.render_stateful_widget(table, area, &mut table_layout); diff --git a/crates/nu-explore/src/views/record/tablew.rs b/crates/nu-explore/src/views/record/table_widget.rs similarity index 92% rename from crates/nu-explore/src/views/record/tablew.rs rename to crates/nu-explore/src/views/record/table_widget.rs index b4b4fe4514..70debfd888 100644 --- a/crates/nu-explore/src/views/record/tablew.rs +++ b/crates/nu-explore/src/views/record/table_widget.rs @@ -18,13 +18,13 @@ use std::{ }; #[derive(Debug, Clone)] -pub struct TableW<'a> { +pub struct TableWidget<'a> { columns: Cow<'a, [String]>, data: Cow<'a, [Vec]>, index_row: usize, index_column: usize, style: TableStyle, - head_position: Orientation, + header_position: Orientation, style_computer: &'a StyleComputer<'a>, } @@ -38,14 +38,13 @@ pub enum Orientation { #[derive(Debug, Default, Clone, Copy)] pub struct TableStyle { pub splitline_style: NuStyle, - pub shift_line_style: NuStyle, pub show_index: bool, pub show_header: bool, pub column_padding_left: usize, pub column_padding_right: usize, } -impl<'a> TableW<'a> { +impl<'a> TableWidget<'a> { #[allow(clippy::too_many_arguments)] pub fn new( columns: impl Into>, @@ -54,7 +53,7 @@ impl<'a> TableW<'a> { index_row: usize, index_column: usize, style: TableStyle, - head_position: Orientation, + header_position: Orientation, ) -> Self { Self { columns: columns.into(), @@ -63,21 +62,21 @@ impl<'a> TableW<'a> { index_row, index_column, style, - head_position, + header_position, } } } #[derive(Debug, Default)] -pub struct TableWState { +pub struct TableWidgetState { pub layout: Layout, pub count_rows: usize, pub count_columns: usize, pub data_height: u16, } -impl StatefulWidget for TableW<'_> { - type State = TableWState; +impl StatefulWidget for TableWidget<'_> { + type State = TableWidgetState; fn render( self, @@ -89,7 +88,7 @@ impl StatefulWidget for TableW<'_> { return; } - let is_horizontal = matches!(self.head_position, Orientation::Top); + let is_horizontal = matches!(self.header_position, Orientation::Top); if is_horizontal { self.render_table_horizontal(area, buf, state); } else { @@ -99,8 +98,8 @@ impl StatefulWidget for TableW<'_> { } // todo: refactoring these to methods as they have quite a bit in common. -impl<'a> TableW<'a> { - fn render_table_horizontal(self, area: Rect, buf: &mut Buffer, state: &mut TableWState) { +impl<'a> TableWidget<'a> { + fn render_table_horizontal(self, area: Rect, buf: &mut Buffer, state: &mut TableWidgetState) { let padding_l = self.style.column_padding_left as u16; let padding_r = self.style.column_padding_right as u16; @@ -108,7 +107,6 @@ impl<'a> TableW<'a> { let show_head = self.style.show_header; let splitline_s = self.style.splitline_style; - let shift_column_s = self.style.shift_line_style; let mut data_height = area.height; let mut data_y = area.y; @@ -164,7 +162,8 @@ impl<'a> TableW<'a> { ); } - let mut do_render_shift_column = false; + // if there is more data than we can show, add an ellipsis to the column headers to hint at that + let mut show_overflow_indicator = false; state.count_rows = data.len(); state.count_columns = 0; state.data_height = data_height; @@ -191,11 +190,11 @@ impl<'a> TableW<'a> { let pad = padding_l + padding_r; let head = show_head.then_some(&mut head); - let (w, ok, shift) = + let (w, ok, overflow) = truncate_column_width(space, 1, use_space, pad, is_last, &mut column, head); - if shift { - do_render_shift_column = true; + if overflow { + show_overflow_indicator = true; } if w == 0 && !ok { @@ -232,14 +231,14 @@ impl<'a> TableW<'a> { state.count_columns += 1; - if do_render_shift_column { + if show_overflow_indicator { break; } } - if do_render_shift_column && show_head { + if show_overflow_indicator && show_head { width += render_space(buf, width, data_y, data_height, padding_l); - width += render_shift_column(buf, width, head_y, 1, shift_column_s); + width += render_overflow_column(buf, width, head_y, 1); width += render_space(buf, width, data_y, data_height, padding_r); } @@ -264,7 +263,7 @@ impl<'a> TableW<'a> { } } - fn render_table_vertical(self, area: Rect, buf: &mut Buffer, state: &mut TableWState) { + fn render_table_vertical(self, area: Rect, buf: &mut Buffer, state: &mut TableWidgetState) { if area.width == 0 || area.height == 0 { return; } @@ -275,7 +274,6 @@ impl<'a> TableW<'a> { let show_index = self.style.show_index; let show_head = self.style.show_header; let splitline_s = self.style.splitline_style; - let shift_column_s = self.style.shift_line_style; let mut left_w = 0; @@ -358,7 +356,8 @@ impl<'a> TableW<'a> { ); } - let mut do_render_shift_column = false; + // if there is more data than we can show, add an ellipsis to the column headers to hint at that + let mut show_overflow_indicator = false; state.count_rows = columns.len(); state.count_columns = 0; @@ -375,11 +374,11 @@ impl<'a> TableW<'a> { let available = area.width - left_w; let is_last = col + 1 == self.data.len(); let pad = padding_l + padding_r; - let (column_width, ok, shift) = + let (column_width, ok, overflow) = truncate_column_width(available, 1, column_width, pad, is_last, &mut column, None); - if shift { - do_render_shift_column = true; + if overflow { + show_overflow_indicator = true; } if column_width == 0 && !ok { @@ -403,16 +402,16 @@ impl<'a> TableW<'a> { state.count_columns += 1; } - if do_render_shift_column { + if show_overflow_indicator { break; } } - if do_render_shift_column { + if show_overflow_indicator { let x = area.x + left_w; left_w += render_space(buf, x, area.y, area.height, padding_l); let x = area.x + left_w; - left_w += render_shift_column(buf, x, area.y, area.height, shift_column_s); + left_w += render_overflow_column(buf, x, area.y, area.height); let x = area.x + left_w; left_w += render_space(buf, x, area.y, area.height, padding_r); } @@ -433,13 +432,13 @@ fn truncate_column_width( ) -> (u16, bool, bool) { let result = check_column_width(space, min, w, pad, is_last); - let (width, shift_column) = match result { + let (width, overflow) = match result { Some(result) => result, None => return (w, true, false), }; if width == 0 { - return (0, false, shift_column); + return (0, false, overflow); } truncate_list(column, width as usize); @@ -447,7 +446,7 @@ fn truncate_column_width( truncate_str(head, width as usize); } - (width, false, shift_column) + (width, false, overflow) } fn check_column_width( @@ -652,10 +651,11 @@ fn truncate_list(list: &mut [NuText], width: usize) { } } -fn render_shift_column(buf: &mut Buffer, x: u16, y: u16, height: u16, style: NuStyle) -> u16 { +/// Render a column with an ellipsis in the header to indicate that there is more data than can be displayed +fn render_overflow_column(buf: &mut Buffer, x: u16, y: u16, height: u16) -> u16 { let style = TextStyle { alignment: Alignment::Left, - color_style: Some(style), + color_style: None, }; repeat_vertical(buf, x, y, 1, height, '…', style);