Generic menus (#5085)

* updated to reedline generic menus

* help menu with examples

* generic menus in the engine

* description menu template

* list of menus in config

* default value for menu

* menu from block

* generic menus examples

* change to reedline git path

* cargo fmt

* menu name typo

* remove commas from default file

* added error message
This commit is contained in:
Fernando Herrera
2022-04-04 15:54:48 +01:00
committed by GitHub
parent a86e6ce89b
commit 608b6f3634
11 changed files with 916 additions and 344 deletions

View File

@ -13,6 +13,17 @@ pub struct ParsedKeybinding {
pub mode: Value,
}
/// Definition of a parsed menu from the config object
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct ParsedMenu {
pub name: Value,
pub marker: Value,
pub only_buffer_difference: Value,
pub style: Value,
pub menu_type: Value,
pub source: Value,
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct Config {
pub filesize_metric: bool,
@ -31,10 +42,8 @@ pub struct Config {
pub max_history_size: i64,
pub sync_history_on_enter: bool,
pub log_level: String,
pub menu_config: HashMap<String, Value>,
pub keybindings: Vec<ParsedKeybinding>,
pub history_config: HashMap<String, Value>,
pub help_config: HashMap<String, Value>,
pub menus: Vec<ParsedMenu>,
pub rm_always_trash: bool,
}
@ -57,10 +66,8 @@ impl Default for Config {
max_history_size: 1000,
sync_history_on_enter: true,
log_level: String::new(),
menu_config: HashMap::new(),
history_config: HashMap::new(),
help_config: HashMap::new(),
keybindings: Vec::new(),
menus: Vec::new(),
rm_always_trash: false,
}
}
@ -215,34 +222,20 @@ impl Value {
eprintln!("$config.log_level is not a string")
}
}
"menu_config" => {
if let Ok(map) = create_map(value, &config) {
config.menu_config = map;
} else {
eprintln!("$config.menu_config is not a record")
"menus" => match create_menus(value, &config) {
Ok(map) => config.menus = map,
Err(e) => {
eprintln!("$config.menus is not a valid list of menus");
eprintln!("{:?}", e);
}
}
"history_config" => {
if let Ok(map) = create_map(value, &config) {
config.history_config = map;
} else {
eprintln!("$config.history_config is not a record")
},
"keybindings" => match create_keybindings(value, &config) {
Ok(keybindings) => config.keybindings = keybindings,
Err(e) => {
eprintln!("$config.keybindings is not a valid keybindings list");
eprintln!("{:?}", e);
}
}
"help_config" => {
if let Ok(map) = create_map(value, &config) {
config.help_config = map;
} else {
eprintln!("$config.help_config is not a record")
}
}
"keybindings" => {
if let Ok(keybindings) = create_keybindings(value, &config) {
config.keybindings = keybindings;
} else {
eprintln!("$config.keybindings is not a valid keybindings list")
}
}
},
x => {
eprintln!("$config.{} is an unknown config setting", x)
}
@ -310,18 +303,19 @@ fn create_keybindings(value: &Value, config: &Config) -> Result<Vec<ParsedKeybin
match value {
Value::Record { cols, vals, span } => {
// Finding the modifier value in the record
let modifier = extract_value("modifier", cols, vals, span)?;
let keycode = extract_value("keycode", cols, vals, span)?;
let mode = extract_value("mode", cols, vals, span)?;
let event = extract_value("event", cols, vals, span)?;
let modifier = extract_value("modifier", cols, vals, span)?.clone();
let keycode = extract_value("keycode", cols, vals, span)?.clone();
let mode = extract_value("mode", cols, vals, span)?.clone();
let event = extract_value("event", cols, vals, span)?.clone();
let keybinding = ParsedKeybinding {
modifier: modifier.clone(),
keycode: keycode.clone(),
mode: mode.clone(),
event: event.clone(),
modifier,
keycode,
mode,
event,
};
// We return a menu to be able to do recursion on the same function
Ok(vec![keybinding])
}
Value::List { vals, .. } => {
@ -341,6 +335,49 @@ fn create_keybindings(value: &Value, config: &Config) -> Result<Vec<ParsedKeybin
}
}
// Parses the config object to extract the strings that will compose a keybinding for reedline
pub fn create_menus(value: &Value, config: &Config) -> Result<Vec<ParsedMenu>, ShellError> {
match value {
Value::Record { cols, vals, span } => {
// Finding the modifier value in the record
let name = extract_value("name", cols, vals, span)?.clone();
let marker = extract_value("marker", cols, vals, span)?.clone();
let only_buffer_difference =
extract_value("only_buffer_difference", cols, vals, span)?.clone();
let style = extract_value("style", cols, vals, span)?.clone();
let menu_type = extract_value("type", cols, vals, span)?.clone();
// Source is an optional value
let source = match extract_value("source", cols, vals, span) {
Ok(source) => source.clone(),
Err(_) => Value::Nothing { span: *span },
};
let menu = ParsedMenu {
name,
only_buffer_difference,
marker,
style,
menu_type,
source,
};
Ok(vec![menu])
}
Value::List { vals, .. } => {
let res = vals
.iter()
.map(|inner_value| create_menus(inner_value, config))
.collect::<Result<Vec<Vec<ParsedMenu>>, ShellError>>();
let res = res?.into_iter().flatten().collect::<Vec<ParsedMenu>>();
Ok(res)
}
_ => Ok(Vec::new()),
}
}
pub fn extract_value<'record>(
name: &str,
cols: &'record [String],