Lift line editor construction out of loop (#5041)

Enables the use of some features on reedline

- Keeping the line when clearing the screen with `Ctrl-L`
- Using the internal cut buffer between lines
- Submitting external commands via keybinding and keeping the line

Additional effect:

Keep the history around and do basic syncs (performance improvement
minimal as session changes have to be read and written)

Additional change:

Give the option to defer writing/rereading the history file to the
closing of the session ($config.sync_history_on_enter)
This commit is contained in:
Stefan Holderbach 2022-03-31 23:25:48 +02:00 committed by GitHub
parent 6a6471b04b
commit 0986c61a5d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 63 additions and 41 deletions

2
Cargo.lock generated
View File

@ -3428,7 +3428,7 @@ dependencies = [
[[package]] [[package]]
name = "reedline" name = "reedline"
version = "0.3.1" version = "0.3.1"
source = "git+https://github.com/nushell/reedline?branch=main#bc18ba3260604172700bf5996f66635ee428d625" source = "git+https://github.com/nushell/reedline?branch=main#0f92985c69efca2567ad40ebc0381864e91aff8c"
dependencies = [ dependencies = [
"chrono", "chrono",
"crossterm", "crossterm",

View File

@ -104,6 +104,45 @@ pub fn evaluate_repl(
}, },
); );
if is_perf_true {
info!(
"load config initially {}:{}:{}",
file!(),
line!(),
column!()
);
}
// Get the config once for the history `max_history_size`
// Updating that will not be possible in one session
let mut config = match stack.get_config() {
Ok(config) => config,
Err(e) => {
let working_set = StateWorkingSet::new(engine_state);
report_error(&working_set, &e);
Config::default()
}
};
if is_perf_true {
info!("setup reedline {}:{}:{}", file!(), line!(), column!());
}
let mut line_editor = Reedline::create().into_diagnostic()?;
if let Some(history_path) = history_path.as_deref() {
if is_perf_true {
info!("setup history {}:{}:{}", file!(), line!(), column!());
}
let history = Box::new(
FileBackedHistory::with_file(
config.max_history_size as usize,
history_path.to_path_buf(),
)
.into_diagnostic()?,
);
line_editor = line_editor.with_history(history).into_diagnostic()?;
};
loop { loop {
if is_perf_true { if is_perf_true {
info!( info!(
@ -114,7 +153,7 @@ pub fn evaluate_repl(
); );
} }
let config = match stack.get_config() { config = match stack.get_config() {
Ok(config) => config, Ok(config) => config,
Err(e) => { Err(e) => {
let working_set = StateWorkingSet::new(engine_state); let working_set = StateWorkingSet::new(engine_state);
@ -123,18 +162,14 @@ pub fn evaluate_repl(
Config::default() Config::default()
} }
}; };
let color_hm = get_color_config(&config);
//Reset the ctrl-c handler //Reset the ctrl-c handler
if let Some(ctrlc) = &mut engine_state.ctrlc { if let Some(ctrlc) = &mut engine_state.ctrlc {
ctrlc.store(false, Ordering::SeqCst); ctrlc.store(false, Ordering::SeqCst);
} }
if is_perf_true { line_editor = line_editor
info!("setup line editor {}:{}:{}", file!(), line!(), column!());
}
let mut line_editor = Reedline::create()
.into_diagnostic()?
.with_highlighter(Box::new(NuHighlighter { .with_highlighter(Box::new(NuHighlighter {
engine_state: engine_state.clone(), engine_state: engine_state.clone(),
config: config.clone(), config: config.clone(),
@ -143,6 +178,9 @@ pub fn evaluate_repl(
.with_validator(Box::new(NuValidator { .with_validator(Box::new(NuValidator {
engine_state: engine_state.clone(), engine_state: engine_state.clone(),
})) }))
.with_hinter(Box::new(
DefaultHinter::default().with_style(color_hm["hints"]),
))
.with_completer(Box::new(NuCompleter::new( .with_completer(Box::new(NuCompleter::new(
engine_state.clone(), engine_state.clone(),
stack.clone(), stack.clone(),
@ -153,7 +191,7 @@ pub fn evaluate_repl(
.with_ansi_colors(config.use_ansi_coloring); .with_ansi_colors(config.use_ansi_coloring);
if is_perf_true { if is_perf_true {
info!("setup reedline {}:{}:{}", file!(), line!(), column!()); info!("update reedline {}:{}:{}", file!(), line!(), column!());
} }
line_editor = add_completion_menu(line_editor, &config); line_editor = add_completion_menu(line_editor, &config);
@ -168,38 +206,12 @@ pub fn evaluate_repl(
//FIXME: if config.use_ansi_coloring is false then we should //FIXME: if config.use_ansi_coloring is false then we should
// turn off the hinter but I don't see any way to do that yet. // turn off the hinter but I don't see any way to do that yet.
let color_hm = get_color_config(&config); if config.sync_history_on_enter {
if is_perf_true { if is_perf_true {
info!( info!("sync history {}:{}:{}", file!(), line!(), column!());
"setup history and hinter {}:{}:{}",
file!(),
line!(),
column!()
);
} }
line_editor.sync_history().into_diagnostic()?;
line_editor = if let Some(history_path) = history_path.clone() {
let history = std::fs::read_to_string(&history_path);
if history.is_ok() {
line_editor
.with_hinter(Box::new(
DefaultHinter::default().with_style(color_hm["hints"]),
))
.with_history(Box::new(
FileBackedHistory::with_file(
config.max_history_size as usize,
history_path.clone(),
)
.into_diagnostic()?,
))
.into_diagnostic()?
} else {
line_editor
} }
} else {
line_editor
};
if is_perf_true { if is_perf_true {
info!("setup keybindings {}:{}:{}", file!(), line!(), column!()); info!("setup keybindings {}:{}:{}", file!(), line!(), column!());

View File

@ -29,6 +29,7 @@ pub struct Config {
pub partial_completions: bool, pub partial_completions: bool,
pub edit_mode: String, pub edit_mode: String,
pub max_history_size: i64, pub max_history_size: i64,
pub sync_history_on_enter: bool,
pub log_level: String, pub log_level: String,
pub menu_config: HashMap<String, Value>, pub menu_config: HashMap<String, Value>,
pub keybindings: Vec<ParsedKeybinding>, pub keybindings: Vec<ParsedKeybinding>,
@ -54,6 +55,7 @@ impl Default for Config {
partial_completions: true, partial_completions: true,
edit_mode: "emacs".into(), edit_mode: "emacs".into(),
max_history_size: 1000, max_history_size: 1000,
sync_history_on_enter: true,
log_level: String::new(), log_level: String::new(),
menu_config: HashMap::new(), menu_config: HashMap::new(),
history_config: HashMap::new(), history_config: HashMap::new(),
@ -199,6 +201,13 @@ impl Value {
eprintln!("$config.max_history_size is not an integer") eprintln!("$config.max_history_size is not an integer")
} }
} }
"sync_history_on_enter" => {
if let Ok(b) = value.as_bool() {
config.sync_history_on_enter = b;
} else {
eprintln!("$config.sync_history_on_enter is not a bool")
}
}
"log_level" => { "log_level" => {
if let Ok(v) = value.as_string() { if let Ok(v) = value.as_string() {
config.log_level = v.to_lowercase(); config.log_level = v.to_lowercase();

View File

@ -196,7 +196,8 @@ let $config = {
use_ansi_coloring: true use_ansi_coloring: true
filesize_format: "auto" # b, kb, kib, mb, mib, gb, gib, tb, tib, pb, pib, eb, eib, zb, zib, auto filesize_format: "auto" # b, kb, kib, mb, mib, gb, gib, tb, tib, pb, pib, eb, eib, zb, zib, auto
edit_mode: emacs # emacs, vi edit_mode: emacs # emacs, vi
max_history_size: 10000 max_history_size: 10000 # Session has to be reloaded for this to take effect
sync_history_on_enter: true # Enable to share the history between multiple sessions, else you have to close the session to persist history to file
menu_config: { menu_config: {
columns: 4 columns: 4
col_width: 20 # Optional value. If missing all the screen width is used to calculate column width col_width: 20 # Optional value. If missing all the screen width is used to calculate column width