mirror of
https://github.com/nushell/nushell.git
synced 2025-08-26 13:20:35 +02:00
nu-explore/ A few things (#7339)
ref #7332 Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com> Co-authored-by: Darren Schroeder <343840+fdncred@users.noreply.github.com>
This commit is contained in:
171
crates/nu-explore/src/commands/config.rs
Normal file
171
crates/nu-explore/src/commands/config.rs
Normal file
@@ -0,0 +1,171 @@
|
||||
use std::io::Result;
|
||||
|
||||
use nu_protocol::{
|
||||
engine::{EngineState, Stack},
|
||||
Value,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
nu_common::{nu_str, NuSpan},
|
||||
registry::Command,
|
||||
views::{configuration, ConfigurationView, Preview},
|
||||
};
|
||||
|
||||
use super::{default_color_list, ConfigOption, HelpManual, ViewCommand};
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
pub struct ConfigCmd {
|
||||
commands: Vec<Command>,
|
||||
groups: Vec<ConfigOption>,
|
||||
}
|
||||
|
||||
impl ConfigCmd {
|
||||
pub const NAME: &'static str = "config";
|
||||
|
||||
pub fn from_commands(commands: Vec<Command>) -> Self {
|
||||
Self {
|
||||
commands,
|
||||
groups: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn register_group(&mut self, group: ConfigOption) {
|
||||
self.groups.push(group);
|
||||
}
|
||||
}
|
||||
|
||||
impl ViewCommand for ConfigCmd {
|
||||
type View = ConfigurationView;
|
||||
|
||||
fn name(&self) -> &'static str {
|
||||
Self::NAME
|
||||
}
|
||||
|
||||
fn usage(&self) -> &'static str {
|
||||
""
|
||||
}
|
||||
|
||||
fn help(&self) -> Option<HelpManual> {
|
||||
let config_options = vec![
|
||||
ConfigOption::new(
|
||||
":config options",
|
||||
"A border color of menus",
|
||||
"config.border_color",
|
||||
default_color_list(),
|
||||
),
|
||||
ConfigOption::new(
|
||||
":config options",
|
||||
"Set a color of entries in a list",
|
||||
"config.list_color",
|
||||
default_color_list(),
|
||||
),
|
||||
ConfigOption::new(
|
||||
":config options",
|
||||
"Set a color of a chosen entry in a list",
|
||||
"config.cursor_color",
|
||||
default_color_list(),
|
||||
),
|
||||
];
|
||||
|
||||
Some(HelpManual {
|
||||
name: Self::NAME,
|
||||
description:
|
||||
"Interactive configuration manager.\nCan be used to set various explore settings.\n\nIt could be consired an interactive version of :tweak",
|
||||
config_options,
|
||||
arguments: vec![],
|
||||
examples: vec![],
|
||||
input: vec![],
|
||||
})
|
||||
}
|
||||
|
||||
fn parse(&mut self, _: &str) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn display_config_option(&mut self, _: String, _: String, _: String) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn spawn(
|
||||
&mut self,
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
_: Option<Value>,
|
||||
) -> Result<Self::View> {
|
||||
let mut options = vec![];
|
||||
|
||||
let default_table = create_default_value();
|
||||
for cmd in &self.commands {
|
||||
let cmd = match cmd {
|
||||
Command::Reactive(_) => continue,
|
||||
Command::View { cmd, .. } => cmd,
|
||||
};
|
||||
|
||||
let help = match cmd.help() {
|
||||
Some(help) => help,
|
||||
None => continue,
|
||||
};
|
||||
|
||||
for opt in help.config_options {
|
||||
let mut values = vec![];
|
||||
for value in opt.values {
|
||||
let mut cmd = cmd.clone();
|
||||
|
||||
let can_be_displayed = cmd.display_config_option(
|
||||
opt.group.clone(),
|
||||
opt.key.clone(),
|
||||
value.example.to_string(),
|
||||
);
|
||||
let view = if can_be_displayed {
|
||||
cmd.spawn(engine_state, stack, Some(default_table.clone()))?
|
||||
} else {
|
||||
Box::new(Preview::new(&opt.description))
|
||||
};
|
||||
|
||||
let option = configuration::ConfigOption::new(value.example.to_string(), view);
|
||||
values.push(option);
|
||||
}
|
||||
|
||||
let group = configuration::ConfigGroup::new(opt.key, values, opt.description);
|
||||
options.push((opt.group, group));
|
||||
}
|
||||
}
|
||||
|
||||
for opt in &self.groups {
|
||||
let mut values = vec![];
|
||||
for value in &opt.values {
|
||||
let view = Box::new(Preview::new(&opt.description));
|
||||
|
||||
let option = configuration::ConfigOption::new(value.example.to_string(), view);
|
||||
values.push(option);
|
||||
}
|
||||
|
||||
let group =
|
||||
configuration::ConfigGroup::new(opt.key.clone(), values, opt.description.clone());
|
||||
options.push((opt.group.clone(), group));
|
||||
}
|
||||
|
||||
options.sort_by(|(group1, opt1), (group2, opt2)| {
|
||||
group1.cmp(group2).then(opt1.group().cmp(opt2.group()))
|
||||
});
|
||||
|
||||
let options = options.into_iter().map(|(_, opt)| opt).collect();
|
||||
|
||||
Ok(ConfigurationView::new(options))
|
||||
}
|
||||
}
|
||||
|
||||
fn create_default_value() -> Value {
|
||||
let span = NuSpan::unknown();
|
||||
|
||||
let record = |i: usize| Value::Record {
|
||||
cols: vec![String::from("key"), String::from("value")],
|
||||
vals: vec![nu_str(format!("key-{}", i)), nu_str(format!("{}", i))],
|
||||
span,
|
||||
};
|
||||
|
||||
Value::List {
|
||||
vals: vec![record(0), record(1), record(2)],
|
||||
span,
|
||||
}
|
||||
}
|
137
crates/nu-explore/src/commands/config_show.rs
Normal file
137
crates/nu-explore/src/commands/config_show.rs
Normal file
@@ -0,0 +1,137 @@
|
||||
use std::io::Result;
|
||||
|
||||
use nu_protocol::{
|
||||
engine::{EngineState, Stack},
|
||||
Value,
|
||||
};
|
||||
use tui::layout::Rect;
|
||||
|
||||
use crate::{
|
||||
nu_common::try_build_table,
|
||||
pager::Frame,
|
||||
util::map_into_value,
|
||||
views::{Layout, Preview, View, ViewConfig},
|
||||
};
|
||||
|
||||
use super::{HelpExample, HelpManual, ViewCommand};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ConfigShowCmd {
|
||||
format: ConfigFormat,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
enum ConfigFormat {
|
||||
Table,
|
||||
Nu,
|
||||
}
|
||||
|
||||
impl ConfigShowCmd {
|
||||
pub fn new() -> Self {
|
||||
ConfigShowCmd {
|
||||
format: ConfigFormat::Table,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ConfigShowCmd {
|
||||
pub const NAME: &'static str = "config-show";
|
||||
}
|
||||
|
||||
impl ViewCommand for ConfigShowCmd {
|
||||
type View = ConfigView;
|
||||
|
||||
fn name(&self) -> &'static str {
|
||||
Self::NAME
|
||||
}
|
||||
|
||||
fn usage(&self) -> &'static str {
|
||||
""
|
||||
}
|
||||
|
||||
fn help(&self) -> Option<HelpManual> {
|
||||
Some(HelpManual {
|
||||
name: Self::NAME,
|
||||
description:
|
||||
"Return a currently used configuration.\nSome default fields might be missing.",
|
||||
arguments: vec![HelpExample::new("nu", "Use a nuon format instead")],
|
||||
config_options: vec![],
|
||||
input: vec![],
|
||||
examples: vec![],
|
||||
})
|
||||
}
|
||||
|
||||
fn display_config_option(&mut self, _: String, _: String, _: String) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn parse(&mut self, args: &str) -> Result<()> {
|
||||
if args.trim() == "nu" {
|
||||
self.format = ConfigFormat::Nu;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn spawn(&mut self, _: &EngineState, _: &mut Stack, _: Option<Value>) -> Result<Self::View> {
|
||||
Ok(ConfigView {
|
||||
preview: Preview::new(""),
|
||||
format: self.format.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ConfigView {
|
||||
preview: Preview,
|
||||
format: ConfigFormat,
|
||||
}
|
||||
|
||||
impl View for ConfigView {
|
||||
fn draw(&mut self, f: &mut Frame, area: Rect, cfg: ViewConfig<'_>, layout: &mut Layout) {
|
||||
self.preview.draw(f, area, cfg, layout)
|
||||
}
|
||||
|
||||
fn handle_input(
|
||||
&mut self,
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
layout: &Layout,
|
||||
info: &mut crate::pager::ViewInfo,
|
||||
key: crossterm::event::KeyEvent,
|
||||
) -> Option<crate::pager::Transition> {
|
||||
self.preview
|
||||
.handle_input(engine_state, stack, layout, info, key)
|
||||
}
|
||||
|
||||
fn setup(&mut self, config: ViewConfig<'_>) {
|
||||
let text = self.create_output_string(config);
|
||||
|
||||
self.preview = Preview::new(&text);
|
||||
self.preview
|
||||
.set_value(map_into_value(config.config.clone()));
|
||||
}
|
||||
|
||||
fn exit(&mut self) -> Option<Value> {
|
||||
self.preview.exit()
|
||||
}
|
||||
|
||||
fn collect_data(&self) -> Vec<crate::nu_common::NuText> {
|
||||
self.preview.collect_data()
|
||||
}
|
||||
|
||||
fn show_data(&mut self, i: usize) -> bool {
|
||||
self.preview.show_data(i)
|
||||
}
|
||||
}
|
||||
|
||||
impl ConfigView {
|
||||
fn create_output_string(&mut self, config: ViewConfig) -> String {
|
||||
match self.format {
|
||||
ConfigFormat::Table => {
|
||||
let value = map_into_value(config.config.clone());
|
||||
try_build_table(None, config.nu_config, config.color_hm, value)
|
||||
}
|
||||
ConfigFormat::Nu => nu_json::to_string(&config.config).unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
}
|
100
crates/nu-explore/src/commands/expand.rs
Normal file
100
crates/nu-explore/src/commands/expand.rs
Normal file
@@ -0,0 +1,100 @@
|
||||
use std::{io::Result, vec};
|
||||
|
||||
use nu_color_config::get_color_config;
|
||||
use nu_protocol::{
|
||||
engine::{EngineState, Stack},
|
||||
Value,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
nu_common::{self, collect_input},
|
||||
views::Preview,
|
||||
};
|
||||
|
||||
use super::{HelpManual, Shortcode, ViewCommand};
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
pub struct ExpandCmd;
|
||||
|
||||
impl ExpandCmd {
|
||||
pub fn new() -> Self {
|
||||
Self
|
||||
}
|
||||
}
|
||||
|
||||
impl ExpandCmd {
|
||||
pub const NAME: &'static str = "expand";
|
||||
}
|
||||
|
||||
impl ViewCommand for ExpandCmd {
|
||||
type View = Preview;
|
||||
|
||||
fn name(&self) -> &'static str {
|
||||
Self::NAME
|
||||
}
|
||||
|
||||
fn usage(&self) -> &'static str {
|
||||
""
|
||||
}
|
||||
|
||||
fn help(&self) -> Option<HelpManual> {
|
||||
#[rustfmt::skip]
|
||||
let shortcodes = vec![
|
||||
Shortcode::new("Up", "", "Moves the viewport one row up"),
|
||||
Shortcode::new("Down", "", "Moves the viewport one row down"),
|
||||
Shortcode::new("Left", "", "Moves the viewport one column left"),
|
||||
Shortcode::new("Right", "", "Moves the viewport one column right"),
|
||||
Shortcode::new("PgDown", "", "Moves the viewport one page of rows down"),
|
||||
Shortcode::new("PgUp", "", "Moves the cursor or viewport one page of rows up"),
|
||||
Shortcode::new("Esc", "", "Exits cursor mode. Exits the currently explored data."),
|
||||
];
|
||||
|
||||
Some(HelpManual {
|
||||
name: "expand",
|
||||
description:
|
||||
"View the currently selected cell's data using the `table` Nushell command",
|
||||
arguments: vec![],
|
||||
examples: vec![],
|
||||
config_options: vec![],
|
||||
input: shortcodes,
|
||||
})
|
||||
}
|
||||
|
||||
fn display_config_option(&mut self, _: String, _: String, _: String) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn parse(&mut self, _: &str) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn spawn(
|
||||
&mut self,
|
||||
engine_state: &EngineState,
|
||||
_stack: &mut Stack,
|
||||
value: Option<Value>,
|
||||
) -> Result<Self::View> {
|
||||
let value = value
|
||||
.map(|v| convert_value_to_string(v, engine_state))
|
||||
.unwrap_or_default();
|
||||
|
||||
Ok(Preview::new(&value))
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_value_to_string(value: Value, engine_state: &EngineState) -> String {
|
||||
let (cols, vals) = collect_input(value.clone());
|
||||
|
||||
let has_no_head = cols.is_empty() || (cols.len() == 1 && cols[0].is_empty());
|
||||
let has_single_value = vals.len() == 1 && vals[0].len() == 1;
|
||||
if !has_no_head && has_single_value {
|
||||
let config = engine_state.get_config();
|
||||
vals[0][0].into_abbreviated_string(config)
|
||||
} else {
|
||||
let ctrlc = engine_state.ctrlc.clone();
|
||||
let config = engine_state.get_config();
|
||||
let color_hm = get_color_config(config);
|
||||
|
||||
nu_common::try_build_table(ctrlc, config, &color_hm, value)
|
||||
}
|
||||
}
|
@@ -3,19 +3,24 @@ use std::{
|
||||
io::{self, Result},
|
||||
};
|
||||
|
||||
use crossterm::event::KeyEvent;
|
||||
use nu_protocol::{
|
||||
engine::{EngineState, Stack},
|
||||
Value,
|
||||
};
|
||||
use tui::layout::Rect;
|
||||
|
||||
use crate::{nu_common::NuSpan, pager::TableConfig, views::RecordView};
|
||||
use crate::{
|
||||
nu_common::{collect_input, NuSpan},
|
||||
pager::{Frame, Transition, ViewInfo},
|
||||
views::{Layout, Preview, RecordView, View, ViewConfig},
|
||||
};
|
||||
|
||||
use super::{HelpExample, HelpManual, ViewCommand};
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct HelpCmd {
|
||||
input_command: String,
|
||||
table_cfg: TableConfig,
|
||||
supported_commands: Vec<HelpManual>,
|
||||
aliases: HashMap<String, Vec<String>>,
|
||||
}
|
||||
@@ -23,18 +28,39 @@ pub struct HelpCmd {
|
||||
impl HelpCmd {
|
||||
pub const NAME: &'static str = "help";
|
||||
|
||||
pub fn new(
|
||||
commands: Vec<HelpManual>,
|
||||
aliases: &[(&str, &str)],
|
||||
table_cfg: TableConfig,
|
||||
) -> Self {
|
||||
const HELP_MESSAGE: &'static str = r#" Explore - main help file
|
||||
|
||||
Move around: Use the cursor keys.
|
||||
Close this window: Use "<Esc>".
|
||||
Get out of Explore: Use ":q<Enter>" (or <Ctrl> + <D>).
|
||||
|
||||
Get specific help: It is possible to go directly to whatewer you want help on,
|
||||
by adding an argument to the ":help" command.
|
||||
|
||||
Currently you can only get help on a few commands.
|
||||
To obtain a list of supported commands run ":help :<Enter>"
|
||||
|
||||
------------------------------------------------------------------------------------
|
||||
|
||||
Regular expressions ~
|
||||
|
||||
Most commands support regular expressions.
|
||||
|
||||
You can type "/" and type a pattern you want to search on.
|
||||
Then hit <Enter> and you will see the search results.
|
||||
|
||||
To go to the next hit use "<n>" key.
|
||||
|
||||
You also can do a reverse search by using "?" instead of "/".
|
||||
"#;
|
||||
|
||||
pub fn new(commands: Vec<HelpManual>, aliases: &[(&str, &str)]) -> Self {
|
||||
let aliases = collect_aliases(aliases);
|
||||
|
||||
Self {
|
||||
input_command: String::new(),
|
||||
supported_commands: commands,
|
||||
aliases,
|
||||
table_cfg,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -51,7 +77,7 @@ fn collect_aliases(aliases: &[(&str, &str)]) -> HashMap<String, Vec<String>> {
|
||||
}
|
||||
|
||||
impl ViewCommand for HelpCmd {
|
||||
type View = RecordView<'static>;
|
||||
type View = HelpView<'static>;
|
||||
|
||||
fn name(&self) -> &'static str {
|
||||
Self::NAME
|
||||
@@ -62,27 +88,32 @@ impl ViewCommand for HelpCmd {
|
||||
}
|
||||
|
||||
fn help(&self) -> Option<HelpManual> {
|
||||
#[rustfmt::skip]
|
||||
let examples = vec![
|
||||
HelpExample::new("help", "Open the help page for all of `explore`"),
|
||||
HelpExample::new("help :nu", "Open the help page for the `nu` explore command"),
|
||||
HelpExample::new("help :help", "...It was supposed to be hidden....until...now..."),
|
||||
];
|
||||
|
||||
#[rustfmt::skip]
|
||||
let arguments = vec![
|
||||
HelpExample::new("help :command", "you can provide a command and a help information for it will be displayed")
|
||||
];
|
||||
|
||||
Some(HelpManual {
|
||||
name: "help",
|
||||
description: "Explore the help page for `explore`",
|
||||
arguments: vec![],
|
||||
examples: vec![
|
||||
HelpExample {
|
||||
example: "help",
|
||||
description: "Open the help page for all of `explore`",
|
||||
},
|
||||
HelpExample {
|
||||
example: "help nu",
|
||||
description: "Open the help page for the `nu` explore command",
|
||||
},
|
||||
HelpExample {
|
||||
example: "help help",
|
||||
description: "...It was supposed to be hidden....until...now...",
|
||||
},
|
||||
],
|
||||
arguments,
|
||||
examples,
|
||||
input: vec![],
|
||||
config_options: vec![],
|
||||
})
|
||||
}
|
||||
|
||||
fn display_config_option(&mut self, _: String, _: String, _: String) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn parse(&mut self, args: &str) -> Result<()> {
|
||||
self.input_command = args.trim().to_owned();
|
||||
|
||||
@@ -91,15 +122,31 @@ impl ViewCommand for HelpCmd {
|
||||
|
||||
fn spawn(&mut self, _: &EngineState, _: &mut Stack, _: Option<Value>) -> Result<Self::View> {
|
||||
if self.input_command.is_empty() {
|
||||
let (headers, data) = help_frame_data(&self.supported_commands, &self.aliases);
|
||||
let view = RecordView::new(headers, data, self.table_cfg);
|
||||
return Ok(view);
|
||||
return Ok(HelpView::Preview(Preview::new(Self::HELP_MESSAGE)));
|
||||
}
|
||||
|
||||
if !self.input_command.starts_with(':') {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"unexpected help argument",
|
||||
));
|
||||
}
|
||||
|
||||
if self.input_command == ":" {
|
||||
let (headers, data) = help_frame_data(&self.supported_commands, &self.aliases);
|
||||
let view = RecordView::new(headers, data);
|
||||
return Ok(HelpView::Records(view));
|
||||
}
|
||||
|
||||
let command = self
|
||||
.input_command
|
||||
.strip_prefix(':')
|
||||
.expect("we just checked the prefix");
|
||||
|
||||
let manual = self
|
||||
.supported_commands
|
||||
.iter()
|
||||
.find(|manual| manual.name == self.input_command)
|
||||
.find(|manual| manual.name == command)
|
||||
.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "a given command was not found"))?;
|
||||
|
||||
let aliases = self
|
||||
@@ -108,9 +155,9 @@ impl ViewCommand for HelpCmd {
|
||||
.map(|l| l.as_slice())
|
||||
.unwrap_or(&[]);
|
||||
let (headers, data) = help_manual_data(manual, aliases);
|
||||
let view = RecordView::new(headers, data, self.table_cfg);
|
||||
let view = RecordView::new(headers, data);
|
||||
|
||||
Ok(view)
|
||||
Ok(HelpView::Records(view))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,20 +165,6 @@ fn help_frame_data(
|
||||
supported_commands: &[HelpManual],
|
||||
aliases: &HashMap<String, Vec<String>>,
|
||||
) -> (Vec<String>, Vec<Vec<Value>>) {
|
||||
macro_rules! null {
|
||||
() => {
|
||||
Value::Nothing {
|
||||
span: NuSpan::unknown(),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! nu_str {
|
||||
($text:expr) => {
|
||||
Value::string($text.to_string(), NuSpan::unknown())
|
||||
};
|
||||
}
|
||||
|
||||
let commands = supported_commands
|
||||
.iter()
|
||||
.map(|manual| {
|
||||
@@ -154,41 +187,13 @@ fn help_frame_data(
|
||||
span: NuSpan::unknown(),
|
||||
};
|
||||
|
||||
let headers = vec!["name", "mode", "information", "description"];
|
||||
|
||||
#[rustfmt::skip]
|
||||
let shortcuts = [
|
||||
(":", "view", commands, "Run an explore command (explore the 'information' cell of this row to list commands)"),
|
||||
("/", "view", null!(), "Search for a pattern"),
|
||||
("?", "view", null!(), "Search for a pattern, but the <n> key now scrolls to the previous result"),
|
||||
("n", "view", null!(), "When searching, scroll to the next search result"),
|
||||
("i", "view", null!(), "Enters cursor mode to inspect individual cells"),
|
||||
("t", "view", null!(), "Transpose table, so that columns become rows and vice versa"),
|
||||
("Up", "", null!(), "Moves the cursor or viewport one row up"),
|
||||
("Down", "", null!(), "Moves the cursor or viewport one row down"),
|
||||
("Left", "", null!(), "Moves the cursor or viewport one column left"),
|
||||
("Right", "", null!(), "Moves the cursor or viewport one column right"),
|
||||
("PgDown", "view", null!(), "Moves the cursor or viewport one page of rows down"),
|
||||
("PgUp", "view", null!(), "Moves the cursor or viewport one page of rows up"),
|
||||
("Esc", "", null!(), "Exits cursor mode. Exits the currently explored data."),
|
||||
("Enter", "cursor", null!(), "In cursor mode, explore the data of the selected cell"),
|
||||
];
|
||||
|
||||
let headers = headers.iter().map(|s| s.to_string()).collect();
|
||||
let data = shortcuts
|
||||
.iter()
|
||||
.map(|(name, mode, info, desc)| {
|
||||
vec![nu_str!(name), nu_str!(mode), info.clone(), nu_str!(desc)]
|
||||
})
|
||||
.collect();
|
||||
|
||||
(headers, data)
|
||||
collect_input(commands)
|
||||
}
|
||||
|
||||
fn help_manual_data(manual: &HelpManual, aliases: &[String]) -> (Vec<String>, Vec<Vec<Value>>) {
|
||||
macro_rules! nu_str {
|
||||
($text:expr) => {
|
||||
Value::string($text, NuSpan::unknown())
|
||||
Value::string($text.to_string(), NuSpan::unknown())
|
||||
};
|
||||
}
|
||||
|
||||
@@ -216,12 +221,69 @@ fn help_manual_data(manual: &HelpManual, aliases: &[String]) -> (Vec<String>, Ve
|
||||
span: NuSpan::unknown(),
|
||||
})
|
||||
.collect();
|
||||
|
||||
let examples = Value::List {
|
||||
vals: examples,
|
||||
span: NuSpan::unknown(),
|
||||
};
|
||||
|
||||
let inputs = manual
|
||||
.input
|
||||
.iter()
|
||||
.map(|e| Value::Record {
|
||||
cols: vec![
|
||||
String::from("name"),
|
||||
String::from("context"),
|
||||
String::from("description"),
|
||||
],
|
||||
vals: vec![nu_str!(e.code), nu_str!(e.context), nu_str!(e.description)],
|
||||
span: NuSpan::unknown(),
|
||||
})
|
||||
.collect();
|
||||
let inputs = Value::List {
|
||||
vals: inputs,
|
||||
span: NuSpan::unknown(),
|
||||
};
|
||||
|
||||
let configuration = manual
|
||||
.config_options
|
||||
.iter()
|
||||
.map(|o| {
|
||||
let values = o
|
||||
.values
|
||||
.iter()
|
||||
.map(|v| Value::Record {
|
||||
cols: vec![String::from("example"), String::from("description")],
|
||||
vals: vec![nu_str!(v.example), nu_str!(v.description)],
|
||||
span: NuSpan::unknown(),
|
||||
})
|
||||
.collect();
|
||||
let values = Value::List {
|
||||
vals: values,
|
||||
span: NuSpan::unknown(),
|
||||
};
|
||||
|
||||
Value::Record {
|
||||
cols: vec![
|
||||
String::from("name"),
|
||||
String::from("context"),
|
||||
String::from("description"),
|
||||
String::from("values"),
|
||||
],
|
||||
vals: vec![
|
||||
nu_str!(o.group),
|
||||
nu_str!(o.key),
|
||||
nu_str!(o.description),
|
||||
values,
|
||||
],
|
||||
span: NuSpan::unknown(),
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
let configuration = Value::List {
|
||||
vals: configuration,
|
||||
span: NuSpan::unknown(),
|
||||
};
|
||||
|
||||
let name = nu_str!(manual.name);
|
||||
let aliases = nu_str!(aliases.join(", "));
|
||||
let desc = nu_str!(manual.description);
|
||||
@@ -230,11 +292,76 @@ fn help_manual_data(manual: &HelpManual, aliases: &[String]) -> (Vec<String>, Ve
|
||||
String::from("name"),
|
||||
String::from("aliases"),
|
||||
String::from("arguments"),
|
||||
String::from("input"),
|
||||
String::from("examples"),
|
||||
String::from("configuration"),
|
||||
String::from("description"),
|
||||
];
|
||||
|
||||
let data = vec![vec![name, aliases, arguments, examples, desc]];
|
||||
let data = vec![vec![
|
||||
name,
|
||||
aliases,
|
||||
arguments,
|
||||
inputs,
|
||||
examples,
|
||||
configuration,
|
||||
desc,
|
||||
]];
|
||||
|
||||
(headers, data)
|
||||
}
|
||||
pub enum HelpView<'a> {
|
||||
Records(RecordView<'a>),
|
||||
Preview(Preview),
|
||||
}
|
||||
|
||||
impl View for HelpView<'_> {
|
||||
fn draw(&mut self, f: &mut Frame, area: Rect, cfg: ViewConfig<'_>, layout: &mut Layout) {
|
||||
match self {
|
||||
HelpView::Records(v) => v.draw(f, area, cfg, layout),
|
||||
HelpView::Preview(v) => v.draw(f, area, cfg, layout),
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_input(
|
||||
&mut self,
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
layout: &Layout,
|
||||
info: &mut ViewInfo,
|
||||
key: KeyEvent,
|
||||
) -> Option<Transition> {
|
||||
match self {
|
||||
HelpView::Records(v) => v.handle_input(engine_state, stack, layout, info, key),
|
||||
HelpView::Preview(v) => v.handle_input(engine_state, stack, layout, info, key),
|
||||
}
|
||||
}
|
||||
|
||||
fn show_data(&mut self, i: usize) -> bool {
|
||||
match self {
|
||||
HelpView::Records(v) => v.show_data(i),
|
||||
HelpView::Preview(v) => v.show_data(i),
|
||||
}
|
||||
}
|
||||
|
||||
fn collect_data(&self) -> Vec<crate::nu_common::NuText> {
|
||||
match self {
|
||||
HelpView::Records(v) => v.collect_data(),
|
||||
HelpView::Preview(v) => v.collect_data(),
|
||||
}
|
||||
}
|
||||
|
||||
fn exit(&mut self) -> Option<Value> {
|
||||
match self {
|
||||
HelpView::Records(v) => v.exit(),
|
||||
HelpView::Preview(v) => v.exit(),
|
||||
}
|
||||
}
|
||||
|
||||
fn setup(&mut self, config: ViewConfig<'_>) {
|
||||
match self {
|
||||
HelpView::Records(v) => v.setup(config),
|
||||
HelpView::Preview(v) => v.setup(config),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -5,19 +5,27 @@ use nu_protocol::{
|
||||
|
||||
use super::pager::{Pager, Transition};
|
||||
|
||||
use std::io::Result;
|
||||
use std::{borrow::Cow, io::Result};
|
||||
|
||||
mod expand;
|
||||
mod help;
|
||||
mod nu;
|
||||
mod preview;
|
||||
mod quit;
|
||||
mod table;
|
||||
mod r#try;
|
||||
mod tweak;
|
||||
|
||||
pub mod config;
|
||||
mod config_show;
|
||||
|
||||
pub use config_show::ConfigShowCmd;
|
||||
pub use expand::ExpandCmd;
|
||||
pub use help::HelpCmd;
|
||||
pub use nu::NuCmd;
|
||||
pub use preview::PreviewCmd;
|
||||
pub use quit::QuitCmd;
|
||||
pub use r#try::TryCmd;
|
||||
pub use table::TableCmd;
|
||||
pub use tweak::TweakCmd;
|
||||
|
||||
pub trait SimpleCommand {
|
||||
fn name(&self) -> &'static str;
|
||||
@@ -48,6 +56,8 @@ pub trait ViewCommand {
|
||||
|
||||
fn parse(&mut self, args: &str) -> Result<()>;
|
||||
|
||||
fn display_config_option(&mut self, group: String, key: String, value: String) -> bool;
|
||||
|
||||
fn spawn(
|
||||
&mut self,
|
||||
engine_state: &EngineState,
|
||||
@@ -62,10 +72,110 @@ pub struct HelpManual {
|
||||
pub description: &'static str,
|
||||
pub arguments: Vec<HelpExample>,
|
||||
pub examples: Vec<HelpExample>,
|
||||
pub config_options: Vec<ConfigOption>,
|
||||
pub input: Vec<Shortcode>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct HelpExample {
|
||||
pub example: &'static str,
|
||||
pub example: Cow<'static, str>,
|
||||
pub description: Cow<'static, str>,
|
||||
}
|
||||
|
||||
impl HelpExample {
|
||||
pub fn new(
|
||||
example: impl Into<Cow<'static, str>>,
|
||||
description: impl Into<Cow<'static, str>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
example: example.into(),
|
||||
description: description.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct Shortcode {
|
||||
pub code: &'static str,
|
||||
pub context: &'static str,
|
||||
pub description: &'static str,
|
||||
}
|
||||
|
||||
impl Shortcode {
|
||||
pub fn new(code: &'static str, context: &'static str, description: &'static str) -> Self {
|
||||
Self {
|
||||
code,
|
||||
context,
|
||||
description,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct ConfigOption {
|
||||
pub group: String,
|
||||
pub description: String,
|
||||
pub key: String,
|
||||
pub values: Vec<HelpExample>,
|
||||
}
|
||||
|
||||
impl ConfigOption {
|
||||
pub fn new<N, D, K>(group: N, description: D, key: K, values: Vec<HelpExample>) -> Self
|
||||
where
|
||||
N: Into<String>,
|
||||
D: Into<String>,
|
||||
K: Into<String>,
|
||||
{
|
||||
Self {
|
||||
group: group.into(),
|
||||
description: description.into(),
|
||||
key: key.into(),
|
||||
values,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn boolean<N, D, K>(group: N, description: D, key: K) -> Self
|
||||
where
|
||||
N: Into<String>,
|
||||
D: Into<String>,
|
||||
K: Into<String>,
|
||||
{
|
||||
Self {
|
||||
group: group.into(),
|
||||
description: description.into(),
|
||||
key: key.into(),
|
||||
values: vec![
|
||||
HelpExample::new("true", "Turn the flag on"),
|
||||
HelpExample::new("false", "Turn the flag on"),
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
pub fn default_color_list() -> Vec<HelpExample> {
|
||||
vec![
|
||||
HelpExample::new("red", "Red foreground"),
|
||||
HelpExample::new("blue", "Blue foreground"),
|
||||
HelpExample::new("green", "Green foreground"),
|
||||
HelpExample::new("yellow", "Yellow foreground"),
|
||||
HelpExample::new("magenta", "Magenta foreground"),
|
||||
HelpExample::new("black", "Black foreground"),
|
||||
HelpExample::new("white", "White foreground"),
|
||||
HelpExample::new("#AA4433", "#AA4433 HEX foreground"),
|
||||
HelpExample::new(r#"{bg: "red"}"#, "Red background"),
|
||||
HelpExample::new(r#"{bg: "blue"}"#, "Blue background"),
|
||||
HelpExample::new(r#"{bg: "green"}"#, "Green background"),
|
||||
HelpExample::new(r#"{bg: "yellow"}"#, "Yellow background"),
|
||||
HelpExample::new(r#"{bg: "magenta"}"#, "Magenta background"),
|
||||
HelpExample::new(r#"{bg: "black"}"#, "Black background"),
|
||||
HelpExample::new(r#"{bg: "white"}"#, "White background"),
|
||||
HelpExample::new(r##"{bg: "#AA4433"}"##, "#AA4433 HEX background"),
|
||||
]
|
||||
}
|
||||
|
||||
pub fn default_int_list() -> Vec<HelpExample> {
|
||||
(0..20)
|
||||
.map(|i| HelpExample::new(i.to_string(), format!("A value equal to {}", i)))
|
||||
.collect()
|
||||
}
|
||||
|
@@ -4,11 +4,12 @@ use nu_protocol::{
|
||||
engine::{EngineState, Stack},
|
||||
PipelineData, Value,
|
||||
};
|
||||
use tui::layout::Rect;
|
||||
|
||||
use crate::{
|
||||
nu_common::{collect_pipeline, has_simple_value, is_ignored_command, run_nu_command},
|
||||
pager::TableConfig,
|
||||
views::{Preview, RecordView, View},
|
||||
nu_common::{collect_pipeline, has_simple_value, run_command_with_value},
|
||||
pager::Frame,
|
||||
views::{Layout, Orientation, Preview, RecordView, View, ViewConfig},
|
||||
};
|
||||
|
||||
use super::{HelpExample, HelpManual, ViewCommand};
|
||||
@@ -16,14 +17,12 @@ use super::{HelpExample, HelpManual, ViewCommand};
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct NuCmd {
|
||||
command: String,
|
||||
table_cfg: TableConfig,
|
||||
}
|
||||
|
||||
impl NuCmd {
|
||||
pub fn new(table_cfg: TableConfig) -> Self {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
command: String::new(),
|
||||
table_cfg,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,28 +41,33 @@ impl ViewCommand for NuCmd {
|
||||
}
|
||||
|
||||
fn help(&self) -> Option<HelpManual> {
|
||||
let examples = vec![
|
||||
HelpExample::new(
|
||||
"where type == 'file'",
|
||||
"Filter data to show only rows whose type is 'file'",
|
||||
),
|
||||
HelpExample::new(
|
||||
"get scope.examples",
|
||||
"Navigate to a deeper value inside the data",
|
||||
),
|
||||
HelpExample::new("open Cargo.toml", "Open a Cargo.toml file"),
|
||||
];
|
||||
|
||||
Some(HelpManual {
|
||||
name: "nu",
|
||||
description:
|
||||
"Run a Nushell command. The data currently being explored is piped into it.",
|
||||
examples,
|
||||
arguments: vec![],
|
||||
examples: vec![
|
||||
HelpExample {
|
||||
example: "where type == 'file'",
|
||||
description: "Filter data to show only rows whose type is 'file'",
|
||||
},
|
||||
HelpExample {
|
||||
example: "get scope.examples",
|
||||
description: "Navigate to a deeper value inside the data",
|
||||
},
|
||||
HelpExample {
|
||||
example: "open Cargo.toml",
|
||||
description: "Open a Cargo.toml file",
|
||||
},
|
||||
],
|
||||
input: vec![],
|
||||
config_options: vec![],
|
||||
})
|
||||
}
|
||||
|
||||
fn display_config_option(&mut self, _: String, _: String, _: String) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn parse(&mut self, args: &str) -> Result<()> {
|
||||
self.command = args.trim().to_owned();
|
||||
|
||||
@@ -76,28 +80,25 @@ impl ViewCommand for NuCmd {
|
||||
stack: &mut Stack,
|
||||
value: Option<Value>,
|
||||
) -> Result<Self::View> {
|
||||
if is_ignored_command(&self.command) {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"The command is ignored",
|
||||
));
|
||||
}
|
||||
|
||||
let value = value.unwrap_or_default();
|
||||
|
||||
let pipeline = PipelineData::Value(value, None);
|
||||
let pipeline = run_nu_command(engine_state, stack, &self.command, pipeline)
|
||||
let pipeline = run_command_with_value(&self.command, &value, engine_state, stack)
|
||||
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
|
||||
|
||||
let is_record = matches!(pipeline, PipelineData::Value(Value::Record { .. }, ..));
|
||||
|
||||
let (columns, values) = collect_pipeline(pipeline);
|
||||
|
||||
if has_simple_value(&values) {
|
||||
let config = &engine_state.config;
|
||||
let text = values[0][0].into_abbreviated_string(config);
|
||||
if let Some(value) = has_simple_value(&values) {
|
||||
let text = value.into_abbreviated_string(&engine_state.config);
|
||||
return Ok(NuView::Preview(Preview::new(&text)));
|
||||
}
|
||||
|
||||
let view = RecordView::new(columns, values, self.table_cfg);
|
||||
let mut view = RecordView::new(columns, values);
|
||||
|
||||
if is_record {
|
||||
view.set_orientation_current(Orientation::Left);
|
||||
}
|
||||
|
||||
Ok(NuView::Records(view))
|
||||
}
|
||||
@@ -109,13 +110,7 @@ pub enum NuView<'a> {
|
||||
}
|
||||
|
||||
impl View for NuView<'_> {
|
||||
fn draw(
|
||||
&mut self,
|
||||
f: &mut crate::pager::Frame,
|
||||
area: tui::layout::Rect,
|
||||
cfg: &crate::ViewConfig,
|
||||
layout: &mut crate::views::Layout,
|
||||
) {
|
||||
fn draw(&mut self, f: &mut Frame, area: Rect, cfg: ViewConfig<'_>, layout: &mut Layout) {
|
||||
match self {
|
||||
NuView::Records(v) => v.draw(f, area, cfg, layout),
|
||||
NuView::Preview(v) => v.draw(f, area, cfg, layout),
|
||||
@@ -126,7 +121,7 @@ impl View for NuView<'_> {
|
||||
&mut self,
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
layout: &crate::views::Layout,
|
||||
layout: &Layout,
|
||||
info: &mut crate::pager::ViewInfo,
|
||||
key: crossterm::event::KeyEvent,
|
||||
) -> Option<crate::pager::Transition> {
|
||||
@@ -156,4 +151,11 @@ impl View for NuView<'_> {
|
||||
NuView::Preview(v) => v.exit(),
|
||||
}
|
||||
}
|
||||
|
||||
fn setup(&mut self, config: ViewConfig<'_>) {
|
||||
match self {
|
||||
NuView::Records(v) => v.setup(config),
|
||||
NuView::Preview(v) => v.setup(config),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,82 +0,0 @@
|
||||
use std::io::Result;
|
||||
|
||||
use nu_color_config::get_color_config;
|
||||
use nu_protocol::{
|
||||
engine::{EngineState, Stack},
|
||||
Value,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
nu_common::{self, collect_input},
|
||||
views::Preview,
|
||||
};
|
||||
|
||||
use super::{HelpManual, ViewCommand};
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
pub struct PreviewCmd;
|
||||
|
||||
impl PreviewCmd {
|
||||
pub fn new() -> Self {
|
||||
Self
|
||||
}
|
||||
}
|
||||
|
||||
impl PreviewCmd {
|
||||
pub const NAME: &'static str = "preview";
|
||||
}
|
||||
|
||||
impl ViewCommand for PreviewCmd {
|
||||
type View = Preview;
|
||||
|
||||
fn name(&self) -> &'static str {
|
||||
Self::NAME
|
||||
}
|
||||
|
||||
fn usage(&self) -> &'static str {
|
||||
""
|
||||
}
|
||||
|
||||
fn help(&self) -> Option<HelpManual> {
|
||||
Some(HelpManual {
|
||||
name: "preview",
|
||||
description:
|
||||
"View the currently selected cell's data using the `table` Nushell command",
|
||||
arguments: vec![],
|
||||
examples: vec![],
|
||||
})
|
||||
}
|
||||
|
||||
fn parse(&mut self, _: &str) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn spawn(
|
||||
&mut self,
|
||||
engine_state: &EngineState,
|
||||
_stack: &mut Stack,
|
||||
value: Option<Value>,
|
||||
) -> Result<Self::View> {
|
||||
let value = match value {
|
||||
Some(value) => {
|
||||
let (cols, vals) = collect_input(value.clone());
|
||||
|
||||
let has_no_head = cols.is_empty() || (cols.len() == 1 && cols[0].is_empty());
|
||||
let has_single_value = vals.len() == 1 && vals[0].len() == 1;
|
||||
if !has_no_head && has_single_value {
|
||||
let config = engine_state.get_config();
|
||||
vals[0][0].into_abbreviated_string(config)
|
||||
} else {
|
||||
let ctrlc = engine_state.ctrlc.clone();
|
||||
let config = engine_state.get_config();
|
||||
let color_hm = get_color_config(config);
|
||||
|
||||
nu_common::try_build_table(ctrlc, config, &color_hm, value)
|
||||
}
|
||||
}
|
||||
None => String::new(),
|
||||
};
|
||||
|
||||
Ok(Preview::new(&value))
|
||||
}
|
||||
}
|
@@ -31,6 +31,8 @@ impl SimpleCommand for QuitCmd {
|
||||
description: "Quit and return to Nushell",
|
||||
arguments: vec![],
|
||||
examples: vec![],
|
||||
input: vec![],
|
||||
config_options: vec![],
|
||||
})
|
||||
}
|
||||
|
||||
|
281
crates/nu-explore/src/commands/table.rs
Normal file
281
crates/nu-explore/src/commands/table.rs
Normal file
@@ -0,0 +1,281 @@
|
||||
use std::io::Result;
|
||||
|
||||
use nu_ansi_term::Style;
|
||||
use nu_color_config::lookup_ansi_color_style;
|
||||
use nu_protocol::{
|
||||
engine::{EngineState, Stack},
|
||||
Value,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
nu_common::collect_input,
|
||||
views::{Orientation, RecordView},
|
||||
};
|
||||
|
||||
use super::{
|
||||
default_color_list, default_int_list, ConfigOption, HelpExample, HelpManual, Shortcode,
|
||||
ViewCommand,
|
||||
};
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct TableCmd {
|
||||
// todo: add arguments to override config right from CMD
|
||||
settings: TableSettings,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
struct TableSettings {
|
||||
orientation: Option<Orientation>,
|
||||
line_head_top: Option<bool>,
|
||||
line_head_bottom: Option<bool>,
|
||||
line_shift: Option<bool>,
|
||||
line_index: Option<bool>,
|
||||
split_line_s: Option<Style>,
|
||||
selected_cell_s: Option<Style>,
|
||||
selected_row_s: Option<Style>,
|
||||
selected_column_s: Option<Style>,
|
||||
show_cursor: Option<bool>,
|
||||
padding_column_left: Option<usize>,
|
||||
padding_column_right: Option<usize>,
|
||||
padding_index_left: Option<usize>,
|
||||
padding_index_right: Option<usize>,
|
||||
turn_on_cursor_mode: bool,
|
||||
}
|
||||
|
||||
impl TableCmd {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub const NAME: &'static str = "table";
|
||||
}
|
||||
|
||||
impl ViewCommand for TableCmd {
|
||||
type View = RecordView<'static>;
|
||||
|
||||
fn name(&self) -> &'static str {
|
||||
Self::NAME
|
||||
}
|
||||
|
||||
fn usage(&self) -> &'static str {
|
||||
""
|
||||
}
|
||||
|
||||
fn help(&self) -> Option<HelpManual> {
|
||||
#[rustfmt::skip]
|
||||
let shortcuts = vec![
|
||||
Shortcode::new("Up", "", "Moves the cursor or viewport one row up"),
|
||||
Shortcode::new("Down", "", "Moves the cursor or viewport one row down"),
|
||||
Shortcode::new("Left", "", "Moves the cursor or viewport one column left"),
|
||||
Shortcode::new("Right", "", "Moves the cursor or viewport one column right"),
|
||||
Shortcode::new("PgDown", "view", "Moves the cursor or viewport one page of rows down"),
|
||||
Shortcode::new("PgUp", "view", "Moves the cursor or viewport one page of rows up"),
|
||||
Shortcode::new("Esc", "", "Exits cursor mode. Exits the just explored dataset."),
|
||||
Shortcode::new("i", "view", "Enters cursor mode to inspect individual cells"),
|
||||
Shortcode::new("t", "view", "Transpose table, so that columns become rows and vice versa"),
|
||||
Shortcode::new("e", "view", "Open expand view (equvalent of :expand)"),
|
||||
Shortcode::new("Enter", "cursor", "In cursor mode, explore the data of the selected cell"),
|
||||
];
|
||||
|
||||
#[rustfmt::skip]
|
||||
let config_options = vec![
|
||||
ConfigOption::new(
|
||||
":table group",
|
||||
"Used to move column header",
|
||||
"table.orientation",
|
||||
vec![
|
||||
HelpExample::new("top", "Sticks column header to the top"),
|
||||
HelpExample::new("bottom", "Sticks column header to the bottom"),
|
||||
HelpExample::new("left", "Sticks column header to the left"),
|
||||
HelpExample::new("right", "Sticks column header to the right"),
|
||||
],
|
||||
),
|
||||
ConfigOption::boolean(":table group", "Show index", "table.show_index"),
|
||||
ConfigOption::boolean(":table group", "Show header", "table.show_head"),
|
||||
|
||||
ConfigOption::boolean(":table group", "Lines are lines", "table.line_head_top"),
|
||||
ConfigOption::boolean(":table group", "Lines are lines", "table.line_head_bottom"),
|
||||
ConfigOption::boolean(":table group", "Lines are lines", "table.line_shift"),
|
||||
ConfigOption::boolean(":table group", "Lines are lines", "table.line_index"),
|
||||
|
||||
ConfigOption::boolean(":table group", "Show cursor", "table.show_cursor"),
|
||||
|
||||
ConfigOption::new(":table group", "Color of selected cell", "table.selected_cell", default_color_list()),
|
||||
ConfigOption::new(":table group", "Color of selected row", "table.selected_row", default_color_list()),
|
||||
ConfigOption::new(":table group", "Color of selected column", "table.selected_column", default_color_list()),
|
||||
|
||||
ConfigOption::new(":table group", "Color of split line", "table.split_line", default_color_list()),
|
||||
|
||||
ConfigOption::new(":table group", "Padding column left", "table.padding_column_left", default_int_list()),
|
||||
ConfigOption::new(":table group", "Padding column right", "table.padding_column_right", default_int_list()),
|
||||
ConfigOption::new(":table group", "Padding index left", "table.padding_index_left", default_int_list()),
|
||||
ConfigOption::new(":table group", "Padding index right", "table.padding_index_right", default_int_list()),
|
||||
];
|
||||
|
||||
Some(HelpManual {
|
||||
name: "table",
|
||||
description: "Display a table view",
|
||||
arguments: vec![],
|
||||
examples: vec![],
|
||||
config_options,
|
||||
input: shortcuts,
|
||||
})
|
||||
}
|
||||
|
||||
fn display_config_option(&mut self, _group: String, key: String, value: String) -> bool {
|
||||
match key.as_str() {
|
||||
"table.orientation" => self.settings.orientation = orientation_from_str(&value),
|
||||
"table.line_head_top" => self.settings.line_head_top = bool_from_str(&value),
|
||||
"table.line_head_bottom" => self.settings.line_head_bottom = bool_from_str(&value),
|
||||
"table.line_shift" => self.settings.line_shift = bool_from_str(&value),
|
||||
"table.line_index" => self.settings.line_index = bool_from_str(&value),
|
||||
"table.show_cursor" => {
|
||||
self.settings.show_cursor = bool_from_str(&value);
|
||||
self.settings.turn_on_cursor_mode = true;
|
||||
}
|
||||
"table.split_line" => {
|
||||
self.settings.split_line_s = Some(lookup_ansi_color_style(&value));
|
||||
self.settings.turn_on_cursor_mode = true;
|
||||
}
|
||||
"table.selected_cell" => {
|
||||
self.settings.selected_cell_s = Some(lookup_ansi_color_style(&value));
|
||||
self.settings.turn_on_cursor_mode = true;
|
||||
}
|
||||
"table.selected_row" => {
|
||||
self.settings.selected_row_s = Some(lookup_ansi_color_style(&value));
|
||||
self.settings.turn_on_cursor_mode = true;
|
||||
}
|
||||
"table.selected_column" => {
|
||||
self.settings.selected_column_s = Some(lookup_ansi_color_style(&value));
|
||||
self.settings.turn_on_cursor_mode = true;
|
||||
}
|
||||
"table.padding_column_left" => {
|
||||
self.settings.padding_column_left = usize_from_str(&value);
|
||||
}
|
||||
"table.padding_column_right" => {
|
||||
self.settings.padding_column_right = usize_from_str(&value);
|
||||
}
|
||||
"table.padding_index_left" => {
|
||||
self.settings.padding_index_left = usize_from_str(&value);
|
||||
}
|
||||
"table.padding_index_right" => {
|
||||
self.settings.padding_index_right = usize_from_str(&value);
|
||||
}
|
||||
_ => return false,
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
fn parse(&mut self, _: &str) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn spawn(
|
||||
&mut self,
|
||||
_: &EngineState,
|
||||
_: &mut Stack,
|
||||
value: Option<Value>,
|
||||
) -> Result<Self::View> {
|
||||
let value = value.unwrap_or_default();
|
||||
let is_record = matches!(value, Value::Record { .. });
|
||||
|
||||
let (columns, data) = collect_input(value);
|
||||
|
||||
let mut view = RecordView::new(columns, data);
|
||||
|
||||
// todo: use setup instead ????
|
||||
|
||||
if is_record {
|
||||
view.set_orientation_current(Orientation::Left);
|
||||
}
|
||||
|
||||
if let Some(o) = self.settings.orientation {
|
||||
view.set_orientation_current(o);
|
||||
}
|
||||
|
||||
if self.settings.line_head_bottom.unwrap_or(false) {
|
||||
view.set_line_head_bottom(true);
|
||||
}
|
||||
|
||||
if self.settings.line_head_top.unwrap_or(false) {
|
||||
view.set_line_head_top(true);
|
||||
}
|
||||
|
||||
if self.settings.line_index.unwrap_or(false) {
|
||||
view.set_line_index(true);
|
||||
}
|
||||
|
||||
if self.settings.line_shift.unwrap_or(false) {
|
||||
view.set_line_traling(true);
|
||||
}
|
||||
|
||||
if self.settings.show_cursor.unwrap_or(false) {
|
||||
view.show_cursor(true);
|
||||
}
|
||||
|
||||
if let Some(style) = self.settings.selected_cell_s {
|
||||
view.set_style_selected_cell(style);
|
||||
}
|
||||
|
||||
if let Some(style) = self.settings.selected_column_s {
|
||||
view.set_style_selected_column(style);
|
||||
}
|
||||
|
||||
if let Some(style) = self.settings.selected_row_s {
|
||||
view.set_style_selected_row(style);
|
||||
}
|
||||
|
||||
if let Some(style) = self.settings.split_line_s {
|
||||
view.set_style_split_line(style);
|
||||
}
|
||||
|
||||
if let Some(p) = self.settings.padding_column_left {
|
||||
let c = view.get_padding_column();
|
||||
view.set_padding_column((p, c.1))
|
||||
}
|
||||
|
||||
if let Some(p) = self.settings.padding_column_right {
|
||||
let c = view.get_padding_column();
|
||||
view.set_padding_column((c.0, p))
|
||||
}
|
||||
|
||||
if let Some(p) = self.settings.padding_index_left {
|
||||
let c = view.get_padding_index();
|
||||
view.set_padding_index((p, c.1))
|
||||
}
|
||||
|
||||
if let Some(p) = self.settings.padding_index_right {
|
||||
let c = view.get_padding_index();
|
||||
view.set_padding_index((c.0, p))
|
||||
}
|
||||
|
||||
if self.settings.turn_on_cursor_mode {
|
||||
view.set_cursor_mode();
|
||||
}
|
||||
|
||||
Ok(view)
|
||||
}
|
||||
}
|
||||
|
||||
fn bool_from_str(s: &str) -> Option<bool> {
|
||||
match s {
|
||||
"true" => Some(true),
|
||||
"false" => Some(false),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn usize_from_str(s: &str) -> Option<usize> {
|
||||
s.parse::<usize>().ok()
|
||||
}
|
||||
|
||||
fn orientation_from_str(s: &str) -> Option<Orientation> {
|
||||
match s {
|
||||
"left" => Some(Orientation::Left),
|
||||
"right" => Some(Orientation::Right),
|
||||
"top" => Some(Orientation::Top),
|
||||
"bottom" => Some(Orientation::Bottom),
|
||||
_ => None,
|
||||
}
|
||||
}
|
@@ -1,25 +1,23 @@
|
||||
use std::io::Result;
|
||||
use std::io::{Error, ErrorKind, Result};
|
||||
|
||||
use nu_protocol::{
|
||||
engine::{EngineState, Stack},
|
||||
Value,
|
||||
};
|
||||
|
||||
use crate::{pager::TableConfig, views::InteractiveView};
|
||||
use crate::views::InteractiveView;
|
||||
|
||||
use super::{HelpExample, HelpManual, ViewCommand};
|
||||
use super::{default_color_list, ConfigOption, HelpExample, HelpManual, Shortcode, ViewCommand};
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct TryCmd {
|
||||
command: String,
|
||||
table_cfg: TableConfig,
|
||||
}
|
||||
|
||||
impl TryCmd {
|
||||
pub fn new(table_cfg: TableConfig) -> Self {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
command: String::new(),
|
||||
table_cfg,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,17 +36,41 @@ impl ViewCommand for TryCmd {
|
||||
}
|
||||
|
||||
fn help(&self) -> Option<HelpManual> {
|
||||
#[rustfmt::skip]
|
||||
let shortcuts = vec![
|
||||
Shortcode::new("Up", "", "Switches between input and a output panes"),
|
||||
Shortcode::new("Down", "", "Switches between input and a output panes"),
|
||||
Shortcode::new("Esc", "", "Switches between input and a output panes"),
|
||||
Shortcode::new("Tab", "", "Switches between input and a output panes"),
|
||||
];
|
||||
|
||||
#[rustfmt::skip]
|
||||
let config_options = vec![
|
||||
ConfigOption::boolean(":try options", "Try makes running command on each input character", "try.reactive"),
|
||||
ConfigOption::new(":try options", "Change a border color of the menus", "try.border_color", default_color_list()),
|
||||
ConfigOption::new(":try options", "Change a highlighed menu color", "try.highlighted_color", default_color_list()),
|
||||
];
|
||||
|
||||
#[rustfmt::skip]
|
||||
let examples = vec![
|
||||
HelpExample::new("try", "Open a interactive :try command"),
|
||||
HelpExample::new("try open Cargo.toml", "Optionally, you can provide a command which will be run immediately"),
|
||||
];
|
||||
|
||||
Some(HelpManual {
|
||||
name: "try",
|
||||
description: "Opens a panel in which to run Nushell commands and explore their output",
|
||||
description: "Opens a panel in which to run Nushell commands and explore their output. The exporer acts liek `:table`.",
|
||||
arguments: vec![],
|
||||
examples: vec![HelpExample {
|
||||
example: "try open Cargo.toml",
|
||||
description: "Optionally, you can provide a command which will be run immediately",
|
||||
}],
|
||||
examples,
|
||||
input: shortcuts,
|
||||
config_options,
|
||||
})
|
||||
}
|
||||
|
||||
fn display_config_option(&mut self, _: String, _: String, _: String) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn parse(&mut self, args: &str) -> Result<()> {
|
||||
self.command = args.trim().to_owned();
|
||||
|
||||
@@ -57,13 +79,15 @@ impl ViewCommand for TryCmd {
|
||||
|
||||
fn spawn(
|
||||
&mut self,
|
||||
_: &EngineState,
|
||||
_: &mut Stack,
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
value: Option<Value>,
|
||||
) -> Result<Self::View> {
|
||||
let value = value.unwrap_or_default();
|
||||
let mut view = InteractiveView::new(value, self.table_cfg);
|
||||
let mut view = InteractiveView::new(value);
|
||||
view.init(self.command.clone());
|
||||
view.try_run(engine_state, stack)
|
||||
.map_err(|e| Error::new(ErrorKind::Other, e))?;
|
||||
|
||||
Ok(view)
|
||||
}
|
||||
|
96
crates/nu-explore/src/commands/tweak.rs
Normal file
96
crates/nu-explore/src/commands/tweak.rs
Normal file
@@ -0,0 +1,96 @@
|
||||
use std::io::{self, Result};
|
||||
|
||||
use nu_protocol::{
|
||||
engine::{EngineState, Stack},
|
||||
Value,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
nu_common::NuSpan,
|
||||
pager::{Pager, Transition},
|
||||
};
|
||||
|
||||
use super::{HelpExample, HelpManual, SimpleCommand};
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
pub struct TweakCmd {
|
||||
path: Vec<String>,
|
||||
value: Value,
|
||||
}
|
||||
|
||||
impl TweakCmd {
|
||||
pub const NAME: &'static str = "tweak";
|
||||
}
|
||||
|
||||
impl SimpleCommand for TweakCmd {
|
||||
fn name(&self) -> &'static str {
|
||||
Self::NAME
|
||||
}
|
||||
|
||||
fn usage(&self) -> &'static str {
|
||||
""
|
||||
}
|
||||
|
||||
fn help(&self) -> Option<HelpManual> {
|
||||
Some(HelpManual {
|
||||
name: "tweak",
|
||||
description:
|
||||
"Set different settings.\nIt could be consired a not interactive version of :config",
|
||||
arguments: vec![],
|
||||
examples: vec![
|
||||
HelpExample::new(":tweak table.show_index false", "Don't show index anymore"),
|
||||
HelpExample::new(":tweak table.show_head false", "Don't show header anymore"),
|
||||
HelpExample::new(
|
||||
":tweak try.border_color {bg: '#FFFFFF', fg: '#F213F1'}",
|
||||
"Make a different color for borders in :try",
|
||||
),
|
||||
],
|
||||
config_options: vec![],
|
||||
input: vec![],
|
||||
})
|
||||
}
|
||||
|
||||
fn parse(&mut self, input: &str) -> Result<()> {
|
||||
let input = input.trim();
|
||||
|
||||
let args = input.split_once(' ');
|
||||
let (key, value) = match args {
|
||||
Some(args) => args,
|
||||
None => {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"expected to get 2 arguments 'key value'",
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
self.value = parse_value(value);
|
||||
|
||||
self.path = key
|
||||
.split_terminator('.')
|
||||
.map(|s| s.to_string())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn react(
|
||||
&mut self,
|
||||
_: &EngineState,
|
||||
_: &mut Stack,
|
||||
p: &mut Pager<'_>,
|
||||
_: Option<Value>,
|
||||
) -> Result<Transition> {
|
||||
p.set_config(&self.path, self.value.clone());
|
||||
|
||||
Ok(Transition::Ok)
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_value(value: &str) -> Value {
|
||||
match value {
|
||||
"true" => Value::boolean(true, NuSpan::unknown()),
|
||||
"false" => Value::boolean(false, NuSpan::unknown()),
|
||||
s => Value::string(s.to_owned(), NuSpan::unknown()),
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user