mirror of
https://github.com/nushell/nushell
synced 2024-12-27 05:23:11 +00:00
nu-table: Improve table -a
(#11905)
Hi there; Sorry took that long to respond. I guess it's good? It will consume the whole stream whether possible. I do believe it will be faster in WSL in general too (in a sense of whole buffer output), but its interesting issue probably needed to be separated. It was not very well explained as well. ```nushell > 0..2000 | table -a 2 ╭───┬──────╮ │ 0 │ 0 │ │ 1 │ 1 │ │ 2 │ ... │ │ 3 │ 1999 │ │ 4 │ 2000 │ ╰───┴──────╯ ``` Take care fix: #11845 cc: @fdncred
This commit is contained in:
parent
f7d647ac3c
commit
6be91a68f3
2 changed files with 146 additions and 82 deletions
|
@ -19,6 +19,7 @@ use nu_table::{
|
||||||
TableOutput,
|
TableOutput,
|
||||||
};
|
};
|
||||||
use nu_utils::get_ls_colors;
|
use nu_utils::get_ls_colors;
|
||||||
|
use std::collections::VecDeque;
|
||||||
use std::io::IsTerminal;
|
use std::io::IsTerminal;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
@ -270,9 +271,14 @@ fn parse_table_config(
|
||||||
let index = get_index_flag(call, state, stack)?;
|
let index = get_index_flag(call, state, stack)?;
|
||||||
|
|
||||||
let term_width = get_width_param(width_param);
|
let term_width = get_width_param(width_param);
|
||||||
let cfg = TableConfig::new(table_view, term_width, theme, abbrivation, index);
|
|
||||||
|
|
||||||
Ok(cfg)
|
Ok(TableConfig::new(
|
||||||
|
table_view,
|
||||||
|
term_width,
|
||||||
|
theme,
|
||||||
|
abbrivation,
|
||||||
|
index,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_index_flag(
|
fn get_index_flag(
|
||||||
|
@ -788,37 +794,26 @@ impl Iterator for PagingTableCreator {
|
||||||
type Item = Result<Vec<u8>, ShellError>;
|
type Item = Result<Vec<u8>, ShellError>;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
let mut batch = vec![];
|
let batch;
|
||||||
|
let end;
|
||||||
|
|
||||||
let start_time = Instant::now();
|
match self.cfg.abbreviation {
|
||||||
|
Some(abbr) => {
|
||||||
let mut idx = 0;
|
(batch, _, end) =
|
||||||
let mut reached_end = true;
|
stream_collect_abbriviated(&mut self.stream, abbr, self.ctrlc.clone());
|
||||||
|
|
||||||
// Pull from stream until time runs out or we have enough items
|
|
||||||
for item in self.stream.by_ref() {
|
|
||||||
batch.push(item);
|
|
||||||
idx += 1;
|
|
||||||
|
|
||||||
// If we've been buffering over a second, go ahead and send out what we have so far
|
|
||||||
if (Instant::now() - start_time).as_secs() >= 1 {
|
|
||||||
reached_end = false;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
None => {
|
||||||
if idx == STREAM_PAGE_SIZE {
|
// Pull from stream until time runs out or we have enough items
|
||||||
reached_end = false;
|
(batch, end) =
|
||||||
break;
|
stream_collect(&mut self.stream, STREAM_PAGE_SIZE, self.ctrlc.clone());
|
||||||
}
|
|
||||||
|
|
||||||
if nu_utils::ctrl_c::was_pressed(&self.ctrlc) {
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let batch_size = batch.len();
|
||||||
|
|
||||||
// Count how much elements were displayed and if end of stream was reached
|
// Count how much elements were displayed and if end of stream was reached
|
||||||
self.elements_displayed += idx;
|
self.elements_displayed += batch_size;
|
||||||
self.reached_end = self.reached_end || reached_end;
|
self.reached_end = self.reached_end || end;
|
||||||
|
|
||||||
if batch.is_empty() {
|
if batch.is_empty() {
|
||||||
// If this iterator has not displayed a single entry and reached its end (no more elements
|
// If this iterator has not displayed a single entry and reached its end (no more elements
|
||||||
|
@ -839,52 +834,113 @@ impl Iterator for PagingTableCreator {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(limit) = self.cfg.abbreviation {
|
|
||||||
// todo: could be optimized cause we already consumed the list there's no point in goint back to pagination;
|
|
||||||
|
|
||||||
if batch.len() > limit * 2 + 1 {
|
|
||||||
batch = abbreviate_list(
|
|
||||||
&batch,
|
|
||||||
limit,
|
|
||||||
Value::string(String::from("..."), Span::unknown()),
|
|
||||||
);
|
|
||||||
|
|
||||||
let is_record_list = batch[..limit]
|
|
||||||
.iter()
|
|
||||||
.all(|value| matches!(value, Value::Record { .. }))
|
|
||||||
&& batch[limit + 1..]
|
|
||||||
.iter()
|
|
||||||
.all(|value| matches!(value, Value::Record { .. }));
|
|
||||||
|
|
||||||
if limit > 0 && is_record_list {
|
|
||||||
// in case it's a record list we set a default text to each column instead of a single value.
|
|
||||||
|
|
||||||
let dummy: Record = batch[0]
|
|
||||||
.as_record()
|
|
||||||
.expect("ok")
|
|
||||||
.columns()
|
|
||||||
.map(|k| {
|
|
||||||
(
|
|
||||||
k.to_owned(),
|
|
||||||
Value::string(String::from("..."), Span::unknown()),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
batch[limit] = Value::record(dummy, Span::unknown());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let table = self.build_table(batch);
|
let table = self.build_table(batch);
|
||||||
|
|
||||||
self.row_offset += idx;
|
self.row_offset += batch_size;
|
||||||
|
|
||||||
let config = get_config(&self.engine_state, &self.stack);
|
let config = get_config(&self.engine_state, &self.stack);
|
||||||
convert_table_to_output(table, &config, &self.ctrlc, self.cfg.term_width)
|
convert_table_to_output(table, &config, &self.ctrlc, self.cfg.term_width)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn stream_collect(
|
||||||
|
stream: &mut ListStream,
|
||||||
|
size: usize,
|
||||||
|
ctrlc: Option<Arc<AtomicBool>>,
|
||||||
|
) -> (Vec<Value>, bool) {
|
||||||
|
let start_time = Instant::now();
|
||||||
|
let mut end = true;
|
||||||
|
|
||||||
|
let mut batch = Vec::with_capacity(size);
|
||||||
|
for (i, item) in stream.by_ref().enumerate() {
|
||||||
|
batch.push(item);
|
||||||
|
|
||||||
|
// If we've been buffering over a second, go ahead and send out what we have so far
|
||||||
|
if (Instant::now() - start_time).as_secs() >= 1 {
|
||||||
|
end = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if i + 1 == size {
|
||||||
|
end = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if nu_utils::ctrl_c::was_pressed(&ctrlc) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(batch, end)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stream_collect_abbriviated(
|
||||||
|
stream: &mut ListStream,
|
||||||
|
size: usize,
|
||||||
|
ctrlc: Option<Arc<AtomicBool>>,
|
||||||
|
) -> (Vec<Value>, usize, bool) {
|
||||||
|
let mut end = true;
|
||||||
|
let mut read = 0;
|
||||||
|
let mut head = Vec::with_capacity(size);
|
||||||
|
let mut tail = VecDeque::with_capacity(size);
|
||||||
|
|
||||||
|
if size == 0 {
|
||||||
|
return (vec![], 0, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
for item in stream.by_ref() {
|
||||||
|
read += 1;
|
||||||
|
|
||||||
|
if read <= size {
|
||||||
|
head.push(item);
|
||||||
|
} else if tail.len() < size {
|
||||||
|
tail.push_back(item);
|
||||||
|
} else {
|
||||||
|
let _ = tail.pop_front();
|
||||||
|
tail.push_back(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
if nu_utils::ctrl_c::was_pressed(&ctrlc) {
|
||||||
|
end = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let have_filled_list = head.len() == size && tail.len() == size;
|
||||||
|
if have_filled_list {
|
||||||
|
let dummy = get_abbriviated_dummy(&head, &tail);
|
||||||
|
head.insert(size, dummy)
|
||||||
|
}
|
||||||
|
|
||||||
|
head.extend(tail);
|
||||||
|
|
||||||
|
(head, read, end)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_abbriviated_dummy(head: &[Value], tail: &VecDeque<Value>) -> Value {
|
||||||
|
let dummy = || Value::string(String::from("..."), Span::unknown());
|
||||||
|
let is_record_list = is_record_list(head.iter()) && is_record_list(tail.iter());
|
||||||
|
|
||||||
|
if is_record_list {
|
||||||
|
// in case it's a record list we set a default text to each column instead of a single value.
|
||||||
|
Value::record(
|
||||||
|
head[0]
|
||||||
|
.as_record()
|
||||||
|
.expect("ok")
|
||||||
|
.columns()
|
||||||
|
.map(|key| (key.clone(), dummy()))
|
||||||
|
.collect(),
|
||||||
|
Span::unknown(),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
dummy()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_record_list<'a>(mut batch: impl Iterator<Item = &'a Value> + ExactSizeIterator) -> bool {
|
||||||
|
batch.len() > 0 && batch.all(|value| matches!(value, Value::Record { .. }))
|
||||||
|
}
|
||||||
|
|
||||||
fn render_path_name(
|
fn render_path_name(
|
||||||
path: &str,
|
path: &str,
|
||||||
config: &Config,
|
config: &Config,
|
||||||
|
@ -1000,21 +1056,6 @@ fn convert_table_to_output(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn abbreviate_list<T>(list: &[T], limit: usize, text: T) -> Vec<T>
|
|
||||||
where
|
|
||||||
T: Clone,
|
|
||||||
{
|
|
||||||
let head = &list[..limit];
|
|
||||||
let tail = &list[list.len() - limit..];
|
|
||||||
|
|
||||||
let mut out = Vec::with_capacity(limit * 2 + 1);
|
|
||||||
out.extend(head.iter().cloned());
|
|
||||||
out.push(text);
|
|
||||||
out.extend(tail.iter().cloned());
|
|
||||||
|
|
||||||
out
|
|
||||||
}
|
|
||||||
|
|
||||||
fn supported_table_modes() -> Vec<Value> {
|
fn supported_table_modes() -> Vec<Value> {
|
||||||
vec![
|
vec![
|
||||||
Value::test_string("basic"),
|
Value::test_string("basic"),
|
||||||
|
|
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue