explore: adopt anyhow, support CustomValue, remove help system (#12692)

This PR:
1. Adds basic support for `CustomValue` to `explore`. Previously `open
foo.db | explore` didn't really work, now we "materialize" the whole
database to a `Value` before loading it
2. Adopts `anyhow` for error handling in `explore`. Previously we were
kind of rolling our own version of `anyhow` by shoving all errors into a
`std::io::Error`; I think this is much nicer. This was necessary because
as part of 1), collecting input is now fallible...
3. Removes a lot of `explore`'s fancy command help system.
- Previously each command (`:help`, `:try`, etc.) had a sophisticated
help system with examples etc... but this was not very visible to users.
You had to know to run `:help :try` or view a list of commands with
`:help :`
- As discussed previously, we eventually want to move to a less modal
approach for `explore`, without the Vim-like commands. And so I don't
think it's worth keeping this command help system around (it's
intertwined with other stuff, and making these changes would have been
harder if keeping it).
4. Rename the `--reverse` flag to `--tail`. The flag scrolls to the end
of the data, which IMO is described better by "tail"
5. Does some renaming+commenting to clear up things I found difficult to
understand when navigating the `explore` code


I initially thought 1) would be just a few lines, and then this PR blew
up into much more extensive changes 😅


## Before
The whole database was being displayed as a single Nuon/JSON line 🤔 

![image](https://github.com/nushell/nushell/assets/26268125/6383f43b-fdff-48b4-9604-398438ad1499)


## After
The database gets displayed like a record

![image](https://github.com/nushell/nushell/assets/26268125/2f00ed7b-a3c4-47f4-a08c-98d07efc7bb4)


## Future work

It is sort of annoying that we have to load a whole SQLite database into
memory to make this work; it will be impractical for large databases.
I'd like to explore improvements to `CustomValue` that can make this
work more efficiently.
This commit is contained in:
Reilly Wood
2024-05-01 15:34:37 -07:00
committed by GitHub
parent bc18cc12d5
commit 3d340657b5
21 changed files with 283 additions and 842 deletions

View File

@ -6,20 +6,19 @@ mod pager;
mod registry;
mod views;
use anyhow::Result;
use commands::{ExpandCmd, HelpCmd, NuCmd, QuitCmd, TableCmd, TryCmd};
pub use default_context::add_explore_context;
pub use explore::Explore;
use commands::{ExpandCmd, HelpCmd, HelpManual, NuCmd, QuitCmd, TableCmd, TryCmd};
use nu_common::{collect_pipeline, has_simple_value, CtrlC};
use nu_protocol::{
engine::{EngineState, Stack},
PipelineData, Value,
};
use pager::{Page, Pager, PagerConfig, StyleConfig};
use registry::{Command, CommandRegistry};
use std::io;
use registry::CommandRegistry;
use terminal_size::{Height, Width};
use views::{BinaryView, InformationView, Orientation, Preview, RecordView};
use views::{BinaryView, Orientation, Preview, RecordView};
mod util {
pub use super::nu_common::{create_lscolors, create_map, map_into_value};
@ -31,7 +30,7 @@ fn run_pager(
ctrlc: CtrlC,
input: PipelineData,
config: PagerConfig,
) -> io::Result<Option<Value>> {
) -> Result<Option<Value>> {
let mut p = Pager::new(config.clone());
let commands = create_command_registry();
@ -45,18 +44,18 @@ fn run_pager(
return p.run(engine_state, stack, ctrlc, view, commands);
}
let (columns, data) = collect_pipeline(input);
let (columns, data) = collect_pipeline(input)?;
let has_no_input = columns.is_empty() && data.is_empty();
if has_no_input {
return p.run(engine_state, stack, ctrlc, information_view(), commands);
return p.run(engine_state, stack, ctrlc, help_view(), commands);
}
p.show_message("For help type :help");
if let Some(value) = has_simple_value(&data) {
let text = value.to_abbreviated_string(config.nu_config);
let view = Some(Page::new(Preview::new(&text), true));
let view = Some(Page::new(Preview::new(&text), false));
return p.run(engine_state, stack, ctrlc, view, commands);
}
@ -67,6 +66,7 @@ fn run_pager(
fn create_record_view(
columns: Vec<String>,
data: Vec<Vec<Value>>,
// wait, why would we use RecordView for something that isn't a record?
is_record: bool,
config: PagerConfig,
) -> Option<Page> {
@ -75,17 +75,17 @@ fn create_record_view(
view.set_orientation_current(Orientation::Left);
}
if config.reverse {
if config.tail {
if let Some((Width(w), Height(h))) = terminal_size::terminal_size() {
view.reverse(w, h);
view.tail(w, h);
}
}
Some(Page::new(view, false))
Some(Page::new(view, true))
}
fn information_view() -> Option<Page> {
Some(Page::new(InformationView, true))
fn help_view() -> Option<Page> {
Some(Page::new(HelpCmd::view(), false))
}
fn binary_view(input: PipelineData) -> Option<Page> {
@ -96,7 +96,7 @@ fn binary_view(input: PipelineData) -> Option<Page> {
let view = BinaryView::new(data);
Some(Page::new(view, false))
Some(Page::new(view, true))
}
fn create_command_registry() -> CommandRegistry {
@ -104,24 +104,16 @@ fn create_command_registry() -> CommandRegistry {
create_commands(&mut registry);
create_aliases(&mut registry);
// reregister help && config commands
let commands = registry.get_commands().cloned().collect::<Vec<_>>();
let aliases = registry.get_aliases().collect::<Vec<_>>();
let help_cmd = create_help_command(&commands, &aliases);
registry.register_command_view(help_cmd, true);
registry
}
fn create_commands(registry: &mut CommandRegistry) {
registry.register_command_view(NuCmd::new(), false);
registry.register_command_view(TableCmd::new(), false);
registry.register_command_view(NuCmd::new(), true);
registry.register_command_view(TableCmd::new(), true);
registry.register_command_view(ExpandCmd::new(), true);
registry.register_command_view(TryCmd::new(), true);
registry.register_command_view(HelpCmd::default(), true);
registry.register_command_view(ExpandCmd::new(), false);
registry.register_command_view(TryCmd::new(), false);
registry.register_command_view(HelpCmd::default(), false);
registry.register_command_reactive(QuitCmd);
}
@ -132,34 +124,3 @@ fn create_aliases(registry: &mut CommandRegistry) {
registry.create_aliases("q", QuitCmd::NAME);
registry.create_aliases("q!", QuitCmd::NAME);
}
fn create_help_command(commands: &[Command], aliases: &[(&str, &str)]) -> HelpCmd {
let help_manuals = create_help_manuals(commands);
HelpCmd::new(help_manuals, aliases)
}
fn create_help_manuals(cmd_list: &[Command]) -> Vec<HelpManual> {
cmd_list.iter().map(create_help_manual).collect()
}
fn create_help_manual(cmd: &Command) -> HelpManual {
let name = match cmd {
Command::Reactive(cmd) => cmd.name(),
Command::View { cmd, .. } => cmd.name(),
};
let manual = match cmd {
Command::Reactive(cmd) => cmd.help(),
Command::View { cmd, .. } => cmd.help(),
};
__create_help_manual(manual, name)
}
fn __create_help_manual(manual: Option<HelpManual>, name: &'static str) -> HelpManual {
manual.unwrap_or(HelpManual {
name,
..HelpManual::default()
})
}