nushell/crates/nu-command/src/viewers/explore.rs

214 lines
6.2 KiB
Rust
Raw Normal View History

[MVP][WIP] `less` like pager (#6984) Run it as `explore`. #### example ```nu ls | explore ``` Configuration points in `config.nu` file. ``` # A 'explore' utility config explore_config: { highlight: { bg: 'yellow', fg: 'black' } status_bar: { bg: '#C4C9C6', fg: '#1D1F21' } command_bar: { fg: '#C4C9C6' } split_line: '#404040' cursor: true # selected_column: 'blue' # selected_row: { fg: 'yellow', bg: '#C1C2A3' } # selected_cell: { fg: 'white', bg: '#777777' } # line_shift: false, # line_index: false, # line_head_top: false, # line_head_bottom: false, } ``` You can start without a pipeline and type `explore` and it'll give you a few tips. ![image](https://user-images.githubusercontent.com/343840/205088971-a8c0262f-f222-4641-b13a-027fbd4f5e1a.png) If you type `:help` you an see the help screen with some information on what tui keybindings are available. ![image](https://user-images.githubusercontent.com/343840/205089461-c4c54217-7ec4-4fa0-96c0-643d68dc0062.png) From the `:help` screen you can now hit `i` and that puts you in `cursor` aka `inspection` mode and you can move the cursor left right up down and it you put it on an area such as `[table 5 rows]` and hit the enter key, you'll see something like this, which shows all the `:` commands. If you hit `esc` it will take you to the previous screen. ![image](https://user-images.githubusercontent.com/343840/205090155-3558a14b-87b7-4072-8dfb-dc8cc2ef4943.png) If you then type `:try` you'll get this type of window where you can type in the top portion and see results in the bottom. ![image](https://user-images.githubusercontent.com/343840/205089185-3c065551-0792-43d6-a13c-a52762856209.png) The `:nu` command is interesting because you can type pipelines like `:nu ls | sort-by type size` or another pipeline of your choosing such as `:nu sys` and that will show the table that looks like this, which we're calling "table mode". ![image](https://user-images.githubusercontent.com/343840/205090809-e686ff0f-6d0b-4347-8ed0-8c59adfbd741.png) If you hit the `t` key it will now transpose the view to look like this. ![image](https://user-images.githubusercontent.com/343840/205090948-a834d7f2-1713-4dfe-92fe-5432f287df3d.png) In table mode or transposed table mode you can use the `i` key to inspect any collapsed field like `{record 8 fields}`, `[table 16 rows]`, `[list x]`, etc. One of the original benefits was that when you're in a view that has a lot of columns, `explore` gives you the ability to scroll left, right, up, and down. `explore` is also smart enough to know when you're in table mode versus preview mode. If you do `open Cargo.toml | explore` you get this. ![image](https://user-images.githubusercontent.com/343840/205091822-cac79130-3a52-4ca8-9210-eba5be30ed58.png) If you type `open --raw Cargo.toml | explore` you get this where you can scroll left, right, up, down. This is called preview mode. ![image](https://user-images.githubusercontent.com/343840/205091990-69455191-ab78-4fea-a961-feafafc16d70.png) When you're in table mode, you can also type `:preview`. So, with `open --raw Cargo.toml | explore`, if you type `:preview`, it will look like this. ![image](https://user-images.githubusercontent.com/343840/205092569-436aa55a-0474-48d5-ab71-baddb1f43027.png) Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com> Co-authored-by: Darren Schroeder <343840+fdncred@users.noreply.github.com>
2022-12-01 15:32:10 +00:00
use std::collections::HashMap;
use nu_ansi_term::{Color, Style};
use nu_color_config::{get_color_config, get_color_map};
use nu_engine::CallExt;
use nu_explore::{StyleConfig, TableConfig, TableSplitLines, ViewConfig};
use nu_protocol::{
ast::Call,
engine::{Command, EngineState, Stack},
Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Value,
};
/// A `less` like program to render a [Value] as a table.
#[derive(Clone)]
pub struct Explore;
impl Command for Explore {
fn name(&self) -> &str {
"explore"
}
fn usage(&self) -> &str {
"Explore acts as a table pager, just like `less` does for text"
}
fn signature(&self) -> nu_protocol::Signature {
// todo: Fix error message when it's empty
// if we set h i short flags it panics????
Signature::build("explore")
.named(
"head",
SyntaxShape::Boolean,
"Setting it to false makes it doesn't show column headers",
None,
)
.switch("index", "A flag to show a index beside the rows", Some('i'))
.switch(
"reverse",
"Makes it start from the end. (like `more`)",
Some('r'),
)
.switch("peek", "Return a last seen cell content", Some('p'))
.category(Category::Viewers)
}
fn extra_usage(&self) -> &str {
r#"Press <:> then <h> to get a help menu."#
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let show_head: bool = call.get_flag(engine_state, stack, "head")?.unwrap_or(true);
let show_index: bool = call.has_flag("index");
let is_reverse: bool = call.has_flag("reverse");
let peek_value: bool = call.has_flag("peek");
let table_cfg = TableConfig {
show_index,
show_head,
peek_value,
reverse: is_reverse,
show_help: false,
};
let ctrlc = engine_state.ctrlc.clone();
let config = engine_state.get_config();
let color_hm = get_color_config(config);
let style = theme_from_config(&config.explore);
[MVP][WIP] `less` like pager (#6984) Run it as `explore`. #### example ```nu ls | explore ``` Configuration points in `config.nu` file. ``` # A 'explore' utility config explore_config: { highlight: { bg: 'yellow', fg: 'black' } status_bar: { bg: '#C4C9C6', fg: '#1D1F21' } command_bar: { fg: '#C4C9C6' } split_line: '#404040' cursor: true # selected_column: 'blue' # selected_row: { fg: 'yellow', bg: '#C1C2A3' } # selected_cell: { fg: 'white', bg: '#777777' } # line_shift: false, # line_index: false, # line_head_top: false, # line_head_bottom: false, } ``` You can start without a pipeline and type `explore` and it'll give you a few tips. ![image](https://user-images.githubusercontent.com/343840/205088971-a8c0262f-f222-4641-b13a-027fbd4f5e1a.png) If you type `:help` you an see the help screen with some information on what tui keybindings are available. ![image](https://user-images.githubusercontent.com/343840/205089461-c4c54217-7ec4-4fa0-96c0-643d68dc0062.png) From the `:help` screen you can now hit `i` and that puts you in `cursor` aka `inspection` mode and you can move the cursor left right up down and it you put it on an area such as `[table 5 rows]` and hit the enter key, you'll see something like this, which shows all the `:` commands. If you hit `esc` it will take you to the previous screen. ![image](https://user-images.githubusercontent.com/343840/205090155-3558a14b-87b7-4072-8dfb-dc8cc2ef4943.png) If you then type `:try` you'll get this type of window where you can type in the top portion and see results in the bottom. ![image](https://user-images.githubusercontent.com/343840/205089185-3c065551-0792-43d6-a13c-a52762856209.png) The `:nu` command is interesting because you can type pipelines like `:nu ls | sort-by type size` or another pipeline of your choosing such as `:nu sys` and that will show the table that looks like this, which we're calling "table mode". ![image](https://user-images.githubusercontent.com/343840/205090809-e686ff0f-6d0b-4347-8ed0-8c59adfbd741.png) If you hit the `t` key it will now transpose the view to look like this. ![image](https://user-images.githubusercontent.com/343840/205090948-a834d7f2-1713-4dfe-92fe-5432f287df3d.png) In table mode or transposed table mode you can use the `i` key to inspect any collapsed field like `{record 8 fields}`, `[table 16 rows]`, `[list x]`, etc. One of the original benefits was that when you're in a view that has a lot of columns, `explore` gives you the ability to scroll left, right, up, and down. `explore` is also smart enough to know when you're in table mode versus preview mode. If you do `open Cargo.toml | explore` you get this. ![image](https://user-images.githubusercontent.com/343840/205091822-cac79130-3a52-4ca8-9210-eba5be30ed58.png) If you type `open --raw Cargo.toml | explore` you get this where you can scroll left, right, up, down. This is called preview mode. ![image](https://user-images.githubusercontent.com/343840/205091990-69455191-ab78-4fea-a961-feafafc16d70.png) When you're in table mode, you can also type `:preview`. So, with `open --raw Cargo.toml | explore`, if you type `:preview`, it will look like this. ![image](https://user-images.githubusercontent.com/343840/205092569-436aa55a-0474-48d5-ab71-baddb1f43027.png) Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com> Co-authored-by: Darren Schroeder <343840+fdncred@users.noreply.github.com>
2022-12-01 15:32:10 +00:00
let view_cfg = ViewConfig::new(config, &color_hm, &style);
let result = nu_explore::run_pager(engine_state, stack, ctrlc, table_cfg, view_cfg, input);
match result {
Ok(Some(value)) => Ok(PipelineData::Value(value, None)),
Ok(None) => Ok(PipelineData::Value(Value::default(), None)),
Err(err) => Ok(PipelineData::Value(
Value::Error { error: err.into() },
None,
)),
}
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "List the files in current directory, an looking at them via explore.",
example: r#"ls | explore"#,
result: None,
},
Example {
description: "Inspect system information (explore with index).",
example: r#"sys | explore -i"#,
result: None,
},
Example {
description: "Inspect $nu information (explore with no column names).",
example: r#"$nu | explore --head false"#,
result: None,
},
Example {
description: "Inspect $nu information and return an entity where you've stopped.",
example: r#"$nu | explore --peek"#,
result: None,
},
]
}
}
fn theme_from_config(config: &HashMap<String, Value>) -> StyleConfig {
let colors = get_color_map(config);
let mut style = default_style();
if let Some(s) = colors.get("status_bar") {
style.status_bar = *s;
}
if let Some(s) = colors.get("command_bar") {
style.cmd_bar = *s;
}
if let Some(s) = colors.get("split_line") {
style.split_line = *s;
}
if let Some(s) = colors.get("highlight") {
style.highlight = *s;
}
if let Some(s) = colors.get("selected_cell") {
style.selected_cell = Some(*s);
}
if let Some(s) = colors.get("selected_row") {
style.selected_row = Some(*s);
}
if let Some(s) = colors.get("selected_column") {
style.selected_column = Some(*s);
}
if let Some(show_cursor) = config.get("cursor").and_then(|v| v.as_bool().ok()) {
style.show_cursow = show_cursor;
}
if let Some(b) = config.get("line_head_top").and_then(|v| v.as_bool().ok()) {
style.split_lines.header_top = b;
}
if let Some(b) = config
.get("line_head_bottom")
.and_then(|v| v.as_bool().ok())
{
style.split_lines.header_bottom = b;
}
if let Some(b) = config.get("line_shift").and_then(|v| v.as_bool().ok()) {
style.split_lines.shift_line = b;
}
if let Some(b) = config.get("line_index").and_then(|v| v.as_bool().ok()) {
style.split_lines.index_line = b;
}
style
}
fn default_style() -> StyleConfig {
StyleConfig {
status_bar: Style {
background: Some(Color::Rgb(196, 201, 198)),
foreground: Some(Color::Rgb(29, 31, 33)),
..Default::default()
},
highlight: Style {
background: Some(Color::Yellow),
foreground: Some(Color::Black),
..Default::default()
},
split_line: Style {
foreground: Some(Color::Rgb(64, 64, 64)),
..Default::default()
},
cmd_bar: Style {
foreground: Some(Color::Rgb(196, 201, 198)),
..Default::default()
},
status_error: Style {
background: Some(Color::Red),
foreground: Some(Color::White),
..Default::default()
},
status_info: Style::default(),
status_warn: Style::default(),
selected_cell: None,
selected_column: None,
selected_row: None,
show_cursow: true,
split_lines: TableSplitLines {
header_bottom: true,
header_top: true,
index_line: true,
shift_line: true,
},
}
}