mirror of
https://github.com/nushell/nushell.git
synced 2024-11-22 08:23:24 +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),
|
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
|
// actually must never happen; unless cursor works incorrectly
|
||||||
// if being sure about cursor it can be deleted;
|
// if being sure about cursor it can be deleted;
|
||||||
return Value::nothing(Span::unknown());
|
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
|
/// scroll to the end of the data
|
||||||
fn tail_data(state: &mut RecordView, page_size: usize) {
|
fn tail_data(state: &mut RecordView, page_size: usize) {
|
||||||
let layer = state.get_layer_last_mut();
|
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 {
|
if count_rows > page_size {
|
||||||
layer
|
layer
|
||||||
.cursor
|
.cursor
|
||||||
@ -722,10 +722,24 @@ fn get_percentage(value: usize, max: usize) -> usize {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn transpose_table(layer: &mut RecordLayer) {
|
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_rows = layer.record_values.len();
|
||||||
let count_columns = layer.column_names.len();
|
let count_columns = layer.column_names.len();
|
||||||
|
|
||||||
if layer.was_transposed {
|
if let Some(data) = &mut layer.record_text {
|
||||||
|
pop_first_column(data);
|
||||||
|
*data = _transpose_table(data, count_rows, count_columns - 1);
|
||||||
|
}
|
||||||
|
|
||||||
let headers = pop_first_column(&mut layer.record_values);
|
let headers = pop_first_column(&mut layer.record_values);
|
||||||
let headers = headers
|
let headers = headers
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@ -739,26 +753,35 @@ fn transpose_table(layer: &mut RecordLayer) {
|
|||||||
|
|
||||||
layer.record_values = data;
|
layer.record_values = data;
|
||||||
layer.column_names = headers;
|
layer.column_names = headers;
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
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);
|
let mut data = _transpose_table(&layer.record_values, count_rows, count_columns);
|
||||||
|
|
||||||
for (column, column_name) in layer.column_names.iter().enumerate() {
|
for (column, column_name) in layer.column_names.iter().enumerate() {
|
||||||
let value = Value::string(column_name, NuSpan::unknown());
|
let value = Value::string(column_name, NuSpan::unknown());
|
||||||
|
|
||||||
data[column].insert(0, value);
|
data[column].insert(0, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
layer.record_values = data;
|
layer.record_values = data;
|
||||||
layer.column_names = (1..count_rows + 1 + 1).map(|i| i.to_string()).collect();
|
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> {
|
fn pop_first_column<T>(values: &mut [Vec<T>]) -> Vec<T>
|
||||||
let mut data = vec![Value::default(); values.len()];
|
where
|
||||||
|
T: Default + Clone,
|
||||||
|
{
|
||||||
|
let mut data = vec![T::default(); values.len()];
|
||||||
for (row, values) in values.iter_mut().enumerate() {
|
for (row, values) in values.iter_mut().enumerate() {
|
||||||
data[row] = values.remove(0);
|
data[row] = values.remove(0);
|
||||||
}
|
}
|
||||||
@ -766,12 +789,11 @@ fn pop_first_column(values: &mut [Vec<Value>]) -> Vec<Value> {
|
|||||||
data
|
data
|
||||||
}
|
}
|
||||||
|
|
||||||
fn _transpose_table(
|
fn _transpose_table<T>(values: &[Vec<T>], count_rows: usize, count_columns: usize) -> Vec<Vec<T>>
|
||||||
values: &[Vec<Value>],
|
where
|
||||||
count_rows: usize,
|
T: Clone + Default,
|
||||||
count_columns: usize,
|
{
|
||||||
) -> Vec<Vec<Value>> {
|
let mut data = vec![vec![T::default(); count_rows]; count_columns];
|
||||||
let mut data = vec![vec![Value::default(); count_rows]; count_columns];
|
|
||||||
for (row, values) in values.iter().enumerate() {
|
for (row, values) in values.iter().enumerate() {
|
||||||
for (column, value) in values.iter().enumerate() {
|
for (column, value) in values.iter().enumerate() {
|
||||||
data[column][row].clone_from(value);
|
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.
|
// todo: refactoring these to methods as they have quite a bit in common.
|
||||||
impl<'a> TableWidget<'a> {
|
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) {
|
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_l = self.config.column_padding_left as u16;
|
||||||
let padding_r = self.config.column_padding_right as u16;
|
let padding_r = self.config.column_padding_right as u16;
|
||||||
@ -130,25 +131,16 @@ impl<'a> TableWidget<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if show_index {
|
if show_index {
|
||||||
let area = Rect::new(width, data_y, area.width, data_height);
|
|
||||||
width += render_index(
|
width += render_index(
|
||||||
buf,
|
buf,
|
||||||
area,
|
Rect::new(width, data_y, area.width, data_height),
|
||||||
self.style_computer,
|
self.style_computer,
|
||||||
self.index_row,
|
self.index_row,
|
||||||
padding_l,
|
padding_l,
|
||||||
padding_r,
|
padding_r,
|
||||||
);
|
);
|
||||||
|
|
||||||
width += render_vertical_line_with_split(
|
width += render_split_line(buf, width, area.y, area.height, show_head, separator_s);
|
||||||
buf,
|
|
||||||
width,
|
|
||||||
data_y,
|
|
||||||
data_height,
|
|
||||||
show_head,
|
|
||||||
false,
|
|
||||||
separator_s,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// if there is more data than we can show, add an ellipsis to the column headers to hint at that
|
// 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() {
|
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 mut column = create_column(data, col);
|
||||||
let column_width = calculate_column_width(&column);
|
let column_width = calculate_column_width(&column);
|
||||||
|
|
||||||
@ -200,6 +197,7 @@ impl<'a> TableWidget<'a> {
|
|||||||
}
|
}
|
||||||
let head_iter = [(&head, head_style)].into_iter();
|
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;
|
let mut w = width;
|
||||||
w += render_space(buf, w, head_y, 1, padding_l);
|
w += render_space(buf, w, head_y, 1, padding_l);
|
||||||
w += render_column(buf, w, head_y, use_space, head_iter);
|
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);
|
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_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);
|
width += render_space(buf, width, data_y, data_height, padding_r);
|
||||||
|
|
||||||
for (row, (text, _)) in column.iter().enumerate() {
|
for (row, (text, _)) in column.iter().enumerate() {
|
||||||
@ -235,15 +233,7 @@ impl<'a> TableWidget<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if width < area.width {
|
if width < area.width {
|
||||||
width += render_vertical_line_with_split(
|
width += render_split_line(buf, width, area.y, area.height, show_head, separator_s);
|
||||||
buf,
|
|
||||||
width,
|
|
||||||
data_y,
|
|
||||||
data_height,
|
|
||||||
show_head,
|
|
||||||
false,
|
|
||||||
separator_s,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let rest = area.width.saturating_sub(width);
|
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) {
|
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;
|
||||||
@ -353,6 +344,9 @@ impl<'a> TableWidget<'a> {
|
|||||||
state.count_rows = columns.len();
|
state.count_rows = columns.len();
|
||||||
state.count_columns = 0;
|
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() {
|
for col in self.index_column..self.data.len() {
|
||||||
let mut column =
|
let mut column =
|
||||||
self.data[col][self.index_row..self.index_row + columns.len()].to_vec();
|
self.data[col][self.index_row..self.index_row + columns.len()].to_vec();
|
||||||
@ -361,6 +355,13 @@ impl<'a> TableWidget<'a> {
|
|||||||
break;
|
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 column_width = column_width as u16;
|
||||||
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();
|
||||||
@ -555,6 +556,51 @@ fn render_index(
|
|||||||
width
|
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(
|
fn render_vertical_line_with_split(
|
||||||
buf: &mut Buffer,
|
buf: &mut Buffer,
|
||||||
x: u16,
|
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);
|
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 {
|
fn calculate_column_width(column: &[NuText]) -> usize {
|
||||||
column
|
column
|
||||||
.iter()
|
.iter()
|
||||||
|
Loading…
Reference in New Issue
Block a user