2022-12-15 15:47:04 +01:00
|
|
|
mod common;
|
2022-07-19 19:35:13 +02:00
|
|
|
|
color_config now accepts closures as color values (#7141)
# Description
Closes #6909. You can now add closures to your `color_config` themes.
Whenever a value would be printed with `table`, the closure is run with
the value piped-in. The closure must return either a {fg,bg,attr} record
or a color name (`'light_red'` etc.). This returned style is used to
colour the value.
This is entirely backwards-compatible with existing config.nu files.
Example code excerpt:
```
let my_theme = {
header: green_bold
bool: { if $in { 'light_cyan' } else { 'light_red' } }
int: purple_bold
filesize: { |e| if $e == 0b { 'gray' } else if $e < 1mb { 'purple_bold' } else { 'cyan_bold' } }
duration: purple_bold
date: { (date now) - $in | if $in > 1wk { 'cyan_bold' } else if $in > 1day { 'green_bold' } else { 'yellow_bold' } }
range: yellow_bold
string: { if $in =~ '^#\w{6}$' { $in } else { 'white' } }
nothing: white
```
Example output with this in effect:
![2022-11-16 12 47 23 AM - style_computer
rs_-_nushell_-_VSCodium](https://user-images.githubusercontent.com/83939/201952558-482de05d-69c7-4bf2-91fc-d0964bf71264.png)
![2022-11-16 12 39 41 AM - style_computer
rs_-_nushell_-_VSCodium](https://user-images.githubusercontent.com/83939/201952580-2384bb86-b680-40fe-8192-71bae396c738.png)
![2022-11-15 09 21 54 PM - run_external
rs_-_nushell_-_VSCodium](https://user-images.githubusercontent.com/83939/201952601-343fc15d-e4a8-4a92-ad89-9a7d17d42748.png)
Slightly important notes:
* Some color_config names, namely "separator", "empty" and "hints", pipe
in `null` instead of a value.
* Currently, doing anything non-trivial inside a closure has an
understandably big perf hit. I currently do not actually recommend
something like `string: { if $in =~ '^#\w{6}$' { $in } else { 'white' }
}` for serious work, mainly because of the abundance of string-type data
in the world. Nevertheless, lesser-used types like "date" and "duration"
work well with this.
* I had to do some reorganisation in order to make it possible to call
`eval_block()` that late in table rendering. I invented a new struct
called "StyleComputer" which holds the engine_state and stack of the
initial `table` command (implicit or explicit).
* StyleComputer has a `compute()` method which takes a color_config name
and a nu value, and always returns the correct Style, so you don't have
to worry about A) the color_config value was set at all, B) whether it
was set to a closure or not, or C) which default style to use in those
cases.
* Currently, errors encountered during execution of the closures are
thrown in the garbage. Any other ideas are welcome. (Nonetheless, errors
result in a huge perf hit when they are encountered. I think what should
be done is to assume something terrible happened to the user's config
and invalidate the StyleComputer for that `table` run, thus causing
subsequent output to just be Style::default().)
* More thorough tests are forthcoming - ran into some difficulty using
`nu!` to take an alternative config, and for some reason `let-env config
=` statements don't seem to work inside `nu!` pipelines(???)
* The default config.nu has not been updated to make use of this yet. Do
tell if you think I should incorporate that into this.
# User-Facing Changes
See above.
# Tests + Formatting
Don't forget to add tests that cover your changes.
Make sure you've run and fixed any issues with these commands:
- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
- `cargo clippy --workspace --features=extra -- -D warnings -D
clippy::unwrap_used -A clippy::needless_collect` to check that you're
using the standard code style
- `cargo test --workspace --features=extra` to check that all tests pass
# After Submitting
If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-17 14:07:56 +01:00
|
|
|
use common::{create_row, styled_str, test_table, TestCase, VecCells};
|
2022-12-15 15:47:04 +01:00
|
|
|
use nu_protocol::TrimStrategy;
|
|
|
|
use nu_table::{Table, TableConfig, TableTheme as theme};
|
|
|
|
|
2022-07-19 19:35:13 +02:00
|
|
|
#[test]
|
|
|
|
fn data_and_header_has_different_size() {
|
2022-12-15 15:47:04 +01:00
|
|
|
let table = Table::new(vec![create_row(3), create_row(5), create_row(5)], (3, 5));
|
|
|
|
|
|
|
|
let table = table.draw(
|
|
|
|
TableConfig::new(theme::heavy(), true, false, false),
|
2022-10-03 18:40:16 +02:00
|
|
|
usize::MAX,
|
|
|
|
);
|
|
|
|
|
2022-12-15 15:47:04 +01:00
|
|
|
assert_eq!(
|
|
|
|
table.as_deref(),
|
|
|
|
Some(
|
|
|
|
"┏━━━┳━━━┳━━━┳━━━┳━━━┓\n\
|
|
|
|
┃ 0 ┃ 1 ┃ 2 ┃ ┃ ┃\n\
|
|
|
|
┣━━━╋━━━╋━━━╋━━━╋━━━┫\n\
|
|
|
|
┃ 0 ┃ 1 ┃ 2 ┃ 3 ┃ 4 ┃\n\
|
|
|
|
┃ 0 ┃ 1 ┃ 2 ┃ 3 ┃ 4 ┃\n\
|
|
|
|
┗━━━┻━━━┻━━━┻━━━┻━━━┛"
|
|
|
|
)
|
|
|
|
);
|
2022-07-19 19:35:13 +02:00
|
|
|
|
2022-12-15 15:47:04 +01:00
|
|
|
let table = Table::new(vec![create_row(5), create_row(3), create_row(3)], (3, 5));
|
2022-07-19 19:35:13 +02:00
|
|
|
|
2022-12-15 15:47:04 +01:00
|
|
|
let table = table.draw(
|
|
|
|
TableConfig::new(theme::heavy(), true, false, false),
|
2022-10-03 18:40:16 +02:00
|
|
|
usize::MAX,
|
|
|
|
);
|
2022-07-19 19:35:13 +02:00
|
|
|
|
2022-12-15 15:47:04 +01:00
|
|
|
assert_eq!(
|
|
|
|
table.as_deref(),
|
|
|
|
Some(
|
|
|
|
"┏━━━┳━━━┳━━━┳━━━┳━━━┓\n\
|
|
|
|
┃ 0 ┃ 1 ┃ 2 ┃ 3 ┃ 4 ┃\n\
|
|
|
|
┣━━━╋━━━╋━━━╋━━━╋━━━┫\n\
|
|
|
|
┃ 0 ┃ 1 ┃ 2 ┃ ┃ ┃\n\
|
|
|
|
┃ 0 ┃ 1 ┃ 2 ┃ ┃ ┃\n\
|
|
|
|
┗━━━┻━━━┻━━━┻━━━┻━━━┛"
|
|
|
|
)
|
|
|
|
);
|
2022-07-19 19:35:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn termwidth_too_small() {
|
2022-12-15 15:47:04 +01:00
|
|
|
let test_loop = |config: TableConfig| {
|
|
|
|
for i in 0..10 {
|
|
|
|
let table = Table::new(vec![create_row(3), create_row(3), create_row(5)], (3, 5));
|
|
|
|
let table = table.draw(config.clone(), i);
|
|
|
|
|
|
|
|
assert!(table.is_none());
|
|
|
|
}
|
2022-07-19 19:35:13 +02:00
|
|
|
};
|
|
|
|
|
2022-12-15 15:47:04 +01:00
|
|
|
let base_config = TableConfig::new(theme::heavy(), true, false, false);
|
|
|
|
|
|
|
|
let config = base_config.clone();
|
|
|
|
test_loop(config);
|
|
|
|
|
|
|
|
let config = base_config.clone().trim(TrimStrategy::truncate(None));
|
|
|
|
test_loop(config);
|
|
|
|
|
|
|
|
let config = base_config
|
|
|
|
.clone()
|
|
|
|
.trim(TrimStrategy::truncate(Some(String::from("**"))));
|
|
|
|
test_loop(config);
|
2022-07-19 22:16:12 +02:00
|
|
|
|
2022-12-15 15:47:04 +01:00
|
|
|
let config = base_config
|
|
|
|
.clone()
|
|
|
|
.trim(TrimStrategy::truncate(Some(String::from(""))));
|
|
|
|
test_loop(config);
|
|
|
|
|
|
|
|
let config = base_config.clone().trim(TrimStrategy::wrap(false));
|
|
|
|
test_loop(config);
|
|
|
|
|
|
|
|
let config = base_config.trim(TrimStrategy::wrap(true));
|
|
|
|
test_loop(config);
|
2022-07-19 19:35:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn wrap_test() {
|
2022-12-15 15:47:04 +01:00
|
|
|
let tests = [
|
|
|
|
(0, None),
|
|
|
|
(1, None),
|
|
|
|
(2, None),
|
|
|
|
(3, None),
|
|
|
|
(4, None),
|
|
|
|
(5, None),
|
|
|
|
(6, None),
|
|
|
|
(7, None),
|
|
|
|
(8, None),
|
|
|
|
(9, None),
|
|
|
|
(10, None),
|
|
|
|
(11, None),
|
|
|
|
(12, Some("┏━━━━┳━━━━━┓\n┃ 12 ┃ ... ┃\n┃ 3 ┃ ┃\n┃ 45 ┃ ┃\n┃ 67 ┃ ┃\n┃ 8 ┃ ┃\n┣━━━━╋━━━━━┫\n┃ 0 ┃ ... ┃\n┃ 0 ┃ ... ┃\n┗━━━━┻━━━━━┛")),
|
|
|
|
(13, Some("┏━━━━━┳━━━━━┓\n┃ 123 ┃ ... ┃\n┃ 45 ┃ ┃\n┃ 678 ┃ ┃\n┣━━━━━╋━━━━━┫\n┃ 0 ┃ ... ┃\n┃ 0 ┃ ... ┃\n┗━━━━━┻━━━━━┛")),
|
|
|
|
(21, Some("┏━━━━━━┳━━━━━━┳━━━━━┓\n┃ 123 ┃ qweq ┃ ... ┃\n┃ 4567 ┃ w eq ┃ ┃\n┃ 8 ┃ we ┃ ┃\n┣━━━━━━╋━━━━━━╋━━━━━┫\n┃ 0 ┃ 1 ┃ ... ┃\n┃ 0 ┃ 1 ┃ ... ┃\n┗━━━━━━┻━━━━━━┻━━━━━┛")),
|
|
|
|
(29, Some("┏━━━━━━━━━━┳━━━━━━━━━━┳━━━━━┓\n┃ 123 4567 ┃ qweqw eq ┃ ... ┃\n┃ 8 ┃ we ┃ ┃\n┣━━━━━━━━━━╋━━━━━━━━━━╋━━━━━┫\n┃ 0 ┃ 1 ┃ ... ┃\n┃ 0 ┃ 1 ┃ ... ┃\n┗━━━━━━━━━━┻━━━━━━━━━━┻━━━━━┛")),
|
|
|
|
(49, Some("┏━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━┳━━━━━┓\n┃ 123 45678 ┃ qweqw eqwe ┃ xxx xx xx x xx ┃ ... ┃\n┃ ┃ ┃ x xx xx ┃ ┃\n┣━━━━━━━━━━━╋━━━━━━━━━━━━╋━━━━━━━━━━━━━━━━╋━━━━━┫\n┃ 0 ┃ 1 ┃ 2 ┃ ... ┃\n┃ 0 ┃ 1 ┃ 2 ┃ ... ┃\n┗━━━━━━━━━━━┻━━━━━━━━━━━━┻━━━━━━━━━━━━━━━━┻━━━━━┛")),
|
|
|
|
];
|
2022-07-19 19:35:13 +02:00
|
|
|
|
2022-12-15 15:47:04 +01:00
|
|
|
test_trim(&tests, TrimStrategy::wrap(false));
|
2022-07-19 19:35:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn wrap_keep_words_test() {
|
2022-12-15 15:47:04 +01:00
|
|
|
let tests = [
|
|
|
|
(0, None),
|
|
|
|
(1, None),
|
|
|
|
(2, None),
|
|
|
|
(3, None),
|
|
|
|
(4, None),
|
|
|
|
(5, None),
|
|
|
|
(6, None),
|
|
|
|
(7, None),
|
|
|
|
(8, None),
|
|
|
|
(9, None),
|
|
|
|
(10, None),
|
|
|
|
(11, None),
|
|
|
|
(12, Some("┏━━━━┳━━━━━┓\n┃ 12 ┃ ... ┃\n┃ 3 ┃ ┃\n┃ 45 ┃ ┃\n┃ 67 ┃ ┃\n┃ 8 ┃ ┃\n┣━━━━╋━━━━━┫\n┃ 0 ┃ ... ┃\n┃ 0 ┃ ... ┃\n┗━━━━┻━━━━━┛")),
|
|
|
|
(13, Some("┏━━━━━┳━━━━━┓\n┃ 123 ┃ ... ┃\n┃ ┃ ┃\n┃ 456 ┃ ┃\n┃ 78 ┃ ┃\n┣━━━━━╋━━━━━┫\n┃ 0 ┃ ... ┃\n┃ 0 ┃ ... ┃\n┗━━━━━┻━━━━━┛")),
|
|
|
|
(21, Some("┏━━━━━━┳━━━━━━┳━━━━━┓\n┃ 123 ┃ qweq ┃ ... ┃\n┃ 4567 ┃ w ┃ ┃\n┃ 8 ┃ eqwe ┃ ┃\n┣━━━━━━╋━━━━━━╋━━━━━┫\n┃ 0 ┃ 1 ┃ ... ┃\n┃ 0 ┃ 1 ┃ ... ┃\n┗━━━━━━┻━━━━━━┻━━━━━┛")),
|
|
|
|
(29, Some("┏━━━━━━━━━━┳━━━━━━━━━━┳━━━━━┓\n┃ 123 ┃ qweqw ┃ ... ┃\n┃ 45678 ┃ eqwe ┃ ┃\n┣━━━━━━━━━━╋━━━━━━━━━━╋━━━━━┫\n┃ 0 ┃ 1 ┃ ... ┃\n┃ 0 ┃ 1 ┃ ... ┃\n┗━━━━━━━━━━┻━━━━━━━━━━┻━━━━━┛")),
|
|
|
|
(49, Some("┏━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━┳━━━━━┓\n┃ 123 45678 ┃ qweqw eqwe ┃ xxx xx xx x xx ┃ ... ┃\n┃ ┃ ┃ x xx xx ┃ ┃\n┣━━━━━━━━━━━╋━━━━━━━━━━━━╋━━━━━━━━━━━━━━━━╋━━━━━┫\n┃ 0 ┃ 1 ┃ 2 ┃ ... ┃\n┃ 0 ┃ 1 ┃ 2 ┃ ... ┃\n┗━━━━━━━━━━━┻━━━━━━━━━━━━┻━━━━━━━━━━━━━━━━┻━━━━━┛")),
|
|
|
|
];
|
2022-07-19 19:35:13 +02:00
|
|
|
|
2022-12-15 15:47:04 +01:00
|
|
|
test_trim(&tests, TrimStrategy::wrap(true));
|
2022-07-19 19:35:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn truncate_test() {
|
2022-12-15 15:47:04 +01:00
|
|
|
let tests = [
|
|
|
|
(0, None),
|
|
|
|
(1, None),
|
|
|
|
(2, None),
|
|
|
|
(3, None),
|
|
|
|
(4, None),
|
|
|
|
(5, None),
|
|
|
|
(6, None),
|
|
|
|
(7, None),
|
|
|
|
(8, None),
|
|
|
|
(9, None),
|
|
|
|
(10, None),
|
|
|
|
(11, None),
|
|
|
|
(12, Some("┏━━━━┳━━━━━┓\n┃ 12 ┃ ... ┃\n┣━━━━╋━━━━━┫\n┃ 0 ┃ ... ┃\n┃ 0 ┃ ... ┃\n┗━━━━┻━━━━━┛")),
|
|
|
|
(13, Some("┏━━━━━┳━━━━━┓\n┃ 123 ┃ ... ┃\n┣━━━━━╋━━━━━┫\n┃ 0 ┃ ... ┃\n┃ 0 ┃ ... ┃\n┗━━━━━┻━━━━━┛")),
|
|
|
|
(21, Some("┏━━━━━━┳━━━━━━┳━━━━━┓\n┃ 123 ┃ qweq ┃ ... ┃\n┣━━━━━━╋━━━━━━╋━━━━━┫\n┃ 0 ┃ 1 ┃ ... ┃\n┃ 0 ┃ 1 ┃ ... ┃\n┗━━━━━━┻━━━━━━┻━━━━━┛")),
|
|
|
|
(29, Some("┏━━━━━━━━━━┳━━━━━━━━━━┳━━━━━┓\n┃ 123 4567 ┃ qweqw eq ┃ ... ┃\n┣━━━━━━━━━━╋━━━━━━━━━━╋━━━━━┫\n┃ 0 ┃ 1 ┃ ... ┃\n┃ 0 ┃ 1 ┃ ... ┃\n┗━━━━━━━━━━┻━━━━━━━━━━┻━━━━━┛")),
|
|
|
|
(49, Some("┏━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━┳━━━━━┓\n┃ 123 45678 ┃ qweqw eqwe ┃ xxx xx xx x xx ┃ ... ┃\n┣━━━━━━━━━━━╋━━━━━━━━━━━━╋━━━━━━━━━━━━━━━━╋━━━━━┫\n┃ 0 ┃ 1 ┃ 2 ┃ ... ┃\n┃ 0 ┃ 1 ┃ 2 ┃ ... ┃\n┗━━━━━━━━━━━┻━━━━━━━━━━━━┻━━━━━━━━━━━━━━━━┻━━━━━┛")),
|
|
|
|
];
|
2022-07-19 19:35:13 +02:00
|
|
|
|
2022-12-15 15:47:04 +01:00
|
|
|
test_trim(&tests, TrimStrategy::truncate(None));
|
2022-07-19 19:35:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn truncate_with_suffix_test() {
|
2022-12-15 15:47:04 +01:00
|
|
|
let tests = [
|
|
|
|
(0, None),
|
|
|
|
(1, None),
|
|
|
|
(2, None),
|
|
|
|
(3, None),
|
|
|
|
(4, None),
|
|
|
|
(5, None),
|
|
|
|
(6, None),
|
|
|
|
(7, None),
|
|
|
|
(8, None),
|
|
|
|
(9, None),
|
|
|
|
(10, None),
|
|
|
|
(11, None),
|
|
|
|
(12, Some("┏━━━━┳━━━━━┓\n┃ .. ┃ ... ┃\n┣━━━━╋━━━━━┫\n┃ 0 ┃ ... ┃\n┃ 0 ┃ ... ┃\n┗━━━━┻━━━━━┛")),
|
|
|
|
(13, Some("┏━━━━━┳━━━━━┓\n┃ ... ┃ ... ┃\n┣━━━━━╋━━━━━┫\n┃ 0 ┃ ... ┃\n┃ 0 ┃ ... ┃\n┗━━━━━┻━━━━━┛")),
|
|
|
|
(21, Some("┏━━━━━━┳━━━━━━┳━━━━━┓\n┃ 1... ┃ q... ┃ ... ┃\n┣━━━━━━╋━━━━━━╋━━━━━┫\n┃ 0 ┃ 1 ┃ ... ┃\n┃ 0 ┃ 1 ┃ ... ┃\n┗━━━━━━┻━━━━━━┻━━━━━┛")),
|
|
|
|
(29, Some("┏━━━━━━━━━━┳━━━━━━━━━━┳━━━━━┓\n┃ 123 4... ┃ qweqw... ┃ ... ┃\n┣━━━━━━━━━━╋━━━━━━━━━━╋━━━━━┫\n┃ 0 ┃ 1 ┃ ... ┃\n┃ 0 ┃ 1 ┃ ... ┃\n┗━━━━━━━━━━┻━━━━━━━━━━┻━━━━━┛")),
|
|
|
|
(49, Some("┏━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━┳━━━━━┓\n┃ 123 45678 ┃ qweqw eqwe ┃ xxx xx xx x... ┃ ... ┃\n┣━━━━━━━━━━━╋━━━━━━━━━━━━╋━━━━━━━━━━━━━━━━╋━━━━━┫\n┃ 0 ┃ 1 ┃ 2 ┃ ... ┃\n┃ 0 ┃ 1 ┃ 2 ┃ ... ┃\n┗━━━━━━━━━━━┻━━━━━━━━━━━━┻━━━━━━━━━━━━━━━━┻━━━━━┛")),
|
|
|
|
];
|
2022-07-19 19:35:13 +02:00
|
|
|
|
2022-12-15 15:47:04 +01:00
|
|
|
test_trim(&tests, TrimStrategy::truncate(Some(String::from("..."))));
|
2022-07-19 19:35:13 +02:00
|
|
|
}
|
|
|
|
|
2022-12-15 15:47:04 +01:00
|
|
|
fn test_trim(tests: &[(usize, Option<&str>)], trim: TrimStrategy) {
|
|
|
|
let config = TableConfig::new(nu_table::TableTheme::heavy(), true, false, false).trim(trim);
|
|
|
|
let tests = tests.iter().map(|&(termwidth, expected)| {
|
|
|
|
TestCase::new(config.clone(), termwidth, expected.map(|s| s.to_string()))
|
|
|
|
});
|
2022-07-19 19:35:13 +02:00
|
|
|
|
2022-12-15 15:47:04 +01:00
|
|
|
let data = create_test_table0();
|
2022-07-19 19:35:13 +02:00
|
|
|
|
2022-12-15 15:47:04 +01:00
|
|
|
test_table(data, tests);
|
2022-07-19 19:35:13 +02:00
|
|
|
}
|
2022-07-19 22:16:12 +02:00
|
|
|
|
2022-12-15 15:47:04 +01:00
|
|
|
fn create_test_table0() -> VecCells {
|
2022-10-03 18:40:16 +02:00
|
|
|
let header = vec![
|
|
|
|
styled_str("123 45678"),
|
|
|
|
styled_str("qweqw eqwe"),
|
|
|
|
styled_str("xxx xx xx x xx x xx xx"),
|
|
|
|
styled_str("qqq qqq qqqq qqq qq"),
|
|
|
|
styled_str("qw"),
|
|
|
|
];
|
|
|
|
|
2022-12-15 15:47:04 +01:00
|
|
|
vec![header, create_row(5), create_row(5)]
|
2022-07-19 22:16:12 +02:00
|
|
|
}
|