general keybindings (#775)
* general keybindings * get value function * check error for keybinding * cmd and send for keybingins * better error message
This commit is contained in:
parent
73ad862042
commit
6514a30b5d
|
@ -220,7 +220,7 @@ pub fn action(
|
||||||
},
|
},
|
||||||
|
|
||||||
Value::Filesize { val: _, .. } => Value::String {
|
Value::Filesize { val: _, .. } => Value::String {
|
||||||
val: input.clone().into_string(", ", config),
|
val: input.into_string(", ", config),
|
||||||
span,
|
span,
|
||||||
},
|
},
|
||||||
Value::Nothing { .. } => Value::String {
|
Value::Nothing { .. } => Value::String {
|
||||||
|
|
|
@ -207,7 +207,7 @@ pub fn group_by(values: PipelineData, head: Span, config: &Config) -> (PipelineD
|
||||||
.or_insert_with(|| vec![val.clone()]);
|
.or_insert_with(|| vec![val.clone()]);
|
||||||
} else {
|
} else {
|
||||||
lists
|
lists
|
||||||
.entry(val.clone().into_string(",", config))
|
.entry(val.into_string(",", config))
|
||||||
.and_modify(|v: &mut Vec<Value>| v.push(val.clone()))
|
.and_modify(|v: &mut Vec<Value>| v.push(val.clone()))
|
||||||
.or_insert_with(|| vec![val.clone()]);
|
.or_insert_with(|| vec![val.clone()]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::{BlockId, ShellError, Span, Spanned, Value};
|
use crate::{BlockId, ShellError, Span, Value};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
@ -41,55 +41,10 @@ impl EnvConversion {
|
||||||
/// Definition of a parsed keybinding from the config object
|
/// Definition of a parsed keybinding from the config object
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
pub struct ParsedKeybinding {
|
pub struct ParsedKeybinding {
|
||||||
pub modifier: Spanned<String>,
|
pub modifier: Value,
|
||||||
pub keycode: Spanned<String>,
|
pub keycode: Value,
|
||||||
pub event: Spanned<EventType>,
|
pub event: Value,
|
||||||
pub mode: Spanned<EventMode>,
|
pub mode: Value,
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for ParsedKeybinding {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
modifier: Spanned {
|
|
||||||
item: "".to_string(),
|
|
||||||
span: Span { start: 0, end: 0 },
|
|
||||||
},
|
|
||||||
keycode: Spanned {
|
|
||||||
item: "".to_string(),
|
|
||||||
span: Span { start: 0, end: 0 },
|
|
||||||
},
|
|
||||||
event: Spanned {
|
|
||||||
item: EventType::Single("".to_string()),
|
|
||||||
span: Span { start: 0, end: 0 },
|
|
||||||
},
|
|
||||||
mode: Spanned {
|
|
||||||
item: EventMode::Emacs,
|
|
||||||
span: Span { start: 0, end: 0 },
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
|
||||||
pub enum EventType {
|
|
||||||
Single(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
|
||||||
pub enum EventMode {
|
|
||||||
Emacs,
|
|
||||||
ViNormal,
|
|
||||||
ViInsert,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl EventMode {
|
|
||||||
pub fn as_str(&self) -> &'static str {
|
|
||||||
match self {
|
|
||||||
EventMode::Emacs => "emacs",
|
|
||||||
EventMode::ViNormal => "vi_normal",
|
|
||||||
EventMode::ViInsert => "vi_insert",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
|
@ -265,10 +220,7 @@ fn color_value_string(
|
||||||
let val: String = inner_cols
|
let val: String = inner_cols
|
||||||
.iter()
|
.iter()
|
||||||
.zip(inner_vals)
|
.zip(inner_vals)
|
||||||
.map(|(x, y)| {
|
.map(|(x, y)| format!("{}: \"{}\" ", x, y.into_string(", ", config)))
|
||||||
let clony = y.clone();
|
|
||||||
format!("{}: \"{}\" ", x, clony.into_string(", ", config))
|
|
||||||
})
|
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
// now insert the braces at the front and the back to fake the json string
|
// now insert the braces at the front and the back to fake the json string
|
||||||
|
@ -281,107 +233,19 @@ fn color_value_string(
|
||||||
// Parses the config object to extract the strings that will compose a keybinding for reedline
|
// Parses the config object to extract the strings that will compose a keybinding for reedline
|
||||||
fn create_keybindings(value: &Value, config: &Config) -> Result<Vec<ParsedKeybinding>, ShellError> {
|
fn create_keybindings(value: &Value, config: &Config) -> Result<Vec<ParsedKeybinding>, ShellError> {
|
||||||
match value {
|
match value {
|
||||||
Value::Record { cols, vals, .. } => {
|
Value::Record { cols, vals, span } => {
|
||||||
let mut keybinding = ParsedKeybinding::default();
|
// 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)?;
|
||||||
|
|
||||||
for (col, val) in cols.iter().zip(vals.iter()) {
|
let keybinding = ParsedKeybinding {
|
||||||
match col.as_str() {
|
modifier: modifier.clone(),
|
||||||
"modifier" => {
|
keycode: keycode.clone(),
|
||||||
keybinding.modifier = Spanned {
|
mode: mode.clone(),
|
||||||
item: val.clone().into_string("", config),
|
event: event.clone(),
|
||||||
span: val.span()?,
|
};
|
||||||
}
|
|
||||||
}
|
|
||||||
"keycode" => {
|
|
||||||
keybinding.keycode = Spanned {
|
|
||||||
item: val.clone().into_string("", config),
|
|
||||||
span: val.span()?,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"mode" => {
|
|
||||||
keybinding.mode = match val.clone().into_string("", config).as_str() {
|
|
||||||
"emacs" => Spanned {
|
|
||||||
item: EventMode::Emacs,
|
|
||||||
span: val.span()?,
|
|
||||||
},
|
|
||||||
"vi_normal" => Spanned {
|
|
||||||
item: EventMode::ViNormal,
|
|
||||||
span: val.span()?,
|
|
||||||
},
|
|
||||||
"vi_insert" => Spanned {
|
|
||||||
item: EventMode::ViInsert,
|
|
||||||
span: val.span()?,
|
|
||||||
},
|
|
||||||
e => {
|
|
||||||
return Err(ShellError::UnsupportedConfigValue(
|
|
||||||
"emacs or vi".to_string(),
|
|
||||||
e.to_string(),
|
|
||||||
val.span()?,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
"event" => match val {
|
|
||||||
Value::Record {
|
|
||||||
cols: event_cols,
|
|
||||||
vals: event_vals,
|
|
||||||
span: event_span,
|
|
||||||
} => {
|
|
||||||
let event_type_idx = event_cols
|
|
||||||
.iter()
|
|
||||||
.position(|key| key == "type")
|
|
||||||
.ok_or_else(|| {
|
|
||||||
ShellError::MissingConfigValue("type".to_string(), *event_span)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let event_idx = event_cols
|
|
||||||
.iter()
|
|
||||||
.position(|key| key == "event")
|
|
||||||
.ok_or_else(|| {
|
|
||||||
ShellError::MissingConfigValue("event".to_string(), *event_span)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let event_type =
|
|
||||||
event_vals[event_type_idx].clone().into_string("", config);
|
|
||||||
|
|
||||||
// Extracting the event type information from the record based on the type
|
|
||||||
match event_type.as_str() {
|
|
||||||
"single" => {
|
|
||||||
let event_value =
|
|
||||||
event_vals[event_idx].clone().into_string("", config);
|
|
||||||
|
|
||||||
keybinding.event = Spanned {
|
|
||||||
item: EventType::Single(event_value),
|
|
||||||
span: *event_span,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
e => {
|
|
||||||
return Err(ShellError::UnsupportedConfigValue(
|
|
||||||
"single".to_string(),
|
|
||||||
e.to_string(),
|
|
||||||
*event_span,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
e => {
|
|
||||||
return Err(ShellError::UnsupportedConfigValue(
|
|
||||||
"record type".to_string(),
|
|
||||||
format!("{:?}", e.get_type()),
|
|
||||||
e.span()?,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"name" => {} // don't need to store name
|
|
||||||
e => {
|
|
||||||
return Err(ShellError::UnsupportedConfigValue(
|
|
||||||
"name, mode, modifier, keycode or event".to_string(),
|
|
||||||
e.to_string(),
|
|
||||||
val.span()?,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(vec![keybinding])
|
Ok(vec![keybinding])
|
||||||
}
|
}
|
||||||
|
@ -401,3 +265,15 @@ fn create_keybindings(value: &Value, config: &Config) -> Result<Vec<ParsedKeybin
|
||||||
_ => Ok(Vec::new()),
|
_ => Ok(Vec::new()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn extract_value<'record>(
|
||||||
|
name: &str,
|
||||||
|
cols: &'record [String],
|
||||||
|
vals: &'record [Value],
|
||||||
|
span: &Span,
|
||||||
|
) -> Result<&'record Value, ShellError> {
|
||||||
|
cols.iter()
|
||||||
|
.position(|col| col.as_str() == name)
|
||||||
|
.and_then(|index| vals.get(index))
|
||||||
|
.ok_or_else(|| ShellError::MissingConfigValue(name.to_string(), *span))
|
||||||
|
}
|
||||||
|
|
|
@ -373,14 +373,14 @@ impl Value {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert Value into string. Note that Streams will be consumed.
|
/// Convert Value into string. Note that Streams will be consumed.
|
||||||
pub fn into_string(self, separator: &str, config: &Config) -> String {
|
pub fn into_string(&self, separator: &str, config: &Config) -> String {
|
||||||
match self {
|
match self {
|
||||||
Value::Bool { val, .. } => val.to_string(),
|
Value::Bool { val, .. } => val.to_string(),
|
||||||
Value::Int { val, .. } => val.to_string(),
|
Value::Int { val, .. } => val.to_string(),
|
||||||
Value::Float { val, .. } => val.to_string(),
|
Value::Float { val, .. } => val.to_string(),
|
||||||
Value::Filesize { val, .. } => format_filesize(val, config),
|
Value::Filesize { val, .. } => format_filesize(*val, config),
|
||||||
Value::Duration { val, .. } => format_duration(val),
|
Value::Duration { val, .. } => format_duration(*val),
|
||||||
Value::Date { val, .. } => HumanTime::from(val).to_string(),
|
Value::Date { val, .. } => HumanTime::from(*val).to_string(),
|
||||||
Value::Range { val, .. } => {
|
Value::Range { val, .. } => {
|
||||||
format!(
|
format!(
|
||||||
"{}..{}",
|
"{}..{}",
|
||||||
|
@ -388,10 +388,10 @@ impl Value {
|
||||||
val.to.into_string(", ", config)
|
val.to.into_string(", ", config)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Value::String { val, .. } => val,
|
Value::String { val, .. } => val.clone(),
|
||||||
Value::List { vals: val, .. } => format!(
|
Value::List { vals: val, .. } => format!(
|
||||||
"[{}]",
|
"[{}]",
|
||||||
val.into_iter()
|
val.iter()
|
||||||
.map(|x| x.into_string(", ", config))
|
.map(|x| x.into_string(", ", config))
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join(separator)
|
.join(separator)
|
||||||
|
@ -400,7 +400,7 @@ impl Value {
|
||||||
"{{{}}}",
|
"{{{}}}",
|
||||||
cols.iter()
|
cols.iter()
|
||||||
.zip(vals.iter())
|
.zip(vals.iter())
|
||||||
.map(|(x, y)| format!("{}: {}", x, y.clone().into_string(", ", config)))
|
.map(|(x, y)| format!("{}: {}", x, y.into_string(", ", config)))
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join(separator)
|
.join(separator)
|
||||||
),
|
),
|
||||||
|
@ -425,8 +425,8 @@ impl Value {
|
||||||
Value::Range { val, .. } => {
|
Value::Range { val, .. } => {
|
||||||
format!(
|
format!(
|
||||||
"{}..{}",
|
"{}..{}",
|
||||||
val.from.clone().into_string(", ", config),
|
val.from.into_string(", ", config),
|
||||||
val.to.clone().into_string(", ", config)
|
val.to.into_string(", ", config)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Value::String { val, .. } => val.to_string(),
|
Value::String { val, .. } => val.to_string(),
|
||||||
|
@ -457,18 +457,18 @@ impl Value {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert Value into a debug string
|
/// Convert Value into a debug string
|
||||||
pub fn debug_value(self) -> String {
|
pub fn debug_value(&self) -> String {
|
||||||
format!("{:#?}", self)
|
format!("{:#?}", self)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert Value into string. Note that Streams will be consumed.
|
/// Convert Value into string. Note that Streams will be consumed.
|
||||||
pub fn debug_string(self, separator: &str, config: &Config) -> String {
|
pub fn debug_string(&self, separator: &str, config: &Config) -> String {
|
||||||
match self {
|
match self {
|
||||||
Value::Bool { val, .. } => val.to_string(),
|
Value::Bool { val, .. } => val.to_string(),
|
||||||
Value::Int { val, .. } => val.to_string(),
|
Value::Int { val, .. } => val.to_string(),
|
||||||
Value::Float { val, .. } => val.to_string(),
|
Value::Float { val, .. } => val.to_string(),
|
||||||
Value::Filesize { val, .. } => format_filesize(val, config),
|
Value::Filesize { val, .. } => format_filesize(*val, config),
|
||||||
Value::Duration { val, .. } => format_duration(val),
|
Value::Duration { val, .. } => format_duration(*val),
|
||||||
Value::Date { val, .. } => format!("{:?}", val),
|
Value::Date { val, .. } => format!("{:?}", val),
|
||||||
Value::Range { val, .. } => {
|
Value::Range { val, .. } => {
|
||||||
format!(
|
format!(
|
||||||
|
@ -477,10 +477,10 @@ impl Value {
|
||||||
val.to.into_string(", ", config)
|
val.to.into_string(", ", config)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Value::String { val, .. } => val,
|
Value::String { val, .. } => val.clone(),
|
||||||
Value::List { vals: val, .. } => format!(
|
Value::List { vals: val, .. } => format!(
|
||||||
"[{}]",
|
"[{}]",
|
||||||
val.into_iter()
|
val.iter()
|
||||||
.map(|x| x.into_string(", ", config))
|
.map(|x| x.into_string(", ", config))
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join(separator)
|
.join(separator)
|
||||||
|
@ -489,7 +489,7 @@ impl Value {
|
||||||
"{{{}}}",
|
"{{{}}}",
|
||||||
cols.iter()
|
cols.iter()
|
||||||
.zip(vals.iter())
|
.zip(vals.iter())
|
||||||
.map(|(x, y)| format!("{}: {}", x, y.clone().into_string(", ", config)))
|
.map(|(x, y)| format!("{}: {}", x, y.into_string(", ", config)))
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join(separator)
|
.join(separator)
|
||||||
),
|
),
|
||||||
|
@ -503,7 +503,7 @@ impl Value {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if the content is empty
|
/// Check if the content is empty
|
||||||
pub fn is_empty(self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Value::String { val, .. } => val.is_empty(),
|
Value::String { val, .. } => val.is_empty(),
|
||||||
Value::List { vals, .. } => {
|
Value::List { vals, .. } => {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crossterm::event::{KeyCode, KeyModifiers};
|
use crossterm::event::{KeyCode, KeyModifiers};
|
||||||
use nu_color_config::lookup_ansi_color_style;
|
use nu_color_config::lookup_ansi_color_style;
|
||||||
use nu_protocol::{Config, EventType, ParsedKeybinding, ShellError};
|
use nu_protocol::{extract_value, Config, ParsedKeybinding, ShellError, Span, Value};
|
||||||
use reedline::{
|
use reedline::{
|
||||||
default_emacs_keybindings, default_vi_insert_keybindings, default_vi_normal_keybindings,
|
default_emacs_keybindings, default_vi_insert_keybindings, default_vi_normal_keybindings,
|
||||||
ContextMenuInput, EditCommand, Keybindings, ReedlineEvent,
|
ContextMenuInput, EditCommand, Keybindings, ReedlineEvent,
|
||||||
|
@ -82,8 +82,8 @@ pub(crate) fn create_keybindings(config: &Config) -> Result<KeybindingsMode, She
|
||||||
);
|
);
|
||||||
|
|
||||||
for parsed_keybinding in parsed_keybindings {
|
for parsed_keybinding in parsed_keybindings {
|
||||||
if parsed_keybinding.mode.item.as_str() == "emacs" {
|
if parsed_keybinding.mode.into_string("", config).as_str() == "emacs" {
|
||||||
add_keybinding(&mut keybindings, parsed_keybinding)?
|
add_keybinding(&mut keybindings, parsed_keybinding, config)?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,10 +94,10 @@ pub(crate) fn create_keybindings(config: &Config) -> Result<KeybindingsMode, She
|
||||||
let mut normal_keybindings = default_vi_normal_keybindings();
|
let mut normal_keybindings = default_vi_normal_keybindings();
|
||||||
|
|
||||||
for parsed_keybinding in parsed_keybindings {
|
for parsed_keybinding in parsed_keybindings {
|
||||||
if parsed_keybinding.mode.item.as_str() == "vi_insert" {
|
if parsed_keybinding.mode.into_string("", config).as_str() == "vi_insert" {
|
||||||
add_keybinding(&mut insert_keybindings, parsed_keybinding)?
|
add_keybinding(&mut insert_keybindings, parsed_keybinding, config)?
|
||||||
} else if parsed_keybinding.mode.item.as_str() == "vi_normal" {
|
} else if parsed_keybinding.mode.into_string("", config).as_str() == "vi_normal" {
|
||||||
add_keybinding(&mut normal_keybindings, parsed_keybinding)?
|
add_keybinding(&mut normal_keybindings, parsed_keybinding, config)?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -111,27 +111,35 @@ pub(crate) fn create_keybindings(config: &Config) -> Result<KeybindingsMode, She
|
||||||
|
|
||||||
fn add_keybinding(
|
fn add_keybinding(
|
||||||
keybindings: &mut Keybindings,
|
keybindings: &mut Keybindings,
|
||||||
parsed_keybinding: &ParsedKeybinding,
|
keybinding: &ParsedKeybinding,
|
||||||
|
config: &Config,
|
||||||
) -> Result<(), ShellError> {
|
) -> Result<(), ShellError> {
|
||||||
let modifier = match parsed_keybinding.modifier.item.as_str() {
|
let modifier = match keybinding.modifier.into_string("", config).as_str() {
|
||||||
"CONTROL" => KeyModifiers::CONTROL,
|
"CONTROL" => KeyModifiers::CONTROL,
|
||||||
"SHIFT" => KeyModifiers::SHIFT,
|
"SHIFT" => KeyModifiers::SHIFT,
|
||||||
"ALT" => KeyModifiers::ALT,
|
"ALT" => KeyModifiers::ALT,
|
||||||
"NONE" => KeyModifiers::NONE,
|
"NONE" => KeyModifiers::NONE,
|
||||||
"CONTROL | ALT" => KeyModifiers::CONTROL | KeyModifiers::ALT,
|
"CONTROL | ALT" => KeyModifiers::CONTROL | KeyModifiers::ALT,
|
||||||
|
"CONTROL | ALT | SHIFT" => KeyModifiers::CONTROL | KeyModifiers::ALT | KeyModifiers::SHIFT,
|
||||||
_ => {
|
_ => {
|
||||||
return Err(ShellError::UnsupportedConfigValue(
|
return Err(ShellError::UnsupportedConfigValue(
|
||||||
|
keybinding.modifier.into_abbreviated_string(config),
|
||||||
"CONTROL, SHIFT, ALT or NONE".to_string(),
|
"CONTROL, SHIFT, ALT or NONE".to_string(),
|
||||||
parsed_keybinding.modifier.item.clone(),
|
keybinding.modifier.span()?,
|
||||||
parsed_keybinding.modifier.span,
|
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let keycode = match parsed_keybinding.keycode.item.as_str() {
|
let keycode = match keybinding.keycode.into_string("", config).as_str() {
|
||||||
c if c.starts_with("Char_") => {
|
c if c.starts_with("Char_") => {
|
||||||
let char = c.replace("Char_", "");
|
let char = c.replace("Char_", "");
|
||||||
let char = char.chars().next().expect("correct");
|
let char = char.chars().next().ok_or({
|
||||||
|
ShellError::UnsupportedConfigValue(
|
||||||
|
c.to_string(),
|
||||||
|
"Char_ plus char".to_string(),
|
||||||
|
keybinding.keycode.span()?,
|
||||||
|
)
|
||||||
|
})?;
|
||||||
KeyCode::Char(char)
|
KeyCode::Char(char)
|
||||||
}
|
}
|
||||||
"down" => KeyCode::Down,
|
"down" => KeyCode::Down,
|
||||||
|
@ -142,33 +150,202 @@ fn add_keybinding(
|
||||||
"BackTab" => KeyCode::BackTab,
|
"BackTab" => KeyCode::BackTab,
|
||||||
_ => {
|
_ => {
|
||||||
return Err(ShellError::UnsupportedConfigValue(
|
return Err(ShellError::UnsupportedConfigValue(
|
||||||
|
keybinding.keycode.into_abbreviated_string(config),
|
||||||
"crossterm KeyCode".to_string(),
|
"crossterm KeyCode".to_string(),
|
||||||
parsed_keybinding.keycode.item.clone(),
|
keybinding.keycode.span()?,
|
||||||
parsed_keybinding.keycode.span,
|
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let event = match &parsed_keybinding.event.item {
|
let event = parse_event(keybinding.event.clone(), config)?;
|
||||||
EventType::Single(name) => match name.as_str() {
|
|
||||||
"ActionHandler" => ReedlineEvent::ActionHandler,
|
|
||||||
"Complete" => ReedlineEvent::Complete,
|
|
||||||
"ContextMenu" => ReedlineEvent::ContextMenu,
|
|
||||||
"NextElement" => ReedlineEvent::NextElement,
|
|
||||||
"NextHistory" => ReedlineEvent::NextHistory,
|
|
||||||
"PreviousElement" => ReedlineEvent::PreviousElement,
|
|
||||||
"PreviousHistory" => ReedlineEvent::PreviousHistory,
|
|
||||||
_ => {
|
|
||||||
return Err(ShellError::UnsupportedConfigValue(
|
|
||||||
"crossterm EventType".to_string(),
|
|
||||||
name.clone(),
|
|
||||||
parsed_keybinding.event.span,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
keybindings.add_binding(modifier, keycode, event);
|
keybindings.add_binding(modifier, keycode, event);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_event(value: Value, config: &Config) -> Result<ReedlineEvent, ShellError> {
|
||||||
|
match value {
|
||||||
|
Value::Record { cols, vals, span } => {
|
||||||
|
let event = match extract_value("send", &cols, &vals, &span) {
|
||||||
|
Ok(event) => match event.into_string("", config).as_str() {
|
||||||
|
"ActionHandler" => ReedlineEvent::ActionHandler,
|
||||||
|
"ClearScreen" => ReedlineEvent::ClearScreen,
|
||||||
|
"ContextMenu" => ReedlineEvent::ContextMenu,
|
||||||
|
"Complete" => ReedlineEvent::Complete,
|
||||||
|
"Enter" => ReedlineEvent::Enter,
|
||||||
|
"Esc" => ReedlineEvent::Esc,
|
||||||
|
"Up" => ReedlineEvent::Up,
|
||||||
|
"Down" => ReedlineEvent::Down,
|
||||||
|
"Right" => ReedlineEvent::Right,
|
||||||
|
"Left" => ReedlineEvent::Left,
|
||||||
|
"NextElement" => ReedlineEvent::NextElement,
|
||||||
|
"NextHistory" => ReedlineEvent::NextHistory,
|
||||||
|
"PreviousElement" => ReedlineEvent::PreviousElement,
|
||||||
|
"PreviousHistory" => ReedlineEvent::PreviousHistory,
|
||||||
|
"SearchHistory" => ReedlineEvent::SearchHistory,
|
||||||
|
"Repaint" => ReedlineEvent::Repaint,
|
||||||
|
"Edit" => {
|
||||||
|
let edit = extract_value("edit", &cols, &vals, &span)?;
|
||||||
|
let edit = parse_edit(edit, config)?;
|
||||||
|
|
||||||
|
ReedlineEvent::Edit(vec![edit])
|
||||||
|
}
|
||||||
|
v => {
|
||||||
|
return Err(ShellError::UnsupportedConfigValue(
|
||||||
|
v.to_string(),
|
||||||
|
"Reedline event".to_string(),
|
||||||
|
span,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(_) => {
|
||||||
|
let edit = extract_value("edit", &cols, &vals, &span);
|
||||||
|
let edit = match edit {
|
||||||
|
Ok(edit_value) => parse_edit(edit_value, config)?,
|
||||||
|
Err(_) => {
|
||||||
|
return Err(ShellError::MissingConfigValue(
|
||||||
|
"send or edit".to_string(),
|
||||||
|
span,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ReedlineEvent::Edit(vec![edit])
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(event)
|
||||||
|
}
|
||||||
|
Value::List { vals, .. } => {
|
||||||
|
let events = vals
|
||||||
|
.into_iter()
|
||||||
|
.map(|value| parse_event(value, config))
|
||||||
|
.collect::<Result<Vec<ReedlineEvent>, ShellError>>()?;
|
||||||
|
|
||||||
|
Ok(ReedlineEvent::Multiple(events))
|
||||||
|
}
|
||||||
|
v => Err(ShellError::UnsupportedConfigValue(
|
||||||
|
v.into_abbreviated_string(config),
|
||||||
|
"record or list of records".to_string(),
|
||||||
|
v.span()?,
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_edit(edit: &Value, config: &Config) -> Result<EditCommand, ShellError> {
|
||||||
|
let edit = match edit {
|
||||||
|
Value::Record {
|
||||||
|
cols: edit_cols,
|
||||||
|
vals: edit_vals,
|
||||||
|
span: edit_span,
|
||||||
|
} => {
|
||||||
|
let cmd = extract_value("cmd", edit_cols, edit_vals, edit_span)?;
|
||||||
|
|
||||||
|
match cmd.into_string("", config).as_str() {
|
||||||
|
"MoveToStart" => EditCommand::MoveToStart,
|
||||||
|
"MoveToLineStart" => EditCommand::MoveToLineStart,
|
||||||
|
"MoveToEnd" => EditCommand::MoveToEnd,
|
||||||
|
"MoveToLineEnd" => EditCommand::MoveToLineEnd,
|
||||||
|
"MoveLeft" => EditCommand::MoveLeft,
|
||||||
|
"MoveRight" => EditCommand::MoveRight,
|
||||||
|
"MoveWordLeft" => EditCommand::MoveWordLeft,
|
||||||
|
"MoveWordRight" => EditCommand::MoveWordRight,
|
||||||
|
"InsertChar" => {
|
||||||
|
let char = extract_char("value", edit_cols, edit_vals, config, edit_span)?;
|
||||||
|
EditCommand::InsertChar(char)
|
||||||
|
}
|
||||||
|
"InsertString" => {
|
||||||
|
let value = extract_value("value", edit_cols, edit_vals, edit_span)?;
|
||||||
|
EditCommand::InsertString(value.into_string("", config))
|
||||||
|
}
|
||||||
|
"Backspace" => EditCommand::Backspace,
|
||||||
|
"Delete" => EditCommand::Delete,
|
||||||
|
"BackspaceWord" => EditCommand::BackspaceWord,
|
||||||
|
"DeleteWord" => EditCommand::DeleteWord,
|
||||||
|
"Clear" => EditCommand::Clear,
|
||||||
|
"ClearToLineEnd" => EditCommand::ClearToLineEnd,
|
||||||
|
"CutCurrentLine" => EditCommand::CutCurrentLine,
|
||||||
|
"CutFromStart" => EditCommand::CutFromStart,
|
||||||
|
"CutFromLineStart" => EditCommand::CutFromLineStart,
|
||||||
|
"CutToEnd" => EditCommand::CutToEnd,
|
||||||
|
"CutToLineEnd" => EditCommand::CutToLineEnd,
|
||||||
|
"CutWordLeft" => EditCommand::CutWordLeft,
|
||||||
|
"CutWordRight" => EditCommand::CutWordRight,
|
||||||
|
"PasteCutBufferBefore" => EditCommand::PasteCutBufferBefore,
|
||||||
|
"PasteCutBufferAfter" => EditCommand::PasteCutBufferAfter,
|
||||||
|
"UppercaseWord" => EditCommand::UppercaseWord,
|
||||||
|
"LowercaseWord" => EditCommand::LowercaseWord,
|
||||||
|
"CapitalizeChar" => EditCommand::CapitalizeChar,
|
||||||
|
"SwapWords" => EditCommand::SwapWords,
|
||||||
|
"SwapGraphemes" => EditCommand::SwapGraphemes,
|
||||||
|
"Undo" => EditCommand::Undo,
|
||||||
|
"Redo" => EditCommand::Redo,
|
||||||
|
"CutRightUntil" => {
|
||||||
|
let char = extract_char("value", edit_cols, edit_vals, config, edit_span)?;
|
||||||
|
EditCommand::CutRightUntil(char)
|
||||||
|
}
|
||||||
|
"CutRightBefore" => {
|
||||||
|
let char = extract_char("value", edit_cols, edit_vals, config, edit_span)?;
|
||||||
|
EditCommand::CutRightBefore(char)
|
||||||
|
}
|
||||||
|
"MoveRightUntil" => {
|
||||||
|
let char = extract_char("value", edit_cols, edit_vals, config, edit_span)?;
|
||||||
|
EditCommand::MoveRightUntil(char)
|
||||||
|
}
|
||||||
|
"MoveRightBefore" => {
|
||||||
|
let char = extract_char("value", edit_cols, edit_vals, config, edit_span)?;
|
||||||
|
EditCommand::MoveRightBefore(char)
|
||||||
|
}
|
||||||
|
"CutLeftUntil" => {
|
||||||
|
let char = extract_char("value", edit_cols, edit_vals, config, edit_span)?;
|
||||||
|
EditCommand::CutLeftUntil(char)
|
||||||
|
}
|
||||||
|
"CutLeftBefore" => {
|
||||||
|
let char = extract_char("value", edit_cols, edit_vals, config, edit_span)?;
|
||||||
|
EditCommand::CutLeftBefore(char)
|
||||||
|
}
|
||||||
|
"MoveLeftUntil" => {
|
||||||
|
let char = extract_char("value", edit_cols, edit_vals, config, edit_span)?;
|
||||||
|
EditCommand::MoveLeftUntil(char)
|
||||||
|
}
|
||||||
|
"MoveLeftBefore" => {
|
||||||
|
let char = extract_char("value", edit_cols, edit_vals, config, edit_span)?;
|
||||||
|
EditCommand::MoveLeftBefore(char)
|
||||||
|
}
|
||||||
|
e => {
|
||||||
|
return Err(ShellError::UnsupportedConfigValue(
|
||||||
|
e.to_string(),
|
||||||
|
"reedline EditCommand".to_string(),
|
||||||
|
edit.span()?,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
e => {
|
||||||
|
return Err(ShellError::UnsupportedConfigValue(
|
||||||
|
e.into_abbreviated_string(config),
|
||||||
|
"record with EditCommand".to_string(),
|
||||||
|
edit.span()?,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(edit)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extract_char<'record>(
|
||||||
|
name: &str,
|
||||||
|
cols: &'record [String],
|
||||||
|
vals: &'record [Value],
|
||||||
|
config: &Config,
|
||||||
|
span: &Span,
|
||||||
|
) -> Result<char, ShellError> {
|
||||||
|
let value = extract_value(name, cols, vals, span)?;
|
||||||
|
|
||||||
|
value
|
||||||
|
.into_string("", config)
|
||||||
|
.chars()
|
||||||
|
.next()
|
||||||
|
.ok_or_else(|| ShellError::MissingConfigValue("char to insert".to_string(), *span))
|
||||||
|
}
|
||||||
|
|
29
src/repl.rs
29
src/repl.rs
|
@ -139,17 +139,24 @@ pub(crate) fn evaluate(ctrlc: Arc<AtomicBool>, engine_state: &mut EngineState) -
|
||||||
};
|
};
|
||||||
|
|
||||||
// Changing the line editor based on the found keybindings
|
// Changing the line editor based on the found keybindings
|
||||||
let mut line_editor = match reedline_config::create_keybindings(&config)? {
|
let mut line_editor = match reedline_config::create_keybindings(&config) {
|
||||||
KeybindingsMode::Emacs(keybindings) => {
|
Ok(keybindings) => match keybindings {
|
||||||
let edit_mode = Box::new(Emacs::new(keybindings));
|
KeybindingsMode::Emacs(keybindings) => {
|
||||||
line_editor.with_edit_mode(edit_mode)
|
let edit_mode = Box::new(Emacs::new(keybindings));
|
||||||
}
|
line_editor.with_edit_mode(edit_mode)
|
||||||
KeybindingsMode::Vi {
|
}
|
||||||
insert_keybindings,
|
KeybindingsMode::Vi {
|
||||||
normal_keybindings,
|
insert_keybindings,
|
||||||
} => {
|
normal_keybindings,
|
||||||
let edit_mode = Box::new(Vi::new(insert_keybindings, normal_keybindings));
|
} => {
|
||||||
line_editor.with_edit_mode(edit_mode)
|
let edit_mode = Box::new(Vi::new(insert_keybindings, normal_keybindings));
|
||||||
|
line_editor.with_edit_mode(edit_mode)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
let working_set = StateWorkingSet::new(engine_state);
|
||||||
|
report_error(&working_set, &e);
|
||||||
|
line_editor
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user