Overhaul explore config (#13075)

Configuration in `explore` has always been confusing to me. This PR
overhauls (and simplifies, I think) how configuration is done.

# Details

1. Configuration is now strongly typed. In `Explore::run()` we create an
`ExploreConfig` struct from the more general Nu configuration and
arguments to `explore`, then pass that struct to other parts of
`explore` that need configuration. IMO this is a lot easier to reason
about and trace than the previous approach of creating a
`HashMap<String, Value>` and then using that to make various structs
elsewhere.
2. We now inherit more configuration from the config used for regular Nu
tables
1. Border/line styling now uses the `separator` style used for regular
Nu tables, the special `explore.split_line` config point has been
retired.
2. Cell padding in tables is now controlled by `table.padding` instead
of the undocumented `column_padding_left`/`column_padding_right` config
3. The (optional, previously not enabled by default) `selected_row` and
`selected_column` configuration has been removed. We now only highlight
the selected cell. I could re-add this if people really like the feature
but I'm guessing nobody uses it.

The interface still looks the same with a default/empty config.nu:


![image](https://github.com/nushell/nushell/assets/26268125/e40161ba-a8ec-407a-932d-5ece6f4dc616)
This commit is contained in:
Reilly Wood 2024-06-06 06:46:43 -07:00 committed by GitHub
parent 5d163c1bcc
commit f2f4b83886
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 229 additions and 636 deletions

View File

@ -4,7 +4,6 @@ use crate::{
views::{Orientation, RecordView},
};
use anyhow::Result;
use nu_ansi_term::Style;
use nu_protocol::{
engine::{EngineState, Stack},
Value,
@ -19,12 +18,6 @@ pub struct TableCmd {
#[derive(Debug, Default, Clone)]
struct TableSettings {
orientation: Option<Orientation>,
split_line_s: Option<Style>,
selected_cell_s: Option<Style>,
selected_row_s: Option<Style>,
selected_column_s: Option<Style>,
padding_column_left: Option<usize>,
padding_column_right: Option<usize>,
turn_on_cursor_mode: bool,
}
@ -64,8 +57,6 @@ impl ViewCommand for TableCmd {
let mut view = RecordView::new(columns, data);
// todo: use setup instead ????
if is_record {
view.set_orientation_current(Orientation::Left);
}
@ -74,32 +65,6 @@ impl ViewCommand for TableCmd {
view.set_orientation_current(o);
}
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 self.settings.turn_on_cursor_mode {
view.set_cursor_mode();
}

View File

@ -1,5 +1,5 @@
use super::ViewCommand;
use crate::views::InteractiveView;
use crate::views::TryView;
use anyhow::Result;
use nu_protocol::{
engine::{EngineState, Stack},
@ -22,7 +22,7 @@ impl TryCmd {
}
impl ViewCommand for TryCmd {
type View = InteractiveView<'static>;
type View = TryView<'static>;
fn name(&self) -> &'static str {
Self::NAME
@ -45,7 +45,7 @@ impl ViewCommand for TryCmd {
value: Option<Value>,
) -> Result<Self::View> {
let value = value.unwrap_or_default();
let mut view = InteractiveView::new(value);
let mut view = TryView::new(value);
view.init(self.command.clone());
view.try_run(engine_state, stack)?;

View File

@ -1,13 +1,12 @@
use crate::{
run_pager,
util::{create_lscolors, create_map, map_into_value},
PagerConfig, StyleConfig,
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 std::collections::HashMap;
use nu_protocol::Config;
/// A `less` like program to render a [`Value`] as a table.
#[derive(Clone)]
@ -68,19 +67,21 @@ impl Command for Explore {
let nu_config = engine_state.get_config();
let style_computer = StyleComputer::from_config(engine_state, stack);
let mut config = nu_config.explore.clone();
include_nu_config(&mut config, &style_computer);
update_config(&mut config, show_index, show_head);
prepare_default_config(&mut config);
let style = style_from_config(&config);
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 mut config = PagerConfig::new(nu_config, &style_computer, &lscolors, config);
config.style = style;
config.peek_value = peek_value;
config.tail = tail;
let config = PagerConfig::new(
nu_config,
&explore_config,
&style_computer,
&lscolors,
peek_value,
tail,
);
let result = run_pager(engine_state, &mut stack.clone(), ctrlc, input, config);
@ -134,153 +135,118 @@ impl Command for Explore {
}
}
fn update_config(config: &mut HashMap<String, Value>, show_index: bool, show_head: bool) {
let mut hm = config.get("table").and_then(create_map).unwrap_or_default();
if show_index {
insert_bool(&mut hm, "show_index", show_index);
}
if show_head {
insert_bool(&mut hm, "show_head", show_head);
}
config.insert(String::from("table"), map_into_value(hm));
#[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,
/// if true, the explore view will immediately try to run the command as it is typed
pub try_reactive: bool,
}
fn style_from_config(config: &HashMap<String, Value>) -> StyleConfig {
let mut style = StyleConfig::default();
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 {
/// take the default explore config and update it with relevant values from the nu config
pub fn from_nu_config(config: &Config) -> Self {
let mut ret = Self::default();
let colors = get_color_map(config);
ret.table.column_padding_left = config.table_indent.left;
ret.table.column_padding_right = config.table_indent.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") {
style.status_bar_text = *s;
ret.status_bar_text = *s;
}
if let Some(s) = colors.get("status_bar_background") {
style.status_bar_background = *s;
ret.status_bar_background = *s;
}
if let Some(s) = colors.get("command_bar_text") {
style.cmd_bar_text = *s;
ret.cmd_bar_text = *s;
}
if let Some(s) = colors.get("command_bar_background") {
style.cmd_bar_background = *s;
ret.cmd_bar_background = *s;
}
if let Some(hm) = config.get("status").and_then(create_map) {
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") {
style.status_info = *s;
ret.status_info = *s;
}
if let Some(s) = colors.get("success") {
style.status_success = *s;
ret.status_success = *s;
}
if let Some(s) = colors.get("warn") {
style.status_warn = *s;
ret.status_warn = *s;
}
if let Some(s) = colors.get("error") {
style.status_error = *s;
ret.status_error = *s;
}
}
style
}
fn prepare_default_config(config: &mut HashMap<String, Value>) {
const STATUS_BAR: Style = color(
Some(Color::Rgb(29, 31, 33)),
Some(Color::Rgb(196, 201, 198)),
);
const INPUT_BAR: Style = color(Some(Color::Rgb(196, 201, 198)), None);
const HIGHLIGHT: Style = color(Some(Color::Black), Some(Color::Yellow));
const STATUS_ERROR: Style = color(Some(Color::White), Some(Color::Red));
const STATUS_INFO: Style = color(None, None);
const STATUS_SUCCESS: Style = color(Some(Color::Black), Some(Color::Green));
const STATUS_WARN: Style = color(None, None);
const TABLE_SPLIT_LINE: Style = color(Some(Color::Rgb(64, 64, 64)), None);
const TABLE_SELECT_CELL: Style = color(None, None);
const TABLE_SELECT_ROW: Style = color(None, None);
const TABLE_SELECT_COLUMN: Style = color(None, None);
const HEXDUMP_INDEX: Style = color(Some(Color::Cyan), None);
const HEXDUMP_SEGMENT: Style = color(Some(Color::Cyan), None).bold();
const HEXDUMP_SEGMENT_ZERO: Style = color(Some(Color::Purple), None).bold();
const HEXDUMP_SEGMENT_UNKNOWN: Style = color(Some(Color::Green), None).bold();
const HEXDUMP_ASCII: Style = color(Some(Color::Cyan), None).bold();
const HEXDUMP_ASCII_ZERO: Style = color(Some(Color::Purple), None).bold();
const HEXDUMP_ASCII_UNKNOWN: Style = color(Some(Color::Green), None).bold();
insert_style(config, "status_bar_background", STATUS_BAR);
insert_style(config, "command_bar_text", INPUT_BAR);
insert_style(config, "highlight", HIGHLIGHT);
// because how config works we need to parse a string into Value::Record
{
let mut hm = config
.get("status")
.and_then(parse_hash_map)
.unwrap_or_default();
insert_style(&mut hm, "info", STATUS_INFO);
insert_style(&mut hm, "success", STATUS_SUCCESS);
insert_style(&mut hm, "warn", STATUS_WARN);
insert_style(&mut hm, "error", STATUS_ERROR);
config.insert(String::from("status"), map_into_value(hm));
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;
}
}
}
{
let mut hm = config
.get("table")
.and_then(parse_hash_map)
.unwrap_or_default();
insert_style(&mut hm, "split_line", TABLE_SPLIT_LINE);
insert_style(&mut hm, "selected_cell", TABLE_SELECT_CELL);
insert_style(&mut hm, "selected_row", TABLE_SELECT_ROW);
insert_style(&mut hm, "selected_column", TABLE_SELECT_COLUMN);
config.insert(String::from("table"), map_into_value(hm));
}
{
let mut hm = config
.get("hex-dump")
.and_then(create_map)
.unwrap_or_default();
insert_style(&mut hm, "color_index", HEXDUMP_INDEX);
insert_style(&mut hm, "color_segment", HEXDUMP_SEGMENT);
insert_style(&mut hm, "color_segment_zero", HEXDUMP_SEGMENT_ZERO);
insert_style(&mut hm, "color_segment_unknown", HEXDUMP_SEGMENT_UNKNOWN);
insert_style(&mut hm, "color_ascii", HEXDUMP_ASCII);
insert_style(&mut hm, "color_ascii_zero", HEXDUMP_ASCII_ZERO);
insert_style(&mut hm, "color_ascii_unknown", HEXDUMP_ASCII_UNKNOWN);
insert_int(&mut hm, "segment_size", 2);
insert_int(&mut hm, "count_segments", 8);
insert_bool(&mut hm, "split", true);
config.insert(String::from("hex-dump"), map_into_value(hm));
ret
}
}
fn parse_hash_map(value: &Value) -> Option<HashMap<String, Value>> {
value.as_record().ok().map(|val| {
val.iter()
.map(|(col, val)| (col.clone(), val.clone()))
.collect::<HashMap<_, _>>()
})
#[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 {
@ -299,49 +265,6 @@ const fn color(foreground: Option<Color>, background: Option<Color>) -> Style {
}
}
fn insert_style(map: &mut HashMap<String, Value>, key: &str, value: Style) {
if map.contains_key(key) {
return;
}
if value == Style::default() {
return;
}
let value = nu_color_config::NuStyle::from(value);
if let Ok(val) = nu_json::to_string_raw(&value) {
map.insert(String::from(key), Value::string(val, Span::unknown()));
}
}
fn insert_bool(map: &mut HashMap<String, Value>, key: &str, value: bool) {
if map.contains_key(key) {
return;
}
map.insert(String::from(key), Value::bool(value, Span::unknown()));
}
fn insert_int(map: &mut HashMap<String, Value>, key: &str, value: i64) {
if map.contains_key(key) {
return;
}
map.insert(String::from(key), Value::int(value, Span::unknown()));
}
fn include_nu_config(config: &mut HashMap<String, Value>, style_computer: &StyleComputer) {
let line_color = lookup_color(style_computer, "separator");
if line_color != nu_ansi_term::Style::default() {
let mut map = config
.get("table")
.and_then(parse_hash_map)
.unwrap_or_default();
insert_style(&mut map, "split_line", line_color);
config.insert(String::from("table"), map_into_value(map));
}
}
fn lookup_color(style_computer: &StyleComputer, key: &str) -> nu_ansi_term::Style {
style_computer.compute(key, &Value::nothing(Span::unknown()))
}

View File

@ -15,13 +15,13 @@ use nu_protocol::{
engine::{EngineState, Stack},
PipelineData, Value,
};
use pager::{Page, Pager, PagerConfig, StyleConfig};
use pager::{Page, Pager, PagerConfig};
use registry::CommandRegistry;
use terminal_size::{Height, Width};
use views::{BinaryView, Orientation, Preview, RecordView};
mod util {
pub use super::nu_common::{create_lscolors, create_map, map_into_value};
pub use super::nu_common::{create_lscolors, create_map};
}
fn run_pager(

View File

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

View File

@ -176,10 +176,6 @@ pub fn create_map(value: &Value) -> Option<HashMap<String, Value>> {
)
}
pub fn map_into_value(hm: HashMap<String, Value>) -> Value {
Value::record(hm.into_iter().collect(), NuSpan::unknown())
}
fn unknown_error_value() -> Value {
Value::string(String::from(""), NuSpan::unknown())
}

View File

@ -10,9 +10,9 @@ use self::{
};
use super::views::{Layout, View};
use crate::{
nu_common::{CtrlC, NuColor, NuConfig, NuSpan, NuStyle},
explore::ExploreConfig,
nu_common::{CtrlC, NuColor, NuConfig, NuStyle},
registry::{Command, CommandRegistry},
util::map_into_value,
views::{util::nu_style_to_tui, ViewConfig},
};
use anyhow::Result;
@ -26,15 +26,14 @@ use crossterm::{
};
use events::UIEvents;
use lscolors::LsColors;
use nu_color_config::{lookup_ansi_color_style, StyleComputer};
use nu_color_config::StyleComputer;
use nu_protocol::{
engine::{EngineState, Stack},
Record, Value,
Value,
};
use ratatui::{backend::CrosstermBackend, layout::Rect, widgets::Block};
use std::{
cmp::min,
collections::HashMap,
io::{self, Stdout},
result,
sync::atomic::Ordering,
@ -42,7 +41,6 @@ use std::{
pub type Frame<'a> = ratatui::Frame<'a>;
pub type Terminal = ratatui::Terminal<CrosstermBackend<Stdout>>;
pub type ConfigMap = HashMap<String, Value>;
#[derive(Debug, Clone)]
pub struct Pager<'a> {
@ -73,19 +71,6 @@ struct CommandBuf {
cmd_exec_info: Option<String>,
}
#[derive(Debug, Default, Clone)]
pub struct StyleConfig {
pub status_info: NuStyle,
pub status_success: NuStyle,
pub status_warn: NuStyle,
pub status_error: NuStyle,
pub status_bar_background: NuStyle,
pub status_bar_text: NuStyle,
pub cmd_bar_text: NuStyle,
pub cmd_bar_background: NuStyle,
pub highlight: NuStyle,
}
impl<'a> Pager<'a> {
pub fn new(config: PagerConfig<'a>) -> Self {
Self {
@ -100,27 +85,6 @@ impl<'a> Pager<'a> {
self.message = Some(text.into());
}
pub fn set_config(&mut self, path: &[String], value: Value) -> bool {
let path = path.iter().map(|s| s.as_str()).collect::<Vec<_>>();
match &path[..] {
["status_bar_text"] => value_as_style(&mut self.config.style.status_bar_text, &value),
["status_bar_background"] => {
value_as_style(&mut self.config.style.status_bar_background, &value)
}
["command_bar_text"] => value_as_style(&mut self.config.style.cmd_bar_text, &value),
["command_bar_background"] => {
value_as_style(&mut self.config.style.cmd_bar_background, &value)
}
["highlight"] => value_as_style(&mut self.config.style.highlight, &value),
["status", "info"] => value_as_style(&mut self.config.style.status_info, &value),
["status", "success"] => value_as_style(&mut self.config.style.status_success, &value),
["status", "warn"] => value_as_style(&mut self.config.style.status_warn, &value),
["status", "error"] => value_as_style(&mut self.config.style.status_error, &value),
path => set_config(&mut self.config.config, path, value),
}
}
pub fn run(
&mut self,
engine_state: &EngineState,
@ -132,8 +96,8 @@ impl<'a> Pager<'a> {
if let Some(page) = &mut view {
page.view.setup(ViewConfig::new(
self.config.nu_config,
self.config.explore_config,
self.config.style_computer,
&self.config.config,
self.config.lscolors,
))
}
@ -153,10 +117,9 @@ pub enum Transition {
#[derive(Debug, Clone)]
pub struct PagerConfig<'a> {
pub nu_config: &'a NuConfig,
pub explore_config: &'a ExploreConfig,
pub style_computer: &'a StyleComputer<'a>,
pub lscolors: &'a LsColors,
pub config: ConfigMap,
pub style: StyleConfig,
// If true, when quitting output the value of the cell the cursor was on
pub peek_value: bool,
pub tail: bool,
@ -165,18 +128,19 @@ pub struct PagerConfig<'a> {
impl<'a> PagerConfig<'a> {
pub fn new(
nu_config: &'a NuConfig,
explore_config: &'a ExploreConfig,
style_computer: &'a StyleComputer,
lscolors: &'a LsColors,
config: ConfigMap,
peek_value: bool,
tail: bool,
) -> Self {
Self {
nu_config,
explore_config,
style_computer,
config,
lscolors,
peek_value: false,
tail: false,
style: StyleConfig::default(),
peek_value,
tail,
}
}
}
@ -401,7 +365,7 @@ fn draw_frame(
draw_info(f, pager, info);
highlight_search_results(f, pager, layout, pager.config.style.highlight);
highlight_search_results(f, pager, layout, pager.config.explore_config.highlight);
set_cursor_cmd_bar(f, area, pager);
}
@ -411,19 +375,24 @@ fn draw_info(f: &mut Frame, pager: &mut Pager<'_>, info: ViewInfo) {
if let Some(report) = info.status {
let last_2nd_line = area.bottom().saturating_sub(2);
let area = Rect::new(area.left(), last_2nd_line, area.width, 1);
render_status_bar(f, area, report, &pager.config.style);
render_status_bar(f, area, report, pager.config.explore_config);
}
{
let last_line = area.bottom().saturating_sub(1);
let area = Rect::new(area.left(), last_line, area.width, 1);
render_cmd_bar(f, area, pager, info.report, &pager.config.style);
render_cmd_bar(f, area, pager, info.report, pager.config.explore_config);
}
}
fn create_view_config<'a>(pager: &'a Pager<'_>) -> ViewConfig<'a> {
let cfg = &pager.config;
ViewConfig::new(cfg.nu_config, cfg.style_computer, &cfg.config, cfg.lscolors)
ViewConfig::new(
cfg.nu_config,
cfg.explore_config,
cfg.style_computer,
cfg.lscolors,
)
}
fn pager_run_command(
@ -463,16 +432,7 @@ fn run_command(
let value = view_stack.curr_view.as_mut().and_then(|p| p.view.exit());
let transition = command.react(engine_state, stack, pager, value)?;
match transition {
Transition::Ok => {
// so we basically allow a change of a config inside a command,
// and cause of this we wanna update all of our views.
//
// THOUGH: MOST LIKELY IT WON'T BE CHANGED AND WE DO A WASTE.......
update_view_stack_setup(view_stack, &pager.config);
Ok(CmdResult::new(false, false, String::new()))
}
Transition::Ok => Ok(CmdResult::new(false, false, String::new())),
Transition::Exit => Ok(CmdResult::new(true, false, String::new())),
Transition::Cmd { .. } => todo!("not used so far"),
}
@ -487,7 +447,8 @@ fn run_command(
}
}
update_view_setup(&mut new_view, &pager.config);
setup_view(&mut new_view, &pager.config);
view_stack.curr_view = Some(Page::raw(new_view, stackable));
Ok(CmdResult::new(false, true, cmd.name().to_owned()))
@ -495,18 +456,13 @@ fn run_command(
}
}
fn update_view_stack_setup(view_stack: &mut ViewStack, cfg: &PagerConfig<'_>) {
if let Some(page) = view_stack.curr_view.as_mut() {
update_view_setup(&mut page.view, cfg);
}
for page in &mut view_stack.stack {
update_view_setup(&mut page.view, cfg);
}
}
fn update_view_setup(view: &mut Box<dyn View>, cfg: &PagerConfig<'_>) {
let cfg = ViewConfig::new(cfg.nu_config, cfg.style_computer, &cfg.config, cfg.lscolors);
fn setup_view(view: &mut Box<dyn View>, cfg: &PagerConfig<'_>) {
let cfg = ViewConfig::new(
cfg.nu_config,
cfg.explore_config,
cfg.style_computer,
cfg.lscolors,
);
view.setup(cfg);
}
@ -528,7 +484,7 @@ fn set_cursor_cmd_bar(f: &mut Frame, area: Rect, pager: &Pager) {
}
}
fn render_status_bar(f: &mut Frame, area: Rect, report: Report, theme: &StyleConfig) {
fn render_status_bar(f: &mut Frame, area: Rect, report: Report, theme: &ExploreConfig) {
let msg_style = report_msg_style(&report, theme, theme.status_bar_text);
let mut status_bar = create_status_bar(report);
status_bar.set_background_style(theme.status_bar_background);
@ -549,11 +505,11 @@ fn create_status_bar(report: Report) -> StatusBar {
)
}
fn report_msg_style(report: &Report, theme: &StyleConfig, style: NuStyle) -> NuStyle {
fn report_msg_style(report: &Report, config: &ExploreConfig, style: NuStyle) -> NuStyle {
if matches!(report.level, Severity::Info) {
style
} else {
report_level_style(report.level, theme)
report_level_style(report.level, config)
}
}
@ -562,15 +518,15 @@ fn render_cmd_bar(
area: Rect,
pager: &Pager,
report: Option<Report>,
theme: &StyleConfig,
config: &ExploreConfig,
) {
if let Some(report) = report {
let style = report_msg_style(&report, theme, theme.cmd_bar_text);
let style = report_msg_style(&report, config, config.cmd_bar_text);
let bar = CommandBar::new(
&report.message,
&report.context1,
style,
theme.cmd_bar_background,
config.cmd_bar_background,
);
f.render_widget(bar, area);
@ -578,16 +534,16 @@ fn render_cmd_bar(
}
if pager.cmd_buf.is_cmd_input {
render_cmd_bar_cmd(f, area, pager, theme);
render_cmd_bar_cmd(f, area, pager, config);
return;
}
if pager.search_buf.is_search_input || !pager.search_buf.buf_cmd_input.is_empty() {
render_cmd_bar_search(f, area, pager, theme);
render_cmd_bar_search(f, area, pager, config);
}
}
fn render_cmd_bar_search(f: &mut Frame, area: Rect, pager: &Pager<'_>, theme: &StyleConfig) {
fn render_cmd_bar_search(f: &mut Frame, area: Rect, pager: &Pager<'_>, config: &ExploreConfig) {
if pager.search_buf.search_results.is_empty() && !pager.search_buf.is_search_input {
let message = format!("Pattern not found: {}", pager.search_buf.buf_cmd_input);
let style = NuStyle {
@ -596,7 +552,7 @@ fn render_cmd_bar_search(f: &mut Frame, area: Rect, pager: &Pager<'_>, theme: &S
..Default::default()
};
let bar = CommandBar::new(&message, "", style, theme.cmd_bar_background);
let bar = CommandBar::new(&message, "", style, config.cmd_bar_background);
f.render_widget(bar, area);
return;
}
@ -615,11 +571,11 @@ fn render_cmd_bar_search(f: &mut Frame, area: Rect, pager: &Pager<'_>, theme: &S
format!("[{index}/{total}]")
};
let bar = CommandBar::new(&text, &info, theme.cmd_bar_text, theme.cmd_bar_background);
let bar = CommandBar::new(&text, &info, config.cmd_bar_text, config.cmd_bar_background);
f.render_widget(bar, area);
}
fn render_cmd_bar_cmd(f: &mut Frame, area: Rect, pager: &Pager, theme: &StyleConfig) {
fn render_cmd_bar_cmd(f: &mut Frame, area: Rect, pager: &Pager, config: &ExploreConfig) {
let mut input = pager.cmd_buf.buf_cmd2.as_str();
if input.len() > area.width as usize + 1 {
// in such case we take last max_cmd_len chars
@ -637,7 +593,7 @@ fn render_cmd_bar_cmd(f: &mut Frame, area: Rect, pager: &Pager, theme: &StyleCon
let prefix = ':';
let text = format!("{prefix}{input}");
let bar = CommandBar::new(&text, "", theme.cmd_bar_text, theme.cmd_bar_background);
let bar = CommandBar::new(&text, "", config.cmd_bar_text, config.cmd_bar_background);
f.render_widget(bar, area);
}
@ -998,72 +954,12 @@ fn cmd_input_key_event(buf: &mut CommandBuf, key: &KeyEvent) -> bool {
}
}
fn value_as_style(style: &mut nu_ansi_term::Style, value: &Value) -> bool {
match value.coerce_str() {
Ok(s) => {
*style = lookup_ansi_color_style(&s);
true
}
Err(_) => false,
}
}
fn set_config(hm: &mut HashMap<String, Value>, path: &[&str], value: Value) -> bool {
if path.is_empty() {
return false;
}
let key = path[0];
if !hm.contains_key(key) {
hm.insert(
key.to_string(),
Value::record(Record::new(), NuSpan::unknown()),
);
}
let val = hm.get_mut(key).expect("...");
if path.len() == 1 {
*val = value;
return true;
}
match val {
Value::Record { val: record, .. } => {
if path.len() == 2 {
let key = path[1];
record.to_mut().insert(key, value);
} else {
let mut hm2: HashMap<String, Value> = HashMap::new();
for (k, v) in record.iter() {
hm2.insert(k.to_string(), v.clone());
}
let result = set_config(&mut hm2, &path[1..], value);
if !result {
*val = map_into_value(hm2);
}
if path.len() == 2 {
} else {
return false;
}
}
true
}
_ => false,
}
}
fn report_level_style(level: Severity, theme: &StyleConfig) -> NuStyle {
fn report_level_style(level: Severity, config: &ExploreConfig) -> NuStyle {
match level {
Severity::Info => theme.status_info,
Severity::Success => theme.status_success,
Severity::Warn => theme.status_warn,
Severity::Err => theme.status_error,
Severity::Info => config.status_info,
Severity::Success => config.status_success,
Severity::Warn => config.status_warn,
Severity::Err => config.status_error,
}
}

View File

@ -3,7 +3,6 @@
mod binary_widget;
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
use nu_color_config::get_color_map;
use nu_protocol::{
engine::{EngineState, Stack},
Value,
@ -11,12 +10,12 @@ use nu_protocol::{
use ratatui::layout::Rect;
use crate::{
explore::ExploreConfig,
nu_common::NuText,
pager::{
report::{Report, Severity},
ConfigMap, Frame, Transition, ViewInfo,
Frame, Transition, ViewInfo,
},
util::create_map,
views::cursor::Position,
};
@ -90,12 +89,7 @@ impl View for BinaryView {
}
fn setup(&mut self, cfg: ViewConfig<'_>) {
let hm = match cfg.config.get("hex-dump").and_then(create_map) {
Some(hm) => hm,
None => return,
};
self.settings = settings_from_config(&hm);
self.settings = settings_from_config(cfg.explore_config);
let count_rows =
BinaryWidget::new(&self.data, self.settings.opts, Default::default()).count_lines();
@ -184,30 +178,18 @@ fn handle_event_view_mode(view: &mut BinaryView, key: &KeyEvent) -> Option<Trans
}
}
fn settings_from_config(config: &ConfigMap) -> Settings {
let colors = get_color_map(config);
fn settings_from_config(config: &ExploreConfig) -> Settings {
// Most of this is hardcoded for now, add it to the config later if needed
Settings {
opts: BinarySettings::new(
config_get_usize(config, "segment_size", 2),
config_get_usize(config, "count_segments", 8),
),
opts: BinarySettings::new(2, 8),
style: BinaryStyle::new(
colors.get("color_index").cloned(),
config_get_usize(config, "column_padding_left", 1) as u16,
config_get_usize(config, "column_padding_right", 1) as u16,
None,
config.table.column_padding_left as u16,
config.table.column_padding_right as u16,
),
}
}
fn config_get_usize(config: &ConfigMap, key: &str, default: usize) -> usize {
config
.get(key)
.and_then(|v| v.coerce_str().ok())
.and_then(|s| s.parse::<usize>().ok())
.unwrap_or(default)
}
fn create_report(cursor: WindowCursor2D) -> Report {
let covered_percent = report_row_position(cursor);
let cursor = report_cursor_position(cursor);

View File

@ -1,16 +1,16 @@
mod binary;
mod colored_text_widget;
mod cursor;
mod interactive;
mod preview;
mod record;
mod r#try;
pub mod util;
use super::{
nu_common::NuText,
pager::{Frame, Transition, ViewInfo},
};
use crate::{nu_common::NuConfig, pager::ConfigMap};
use crate::{explore::ExploreConfig, nu_common::NuConfig};
use crossterm::event::KeyEvent;
use lscolors::LsColors;
use nu_color_config::StyleComputer;
@ -21,8 +21,8 @@ use nu_protocol::{
use ratatui::layout::Rect;
pub use binary::BinaryView;
pub use interactive::InteractiveView;
pub use preview::Preview;
pub use r#try::TryView;
pub use record::{Orientation, RecordView};
#[derive(Debug, Default)]
@ -55,22 +55,22 @@ impl ElementInfo {
#[derive(Debug, Clone, Copy)]
pub struct ViewConfig<'a> {
pub nu_config: &'a NuConfig,
pub explore_config: &'a ExploreConfig,
pub style_computer: &'a StyleComputer<'a>,
pub config: &'a ConfigMap,
pub lscolors: &'a LsColors,
}
impl<'a> ViewConfig<'a> {
pub fn new(
nu_config: &'a NuConfig,
explore_config: &'a ExploreConfig,
style_computer: &'a StyleComputer<'a>,
config: &'a ConfigMap,
lscolors: &'a LsColors,
) -> Self {
Self {
nu_config,
explore_config,
style_computer,
config,
lscolors,
}
}

View File

@ -1,26 +1,26 @@
mod table_widget;
use self::table_widget::{TableStyle, TableWidget, TableWidgetState};
use self::table_widget::{TableWidget, TableWidgetState};
use super::{
cursor::{Position, WindowCursor2D},
util::{make_styled_string, nu_style_to_tui},
Layout, View, ViewConfig,
};
use crate::{
nu_common::{collect_input, lscolorize, NuConfig, NuSpan, NuStyle, NuText},
explore::ExploreConfig,
nu_common::{collect_input, lscolorize, NuSpan, NuText},
pager::{
report::{Report, Severity},
ConfigMap, Frame, Transition, ViewInfo,
Frame, Transition, ViewInfo,
},
util::create_map,
views::ElementInfo,
};
use anyhow::Result;
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
use nu_color_config::{get_color_map, StyleComputer};
use nu_color_config::StyleComputer;
use nu_protocol::{
engine::{EngineState, Stack},
Record, Span, Value,
Config, Record, Span, Value,
};
use ratatui::{layout::Rect, widgets::Block};
use std::{borrow::Cow, collections::HashMap};
@ -32,7 +32,7 @@ pub struct RecordView<'a> {
layer_stack: Vec<RecordLayer<'a>>,
mode: UIMode,
orientation: Orientation,
theme: TableTheme,
cfg: ExploreConfig,
}
impl<'a> RecordView<'a> {
@ -44,52 +44,18 @@ impl<'a> RecordView<'a> {
layer_stack: vec![RecordLayer::new(columns, records)],
mode: UIMode::View,
orientation: Orientation::Top,
theme: TableTheme::default(),
// TODO: It's kind of gross how this temporarily has an incorrect/default config.
// See if we can pass correct config in through the constructor
cfg: ExploreConfig::default(),
}
}
pub fn tail(&mut self, width: u16, height: u16) {
let page_size =
estimate_page_size(Rect::new(0, 0, width, height), self.theme.table.show_header);
estimate_page_size(Rect::new(0, 0, width, height), self.cfg.table.show_header);
tail_data(self, page_size as usize);
}
pub fn set_style_split_line(&mut self, style: NuStyle) {
self.theme.table.splitline_style = style
}
pub fn set_style_selected_cell(&mut self, style: NuStyle) {
self.theme.cursor.selected_cell = Some(style)
}
pub fn set_style_selected_row(&mut self, style: NuStyle) {
self.theme.cursor.selected_row = Some(style)
}
pub fn set_style_selected_column(&mut self, style: NuStyle) {
self.theme.cursor.selected_column = Some(style)
}
pub fn set_padding_column(&mut self, (left, right): (usize, usize)) {
self.theme.table.column_padding_left = left;
self.theme.table.column_padding_right = right;
}
pub fn get_padding_column(&self) -> (usize, usize) {
(
self.theme.table.column_padding_left,
self.theme.table.column_padding_right,
)
}
pub fn get_theme(&self) -> &TableTheme {
&self.theme
}
pub fn set_theme(&mut self, theme: TableTheme) {
self.theme = theme;
}
pub fn transpose(&mut self) {
let layer = self.get_layer_last_mut();
transpose_table(layer);
@ -193,7 +159,7 @@ impl<'a> RecordView<'a> {
style_computer,
row,
column,
self.theme.table,
self.cfg.table,
layer.orientation,
)
}
@ -252,11 +218,11 @@ impl View for RecordView<'_> {
column,
table_layout.count_rows,
self.get_layer_last().orientation,
self.theme.table.show_header,
self.cfg.table.show_header,
);
if let Some(info) = info {
highlight_cell(f, area, info.clone(), &self.theme.cursor);
highlight_selected_cell(f, info.clone(), &self.cfg);
}
}
}
@ -300,7 +266,7 @@ impl View for RecordView<'_> {
let data = convert_records_to_string(
&self.get_layer_last().records,
&NuConfig::default(),
&nu_protocol::Config::default(),
&style_computer,
);
@ -338,22 +304,7 @@ impl View for RecordView<'_> {
// todo: move the method to Command?
fn setup(&mut self, cfg: ViewConfig<'_>) {
if let Some(hm) = cfg.config.get("table").and_then(create_map) {
self.theme = theme_from_config(&hm);
if let Some(orientation) = hm.get("orientation").and_then(|v| v.coerce_str().ok()) {
let orientation = match orientation.as_ref() {
"left" => Some(Orientation::Left),
"top" => Some(Orientation::Top),
_ => None,
};
if let Some(orientation) = orientation {
self.set_orientation(orientation);
self.set_orientation_current(orientation);
}
}
}
self.cfg = cfg.explore_config.clone();
}
}
@ -660,7 +611,7 @@ fn tail_data(state: &mut RecordView<'_>, page_size: usize) {
fn convert_records_to_string(
records: &[Vec<Value>],
cfg: &NuConfig,
cfg: &Config,
style_computer: &StyleComputer,
) -> Vec<Vec<NuText>> {
records
@ -678,31 +629,8 @@ fn convert_records_to_string(
.collect::<Vec<_>>()
}
fn highlight_cell(f: &mut Frame, area: Rect, info: ElementInfo, theme: &CursorStyle) {
// highlight selected column
if let Some(style) = theme.selected_column {
let highlight_block = Block::default().style(nu_style_to_tui(style));
let area = Rect::new(info.area.x, area.y, info.area.width, area.height);
f.render_widget(highlight_block.clone(), area);
}
// highlight selected row
if let Some(style) = theme.selected_row {
let highlight_block = Block::default().style(nu_style_to_tui(style));
let area = Rect::new(area.x, info.area.y, area.width, 1);
f.render_widget(highlight_block.clone(), area);
}
// highlight selected cell
let cell_style = match theme.selected_cell {
Some(s) => s,
None => {
let mut style = nu_ansi_term::Style::new();
// light blue chosen somewhat arbitrarily, looks OK but I'm not set on it
style.background = Some(nu_ansi_term::Color::LightBlue);
style
}
};
fn highlight_selected_cell(f: &mut Frame, info: ElementInfo, cfg: &ExploreConfig) {
let cell_style = cfg.selected_cell;
let highlight_block = Block::default().style(nu_style_to_tui(cell_style));
let area = Rect::new(info.area.x, info.area.y, info.area.width, 1);
f.render_widget(highlight_block.clone(), area)
@ -842,53 +770,3 @@ fn _transpose_table(
data
}
fn theme_from_config(config: &ConfigMap) -> TableTheme {
let mut theme = TableTheme::default();
let colors = get_color_map(config);
if let Some(s) = colors.get("split_line") {
theme.table.splitline_style = *s;
}
theme.cursor.selected_cell = colors.get("selected_cell").cloned();
theme.cursor.selected_row = colors.get("selected_row").cloned();
theme.cursor.selected_column = colors.get("selected_column").cloned();
theme.table.show_header = config_get_bool(config, "show_head", true);
theme.table.show_index = config_get_bool(config, "show_index", false);
theme.table.column_padding_left = config_get_usize(config, "column_padding_left", 1);
theme.table.column_padding_right = config_get_usize(config, "column_padding_right", 1);
theme
}
fn config_get_bool(config: &ConfigMap, key: &str, default: bool) -> bool {
config
.get(key)
.and_then(|v| v.as_bool().ok())
.unwrap_or(default)
}
fn config_get_usize(config: &ConfigMap, key: &str, default: usize) -> usize {
config
.get(key)
.and_then(|v| v.coerce_str().ok())
.and_then(|s| s.parse::<usize>().ok())
.unwrap_or(default)
}
#[derive(Debug, Default, Clone)]
pub struct TableTheme {
table: TableStyle,
cursor: CursorStyle,
}
#[derive(Debug, Default, Clone)]
struct CursorStyle {
selected_cell: Option<NuStyle>,
selected_column: Option<NuStyle>,
selected_row: Option<NuStyle>,
}

View File

@ -1,5 +1,6 @@
use super::Layout;
use crate::{
explore::TableConfig,
nu_common::{truncate_str, NuStyle, NuText},
views::util::{nu_style_to_tui, text_style_to_tui_style},
};
@ -23,7 +24,7 @@ pub struct TableWidget<'a> {
data: Cow<'a, [Vec<NuText>]>,
index_row: usize,
index_column: usize,
style: TableStyle,
config: TableConfig,
header_position: Orientation,
style_computer: &'a StyleComputer<'a>,
}
@ -35,15 +36,6 @@ pub enum Orientation {
Left,
}
#[derive(Debug, Default, Clone, Copy)]
pub struct TableStyle {
pub splitline_style: NuStyle,
pub show_index: bool,
pub show_header: bool,
pub column_padding_left: usize,
pub column_padding_right: usize,
}
impl<'a> TableWidget<'a> {
#[allow(clippy::too_many_arguments)]
pub fn new(
@ -52,7 +44,7 @@ impl<'a> TableWidget<'a> {
style_computer: &'a StyleComputer<'a>,
index_row: usize,
index_column: usize,
style: TableStyle,
config: TableConfig,
header_position: Orientation,
) -> Self {
Self {
@ -61,7 +53,7 @@ impl<'a> TableWidget<'a> {
style_computer,
index_row,
index_column,
style,
config,
header_position,
}
}
@ -100,13 +92,13 @@ impl StatefulWidget for TableWidget<'_> {
// todo: refactoring these to methods as they have quite a bit in common.
impl<'a> TableWidget<'a> {
fn render_table_horizontal(self, area: Rect, buf: &mut Buffer, state: &mut TableWidgetState) {
let padding_l = self.style.column_padding_left as u16;
let padding_r = self.style.column_padding_right as u16;
let padding_l = self.config.column_padding_left as u16;
let padding_r = self.config.column_padding_right as u16;
let show_index = self.style.show_index;
let show_head = self.style.show_header;
let show_index = self.config.show_index;
let show_head = self.config.show_header;
let splitline_s = self.style.splitline_style;
let separator_s = self.config.separator_style;
let mut data_height = area.height;
let mut data_y = area.y;
@ -137,7 +129,7 @@ impl<'a> TableWidget<'a> {
}
if show_head {
render_header_borders(buf, area, 1, splitline_s);
render_header_borders(buf, area, 1, separator_s);
}
if show_index {
@ -158,7 +150,7 @@ impl<'a> TableWidget<'a> {
data_height,
show_head,
false,
splitline_s,
separator_s,
);
}
@ -250,7 +242,7 @@ impl<'a> TableWidget<'a> {
data_height,
show_head,
false,
splitline_s,
separator_s,
);
}
@ -268,12 +260,12 @@ impl<'a> TableWidget<'a> {
return;
}
let padding_l = self.style.column_padding_left as u16;
let padding_r = self.style.column_padding_right as u16;
let padding_l = self.config.column_padding_left as u16;
let padding_r = self.config.column_padding_right as u16;
let show_index = self.style.show_index;
let show_head = self.style.show_header;
let splitline_s = self.style.splitline_style;
let show_index = self.config.show_index;
let show_head = self.config.show_header;
let separator_s = self.config.separator_style;
let mut left_w = 0;
@ -295,7 +287,7 @@ impl<'a> TableWidget<'a> {
area.height,
false,
false,
splitline_s,
separator_s,
);
}
@ -327,7 +319,7 @@ impl<'a> TableWidget<'a> {
area.height,
false,
false,
splitline_s,
separator_s,
);
}
@ -352,7 +344,7 @@ impl<'a> TableWidget<'a> {
area.height,
false,
false,
splitline_s,
separator_s,
);
}

View File

@ -1,16 +1,10 @@
use super::{
record::{RecordView, TableTheme},
util::{lookup_tui_color, nu_style_to_tui},
Layout, Orientation, View, ViewConfig,
};
use super::{record::RecordView, util::nu_style_to_tui, Layout, Orientation, View, ViewConfig};
use crate::{
nu_common::{collect_pipeline, run_command_with_value},
pager::{report::Report, Frame, Transition, ViewInfo},
util::create_map,
};
use anyhow::Result;
use crossterm::event::{KeyCode, KeyEvent};
use nu_color_config::get_color_map;
use nu_protocol::{
engine::{EngineState, Stack},
PipelineData, Value,
@ -22,26 +16,22 @@ use ratatui::{
};
use std::cmp::min;
pub struct InteractiveView<'a> {
pub struct TryView<'a> {
input: Value,
command: String,
immediate: bool,
reactive: bool,
table: Option<RecordView<'a>>,
table_theme: TableTheme,
view_mode: bool,
border_color: Style,
highlighted_color: Style,
}
impl<'a> InteractiveView<'a> {
impl<'a> TryView<'a> {
pub fn new(input: Value) -> Self {
Self {
input,
table: None,
immediate: false,
table_theme: TableTheme::default(),
reactive: false,
border_color: Style::default(),
highlighted_color: Style::default(),
view_mode: false,
command: String::new(),
}
@ -52,18 +42,15 @@ impl<'a> InteractiveView<'a> {
}
pub fn try_run(&mut self, engine_state: &EngineState, stack: &mut Stack) -> Result<()> {
let mut view = run_command(&self.command, &self.input, engine_state, stack)?;
view.set_theme(self.table_theme.clone());
let view = run_command(&self.command, &self.input, engine_state, stack)?;
self.table = Some(view);
Ok(())
}
}
impl View for InteractiveView<'_> {
impl View for TryView<'_> {
fn draw(&mut self, f: &mut Frame, area: Rect, cfg: ViewConfig<'_>, layout: &mut Layout) {
let border_color = self.border_color;
let highlighted_color = self.highlighted_color;
let cmd_block = ratatui::widgets::Block::default()
.borders(Borders::ALL)
@ -77,7 +64,7 @@ impl View for InteractiveView<'_> {
cmd_block
.border_style(Style::default().add_modifier(Modifier::BOLD))
.border_type(BorderType::Double)
.border_style(highlighted_color)
.border_style(border_color)
};
f.render_widget(cmd_block, cmd_area);
@ -127,7 +114,7 @@ impl View for InteractiveView<'_> {
table_block
.border_style(Style::default().add_modifier(Modifier::BOLD))
.border_type(BorderType::Double)
.border_style(highlighted_color)
.border_style(border_color)
} else {
table_block
};
@ -135,6 +122,7 @@ impl View for InteractiveView<'_> {
f.render_widget(table_block, table_area);
if let Some(table) = &mut self.table {
table.setup(cfg);
let area = Rect::new(
area.x + 2,
area.y + 4,
@ -190,7 +178,7 @@ impl View for InteractiveView<'_> {
if !self.command.is_empty() {
self.command.pop();
if self.immediate {
if self.reactive {
match self.try_run(engine_state, stack) {
Ok(_) => info.report = Some(Report::default()),
Err(err) => info.report = Some(Report::error(format!("Error: {err}"))),
@ -203,7 +191,7 @@ impl View for InteractiveView<'_> {
KeyCode::Char(c) => {
self.command.push(*c);
if self.immediate {
if self.reactive {
match self.try_run(engine_state, stack) {
Ok(_) => info.report = Some(Report::default()),
Err(err) => info.report = Some(Report::error(format!("Error: {err}"))),
@ -246,31 +234,14 @@ impl View for InteractiveView<'_> {
}
fn setup(&mut self, config: ViewConfig<'_>) {
self.border_color = lookup_tui_color(config.style_computer, "separator");
if let Some(hm) = config.config.get("try").and_then(create_map) {
let colors = get_color_map(&hm);
if let Some(color) = colors.get("highlighted_color").copied() {
self.highlighted_color = nu_style_to_tui(color);
}
if self.border_color != Style::default() && self.highlighted_color == Style::default() {
self.highlighted_color = self.border_color;
}
if let Some(val) = hm.get("reactive").and_then(|v| v.as_bool().ok()) {
self.immediate = val;
}
}
self.border_color = nu_style_to_tui(config.explore_config.table.separator_style);
self.reactive = config.explore_config.try_reactive;
let mut r = RecordView::new(vec![], vec![]);
r.setup(config);
self.table_theme = r.get_theme().clone();
if let Some(view) = &mut self.table {
view.set_theme(self.table_theme.clone());
view.setup(config);
view.set_orientation(r.get_orientation_current());
view.set_orientation_current(r.get_orientation_current());
}

View File

@ -31,11 +31,6 @@ pub fn set_span(
text_width as u16
}
pub fn lookup_tui_color(style_computer: &StyleComputer, key: &str) -> Style {
let nu_style = style_computer.compute(key, &Value::nothing(nu_protocol::Span::unknown()));
nu_style_to_tui(nu_style)
}
pub fn nu_style_to_tui(style: NuStyle) -> ratatui::style::Style {
let mut out = ratatui::style::Style::default();
if let Some(clr) = style.background {

View File

@ -189,12 +189,7 @@ $env.config = {
warn: {}
info: {}
},
table: {
split_line: { fg: "#404040" },
selected_cell: { bg: light_blue },
selected_row: {},
selected_column: {},
},
}
history: {