mirror of
https://github.com/nushell/nushell.git
synced 2024-11-22 00:13:21 +01:00
nu-explore: Add vertical lines && fix index/transpose issue (#13147)
Somehow I believe that split lines were implemented originally; (I haven't got to find it though; from a quick look) I mean a long time ago before a lot a changes were made. Probably adding horizontal lines would make also some sense. ref #13116 close #13140 Take care ________________ If `explore` is used, frequently, or planned to be so. I guess it would be a good one to create a test suite for it; to not break things occasionally 😅 I did approached it one time back then using `expectrl` (literally `expect`), but there was some issues. Maybe smth. did change. Or some `clean` mode could be introduced for it, to being able to be used by outer programs; to control `nu`. Just thoughts here.
This commit is contained in:
parent
91d44f15c1
commit
10e84038af
@ -131,7 +131,7 @@ impl RecordView {
|
||||
Orientation::Left => (column, row),
|
||||
};
|
||||
|
||||
if row >= layer.count_rows() || column >= layer.count_columns() {
|
||||
if row >= layer.record_values.len() || column >= layer.column_names.len() {
|
||||
// actually must never happen; unless cursor works incorrectly
|
||||
// if being sure about cursor it can be deleted;
|
||||
return Value::nothing(Span::unknown());
|
||||
@ -610,7 +610,7 @@ fn estimate_page_size(area: Rect, show_head: bool) -> u16 {
|
||||
/// scroll to the end of the data
|
||||
fn tail_data(state: &mut RecordView, page_size: usize) {
|
||||
let layer = state.get_layer_last_mut();
|
||||
let count_rows = layer.count_rows();
|
||||
let count_rows = layer.record_values.len();
|
||||
if count_rows > page_size {
|
||||
layer
|
||||
.cursor
|
||||
@ -722,43 +722,66 @@ fn get_percentage(value: usize, max: usize) -> usize {
|
||||
}
|
||||
|
||||
fn transpose_table(layer: &mut RecordLayer) {
|
||||
if layer.was_transposed {
|
||||
transpose_from(layer);
|
||||
} else {
|
||||
transpose_to(layer);
|
||||
}
|
||||
|
||||
layer.was_transposed = !layer.was_transposed;
|
||||
}
|
||||
|
||||
fn transpose_from(layer: &mut RecordLayer) {
|
||||
let count_rows = layer.record_values.len();
|
||||
let count_columns = layer.column_names.len();
|
||||
|
||||
if layer.was_transposed {
|
||||
let headers = pop_first_column(&mut layer.record_values);
|
||||
let headers = headers
|
||||
.into_iter()
|
||||
.map(|value| match value {
|
||||
Value::String { val, .. } => val,
|
||||
_ => unreachable!("must never happen"),
|
||||
})
|
||||
.collect();
|
||||
if let Some(data) = &mut layer.record_text {
|
||||
pop_first_column(data);
|
||||
*data = _transpose_table(data, count_rows, count_columns - 1);
|
||||
}
|
||||
|
||||
let data = _transpose_table(&layer.record_values, count_rows, count_columns - 1);
|
||||
let headers = pop_first_column(&mut layer.record_values);
|
||||
let headers = headers
|
||||
.into_iter()
|
||||
.map(|value| match value {
|
||||
Value::String { val, .. } => val,
|
||||
_ => unreachable!("must never happen"),
|
||||
})
|
||||
.collect();
|
||||
|
||||
layer.record_values = data;
|
||||
layer.column_names = headers;
|
||||
let data = _transpose_table(&layer.record_values, count_rows, count_columns - 1);
|
||||
|
||||
return;
|
||||
layer.record_values = data;
|
||||
layer.column_names = headers;
|
||||
}
|
||||
|
||||
fn transpose_to(layer: &mut RecordLayer) {
|
||||
let count_rows = layer.record_values.len();
|
||||
let count_columns = layer.column_names.len();
|
||||
|
||||
if let Some(data) = &mut layer.record_text {
|
||||
*data = _transpose_table(data, count_rows, count_columns);
|
||||
for (column, column_name) in layer.column_names.iter().enumerate() {
|
||||
let value = (column_name.to_owned(), Default::default());
|
||||
data[column].insert(0, value);
|
||||
}
|
||||
}
|
||||
|
||||
let mut data = _transpose_table(&layer.record_values, count_rows, count_columns);
|
||||
|
||||
for (column, column_name) in layer.column_names.iter().enumerate() {
|
||||
let value = Value::string(column_name, NuSpan::unknown());
|
||||
|
||||
data[column].insert(0, value);
|
||||
}
|
||||
|
||||
layer.record_values = data;
|
||||
layer.column_names = (1..count_rows + 1 + 1).map(|i| i.to_string()).collect();
|
||||
|
||||
layer.was_transposed = !layer.was_transposed;
|
||||
}
|
||||
|
||||
fn pop_first_column(values: &mut [Vec<Value>]) -> Vec<Value> {
|
||||
let mut data = vec![Value::default(); values.len()];
|
||||
fn pop_first_column<T>(values: &mut [Vec<T>]) -> Vec<T>
|
||||
where
|
||||
T: Default + Clone,
|
||||
{
|
||||
let mut data = vec![T::default(); values.len()];
|
||||
for (row, values) in values.iter_mut().enumerate() {
|
||||
data[row] = values.remove(0);
|
||||
}
|
||||
@ -766,12 +789,11 @@ fn pop_first_column(values: &mut [Vec<Value>]) -> Vec<Value> {
|
||||
data
|
||||
}
|
||||
|
||||
fn _transpose_table(
|
||||
values: &[Vec<Value>],
|
||||
count_rows: usize,
|
||||
count_columns: usize,
|
||||
) -> Vec<Vec<Value>> {
|
||||
let mut data = vec![vec![Value::default(); count_rows]; count_columns];
|
||||
fn _transpose_table<T>(values: &[Vec<T>], count_rows: usize, count_columns: usize) -> Vec<Vec<T>>
|
||||
where
|
||||
T: Clone + Default,
|
||||
{
|
||||
let mut data = vec![vec![T::default(); count_rows]; count_columns];
|
||||
for (row, values) in values.iter().enumerate() {
|
||||
for (column, value) in values.iter().enumerate() {
|
||||
data[column][row].clone_from(value);
|
||||
|
@ -88,6 +88,7 @@ impl StatefulWidget for TableWidget<'_> {
|
||||
|
||||
// todo: refactoring these to methods as they have quite a bit in common.
|
||||
impl<'a> TableWidget<'a> {
|
||||
// header at the top; header is always 1 line
|
||||
fn render_table_horizontal(self, area: Rect, buf: &mut Buffer, state: &mut TableWidgetState) {
|
||||
let padding_l = self.config.column_padding_left as u16;
|
||||
let padding_r = self.config.column_padding_right as u16;
|
||||
@ -130,25 +131,16 @@ impl<'a> TableWidget<'a> {
|
||||
}
|
||||
|
||||
if show_index {
|
||||
let area = Rect::new(width, data_y, area.width, data_height);
|
||||
width += render_index(
|
||||
buf,
|
||||
area,
|
||||
Rect::new(width, data_y, area.width, data_height),
|
||||
self.style_computer,
|
||||
self.index_row,
|
||||
padding_l,
|
||||
padding_r,
|
||||
);
|
||||
|
||||
width += render_vertical_line_with_split(
|
||||
buf,
|
||||
width,
|
||||
data_y,
|
||||
data_height,
|
||||
show_head,
|
||||
false,
|
||||
separator_s,
|
||||
);
|
||||
width += render_split_line(buf, width, area.y, area.height, show_head, separator_s);
|
||||
}
|
||||
|
||||
// if there is more data than we can show, add an ellipsis to the column headers to hint at that
|
||||
@ -162,6 +154,11 @@ impl<'a> TableWidget<'a> {
|
||||
}
|
||||
|
||||
for col in self.index_column..self.columns.len() {
|
||||
let need_split_line = state.count_columns > 0 && width < area.width;
|
||||
if need_split_line {
|
||||
width += render_split_line(buf, width, area.y, area.height, show_head, separator_s);
|
||||
}
|
||||
|
||||
let mut column = create_column(data, col);
|
||||
let column_width = calculate_column_width(&column);
|
||||
|
||||
@ -200,6 +197,7 @@ impl<'a> TableWidget<'a> {
|
||||
}
|
||||
let head_iter = [(&head, head_style)].into_iter();
|
||||
|
||||
// we don't change width here cause the whole column have the same width; so we add it when we print data
|
||||
let mut w = width;
|
||||
w += render_space(buf, w, head_y, 1, padding_l);
|
||||
w += render_column(buf, w, head_y, use_space, head_iter);
|
||||
@ -209,10 +207,10 @@ impl<'a> TableWidget<'a> {
|
||||
state.layout.push(&head, x, head_y, use_space, 1);
|
||||
}
|
||||
|
||||
let head_rows = column.iter().map(|(t, s)| (t, *s));
|
||||
let column_rows = column.iter().map(|(t, s)| (t, *s));
|
||||
|
||||
width += render_space(buf, width, data_y, data_height, padding_l);
|
||||
width += render_column(buf, width, data_y, use_space, head_rows);
|
||||
width += render_column(buf, width, data_y, use_space, column_rows);
|
||||
width += render_space(buf, width, data_y, data_height, padding_r);
|
||||
|
||||
for (row, (text, _)) in column.iter().enumerate() {
|
||||
@ -235,15 +233,7 @@ impl<'a> TableWidget<'a> {
|
||||
}
|
||||
|
||||
if width < area.width {
|
||||
width += render_vertical_line_with_split(
|
||||
buf,
|
||||
width,
|
||||
data_y,
|
||||
data_height,
|
||||
show_head,
|
||||
false,
|
||||
separator_s,
|
||||
);
|
||||
width += render_split_line(buf, width, area.y, area.height, show_head, separator_s);
|
||||
}
|
||||
|
||||
let rest = area.width.saturating_sub(width);
|
||||
@ -255,6 +245,7 @@ impl<'a> TableWidget<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
// header at the left; header is always 1 line
|
||||
fn render_table_vertical(self, area: Rect, buf: &mut Buffer, state: &mut TableWidgetState) {
|
||||
if area.width == 0 || area.height == 0 {
|
||||
return;
|
||||
@ -353,6 +344,9 @@ impl<'a> TableWidget<'a> {
|
||||
state.count_rows = columns.len();
|
||||
state.count_columns = 0;
|
||||
|
||||
// note: is there a time where we would have more then 1 column?
|
||||
// seems like not really; cause it's literally KV table, or am I wrong?
|
||||
|
||||
for col in self.index_column..self.data.len() {
|
||||
let mut column =
|
||||
self.data[col][self.index_row..self.index_row + columns.len()].to_vec();
|
||||
@ -361,6 +355,13 @@ impl<'a> TableWidget<'a> {
|
||||
break;
|
||||
}
|
||||
|
||||
// see KV comment; this block might never got used
|
||||
let need_split_line = state.count_columns > 0 && left_w < area.width;
|
||||
if need_split_line {
|
||||
render_vertical_line(buf, area.x + left_w, area.y, area.height, separator_s);
|
||||
left_w += 1;
|
||||
}
|
||||
|
||||
let column_width = column_width as u16;
|
||||
let available = area.width - left_w;
|
||||
let is_last = col + 1 == self.data.len();
|
||||
@ -555,6 +556,51 @@ fn render_index(
|
||||
width
|
||||
}
|
||||
|
||||
fn render_split_line(
|
||||
buf: &mut Buffer,
|
||||
x: u16,
|
||||
y: u16,
|
||||
height: u16,
|
||||
has_head: bool,
|
||||
style: NuStyle,
|
||||
) -> u16 {
|
||||
if has_head {
|
||||
render_vertical_split_line(buf, x, y, height, &[0], &[2], &[], style);
|
||||
} else {
|
||||
render_vertical_split_line(buf, x, y, height, &[], &[], &[], style);
|
||||
}
|
||||
|
||||
1
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn render_vertical_split_line(
|
||||
buf: &mut Buffer,
|
||||
x: u16,
|
||||
y: u16,
|
||||
height: u16,
|
||||
top_slit: &[u16],
|
||||
inner_slit: &[u16],
|
||||
bottom_slit: &[u16],
|
||||
style: NuStyle,
|
||||
) -> u16 {
|
||||
render_vertical_line(buf, x, y, height, style);
|
||||
|
||||
for &y in top_slit {
|
||||
render_top_connector(buf, x, y, style);
|
||||
}
|
||||
|
||||
for &y in inner_slit {
|
||||
render_inner_connector(buf, x, y, style);
|
||||
}
|
||||
|
||||
for &y in bottom_slit {
|
||||
render_bottom_connector(buf, x, y, style);
|
||||
}
|
||||
|
||||
1
|
||||
}
|
||||
|
||||
fn render_vertical_line_with_split(
|
||||
buf: &mut Buffer,
|
||||
x: u16,
|
||||
@ -668,6 +714,12 @@ fn render_bottom_connector(buf: &mut Buffer, x: u16, y: u16, style: NuStyle) {
|
||||
buf.set_span(x, y, &span, 1);
|
||||
}
|
||||
|
||||
fn render_inner_connector(buf: &mut Buffer, x: u16, y: u16, style: NuStyle) {
|
||||
let style = nu_style_to_tui(style);
|
||||
let span = Span::styled("┼", style);
|
||||
buf.set_span(x, y, &span, 1);
|
||||
}
|
||||
|
||||
fn calculate_column_width(column: &[NuText]) -> usize {
|
||||
column
|
||||
.iter()
|
||||
|
Loading…
Reference in New Issue
Block a user