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.
This commit is contained in:
Reilly Wood 2024-05-23 06:51:39 -07:00 committed by GitHub
parent f53aa6fcbf
commit 0b5a4c0d95
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 55 additions and 51 deletions

View File

@ -8,24 +8,27 @@ use ratatui::{
widgets::Widget, 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, text: &'a str,
/// Column to start rendering from
col: usize, col: usize,
} }
impl<'a> ColoredTextW<'a> { impl<'a> ColoredTextWidget<'a> {
pub fn new(text: &'a str, col: usize) -> Self { pub fn new(text: &'a str, col: usize) -> Self {
Self { text, col } Self { text, col }
} }
pub fn what(&self, area: Rect) -> String { /// Return a window of the text that fits into the given width, with ANSI styles stripped.
cut_string(self.text, self.col, area.width as usize) pub fn get_plain_text(&self, max_width: usize) -> String {
cut_string(self.text, self.col, max_width)
.ansi_strip() .ansi_strip()
.into_owned() .into_owned()
} }
} }
impl Widget for ColoredTextW<'_> { impl Widget for ColoredTextWidget<'_> {
fn render(self, area: Rect, buf: &mut ratatui::buffer::Buffer) { fn render(self, area: Rect, buf: &mut ratatui::buffer::Buffer) {
let text = cut_string(self.text, self.col, area.width as usize); let text = cut_string(self.text, self.col, area.width as usize);

View File

@ -1,5 +1,5 @@
mod binary; mod binary;
mod coloredtextw; mod colored_text_widget;
mod cursor; mod cursor;
mod interactive; mod interactive;
mod preview; mod preview;

View File

@ -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::{ use crate::{
nu_common::{NuSpan, NuText}, nu_common::{NuSpan, NuText},
pager::{report::Report, Frame, Transition, ViewInfo}, pager::{report::Report, Frame, Transition, ViewInfo},
@ -43,13 +43,14 @@ impl View for Preview {
let lines = &self.lines[self.cursor.row_starts_at()..]; let lines = &self.lines[self.cursor.row_starts_at()..];
for (i, line) in lines.iter().enumerate().take(area.height as usize) { for (i, line) in lines.iter().enumerate().take(area.height as usize) {
let text = ColoredTextW::new(line, self.cursor.column()); let text_widget = ColoredTextWidget::new(line, self.cursor.column());
let s = text.what(area); 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); 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);
} }
} }

View File

@ -1,6 +1,6 @@
mod tablew; mod table_widget;
use self::tablew::{TableStyle, TableW, TableWState}; use self::table_widget::{TableStyle, TableWidget, TableWidgetState};
use super::{ use super::{
cursor::XYCursor, cursor::XYCursor,
util::{make_styled_string, nu_style_to_tui}, util::{make_styled_string, nu_style_to_tui},
@ -25,7 +25,7 @@ use nu_protocol::{
use ratatui::{layout::Rect, widgets::Block}; use ratatui::{layout::Rect, widgets::Block};
use std::{borrow::Cow, collections::HashMap}; use std::{borrow::Cow, collections::HashMap};
pub use self::tablew::Orientation; pub use self::table_widget::Orientation;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct RecordView<'a> { 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 layer = self.get_layer_last();
let mut data = convert_records_to_string(&layer.records, cfg.nu_config, cfg.style_computer); 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 style_computer = cfg.style_computer;
let (row, column) = self.get_current_offset(); let (row, column) = self.get_current_offset();
TableW::new( TableWidget::new(
headers, headers,
data, data,
style_computer, style_computer,
@ -225,7 +225,7 @@ impl<'a> RecordView<'a> {
impl View for RecordView<'_> { impl View for RecordView<'_> {
fn draw(&mut self, f: &mut Frame, area: Rect, cfg: ViewConfig<'_>, layout: &mut Layout) { 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); let table = self.create_tablew(cfg);
f.render_stateful_widget(table, area, &mut table_layout); f.render_stateful_widget(table, area, &mut table_layout);

View File

@ -18,13 +18,13 @@ use std::{
}; };
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct TableW<'a> { pub struct TableWidget<'a> {
columns: Cow<'a, [String]>, columns: Cow<'a, [String]>,
data: Cow<'a, [Vec<NuText>]>, data: Cow<'a, [Vec<NuText>]>,
index_row: usize, index_row: usize,
index_column: usize, index_column: usize,
style: TableStyle, style: TableStyle,
head_position: Orientation, header_position: Orientation,
style_computer: &'a StyleComputer<'a>, style_computer: &'a StyleComputer<'a>,
} }
@ -38,14 +38,13 @@ pub enum Orientation {
#[derive(Debug, Default, Clone, Copy)] #[derive(Debug, Default, Clone, Copy)]
pub struct TableStyle { pub struct TableStyle {
pub splitline_style: NuStyle, pub splitline_style: NuStyle,
pub shift_line_style: NuStyle,
pub show_index: bool, pub show_index: bool,
pub show_header: bool, pub show_header: bool,
pub column_padding_left: usize, pub column_padding_left: usize,
pub column_padding_right: usize, pub column_padding_right: usize,
} }
impl<'a> TableW<'a> { impl<'a> TableWidget<'a> {
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub fn new( pub fn new(
columns: impl Into<Cow<'a, [String]>>, columns: impl Into<Cow<'a, [String]>>,
@ -54,7 +53,7 @@ impl<'a> TableW<'a> {
index_row: usize, index_row: usize,
index_column: usize, index_column: usize,
style: TableStyle, style: TableStyle,
head_position: Orientation, header_position: Orientation,
) -> Self { ) -> Self {
Self { Self {
columns: columns.into(), columns: columns.into(),
@ -63,21 +62,21 @@ impl<'a> TableW<'a> {
index_row, index_row,
index_column, index_column,
style, style,
head_position, header_position,
} }
} }
} }
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct TableWState { pub struct TableWidgetState {
pub layout: Layout, pub layout: Layout,
pub count_rows: usize, pub count_rows: usize,
pub count_columns: usize, pub count_columns: usize,
pub data_height: u16, pub data_height: u16,
} }
impl StatefulWidget for TableW<'_> { impl StatefulWidget for TableWidget<'_> {
type State = TableWState; type State = TableWidgetState;
fn render( fn render(
self, self,
@ -89,7 +88,7 @@ impl StatefulWidget for TableW<'_> {
return; return;
} }
let is_horizontal = matches!(self.head_position, Orientation::Top); let is_horizontal = matches!(self.header_position, Orientation::Top);
if is_horizontal { if is_horizontal {
self.render_table_horizontal(area, buf, state); self.render_table_horizontal(area, buf, state);
} else { } else {
@ -99,8 +98,8 @@ impl StatefulWidget for TableW<'_> {
} }
// todo: refactoring these to methods as they have quite a bit in common. // todo: refactoring these to methods as they have quite a bit in common.
impl<'a> TableW<'a> { impl<'a> TableWidget<'a> {
fn render_table_horizontal(self, area: Rect, buf: &mut Buffer, state: &mut TableWState) { 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_l = self.style.column_padding_left as u16;
let padding_r = self.style.column_padding_right 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 show_head = self.style.show_header;
let splitline_s = self.style.splitline_style; 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_height = area.height;
let mut data_y = area.y; 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_rows = data.len();
state.count_columns = 0; state.count_columns = 0;
state.data_height = data_height; state.data_height = data_height;
@ -191,11 +190,11 @@ impl<'a> TableW<'a> {
let pad = padding_l + padding_r; let pad = padding_l + padding_r;
let head = show_head.then_some(&mut head); 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); truncate_column_width(space, 1, use_space, pad, is_last, &mut column, head);
if shift { if overflow {
do_render_shift_column = true; show_overflow_indicator = true;
} }
if w == 0 && !ok { if w == 0 && !ok {
@ -232,14 +231,14 @@ impl<'a> TableW<'a> {
state.count_columns += 1; state.count_columns += 1;
if do_render_shift_column { if show_overflow_indicator {
break; 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_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); 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 { if area.width == 0 || area.height == 0 {
return; return;
} }
@ -275,7 +274,6 @@ impl<'a> TableW<'a> {
let show_index = self.style.show_index; let show_index = self.style.show_index;
let show_head = self.style.show_header; let show_head = self.style.show_header;
let splitline_s = self.style.splitline_style; let splitline_s = self.style.splitline_style;
let shift_column_s = self.style.shift_line_style;
let mut left_w = 0; 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_rows = columns.len();
state.count_columns = 0; state.count_columns = 0;
@ -375,11 +374,11 @@ impl<'a> TableW<'a> {
let available = area.width - left_w; let available = area.width - left_w;
let is_last = col + 1 == self.data.len(); let is_last = col + 1 == self.data.len();
let pad = padding_l + padding_r; 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); truncate_column_width(available, 1, column_width, pad, is_last, &mut column, None);
if shift { if overflow {
do_render_shift_column = true; show_overflow_indicator = true;
} }
if column_width == 0 && !ok { if column_width == 0 && !ok {
@ -403,16 +402,16 @@ impl<'a> TableW<'a> {
state.count_columns += 1; state.count_columns += 1;
} }
if do_render_shift_column { if show_overflow_indicator {
break; break;
} }
} }
if do_render_shift_column { if show_overflow_indicator {
let x = area.x + left_w; let x = area.x + left_w;
left_w += render_space(buf, x, area.y, area.height, padding_l); left_w += render_space(buf, x, area.y, area.height, padding_l);
let x = area.x + left_w; 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; let x = area.x + left_w;
left_w += render_space(buf, x, area.y, area.height, padding_r); left_w += render_space(buf, x, area.y, area.height, padding_r);
} }
@ -433,13 +432,13 @@ fn truncate_column_width(
) -> (u16, bool, bool) { ) -> (u16, bool, bool) {
let result = check_column_width(space, min, w, pad, is_last); 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, Some(result) => result,
None => return (w, true, false), None => return (w, true, false),
}; };
if width == 0 { if width == 0 {
return (0, false, shift_column); return (0, false, overflow);
} }
truncate_list(column, width as usize); truncate_list(column, width as usize);
@ -447,7 +446,7 @@ fn truncate_column_width(
truncate_str(head, width as usize); truncate_str(head, width as usize);
} }
(width, false, shift_column) (width, false, overflow)
} }
fn check_column_width( 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 { let style = TextStyle {
alignment: Alignment::Left, alignment: Alignment::Left,
color_style: Some(style), color_style: None,
}; };
repeat_vertical(buf, x, y, 1, height, '…', style); repeat_vertical(buf, x, y, 1, height, '…', style);