use crate::{
run_pager,
util::{create_lscolors, create_map},
PagerConfig,
};
use nu_ansi_term::{Color, Style};
use nu_color_config::{get_color_map, StyleComputer};
use nu_engine::command_prelude::*;
use nu_protocol::Config;
#[derive(Clone)]
pub struct Explore;
impl Command for Explore {
fn name(&self) -> &str {
"explore"
}
fn description(&self) -> &str {
"Explore acts as a table pager, just like `less` does for text."
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build("explore")
.input_output_types(vec![(Type::Any, Type::Any)])
.named(
"head",
SyntaxShape::Boolean,
"Show or hide column headers (default true)",
None,
)
.switch("index", "Show row indexes when viewing a list", Some('i'))
.switch(
"tail",
"Start with the viewport scrolled to the bottom",
Some('t'),
)
.switch(
"peek",
"When quitting, output the value of the cell the cursor was on",
Some('p'),
)
.category(Category::Viewers)
}
fn extra_description(&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(engine_state, stack, "index")?;
let tail: bool = call.has_flag(engine_state, stack, "tail")?;
let peek_value: bool = call.has_flag(engine_state, stack, "peek")?;
let nu_config = stack.get_config(engine_state);
let style_computer = StyleComputer::from_config(engine_state, stack);
let mut explore_config = ExploreConfig::from_nu_config(&nu_config);
explore_config.table.show_header = show_head;
explore_config.table.show_index = show_index;
explore_config.table.separator_style = lookup_color(&style_computer, "separator");
let lscolors = create_lscolors(engine_state, stack);
let cwd = engine_state.cwd(Some(stack)).map_or(String::new(), |path| {
path.to_str().unwrap_or("").to_string()
});
let config = PagerConfig::new(
&nu_config,
&explore_config,
&style_computer,
&lscolors,
peek_value,
tail,
&cwd,
);
let result = run_pager(engine_state, &mut stack.clone(), input, config);
match result {
Ok(Some(value)) => Ok(PipelineData::Value(value, None)),
Ok(None) => Ok(PipelineData::Value(Value::default(), None)),
Err(err) => {
let shell_error = match err.downcast::<ShellError>() {
Ok(e) => e,
Err(e) => ShellError::GenericError {
error: e.to_string(),
msg: "".into(),
span: None,
help: None,
inner: vec![],
},
};
Ok(PipelineData::Value(
Value::error(shell_error, call.head),
None,
))
}
}
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Explore the system host information record",
example: r#"sys host | explore"#,
result: None,
},
Example {
description: "Explore the output of `ls` without column names",
example: r#"ls | explore --head false"#,
result: None,
},
Example {
description: "Explore a list of Markdown files' contents, with row indexes",
example: r#"glob *.md | each {|| open } | explore --index"#,
result: None,
},
Example {
description:
"Explore a JSON file, then save the last visited sub-structure to a file",
example: r#"open file.json | explore --peek | to json | save part.json"#,
result: None,
},
]
}
}
#[derive(Debug, Clone)]
pub struct ExploreConfig {
pub table: TableConfig,
pub selected_cell: Style,
pub status_info: Style,
pub status_success: Style,
pub status_warn: Style,
pub status_error: Style,
pub status_bar_background: Style,
pub status_bar_text: Style,
pub cmd_bar_text: Style,
pub cmd_bar_background: Style,
pub highlight: Style,
pub try_reactive: bool,
}
impl Default for ExploreConfig {
fn default() -> Self {
Self {
table: TableConfig::default(),
selected_cell: color(None, Some(Color::LightBlue)),
status_info: color(None, None),
status_success: color(Some(Color::Black), Some(Color::Green)),
status_warn: color(None, None),
status_error: color(Some(Color::White), Some(Color::Red)),
status_bar_background: color(
Some(Color::Rgb(29, 31, 33)),
Some(Color::Rgb(196, 201, 198)),
),
status_bar_text: color(None, None),
cmd_bar_text: color(Some(Color::Rgb(196, 201, 198)), None),
cmd_bar_background: color(None, None),
highlight: color(Some(Color::Black), Some(Color::Yellow)),
try_reactive: false,
}
}
}
impl ExploreConfig {
pub fn from_nu_config(config: &Config) -> Self {
let mut ret = Self::default();
ret.table.column_padding_left = config.table.padding.left;
ret.table.column_padding_right = config.table.padding.right;
let explore_cfg_hash_map = config.explore.clone();
let colors = get_color_map(&explore_cfg_hash_map);
if let Some(s) = colors.get("status_bar_text") {
ret.status_bar_text = *s;
}
if let Some(s) = colors.get("status_bar_background") {
ret.status_bar_background = *s;
}
if let Some(s) = colors.get("command_bar_text") {
ret.cmd_bar_text = *s;
}
if let Some(s) = colors.get("command_bar_background") {
ret.cmd_bar_background = *s;
}
if let Some(s) = colors.get("command_bar_background") {
ret.cmd_bar_background = *s;
}
if let Some(s) = colors.get("selected_cell") {
ret.selected_cell = *s;
}
if let Some(hm) = explore_cfg_hash_map.get("status").and_then(create_map) {
let colors = get_color_map(&hm);
if let Some(s) = colors.get("info") {
ret.status_info = *s;
}
if let Some(s) = colors.get("success") {
ret.status_success = *s;
}
if let Some(s) = colors.get("warn") {
ret.status_warn = *s;
}
if let Some(s) = colors.get("error") {
ret.status_error = *s;
}
}
if let Some(hm) = explore_cfg_hash_map.get("try").and_then(create_map) {
if let Some(reactive) = hm.get("reactive") {
if let Ok(b) = reactive.as_bool() {
ret.try_reactive = b;
}
}
}
ret
}
}
#[derive(Debug, Default, Clone, Copy)]
pub struct TableConfig {
pub separator_style: Style,
pub show_index: bool,
pub show_header: bool,
pub column_padding_left: usize,
pub column_padding_right: usize,
}
const fn color(foreground: Option<Color>, background: Option<Color>) -> Style {
Style {
background,
foreground,
is_blink: false,
is_bold: false,
is_dimmed: false,
is_hidden: false,
is_italic: false,
is_reverse: false,
is_strikethrough: false,
is_underline: false,
prefix_with_reset: false,
}
}
fn lookup_color(style_computer: &StyleComputer, key: &str) -> nu_ansi_term::Style {
style_computer.compute(key, &Value::nothing(Span::unknown()))
}