mirror of
https://github.com/nushell/nushell.git
synced 2025-07-01 07:00:37 +02:00
Bump tabled to 0.17 (#14415)
With this comes a new `unicode-width` as I remember there was some issue with `ratatui`. And a bit of refactorings which are ment to reduce code lines while not breaking anything. Not yet complete, I think I'll try to improve some more places, just wanted to trigger CI 😄 And yessssssssss we have a new `unicode-width` but I sort of doubtful, I mean the original issue with emojie. I think it may require an additional "clean" call. I am just saying I was not testing it with that case of complex emojies. --------- Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>
This commit is contained in:
@ -1,14 +1,17 @@
|
||||
use crate::debug::inspect_table::{
|
||||
global_horizontal_char::SetHorizontalChar, set_widths::SetWidths,
|
||||
};
|
||||
// note: Seems like could be simplified
|
||||
// IMHO: it shall not take 300+ lines :)
|
||||
|
||||
use nu_protocol::Value;
|
||||
use nu_table::{string_width, string_wrap};
|
||||
|
||||
use tabled::{
|
||||
grid::config::ColoredConfig,
|
||||
settings::{peaker::PriorityMax, width::Wrap, Settings, Style},
|
||||
settings::{peaker::Priority, width::Wrap, Settings, Style},
|
||||
Table,
|
||||
};
|
||||
|
||||
use self::{global_horizontal_char::SetHorizontalChar, set_widths::SetWidths};
|
||||
|
||||
pub fn build_table(value: Value, description: String, termsize: usize) -> String {
|
||||
let (head, mut data) = util::collect_input(value);
|
||||
let count_columns = head.len();
|
||||
@ -57,7 +60,7 @@ pub fn build_table(value: Value, description: String, termsize: usize) -> String
|
||||
Settings::default()
|
||||
.with(Style::rounded().corner_top_left('├').corner_top_right('┤'))
|
||||
.with(SetWidths(widths))
|
||||
.with(Wrap::new(width).priority(PriorityMax))
|
||||
.with(Wrap::new(width).priority(Priority::max(true)))
|
||||
.with(SetHorizontalChar::new('┼', '┴', 11 + 2 + 1)),
|
||||
);
|
||||
|
||||
|
@ -2,7 +2,12 @@
|
||||
// overall reduce the redundant calls to StyleComputer etc.
|
||||
// the goal is to configure it once...
|
||||
|
||||
use std::{collections::VecDeque, io::IsTerminal, io::Read, path::PathBuf, str::FromStr};
|
||||
|
||||
use lscolors::{LsColors, Style};
|
||||
use url::Url;
|
||||
use web_time::Instant;
|
||||
|
||||
use nu_color_config::{color_from_hex, StyleComputer, TextStyle};
|
||||
use nu_engine::{command_prelude::*, env_to_string};
|
||||
use nu_path::form::Absolute;
|
||||
@ -11,25 +16,16 @@ use nu_protocol::{
|
||||
ByteStream, Config, DataSource, ListStream, PipelineMetadata, Signals, TableMode, ValueIterator,
|
||||
};
|
||||
use nu_table::{
|
||||
common::create_nu_table_config, CollapsedTable, ExpandedTable, JustTable, NuTable, NuTableCell,
|
||||
common::configure_table, CollapsedTable, ExpandedTable, JustTable, NuRecordsValue, NuTable,
|
||||
StringResult, TableOpts, TableOutput,
|
||||
};
|
||||
use nu_utils::{get_ls_colors, terminal_size};
|
||||
use std::{collections::VecDeque, io::Read, path::PathBuf, str::FromStr};
|
||||
use url::Url;
|
||||
use web_time::Instant;
|
||||
|
||||
type ShellResult<T> = Result<T, ShellError>;
|
||||
type NuPathBuf = nu_path::PathBuf<Absolute>;
|
||||
|
||||
const STREAM_PAGE_SIZE: usize = 1000;
|
||||
|
||||
fn get_width_param(width_param: Option<i64>) -> usize {
|
||||
if let Some(col) = width_param {
|
||||
col as usize
|
||||
} else if let Ok((w, _h)) = terminal_size() {
|
||||
w as usize
|
||||
} else {
|
||||
80
|
||||
}
|
||||
}
|
||||
const DEFAULT_TABLE_WIDTH: usize = 80;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Table;
|
||||
@ -113,17 +109,15 @@ impl Command for Table {
|
||||
stack: &mut Stack,
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
) -> ShellResult<PipelineData> {
|
||||
let list_themes: bool = call.has_flag(engine_state, stack, "list")?;
|
||||
// if list argument is present we just need to return a list of supported table themes
|
||||
if list_themes {
|
||||
let val = Value::list(supported_table_modes(), Span::test_data());
|
||||
return Ok(val.into_pipeline_data());
|
||||
}
|
||||
#[cfg(feature = "os")]
|
||||
let cwd = engine_state.cwd(Some(stack))?;
|
||||
let cfg = parse_table_config(call, engine_state, stack)?;
|
||||
let input = CmdInput::new(engine_state, stack, call, input);
|
||||
|
||||
let input = CmdInput::parse(engine_state, stack, call, input)?;
|
||||
|
||||
// reset vt processing, aka ansi because illbehaved externals can break it
|
||||
#[cfg(windows)]
|
||||
@ -131,12 +125,7 @@ impl Command for Table {
|
||||
let _ = nu_utils::enable_vt_processing();
|
||||
}
|
||||
|
||||
handle_table_command(
|
||||
input,
|
||||
cfg,
|
||||
#[cfg(feature = "os")]
|
||||
cwd,
|
||||
)
|
||||
handle_table_command(input)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -221,80 +210,127 @@ impl Command for Table {
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct TableConfig {
|
||||
index: Option<usize>,
|
||||
table_view: TableView,
|
||||
term_width: usize,
|
||||
view: TableView,
|
||||
width: usize,
|
||||
theme: TableMode,
|
||||
abbreviation: Option<usize>,
|
||||
index: Option<usize>,
|
||||
use_ansi_coloring: bool,
|
||||
}
|
||||
|
||||
impl TableConfig {
|
||||
fn new(
|
||||
table_view: TableView,
|
||||
term_width: usize,
|
||||
view: TableView,
|
||||
width: usize,
|
||||
theme: TableMode,
|
||||
abbreviation: Option<usize>,
|
||||
index: Option<usize>,
|
||||
use_ansi_coloring: bool,
|
||||
) -> Self {
|
||||
Self {
|
||||
index,
|
||||
table_view,
|
||||
term_width,
|
||||
abbreviation,
|
||||
view,
|
||||
width,
|
||||
theme,
|
||||
abbreviation,
|
||||
index,
|
||||
use_ansi_coloring,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
enum TableView {
|
||||
General,
|
||||
Collapsed,
|
||||
Expanded {
|
||||
limit: Option<usize>,
|
||||
flatten: bool,
|
||||
flatten_separator: Option<String>,
|
||||
},
|
||||
}
|
||||
|
||||
struct CLIArgs {
|
||||
width: Option<i64>,
|
||||
abbrivation: Option<usize>,
|
||||
theme: TableMode,
|
||||
expand: bool,
|
||||
expand_limit: Option<usize>,
|
||||
expand_flatten: bool,
|
||||
expand_flatten_separator: Option<String>,
|
||||
collapse: bool,
|
||||
index: Option<usize>,
|
||||
use_ansi_coloring: bool,
|
||||
}
|
||||
|
||||
fn parse_table_config(
|
||||
call: &Call,
|
||||
state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
) -> Result<TableConfig, ShellError> {
|
||||
let width_param: Option<i64> = call.get_flag(state, stack, "width")?;
|
||||
let expand: bool = call.has_flag(state, stack, "expand")?;
|
||||
let expand_limit: Option<usize> = call.get_flag(state, stack, "expand-deep")?;
|
||||
let collapse: bool = call.has_flag(state, stack, "collapse")?;
|
||||
let flatten: bool = call.has_flag(state, stack, "flatten")?;
|
||||
let flatten_separator: Option<String> = call.get_flag(state, stack, "flatten-separator")?;
|
||||
let abbrivation: Option<usize> = call
|
||||
.get_flag(state, stack, "abbreviated")?
|
||||
.or_else(|| stack.get_config(state).table.abbreviated_row_count);
|
||||
let table_view = match (expand, collapse) {
|
||||
) -> ShellResult<TableConfig> {
|
||||
let args = get_cli_args(call, state, stack)?;
|
||||
let table_view = get_table_view(&args);
|
||||
let term_width = get_table_width(args.width);
|
||||
|
||||
let cfg = TableConfig::new(
|
||||
table_view,
|
||||
term_width,
|
||||
args.theme,
|
||||
args.abbrivation,
|
||||
args.index,
|
||||
args.use_ansi_coloring,
|
||||
);
|
||||
|
||||
Ok(cfg)
|
||||
}
|
||||
|
||||
fn get_table_view(args: &CLIArgs) -> TableView {
|
||||
match (args.expand, args.collapse) {
|
||||
(false, false) => TableView::General,
|
||||
(_, true) => TableView::Collapsed,
|
||||
(true, _) => TableView::Expanded {
|
||||
limit: expand_limit,
|
||||
flatten,
|
||||
flatten_separator,
|
||||
limit: args.expand_limit,
|
||||
flatten: args.expand_flatten,
|
||||
flatten_separator: args.expand_flatten_separator.clone(),
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn get_cli_args(call: &Call<'_>, state: &EngineState, stack: &mut Stack) -> ShellResult<CLIArgs> {
|
||||
let width: Option<i64> = call.get_flag(state, stack, "width")?;
|
||||
let expand: bool = call.has_flag(state, stack, "expand")?;
|
||||
let expand_limit: Option<usize> = call.get_flag(state, stack, "expand-deep")?;
|
||||
let expand_flatten: bool = call.has_flag(state, stack, "flatten")?;
|
||||
let expand_flatten_separator: Option<String> =
|
||||
call.get_flag(state, stack, "flatten-separator")?;
|
||||
let collapse: bool = call.has_flag(state, stack, "collapse")?;
|
||||
let abbrivation: Option<usize> = call
|
||||
.get_flag(state, stack, "abbreviated")?
|
||||
.or_else(|| stack.get_config(state).table.abbreviated_row_count);
|
||||
let theme =
|
||||
get_theme_flag(call, state, stack)?.unwrap_or_else(|| stack.get_config(state).table.mode);
|
||||
let index = get_index_flag(call, state, stack)?;
|
||||
|
||||
let term_width = get_width_param(width_param);
|
||||
|
||||
let use_ansi_coloring = state.get_config().use_ansi_coloring.get(state);
|
||||
|
||||
Ok(TableConfig::new(
|
||||
table_view,
|
||||
term_width,
|
||||
Ok(CLIArgs {
|
||||
theme,
|
||||
abbrivation,
|
||||
collapse,
|
||||
expand,
|
||||
expand_limit,
|
||||
expand_flatten,
|
||||
expand_flatten_separator,
|
||||
width,
|
||||
index,
|
||||
use_ansi_coloring,
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
fn get_index_flag(
|
||||
call: &Call,
|
||||
state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
) -> Result<Option<usize>, ShellError> {
|
||||
) -> ShellResult<Option<usize>> {
|
||||
let index: Option<Value> = call.get_flag(state, stack, "index")?;
|
||||
let value = match index {
|
||||
Some(value) => value,
|
||||
@ -335,7 +371,7 @@ fn get_theme_flag(
|
||||
call: &Call,
|
||||
state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
) -> Result<Option<TableMode>, ShellError> {
|
||||
) -> ShellResult<Option<TableMode>> {
|
||||
call.get_flag(state, stack, "theme")?
|
||||
.map(|theme: String| {
|
||||
TableMode::from_str(&theme).map_err(|err| ShellError::CantConvert {
|
||||
@ -353,29 +389,36 @@ struct CmdInput<'a> {
|
||||
stack: &'a mut Stack,
|
||||
call: &'a Call<'a>,
|
||||
data: PipelineData,
|
||||
cfg: TableConfig,
|
||||
cwd: Option<NuPathBuf>,
|
||||
}
|
||||
|
||||
impl<'a> CmdInput<'a> {
|
||||
fn new(
|
||||
fn parse(
|
||||
engine_state: &'a EngineState,
|
||||
stack: &'a mut Stack,
|
||||
call: &'a Call<'a>,
|
||||
data: PipelineData,
|
||||
) -> Self {
|
||||
Self {
|
||||
) -> ShellResult<Self> {
|
||||
let cfg = parse_table_config(call, engine_state, stack)?;
|
||||
let cwd = get_cwd(engine_state, stack)?;
|
||||
|
||||
Ok(Self {
|
||||
engine_state,
|
||||
stack,
|
||||
call,
|
||||
data,
|
||||
}
|
||||
cfg,
|
||||
cwd,
|
||||
})
|
||||
}
|
||||
|
||||
fn get_config(&self) -> std::sync::Arc<Config> {
|
||||
self.stack.get_config(self.engine_state)
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_table_command(
|
||||
mut input: CmdInput<'_>,
|
||||
cfg: TableConfig,
|
||||
#[cfg(feature = "os")] cwd: nu_path::PathBuf<Absolute>,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
fn handle_table_command(mut input: CmdInput<'_>) -> ShellResult<PipelineData> {
|
||||
let span = input.data.span().unwrap_or(input.call.head);
|
||||
match input.data {
|
||||
// Binary streams should behave as if they really are `binary` data, and printed as hex
|
||||
@ -397,29 +440,15 @@ fn handle_table_command(
|
||||
let stream = ListStream::new(vals.into_iter(), span, signals);
|
||||
input.data = PipelineData::Empty;
|
||||
|
||||
handle_row_stream(
|
||||
input,
|
||||
cfg,
|
||||
stream,
|
||||
metadata,
|
||||
#[cfg(feature = "os")]
|
||||
cwd,
|
||||
)
|
||||
handle_row_stream(input, stream, metadata)
|
||||
}
|
||||
PipelineData::ListStream(stream, metadata) => {
|
||||
input.data = PipelineData::Empty;
|
||||
handle_row_stream(
|
||||
input,
|
||||
cfg,
|
||||
stream,
|
||||
metadata,
|
||||
#[cfg(feature = "os")]
|
||||
cwd,
|
||||
)
|
||||
handle_row_stream(input, stream, metadata)
|
||||
}
|
||||
PipelineData::Value(Value::Record { val, .. }, ..) => {
|
||||
input.data = PipelineData::Empty;
|
||||
handle_record(input, cfg, val.into_owned())
|
||||
handle_record(input, val.into_owned())
|
||||
}
|
||||
PipelineData::Value(Value::Error { error, .. }, ..) => {
|
||||
// Propagate this error outward, so that it goes to stderr
|
||||
@ -435,14 +464,7 @@ fn handle_table_command(
|
||||
let stream =
|
||||
ListStream::new(val.into_range_iter(span, Signals::empty()), span, signals);
|
||||
input.data = PipelineData::Empty;
|
||||
handle_row_stream(
|
||||
input,
|
||||
cfg,
|
||||
stream,
|
||||
metadata,
|
||||
#[cfg(feature = "os")]
|
||||
cwd,
|
||||
)
|
||||
handle_row_stream(input, stream, metadata)
|
||||
}
|
||||
x => Ok(x),
|
||||
}
|
||||
@ -517,60 +539,55 @@ fn pretty_hex_stream(stream: ByteStream, span: Span) -> ByteStream {
|
||||
)
|
||||
}
|
||||
|
||||
fn handle_record(
|
||||
input: CmdInput,
|
||||
cfg: TableConfig,
|
||||
mut record: Record,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let config = {
|
||||
let state = input.engine_state;
|
||||
let stack: &Stack = input.stack;
|
||||
stack.get_config(state)
|
||||
};
|
||||
fn handle_record(input: CmdInput, mut record: Record) -> ShellResult<PipelineData> {
|
||||
let span = input.data.span().unwrap_or(input.call.head);
|
||||
let styles = &StyleComputer::from_config(input.engine_state, input.stack);
|
||||
|
||||
if record.is_empty() {
|
||||
let value =
|
||||
create_empty_placeholder("record", cfg.term_width, input.engine_state, input.stack);
|
||||
create_empty_placeholder("record", input.cfg.width, input.engine_state, input.stack);
|
||||
let value = Value::string(value, span);
|
||||
return Ok(value.into_pipeline_data());
|
||||
};
|
||||
|
||||
if let Some(limit) = cfg.abbreviation {
|
||||
let prev_len = record.len();
|
||||
if record.len() > limit * 2 + 1 {
|
||||
// TODO: see if the following table builders would be happy with a simple iterator
|
||||
let mut record_iter = record.into_iter();
|
||||
record = Record::with_capacity(limit * 2 + 1);
|
||||
record.extend(record_iter.by_ref().take(limit));
|
||||
record.push(String::from("..."), Value::string("...", Span::unknown()));
|
||||
record.extend(record_iter.skip(prev_len - 2 * limit));
|
||||
}
|
||||
if let Some(limit) = input.cfg.abbreviation {
|
||||
record = make_record_abbreviation(record, limit);
|
||||
}
|
||||
|
||||
let indent = (config.table.padding.left, config.table.padding.right);
|
||||
let opts = TableOpts::new(
|
||||
let config = input.get_config();
|
||||
let opts = create_table_opts(
|
||||
input.engine_state,
|
||||
input.stack,
|
||||
&config,
|
||||
styles,
|
||||
input.engine_state.signals(),
|
||||
&input.cfg,
|
||||
span,
|
||||
cfg.term_width,
|
||||
indent,
|
||||
cfg.theme,
|
||||
cfg.index.unwrap_or(0),
|
||||
cfg.index.is_none(),
|
||||
0,
|
||||
);
|
||||
let result = build_table_kv(record, cfg.table_view, opts, span)?;
|
||||
let result = build_table_kv(record, input.cfg.view.clone(), opts, span)?;
|
||||
|
||||
let result = match result {
|
||||
Some(output) => maybe_strip_color(output, cfg.use_ansi_coloring),
|
||||
None => report_unsuccessful_output(input.engine_state.signals(), cfg.term_width),
|
||||
Some(output) => maybe_strip_color(output, input.cfg.use_ansi_coloring),
|
||||
None => report_unsuccessful_output(input.engine_state.signals(), input.cfg.width),
|
||||
};
|
||||
|
||||
let val = Value::string(result, span);
|
||||
let data = val.into_pipeline_data();
|
||||
|
||||
Ok(val.into_pipeline_data())
|
||||
Ok(data)
|
||||
}
|
||||
|
||||
fn make_record_abbreviation(mut record: Record, limit: usize) -> Record {
|
||||
if record.len() <= limit * 2 + 1 {
|
||||
return record;
|
||||
}
|
||||
|
||||
// TODO: see if the following table builders would be happy with a simple iterator
|
||||
let prev_len = record.len();
|
||||
let mut record_iter = record.into_iter();
|
||||
record = Record::with_capacity(limit * 2 + 1);
|
||||
record.extend(record_iter.by_ref().take(limit));
|
||||
record.push(String::from("..."), Value::string("...", Span::unknown()));
|
||||
record.extend(record_iter.skip(prev_len - 2 * limit));
|
||||
record
|
||||
}
|
||||
|
||||
fn report_unsuccessful_output(signals: &Signals, term_width: usize) -> String {
|
||||
@ -608,11 +625,11 @@ fn build_table_kv(
|
||||
|
||||
fn build_table_batch(
|
||||
vals: Vec<Value>,
|
||||
table_view: TableView,
|
||||
view: TableView,
|
||||
opts: TableOpts<'_>,
|
||||
span: Span,
|
||||
) -> StringResult {
|
||||
match table_view {
|
||||
match view {
|
||||
TableView::General => JustTable::table(&vals, opts),
|
||||
TableView::Expanded {
|
||||
limit,
|
||||
@ -631,22 +648,17 @@ fn build_table_batch(
|
||||
|
||||
fn handle_row_stream(
|
||||
input: CmdInput<'_>,
|
||||
cfg: TableConfig,
|
||||
stream: ListStream,
|
||||
metadata: Option<PipelineMetadata>,
|
||||
#[cfg(feature = "os")] cwd: nu_path::PathBuf<Absolute>,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
) -> ShellResult<PipelineData> {
|
||||
let cfg = input.get_config();
|
||||
let stream = match metadata.as_ref() {
|
||||
// First, `ls` sources:
|
||||
Some(PipelineMetadata {
|
||||
data_source: DataSource::Ls,
|
||||
..
|
||||
}) => {
|
||||
let config = {
|
||||
let state = input.engine_state;
|
||||
let stack: &Stack = input.stack;
|
||||
stack.get_config(state)
|
||||
};
|
||||
let config = cfg.clone();
|
||||
let ls_colors_env_str = match input.stack.get_env_var(input.engine_state, "LS_COLORS") {
|
||||
Some(v) => Some(env_to_string(
|
||||
"LS_COLORS",
|
||||
@ -664,14 +676,9 @@ fn handle_row_stream(
|
||||
if let Some(value) = record.to_mut().get_mut("name") {
|
||||
let span = value.span();
|
||||
if let Value::String { val, .. } = value {
|
||||
if let Some(val) = render_path_name(
|
||||
val,
|
||||
&config,
|
||||
&ls_colors,
|
||||
#[cfg(feature = "os")]
|
||||
cwd.clone(),
|
||||
span,
|
||||
) {
|
||||
if let Some(val) =
|
||||
render_path_name(val, &config, &ls_colors, input.cwd.clone(), span)
|
||||
{
|
||||
*value = val;
|
||||
}
|
||||
}
|
||||
@ -726,6 +733,7 @@ fn handle_row_stream(
|
||||
// for the values it outputs. Because engine_state is passed in, config doesn't need to.
|
||||
input.engine_state.clone(),
|
||||
input.stack.clone(),
|
||||
input.cfg,
|
||||
cfg,
|
||||
);
|
||||
let stream = ByteStream::from_result_iter(
|
||||
@ -787,8 +795,9 @@ struct PagingTableCreator {
|
||||
stack: Stack,
|
||||
elements_displayed: usize,
|
||||
reached_end: bool,
|
||||
cfg: TableConfig,
|
||||
table_config: TableConfig,
|
||||
row_offset: usize,
|
||||
config: std::sync::Arc<Config>,
|
||||
}
|
||||
|
||||
impl PagingTableCreator {
|
||||
@ -797,114 +806,51 @@ impl PagingTableCreator {
|
||||
stream: ListStream,
|
||||
engine_state: EngineState,
|
||||
stack: Stack,
|
||||
cfg: TableConfig,
|
||||
table_config: TableConfig,
|
||||
config: std::sync::Arc<Config>,
|
||||
) -> Self {
|
||||
PagingTableCreator {
|
||||
head,
|
||||
stream: stream.into_inner(),
|
||||
engine_state,
|
||||
stack,
|
||||
cfg,
|
||||
config,
|
||||
table_config,
|
||||
elements_displayed: 0,
|
||||
reached_end: false,
|
||||
row_offset: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn build_extended(
|
||||
&mut self,
|
||||
batch: Vec<Value>,
|
||||
limit: Option<usize>,
|
||||
flatten: bool,
|
||||
flatten_separator: Option<String>,
|
||||
) -> StringResult {
|
||||
fn build_table(&mut self, batch: Vec<Value>) -> ShellResult<Option<String>> {
|
||||
if batch.is_empty() {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let cfg = {
|
||||
let state = &self.engine_state;
|
||||
let stack = &self.stack;
|
||||
stack.get_config(state)
|
||||
};
|
||||
let style_comp = StyleComputer::from_config(&self.engine_state, &self.stack);
|
||||
let opts = self.create_table_opts(&cfg, &style_comp);
|
||||
let view = TableView::Expanded {
|
||||
limit,
|
||||
flatten,
|
||||
flatten_separator,
|
||||
};
|
||||
|
||||
build_table_batch(batch, view, opts, self.head)
|
||||
let opts = self.create_table_opts();
|
||||
build_table_batch(batch, self.table_config.view.clone(), opts, self.head)
|
||||
}
|
||||
|
||||
fn build_collapsed(&mut self, batch: Vec<Value>) -> StringResult {
|
||||
if batch.is_empty() {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let cfg = {
|
||||
let state = &self.engine_state;
|
||||
let stack = &self.stack;
|
||||
stack.get_config(state)
|
||||
};
|
||||
let style_comp = StyleComputer::from_config(&self.engine_state, &self.stack);
|
||||
let opts = self.create_table_opts(&cfg, &style_comp);
|
||||
|
||||
build_table_batch(batch, TableView::Collapsed, opts, self.head)
|
||||
}
|
||||
|
||||
fn build_general(&mut self, batch: Vec<Value>) -> StringResult {
|
||||
let cfg = {
|
||||
let state = &self.engine_state;
|
||||
let stack = &self.stack;
|
||||
stack.get_config(state)
|
||||
};
|
||||
let style_comp = StyleComputer::from_config(&self.engine_state, &self.stack);
|
||||
let opts = self.create_table_opts(&cfg, &style_comp);
|
||||
|
||||
build_table_batch(batch, TableView::General, opts, self.head)
|
||||
}
|
||||
|
||||
fn create_table_opts<'a>(
|
||||
&'a self,
|
||||
cfg: &'a Config,
|
||||
style_comp: &'a StyleComputer<'a>,
|
||||
) -> TableOpts<'a> {
|
||||
TableOpts::new(
|
||||
cfg,
|
||||
style_comp,
|
||||
self.engine_state.signals(),
|
||||
fn create_table_opts(&self) -> TableOpts<'_> {
|
||||
create_table_opts(
|
||||
&self.engine_state,
|
||||
&self.stack,
|
||||
&self.config,
|
||||
&self.table_config,
|
||||
self.head,
|
||||
self.cfg.term_width,
|
||||
(cfg.table.padding.left, cfg.table.padding.right),
|
||||
self.cfg.theme,
|
||||
self.cfg.index.unwrap_or(0) + self.row_offset,
|
||||
self.cfg.index.is_none(),
|
||||
self.row_offset,
|
||||
)
|
||||
}
|
||||
|
||||
fn build_table(&mut self, batch: Vec<Value>) -> Result<Option<String>, ShellError> {
|
||||
match &self.cfg.table_view {
|
||||
TableView::General => self.build_general(batch),
|
||||
TableView::Collapsed => self.build_collapsed(batch),
|
||||
TableView::Expanded {
|
||||
limit,
|
||||
flatten,
|
||||
flatten_separator,
|
||||
} => self.build_extended(batch, *limit, *flatten, flatten_separator.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for PagingTableCreator {
|
||||
type Item = Result<Vec<u8>, ShellError>;
|
||||
type Item = ShellResult<Vec<u8>>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let batch;
|
||||
let end;
|
||||
|
||||
match self.cfg.abbreviation {
|
||||
match self.table_config.abbreviation {
|
||||
Some(abbr) => {
|
||||
(batch, _, end) =
|
||||
stream_collect_abbriviated(&mut self.stream, abbr, self.engine_state.signals());
|
||||
@ -934,7 +880,7 @@ impl Iterator for PagingTableCreator {
|
||||
self.elements_displayed = 1;
|
||||
let result = create_empty_placeholder(
|
||||
"list",
|
||||
self.cfg.term_width,
|
||||
self.table_config.width,
|
||||
&self.engine_state,
|
||||
&self.stack,
|
||||
);
|
||||
@ -950,9 +896,9 @@ impl Iterator for PagingTableCreator {
|
||||
|
||||
convert_table_to_output(
|
||||
table,
|
||||
&self.cfg,
|
||||
self.engine_state.signals(),
|
||||
self.cfg.term_width,
|
||||
self.table_config.width,
|
||||
self.table_config.use_ansi_coloring,
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -1059,17 +1005,17 @@ fn render_path_name(
|
||||
path: &str,
|
||||
config: &Config,
|
||||
ls_colors: &LsColors,
|
||||
#[cfg(feature = "os")] cwd: nu_path::PathBuf<Absolute>,
|
||||
cwd: Option<NuPathBuf>,
|
||||
span: Span,
|
||||
) -> Option<Value> {
|
||||
if !config.ls.use_ls_colors {
|
||||
return None;
|
||||
}
|
||||
|
||||
#[cfg(feature = "os")]
|
||||
let fullpath = cwd.join(path);
|
||||
#[cfg(not(feature = "os"))]
|
||||
let fullpath = path;
|
||||
let fullpath = match cwd {
|
||||
Some(cwd) => PathBuf::from(cwd.join(path)),
|
||||
None => PathBuf::from(path),
|
||||
};
|
||||
|
||||
let stripped_path = nu_utils::strip_ansi_unlikely(path);
|
||||
let metadata = std::fs::symlink_metadata(fullpath);
|
||||
@ -1101,19 +1047,9 @@ fn render_path_name(
|
||||
Some(Value::string(val, span))
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
enum TableView {
|
||||
General,
|
||||
Collapsed,
|
||||
Expanded {
|
||||
limit: Option<usize>,
|
||||
flatten: bool,
|
||||
flatten_separator: Option<String>,
|
||||
},
|
||||
}
|
||||
|
||||
fn maybe_strip_color(output: String, use_ansi_coloring: bool) -> String {
|
||||
if !use_ansi_coloring {
|
||||
// the terminal is for when people do ls from vim, there should be no coloring there
|
||||
if !use_ansi_coloring || !std::io::stdout().is_terminal() {
|
||||
// Draw the table without ansi colors
|
||||
nu_utils::strip_ansi_string_likely(output)
|
||||
} else {
|
||||
@ -1133,29 +1069,29 @@ fn create_empty_placeholder(
|
||||
return String::new();
|
||||
}
|
||||
|
||||
let cell = NuTableCell::new(format!("empty {}", value_type_name));
|
||||
let cell = NuRecordsValue::new(format!("empty {}", value_type_name));
|
||||
let data = vec![vec![cell]];
|
||||
let mut table = NuTable::from(data);
|
||||
table.set_data_style(TextStyle::default().dimmed());
|
||||
let out = TableOutput::new(table, false, false, 1);
|
||||
let mut out = TableOutput::from_table(table, false, false);
|
||||
|
||||
let style_computer = &StyleComputer::from_config(engine_state, stack);
|
||||
let config = create_nu_table_config(&config, style_computer, &out, false, TableMode::default());
|
||||
configure_table(&mut out, &config, style_computer, TableMode::default());
|
||||
|
||||
out.table
|
||||
.draw(config, termwidth)
|
||||
.draw(termwidth)
|
||||
.expect("Could not create empty table placeholder")
|
||||
}
|
||||
|
||||
fn convert_table_to_output(
|
||||
table: Result<Option<String>, ShellError>,
|
||||
cfg: &TableConfig,
|
||||
table: ShellResult<Option<String>>,
|
||||
signals: &Signals,
|
||||
term_width: usize,
|
||||
) -> Option<Result<Vec<u8>, ShellError>> {
|
||||
use_ansi_coloring: bool,
|
||||
) -> Option<ShellResult<Vec<u8>>> {
|
||||
match table {
|
||||
Ok(Some(table)) => {
|
||||
let table = maybe_strip_color(table, cfg.use_ansi_coloring);
|
||||
let table = maybe_strip_color(table, use_ansi_coloring);
|
||||
|
||||
let mut bytes = table.as_bytes().to_vec();
|
||||
bytes.push(b'\n'); // nu-table tables don't come with a newline on the end
|
||||
@ -1198,3 +1134,41 @@ fn supported_table_modes() -> Vec<Value> {
|
||||
Value::test_string("basic_compact"),
|
||||
]
|
||||
}
|
||||
|
||||
fn create_table_opts<'a>(
|
||||
engine_state: &'a EngineState,
|
||||
stack: &'a Stack,
|
||||
cfg: &'a Config,
|
||||
table_cfg: &'a TableConfig,
|
||||
span: Span,
|
||||
offset: usize,
|
||||
) -> TableOpts<'a> {
|
||||
let comp = StyleComputer::from_config(engine_state, stack);
|
||||
let signals = engine_state.signals();
|
||||
let offset = table_cfg.index.unwrap_or(0) + offset;
|
||||
let index = table_cfg.index.is_none();
|
||||
let width = table_cfg.width;
|
||||
let theme = table_cfg.theme;
|
||||
|
||||
TableOpts::new(cfg, comp, signals, span, width, theme, offset, index)
|
||||
}
|
||||
|
||||
fn get_cwd(engine_state: &EngineState, stack: &mut Stack) -> ShellResult<Option<NuPathBuf>> {
|
||||
#[cfg(feature = "os")]
|
||||
let cwd = engine_state.cwd(Some(stack)).map(Some)?;
|
||||
|
||||
#[cfg(not(feature = "os"))]
|
||||
let cwd = None;
|
||||
|
||||
Ok(cwd)
|
||||
}
|
||||
|
||||
fn get_table_width(width_param: Option<i64>) -> usize {
|
||||
if let Some(col) = width_param {
|
||||
col as usize
|
||||
} else if let Ok((w, _h)) = terminal_size() {
|
||||
w as usize
|
||||
} else {
|
||||
DEFAULT_TABLE_WIDTH
|
||||
}
|
||||
}
|
||||
|
@ -2867,13 +2867,52 @@ fn table_index_arg() {
|
||||
#[test]
|
||||
fn table_expand_index_arg() {
|
||||
let actual = nu!("[[a b]; [1 2] [2 [4 4]]] | table --width=80 --theme basic --expand -i false");
|
||||
assert_eq!(actual.out, "+---+-------+| a | b |+---+-------+| 1 | 2 |+---+-------+| 2 | +---+ || | | 4 | || | +---+ || | | 4 | || | +---+ |+---+-------+");
|
||||
assert_eq!(
|
||||
actual.out,
|
||||
"+---+-------+\
|
||||
| a | b |\
|
||||
+---+-------+\
|
||||
| 1 | 2 |\
|
||||
+---+-------+\
|
||||
| 2 | +---+ |\
|
||||
| | | 4 | |\
|
||||
| | +---+ |\
|
||||
| | | 4 | |\
|
||||
| | +---+ |\
|
||||
+---+-------+"
|
||||
);
|
||||
|
||||
let actual = nu!("[[a b]; [1 2] [2 [4 4]]] | table --width=80 --theme basic --expand -i true");
|
||||
assert_eq!(actual.out, "+---+---+-----------+| # | a | b |+---+---+-----------+| 0 | 1 | 2 |+---+---+-----------+| 1 | 2 | +---+---+ || | | | 0 | 4 | || | | +---+---+ || | | | 1 | 4 | || | | +---+---+ |+---+---+-----------+");
|
||||
assert_eq!(
|
||||
actual.out,
|
||||
"+---+---+-----------+\
|
||||
| # | a | b |\
|
||||
+---+---+-----------+\
|
||||
| 0 | 1 | 2 |\
|
||||
+---+---+-----------+\
|
||||
| 1 | 2 | +---+---+ |\
|
||||
| | | | 0 | 4 | |\
|
||||
| | | +---+---+ |\
|
||||
| | | | 1 | 4 | |\
|
||||
| | | +---+---+ |\
|
||||
+---+---+-----------+"
|
||||
);
|
||||
|
||||
let actual = nu!("[[a b]; [1 2] [2 [4 4]]] | table --width=80 --theme basic --expand -i 10");
|
||||
assert_eq!(actual.out, "+----+---+-----------+| # | a | b |+----+---+-----------+| 10 | 1 | 2 |+----+---+-----------+| 11 | 2 | +---+---+ || | | | 0 | 4 | || | | +---+---+ || | | | 1 | 4 | || | | +---+---+ |+----+---+-----------+");
|
||||
assert_eq!(
|
||||
actual.out,
|
||||
"+----+---+-----------+\
|
||||
| # | a | b |\
|
||||
+----+---+-----------+\
|
||||
| 10 | 1 | 2 |\
|
||||
+----+---+-----------+\
|
||||
| 11 | 2 | +---+---+ |\
|
||||
| | | | 0 | 4 | |\
|
||||
| | | +---+---+ |\
|
||||
| | | | 1 | 4 | |\
|
||||
| | | +---+---+ |\
|
||||
+----+---+-----------+"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
Reference in New Issue
Block a user