explore: remove :config, :show-config, :tweak commands (#10259)

More trimming of underused `explore` functionality.

The `explore` command has subcommands that can be run like `:config` or
`:try` or whatnot. This PR removes the `:config`, `:show-config`, and
`:tweak` commands which are all for viewing+modifying config.

These are interesting commands and they were cool experiments, but
ultimately I don't think they fit with our plans for a simplified
`explore`. They'd need a lot more polish if we want to keep them and I
don't think we do. Happy to discuss if I've missed a good reason to keep
these.

cc @fdncred
This commit is contained in:
Reilly Wood 2023-09-07 08:34:08 -07:00 committed by GitHub
parent c7c6445b03
commit b6189879e3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 2 additions and 1010 deletions

View File

@ -1,172 +0,0 @@
use std::io::Result;
use nu_protocol::{
engine::{EngineState, Stack},
record, 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\nLike 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(
record! {
"key" => nu_str(format!("key-{i}")),
"value" => nu_str(format!("{i}")),
},
span,
)
};
Value::list(vec![record(0), record(1), record(2)], span)
}

View File

@ -1,180 +0,0 @@
use nu_protocol::{
engine::{EngineState, Stack},
record, Value,
};
use ratatui::layout::Rect;
use std::collections::HashMap;
use std::io::Result;
use crate::{
nu_common::{try_build_table, NuSpan},
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:
"Show the current `explore` 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 mut m = config.config.clone();
convert_styles(&mut m);
let value = map_into_value(m);
try_build_table(None, config.nu_config, config.style_computer, value)
}
ConfigFormat::Nu => nu_json::to_string(&config.config).unwrap_or_default(),
}
}
}
fn convert_styles(m: &mut HashMap<String, Value>) {
for value in m.values_mut() {
convert_styles_value(value);
}
}
fn convert_styles_value(value: &mut Value) {
match value {
Value::String { val, .. } => {
if let Some(v) = convert_style_from_string(val) {
*value = v;
}
}
Value::List { vals, .. } => {
for value in vals {
convert_styles_value(value);
}
}
Value::Record { val, .. } => {
for value in &mut val.vals {
convert_styles_value(value);
}
}
_ => (),
}
}
fn convert_style_from_string(s: &str) -> Option<Value> {
let style = nu_json::from_str::<nu_color_config::NuStyle>(s).ok()?;
Some(Value::record(
record! {
"bg" => Value::string(style.bg.unwrap_or_default(), NuSpan::unknown()),
"fg" => Value::string(style.fg.unwrap_or_default(), NuSpan::unknown()),
"attr" => Value::string(style.attr.unwrap_or_default(), NuSpan::unknown()),
},
NuSpan::unknown(),
))
}

View File

@ -60,10 +60,6 @@ impl ViewCommand for ExpandCmd {
}) })
} }
fn display_config_option(&mut self, _: String, _: String, _: String) -> bool {
false
}
fn parse(&mut self, _: &str) -> Result<()> { fn parse(&mut self, _: &str) -> Result<()> {
Ok(()) Ok(())
} }

View File

