feat: add args to new-tab action (#2072)

* fix(themes): missing tokyo-night-dark theme

* feat: add args to new-tab action

* fix: name can be set without layout

* feat: pass config options to action parser

* chore: remove unnecessary default values

* chore: update snapshots

* fix(status-bar): add exception for NewTab

* fix: update code review

* feat: add shallow_eq for action
This commit is contained in:
Jae-Heon Ji 2023-02-16 22:32:27 +09:00 committed by GitHub
parent 5817ebe2d2
commit 00cd33637b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 94 additions and 15 deletions

View File

@ -351,7 +351,13 @@ pub fn action_key(keymap: &[(Key, Vec<Action>)], action: &[Action]) -> Vec<Key>
keymap
.iter()
.filter_map(|(key, acvec)| {
if acvec.as_slice() == action {
let matching = acvec
.iter()
.zip(action)
.filter(|(a, b)| a.shallow_eq(b))
.count();
if matching == acvec.len() && matching == action.len() {
Some(*key)
} else {
None

View File

@ -208,6 +208,14 @@ pub enum Action {
}
impl Action {
/// Checks that two Action are match except their mutable attributes.
pub fn shallow_eq(&self, other_action: &Action) -> bool {
match (self, other_action) {
(Action::NewTab(_, _, _), Action::NewTab(_, _, _)) => true,
_ => self == other_action,
}
}
pub fn actions_from_cli(
cli_action: CliAction,
get_current_dir: Box<dyn Fn() -> PathBuf>,

View File

@ -301,10 +301,10 @@ macro_rules! keys_from_kdl {
#[macro_export]
macro_rules! actions_from_kdl {
( $kdl_node:expr ) => {
( $kdl_node:expr, $config_options:expr ) => {
kdl_children_nodes_or_error!($kdl_node, "no actions found for key_block")
.iter()
.map(|kdl_action| Action::try_from(kdl_action))
.map(|kdl_action| Action::try_from((kdl_action, $config_options)))
.collect::<Result<_, _>>()?
};
}
@ -614,9 +614,9 @@ impl TryFrom<(&str, &KdlDocument)> for PaletteColor {
}
}
impl TryFrom<&KdlNode> for Action {
impl TryFrom<(&KdlNode, &Options)> for Action {
type Error = ConfigError;
fn try_from(kdl_action: &KdlNode) -> Result<Self, Self::Error> {
fn try_from((kdl_action, config_options): (&KdlNode, &Options)) -> Result<Self, Self::Error> {
let action_name = kdl_name!(kdl_action);
let action_arguments: Vec<&KdlEntry> = kdl_argument_values!(kdl_action);
let action_children: Vec<&KdlDocument> = kdl_children!(kdl_action);
@ -742,7 +742,64 @@ impl TryFrom<&KdlNode> for Action {
"PaneNameInput" => {
parse_kdl_action_u8_arguments!(action_name, action_arguments, kdl_action)
},
"NewTab" => Ok(Action::NewTab(None, vec![], None)),
"NewTab" => {
let command_metadata = action_children.iter().next();
if command_metadata.is_none() {
return Ok(Action::NewTab(None, vec![], None));
}
let current_dir = std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."));
let layout = command_metadata
.and_then(|c_m| kdl_child_string_value_for_entry(c_m, "layout"))
.map(|layout_string| PathBuf::from(layout_string))
.or_else(|| config_options.default_layout.clone());
let cwd = command_metadata
.and_then(|c_m| kdl_child_string_value_for_entry(c_m, "cwd"))
.map(|cwd_string| PathBuf::from(cwd_string))
.map(|cwd| current_dir.join(cwd));
let name = command_metadata
.and_then(|c_m| kdl_child_string_value_for_entry(c_m, "name"))
.map(|name_string| name_string.to_string());
let (path_to_raw_layout, raw_layout) =
Layout::stringified_from_path_or_default(layout.as_ref(), None).map_err(
|e| {
ConfigError::new_kdl_error(
format!("Failed to load layout: {}", e),
kdl_action.span().offset(),
kdl_action.span().len(),
)
},
)?;
let layout =
Layout::from_str(&raw_layout, path_to_raw_layout, cwd).map_err(|e| {
ConfigError::new_kdl_error(
format!("Failed to load layout: {}", e),
kdl_action.span().offset(),
kdl_action.span().len(),
)
})?;
let mut tabs = layout.tabs();
if tabs.len() > 1 {
return Err(ConfigError::new_kdl_error(
"Tab layout cannot itself have tabs".to_string(),
kdl_action.span().offset(),
kdl_action.span().len(),
));
} else if !tabs.is_empty() {
let (tab_name, layout, floating_panes_layout) = tabs.drain(..).next().unwrap();
let name = tab_name.or(name);
Ok(Action::NewTab(Some(layout), floating_panes_layout, name))
} else {
let (layout, floating_panes_layout) = layout.new_tab();
Ok(Action::NewTab(Some(layout), floating_panes_layout, name))
}
},
"GoToTab" => parse_kdl_action_u8_arguments!(action_name, action_arguments, kdl_action),
"TabNameInput" => {
parse_kdl_action_u8_arguments!(action_name, action_arguments, kdl_action)
@ -1370,12 +1427,13 @@ impl Keybinds {
fn bind_keys_in_block(
block: &KdlNode,
input_mode_keybinds: &mut HashMap<Key, Vec<Action>>,
config_options: &Options,
) -> Result<(), ConfigError> {
let all_nodes = kdl_children_nodes_or_error!(block, "no keybinding block for mode");
let bind_nodes = all_nodes.iter().filter(|n| kdl_name!(n) == "bind");
let unbind_nodes = all_nodes.iter().filter(|n| kdl_name!(n) == "unbind");
for key_block in bind_nodes {
Keybinds::bind_actions_for_each_key(key_block, input_mode_keybinds)?;
Keybinds::bind_actions_for_each_key(key_block, input_mode_keybinds, config_options)?;
}
// we loop a second time so that the unbinds always happen after the binds
for key_block in unbind_nodes {
@ -1392,7 +1450,11 @@ impl Keybinds {
}
Ok(())
}
pub fn from_kdl(kdl_keybinds: &KdlNode, base_keybinds: Keybinds) -> Result<Self, ConfigError> {
pub fn from_kdl(
kdl_keybinds: &KdlNode,
base_keybinds: Keybinds,
config_options: &Options,
) -> Result<Self, ConfigError> {
let clear_defaults = kdl_arg_is_truthy!(kdl_keybinds, "clear-defaults");
let mut keybinds_from_config = if clear_defaults {
Keybinds::default()
@ -1416,7 +1478,7 @@ impl Keybinds {
continue;
}
let mut input_mode_keybinds = keybinds_from_config.get_input_mode_mut(&mode);
Keybinds::bind_keys_in_block(block, &mut input_mode_keybinds)?;
Keybinds::bind_keys_in_block(block, &mut input_mode_keybinds, config_options)?;
}
}
if kdl_name!(block) == "shared_among" {
@ -1429,7 +1491,7 @@ impl Keybinds {
continue;
}
let mut input_mode_keybinds = keybinds_from_config.get_input_mode_mut(&mode);
Keybinds::bind_keys_in_block(block, &mut input_mode_keybinds)?;
Keybinds::bind_keys_in_block(block, &mut input_mode_keybinds, config_options)?;
}
}
}
@ -1443,7 +1505,7 @@ impl Keybinds {
}
let mut input_mode_keybinds =
Keybinds::input_mode_keybindings(mode, &mut keybinds_from_config)?;
Keybinds::bind_keys_in_block(mode, &mut input_mode_keybinds)?;
Keybinds::bind_keys_in_block(mode, &mut input_mode_keybinds, config_options)?;
}
if let Some(global_unbind) = kdl_keybinds.children().and_then(|c| c.get("unbind")) {
Keybinds::unbind_keys_in_all_modes(global_unbind, &mut keybinds_from_config)?;
@ -1453,9 +1515,10 @@ impl Keybinds {
fn bind_actions_for_each_key(
key_block: &KdlNode,
input_mode_keybinds: &mut HashMap<Key, Vec<Action>>,
config_options: &Options,
) -> Result<(), ConfigError> {
let keys: Vec<Key> = keys_from_kdl!(key_block);
let actions: Vec<Action> = actions_from_kdl!(key_block);
let actions: Vec<Action> = actions_from_kdl!(key_block, config_options);
for key in keys {
input_mode_keybinds.insert(key, actions.clone());
}
@ -1508,13 +1571,15 @@ impl Config {
pub fn from_kdl(kdl_config: &str, base_config: Option<Config>) -> Result<Config, ConfigError> {
let mut config = base_config.unwrap_or_else(|| Config::default());
let kdl_config: KdlDocument = kdl_config.parse()?;
let config_options = Options::from_kdl(&kdl_config)?;
config.options = config.options.merge(config_options);
// TODO: handle cases where we have more than one of these blocks (eg. two "keybinds")
// this should give an informative parsing error
if let Some(kdl_keybinds) = kdl_config.get("keybinds") {
config.keybinds = Keybinds::from_kdl(&kdl_keybinds, config.keybinds)?;
config.keybinds = Keybinds::from_kdl(&kdl_keybinds, config.keybinds, &config.options)?;
}
let config_options = Options::from_kdl(&kdl_config)?;
config.options = config.options.merge(config_options);
if let Some(kdl_themes) = kdl_config.get("themes") {
let config_themes = Themes::from_kdl(kdl_themes)?;
config.themes = config.themes.merge(config_themes);