@ -104,10 +104,6 @@ impl ViewCommand for HelpCmd {
}) })
} }
fn display_config_option(&mut self, _: String, _: String, _: String) -> bool {
false
}
fn parse(&mut self, args: &str) -> Result<()> { fn parse(&mut self, args: &str) -> Result<()> {
self.input_command = args.trim().to_owned(); self.input_command = args.trim().to_owned();

View File

@ -13,19 +13,13 @@ mod nu;
mod quit; mod quit;
mod table; mod table;
mod r#try; mod r#try;
mod tweak;
pub mod config;
mod config_show;
pub use config_show::ConfigShowCmd;
pub use expand::ExpandCmd; pub use expand::ExpandCmd;
pub use help::HelpCmd; pub use help::HelpCmd;
pub use nu::NuCmd; pub use nu::NuCmd;
pub use quit::QuitCmd; pub use quit::QuitCmd;
pub use r#try::TryCmd; pub use r#try::TryCmd;
pub use table::TableCmd; pub use table::TableCmd;
pub use tweak::TweakCmd;
pub trait SimpleCommand { pub trait SimpleCommand {
fn name(&self) -> &'static str; fn name(&self) -> &'static str;
@ -56,8 +50,6 @@ pub trait ViewCommand {
fn parse(&mut self, args: &str) -> Result<()>; fn parse(&mut self, args: &str) -> Result<()>;
fn display_config_option(&mut self, group: String, key: String, value: String) -> bool;
fn spawn( fn spawn(
&mut self, &mut self,
engine_state: &EngineState, engine_state: &EngineState,

View File

@ -64,10 +64,6 @@ impl ViewCommand for NuCmd {
}) })
} }
fn display_config_option(&mut self, _: String, _: String, _: String) -> bool {
false
}
fn parse(&mut self, args: &str) -> Result<()> { fn parse(&mut self, args: &str) -> Result<()> {
self.command = args.trim().to_owned(); self.command = args.trim().to_owned();

View File

@ -1,7 +1,6 @@
use std::io::Result; use std::io::Result;
use nu_ansi_term::Style; use nu_ansi_term::Style;
use nu_color_config::lookup_ansi_color_style;
use nu_protocol::{ use nu_protocol::{
engine::{EngineState, Stack}, engine::{EngineState, Stack},
Value, Value,
@ -122,51 +121,6 @@ impl ViewCommand for TableCmd {
}) })
} }
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<()> { fn parse(&mut self, _: &str) -> Result<()> {
Ok(()) Ok(())
} }
@ -257,25 +211,3 @@ impl ViewCommand for TableCmd {
Ok(view) 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,
}
}

View File

@ -67,10 +67,6 @@ impl ViewCommand for TryCmd {
}) })
} }
fn display_config_option(&mut self, _: String, _: String, _: String) -> bool {
false
}
fn parse(&mut self, args: &str) -> Result<()> { fn parse(&mut self, args: &str) -> Result<()> {
self.command = args.trim().to_owned(); self.command = args.trim().to_owned();

View File

@ -1,92 +0,0 @@
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 `explore` settings.\nLike a non-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) = args.ok_or_else(|| {
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::bool(true, NuSpan::unknown()),
"false" => Value::bool(false, NuSpan::unknown()),
s => Value::string(s.to_owned(), NuSpan::unknown()),
}
}

View File

@ -11,10 +11,7 @@ pub use explore::Explore;
use std::io; use std::io;
use commands::{ use commands::{ExpandCmd, HelpCmd, HelpManual, NuCmd, QuitCmd, TableCmd, TryCmd};
config::ConfigCmd, default_color_list, ConfigOption, ConfigShowCmd, ExpandCmd, HelpCmd,
HelpManual, NuCmd, QuitCmd, TableCmd, TryCmd, TweakCmd,
};
use nu_common::{collect_pipeline, has_simple_value, CtrlC}; use nu_common::{collect_pipeline, has_simple_value, CtrlC};
use nu_protocol::{ use nu_protocol::{
engine::{EngineState, Stack}, engine::{EngineState, Stack},
@ -96,10 +93,8 @@ fn create_command_registry() -> CommandRegistry {
let aliases = registry.get_aliases().collect::<Vec<_>>(); let aliases = registry.get_aliases().collect::<Vec<_>>();
let help_cmd = create_help_command(&commands, &aliases); let help_cmd = create_help_command(&commands, &aliases);
let config_cmd = create_config_command(&commands);
registry.register_command_view(help_cmd, true); registry.register_command_view(help_cmd, true);
registry.register_command_view(config_cmd, true);
registry registry
} }
@ -110,12 +105,9 @@ fn create_commands(registry: &mut CommandRegistry) {
registry.register_command_view(ExpandCmd::new(), true); registry.register_command_view(ExpandCmd::new(), true);
registry.register_command_view(TryCmd::new(), true); registry.register_command_view(TryCmd::new(), true);
registry.register_command_view(ConfigShowCmd::new(), true);
registry.register_command_view(ConfigCmd::default(), true);
registry.register_command_view(HelpCmd::default(), true); registry.register_command_view(HelpCmd::default(), true);
registry.register_command_reactive(QuitCmd); registry.register_command_reactive(QuitCmd);
registry.register_command_reactive(TweakCmd::default());
} }
fn create_aliases(registry: &mut CommandRegistry) { fn create_aliases(registry: &mut CommandRegistry) {
@ -125,28 +117,6 @@ fn create_aliases(registry: &mut CommandRegistry) {
registry.create_aliases("q!", QuitCmd::NAME); registry.create_aliases("q!", QuitCmd::NAME);
} }
#[rustfmt::skip]
fn create_config_command(commands: &[Command]) -> ConfigCmd {
const GROUP: &str = "Explore configuration";
let mut config = ConfigCmd::from_commands(commands.to_vec());
config.register_group(ConfigOption::new(GROUP, "Status bar information color", "status.info", default_color_list()));
config.register_group(ConfigOption::new(GROUP, "Status bar success color", "status.success", default_color_list()));
config.register_group(ConfigOption::new(GROUP, "Status bar warning color", "status.warn", default_color_list()));
config.register_group(ConfigOption::new(GROUP, "Status bar error color", "status.error", default_color_list()));
config.register_group(ConfigOption::new(GROUP, "Status bar default text color", "status_bar_text", default_color_list()));
config.register_group(ConfigOption::new(GROUP, "Status bar background", "status_bar_background", default_color_list()));
config.register_group(ConfigOption::new(GROUP, "Command bar text color", "command_bar_text", default_color_list()));
config.register_group(ConfigOption::new(GROUP, "Command bar background", "command_bar_background", default_color_list()));
config.register_group(ConfigOption::new(GROUP, "Highlight color in search", "highlight", default_color_list()));
config
}
fn create_help_command(commands: &[Command], aliases: &[(&str, &str)]) -> HelpCmd { fn create_help_command(commands: &[Command], aliases: &[(&str, &str)]) -> HelpCmd {
let help_manuals = create_help_manuals(commands); let help_manuals = create_help_manuals(commands);

View File

@ -19,7 +19,7 @@ pub use command::{is_ignored_command, run_command_with_value, run_nu_command};
pub use lscolor::{create_lscolors, lscolorize}; pub use lscolor::{create_lscolors, lscolorize};
pub use string::{string_width, truncate_str}; pub use string::{string_width, truncate_str};
pub use table::try_build_table; pub use table::try_build_table;
pub use value::{collect_input, collect_pipeline, create_map, map_into_value, nu_str}; pub use value::{collect_input, collect_pipeline, create_map, map_into_value};
pub fn has_simple_value(data: &[Vec<Value>]) -> Option<&Value> { pub fn has_simple_value(data: &[Vec<Value>]) -> Option<&Value> {
if data.len() == 1 if data.len() == 1

View File

@ -194,10 +194,6 @@ pub fn map_into_value(hm: HashMap<String, Value>) -> Value {
Value::record(hm.into_iter().collect(), NuSpan::unknown()) Value::record(hm.into_iter().collect(), NuSpan::unknown())
} }
pub fn nu_str<S: AsRef<str>>(s: S) -> Value {
Value::string(s.as_ref().to_owned(), NuSpan::unknown())
}
fn unknown_error_value() -> Value { fn unknown_error_value() -> Value {
Value::string(String::from(""), NuSpan::unknown()) Value::string(String::from(""), NuSpan::unknown())
} }

View File

@ -72,10 +72,6 @@ where
self.0.help() self.0.help()
} }
fn display_config_option(&mut self, group: String, key: String, value: String) -> bool {
self.0.display_config_option(group, key, value)
}
fn parse(&mut self, args: &str) -> std::io::Result<()> { fn parse(&mut self, args: &str) -> std::io::Result<()> {
self.0.parse(args) self.0.parse(args)
} }

View File

@ -1,427 +0,0 @@
use std::{cmp::Ordering, fmt::Debug, ptr::addr_of};
use crossterm::event::{KeyCode, KeyEvent};
use nu_color_config::get_color_map;
use nu_protocol::{
engine::{EngineState, Stack},
Value,
};
use nu_table::TextStyle;
use ratatui::{
layout::Rect,
style::Style,
widgets::{BorderType, Borders, Paragraph},
};
use crate::{
nu_common::{truncate_str, NuText},
pager::{Frame, Transition, ViewInfo},
util::create_map,
views::util::nu_style_to_tui,
};
use super::{cursor::WindowCursor, Layout, View, ViewConfig};
#[derive(Debug, Default)]
pub struct ConfigurationView {
options: Vec<ConfigGroup>,
peeked_cursor: Option<WindowCursor>,
cursor: WindowCursor,
border_color: Style,
cursor_color: Style,
list_color: Style,
}
impl ConfigurationView {
pub fn new(options: Vec<ConfigGroup>) -> Self {
let cursor = WindowCursor::new(options.len(), options.len()).expect("...");
Self {
options,
cursor,
peeked_cursor: None,
border_color: Style::default(),
cursor_color: Style::default(),
list_color: Style::default(),
}
}
fn update_cursors(&mut self, height: usize) {
self.cursor.set_window(height);
if let Some(cursor) = &mut self.peeked_cursor {
cursor.set_window(height);
}
}
fn render_option_list(
&mut self,
f: &mut Frame,
area: Rect,
list_color: Style,
cursor_color: Style,
layout: &mut Layout,
) {
let (data, data_c) = match self.peeked_cursor {
Some(cursor) => {
let i = self.cursor.index();
let opt = &self.options[i];
let data = opt
.options
.iter()
.map(|e| e.name.clone())
.collect::<Vec<_>>();
(data, cursor)
}
None => {
let data = self
.options
.iter()
.map(|o| o.group.clone())
.collect::<Vec<_>>();
(data, self.cursor)
}
};
render_list(f, area, &data, data_c, list_color, cursor_color, layout);
}
fn peek_current(&self) -> Option<(&ConfigGroup, &ConfigOption)> {
let cursor = match self.peeked_cursor {
Some(cursor) => cursor,
None => return None,
};
let i = self.cursor.index();
let j = cursor.index();
let group = &self.options[i];
let opt = &group.options[j];
Some((group, opt))
}
fn peek_current_group(&self) -> &ConfigGroup {
let i = self.cursor.index();
&self.options[i]
}
fn peek_current_opt(&mut self) -> Option<&mut ConfigOption> {
let cursor = match self.peeked_cursor {
Some(cursor) => cursor,
None => return None,
};
let i = self.cursor.index();
let j = cursor.index();
Some(&mut self.options[i].options[j])
}
fn get_cursor_mut(&mut self) -> &mut WindowCursor {
self.peeked_cursor.as_mut().unwrap_or(&mut self.cursor)
}
}
#[derive(Debug, Default)]
pub struct ConfigGroup {
group: String,
description: String,
options: Vec<ConfigOption>,
}
impl ConfigGroup {
pub fn new(group: String, options: Vec<ConfigOption>, description: String) -> Self {
Self {
group,
options,
description,
}
}
pub fn group(&self) -> &str {
self.group.as_ref()
}
}
pub struct ConfigOption {
name: String,
view: Box<dyn View>,
}
impl ConfigOption {
pub fn new(name: String, view: Box<dyn View>) -> Self {
Self { name, view }
}
}
impl Debug for ConfigOption {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ConfigOption")
.field("name", &self.name)
.field("view", &addr_of!(self.view))
.finish()
}
}
impl View for ConfigurationView {
fn draw(&mut self, f: &mut Frame, area: Rect, cfg: ViewConfig<'_>, layout: &mut Layout) {
const LEFT_PADDING: u16 = 1;
const BLOCK_PADDING: u16 = 1;
const OPTION_BLOCK_WIDTH: u16 = 30;
const USED_HEIGHT_BY_BORDERS: u16 = 2;
if area.width < 40 {
return;
}
let list_color = self.list_color;
let border_color = self.border_color;
let cursor_color = self.cursor_color;
let height = area.height - USED_HEIGHT_BY_BORDERS;
let option_b_x1 = area.x + LEFT_PADDING;
let option_b_x2 = area.x + LEFT_PADDING + OPTION_BLOCK_WIDTH;
let view_b_x1 = option_b_x2 + BLOCK_PADDING;
let view_b_w = area.width - (LEFT_PADDING + BLOCK_PADDING + OPTION_BLOCK_WIDTH);
let option_content_x1 = option_b_x1 + 1;
let option_content_w = OPTION_BLOCK_WIDTH - 2;
let option_content_h = height;
let option_content_area =
Rect::new(option_content_x1, 1, option_content_w, option_content_h);
let view_content_x1 = view_b_x1 + 1;
let view_content_w = view_b_w - 2;
let view_content_h = height;
let view_content_area = Rect::new(view_content_x1, 1, view_content_w, view_content_h);
let option_block = ratatui::widgets::Block::default()
.borders(Borders::ALL)
.border_type(BorderType::Plain)
.border_style(border_color);
let option_area = Rect::new(option_b_x1, area.y, OPTION_BLOCK_WIDTH, area.height);
let view_block = ratatui::widgets::Block::default()
.borders(Borders::ALL)
.border_type(BorderType::Plain)
.border_style(border_color);
let view_area = Rect::new(view_b_x1, area.y, view_b_w, area.height);
f.render_widget(option_block, option_area);
f.render_widget(view_block, view_area);
self.render_option_list(f, option_content_area, list_color, cursor_color, layout);
if let Some(opt) = self.peek_current_opt() {
let mut layout = Layout::default();
opt.view.draw(f, view_content_area, cfg, &mut layout);
} else {
let group = self.peek_current_group();
let description = &group.description;
f.render_widget(Paragraph::new(description.as_str()), view_content_area);
}
self.update_cursors(height as usize);
}
fn handle_input(
&mut self,
_: &EngineState,
_: &mut Stack,
_: &Layout,
_: &mut ViewInfo,
key: KeyEvent,
) -> Option<Transition> {
match key.code {
KeyCode::Esc => {
if self.peeked_cursor.is_some() {
self.peeked_cursor = None;
Some(Transition::Ok)
} else {
Some(Transition::Exit)
}
}
KeyCode::Up => {
let cursor = self.get_cursor_mut();
cursor.prev(1);
Some(transition_tweak_if(self))
}
KeyCode::Down => {
let cursor = self.get_cursor_mut();
cursor.next(1);
Some(transition_tweak_if(self))
}
KeyCode::PageUp => {
let cursor = self.get_cursor_mut();
cursor.prev_window();
Some(transition_tweak_if(self))
}
KeyCode::PageDown => {
let cursor = self.get_cursor_mut();
cursor.next_window();
Some(transition_tweak_if(self))
}
KeyCode::Home => {
let cursor = self.get_cursor_mut();
cursor.prev(cursor.index());
Some(transition_tweak_if(self))
}
KeyCode::End => {
let cursor = self.get_cursor_mut();
cursor.next(cursor.cap());
Some(transition_tweak_if(self))
}
KeyCode::Enter => {
if self.peeked_cursor.is_some() {
return Some(Transition::Ok);
}
self.peeked_cursor = Some(WindowCursor::default());
let length = self.peek_current().expect("...").0.options.len();
self.peeked_cursor = WindowCursor::new(length, length);
let (group, opt) = self.peek_current().expect("...");
Some(Transition::Cmd(build_tweak_cmd(group, opt)))
}
_ => None,
}
}
fn exit(&mut self) -> Option<Value> {
None
}
fn collect_data(&self) -> Vec<NuText> {
if self.peeked_cursor.is_some() {
let i = self.cursor.index();
let opt = &self.options[i];
opt.options
.iter()
.map(|e| (e.name.clone(), TextStyle::default()))
.collect::<Vec<_>>()
} else {
self.options
.iter()
.map(|s| (s.group.to_string(), TextStyle::default()))
.collect()
}
}
fn show_data(&mut self, i: usize) -> bool {
if let Some(c) = &mut self.peeked_cursor {
let i = self.cursor.index();
if i > self.options[i].options.len() {
return false;
}
loop {
let p = c.index();
match i.cmp(&p) {
Ordering::Equal => return true,
Ordering::Less => c.prev(1),
Ordering::Greater => c.next(1),
};
}
} else {
if i > self.options.len() {
return false;
}
loop {
let p = self.cursor.index();
match i.cmp(&p) {
Ordering::Equal => return true,
Ordering::Less => self.cursor.prev(1),
Ordering::Greater => self.cursor.next(1),
};
}
}
}
fn setup(&mut self, config: ViewConfig<'_>) {
if let Some(hm) = config.config.get("config").and_then(create_map) {
let colors = get_color_map(&hm);
if let Some(style) = colors.get("border_color").copied() {
self.border_color = nu_style_to_tui(style);
}
if let Some(style) = colors.get("cursor_color").copied() {
self.cursor_color = nu_style_to_tui(style);
}
if let Some(style) = colors.get("list_color").copied() {
self.list_color = nu_style_to_tui(style);
}
}
for group in &mut self.options {
for opt in &mut group.options {
opt.view.setup(config);
}
}
}
}
fn build_tweak_cmd(group: &ConfigGroup, opt: &ConfigOption) -> String {
format!("tweak {} {}", group.group(), opt.name)
}
fn render_list(
f: &mut Frame,
area: Rect,
data: &[String],
cursor: WindowCursor,
not_picked_s: Style,
picked_s: Style,
layout: &mut Layout,
) {
let height = area.height as usize;
let width = area.width as usize;
let mut data = &data[cursor.starts_at()..];
if data.len() > height {
data = &data[..height];
}
let selected_row = cursor.offset();
for (i, name) in data.iter().enumerate() {
let mut name = name.to_owned();
truncate_str(&mut name, width);
let area = Rect::new(area.x, area.y + i as u16, area.width, 1);
let mut text = Paragraph::new(name.clone());
if i == selected_row {
text = text.style(picked_s);
} else {
text = text.style(not_picked_s);
}
f.render_widget(text, area);
layout.push(&name, area.x, area.y, area.width, 1);
}
}
fn transition_tweak_if(view: &ConfigurationView) -> Transition {
view.peek_current().map_or(Transition::Ok, |(group, opt)| {
Transition::Cmd(build_tweak_cmd(group, opt))
})
}

View File

@ -22,9 +22,6 @@ use super::{
pager::{Frame, Transition, ViewInfo}, pager::{Frame, Transition, ViewInfo},
}; };
pub mod configuration;
pub use configuration::ConfigurationView;
pub use information::InformationView; pub use information::InformationView;
pub use interactive::InteractiveView; pub use interactive::InteractiveView;
pub use preview::Preview; pub use preview::Preview;

View File

@ -37,10 +37,6 @@ impl Preview {
underlying_value: None, underlying_value: None,
} }
} }
pub fn set_value(&mut self, value: Value) {
self.underlying_value = Some(value);
}
} }
impl View for Preview { impl View for Preview {