mirror of
https://github.com/youwen5/iamb.git
synced 2025-06-20 05:39:52 -07:00
Add support for custom key macros (#217)
This commit is contained in:
parent
ef868175cb
commit
e7f158ffcd
8 changed files with 200 additions and 46 deletions
13
Cargo.lock
generated
13
Cargo.lock
generated
|
@ -1993,7 +1993,8 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "keybindings"
|
name = "keybindings"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
source = "git+https://github.com/ulyssa/modalkit?rev=cb8c8aeb9a499b9b16615ce144f9014d78036e01#cb8c8aeb9a499b9b16615ce144f9014d78036e01"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "680e4699c91c0622dd70da32c274881aadb1ac86252d738c3641266e90e4ca15"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"textwrap",
|
"textwrap",
|
||||||
"unicode-segmentation",
|
"unicode-segmentation",
|
||||||
|
@ -2507,8 +2508,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "modalkit"
|
name = "modalkit"
|
||||||
version = "0.0.17"
|
version = "0.0.18"
|
||||||
source = "git+https://github.com/ulyssa/modalkit?rev=cb8c8aeb9a499b9b16615ce144f9014d78036e01#cb8c8aeb9a499b9b16615ce144f9014d78036e01"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f7d68711785c96d06bede5bd38fee2e2ac856cfccce7ea0b3e302bc4c5688010"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anymap2",
|
"anymap2",
|
||||||
"arboard",
|
"arboard",
|
||||||
|
@ -2528,8 +2530,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "modalkit-ratatui"
|
name = "modalkit-ratatui"
|
||||||
version = "0.0.17"
|
version = "0.0.18"
|
||||||
source = "git+https://github.com/ulyssa/modalkit?rev=cb8c8aeb9a499b9b16615ce144f9014d78036e01#cb8c8aeb9a499b9b16615ce144f9014d78036e01"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "747e3dc36bfc4b62a152a37b6631f471797269afa094f6ba0d7aea768be31e2b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crossterm",
|
"crossterm",
|
||||||
"intervaltree",
|
"intervaltree",
|
||||||
|
|
12
Cargo.toml
12
Cargo.toml
|
@ -59,14 +59,14 @@ url = {version = "^2.2.2", features = ["serde"]}
|
||||||
edit = "0.1.4"
|
edit = "0.1.4"
|
||||||
|
|
||||||
[dependencies.modalkit]
|
[dependencies.modalkit]
|
||||||
version = "0.0.17"
|
version = "0.0.18"
|
||||||
git = "https://github.com/ulyssa/modalkit"
|
#git = "https://github.com/ulyssa/modalkit"
|
||||||
rev = "cb8c8aeb9a499b9b16615ce144f9014d78036e01"
|
#rev = "cb8c8aeb9a499b9b16615ce144f9014d78036e01"
|
||||||
|
|
||||||
[dependencies.modalkit-ratatui]
|
[dependencies.modalkit-ratatui]
|
||||||
version = "0.0.17"
|
version = "0.0.18"
|
||||||
git = "https://github.com/ulyssa/modalkit"
|
#git = "https://github.com/ulyssa/modalkit"
|
||||||
rev = "cb8c8aeb9a499b9b16615ce144f9014d78036e01"
|
#rev = "cb8c8aeb9a499b9b16615ce144f9014d78036e01"
|
||||||
|
|
||||||
[dependencies.matrix-sdk]
|
[dependencies.matrix-sdk]
|
||||||
version = "0.7.1"
|
version = "0.7.1"
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"default_profile": "default",
|
"default_profile": "default",
|
||||||
"profiles": {
|
"profiles": {
|
||||||
"default": {
|
"default": {
|
||||||
"user_id": "",
|
"user_id": "@user:matrix.org",
|
||||||
"url": "https://matrix.org",
|
"url": "https://matrix.org",
|
||||||
"settings": {},
|
"settings": {},
|
||||||
"dirs": {}
|
"dirs": {}
|
||||||
|
@ -34,6 +34,14 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"layout": {
|
||||||
|
"style": "restore"
|
||||||
|
},
|
||||||
|
"macros": {
|
||||||
|
"i": {
|
||||||
|
"jj": "<Esc>"
|
||||||
|
}
|
||||||
|
},
|
||||||
"dirs": {
|
"dirs": {
|
||||||
"cache": "/home/user/.cache/iamb/",
|
"cache": "/home/user/.cache/iamb/",
|
||||||
"logs": "/home/user/.local/share/iamb/logs/",
|
"logs": "/home/user/.local/share/iamb/logs/",
|
||||||
|
|
|
@ -24,9 +24,6 @@
|
||||||
src = ./.;
|
src = ./.;
|
||||||
cargoLock = {
|
cargoLock = {
|
||||||
lockFile = ./Cargo.lock;
|
lockFile = ./Cargo.lock;
|
||||||
outputHashes = {
|
|
||||||
"keybindings-0.0.1" = "sha256-6gGviJF4/gzoPxgh0XJXXrhQoWxOTqyI9fwiOE+TY7s=";
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
nativeBuildInputs = [ pkg-config ];
|
nativeBuildInputs = [ pkg-config ];
|
||||||
buildInputs = [ openssl ] ++ lib.optionals stdenv.isDarwin
|
buildInputs = [ openssl ] ++ lib.optionals stdenv.isDarwin
|
||||||
|
|
145
src/config.rs
145
src/config.rs
|
@ -19,6 +19,8 @@ use serde::{de::Error as SerdeError, de::Visitor, Deserialize, Deserializer, Ser
|
||||||
use tracing::Level;
|
use tracing::Level;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
|
use modalkit::{env::vim::VimMode, key::TerminalKey, keybindings::InputKey};
|
||||||
|
|
||||||
use super::base::{
|
use super::base::{
|
||||||
IambError,
|
IambError,
|
||||||
IambId,
|
IambId,
|
||||||
|
@ -29,6 +31,8 @@ use super::base::{
|
||||||
SortOrder,
|
SortOrder,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type Macros = HashMap<VimModes, HashMap<Keys, Keys>>;
|
||||||
|
|
||||||
macro_rules! usage {
|
macro_rules! usage {
|
||||||
( $($args: tt)* ) => {
|
( $($args: tt)* ) => {
|
||||||
println!($($args)*);
|
println!($($args)*);
|
||||||
|
@ -136,6 +140,81 @@ pub enum ConfigError {
|
||||||
Invalid(#[from] serde_json::Error),
|
Invalid(#[from] serde_json::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
|
||||||
|
pub struct Keys(pub Vec<TerminalKey>, pub String);
|
||||||
|
pub struct KeysVisitor;
|
||||||
|
|
||||||
|
impl<'de> Visitor<'de> for KeysVisitor {
|
||||||
|
type Value = Keys;
|
||||||
|
|
||||||
|
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
formatter.write_str("a valid Vim mode (e.g. \"normal\" or \"insert\")")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
|
||||||
|
where
|
||||||
|
E: SerdeError,
|
||||||
|
{
|
||||||
|
match TerminalKey::from_macro_str(value) {
|
||||||
|
Ok(keys) => Ok(Keys(keys, value.to_string())),
|
||||||
|
Err(e) => Err(E::custom(format!("Could not parse key sequence: {e}"))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for Keys {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
deserializer.deserialize_str(KeysVisitor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
|
||||||
|
pub struct VimModes(pub Vec<VimMode>);
|
||||||
|
pub struct VimModesVisitor;
|
||||||
|
|
||||||
|
impl<'de> Visitor<'de> for VimModesVisitor {
|
||||||
|
type Value = VimModes;
|
||||||
|
|
||||||
|
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
formatter.write_str("a valid Vim mode (e.g. \"normal\" or \"insert\")")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
|
||||||
|
where
|
||||||
|
E: SerdeError,
|
||||||
|
{
|
||||||
|
let mut modes = vec![];
|
||||||
|
|
||||||
|
for mode in value.split('|') {
|
||||||
|
let mode = match mode.to_ascii_lowercase().as_str() {
|
||||||
|
"insert" | "i" => VimMode::Insert,
|
||||||
|
"normal" | "n" => VimMode::Normal,
|
||||||
|
"visual" | "v" => VimMode::Visual,
|
||||||
|
"command" | "c" => VimMode::Command,
|
||||||
|
"select" => VimMode::Select,
|
||||||
|
"operator-pending" => VimMode::OperationPending,
|
||||||
|
_ => return Err(E::custom("Could not parse into a Vim mode")),
|
||||||
|
};
|
||||||
|
|
||||||
|
modes.push(mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(VimModes(modes))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for VimModes {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
deserializer.deserialize_str(VimModesVisitor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
pub struct LogLevel(pub Level);
|
pub struct LogLevel(pub Level);
|
||||||
pub struct LogLevelVisitor;
|
pub struct LogLevelVisitor;
|
||||||
|
@ -276,7 +355,10 @@ fn merge_sorts(a: SortOverrides, b: SortOverrides) -> SortOverrides {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn merge_users(a: Option<UserOverrides>, b: Option<UserOverrides>) -> Option<UserOverrides> {
|
fn merge_maps<K, V>(a: Option<HashMap<K, V>>, b: Option<HashMap<K, V>>) -> Option<HashMap<K, V>>
|
||||||
|
where
|
||||||
|
K: Eq + Hash,
|
||||||
|
{
|
||||||
match (a, b) {
|
match (a, b) {
|
||||||
(Some(a), None) => Some(a),
|
(Some(a), None) => Some(a),
|
||||||
(None, Some(b)) => Some(b),
|
(None, Some(b)) => Some(b),
|
||||||
|
@ -431,7 +513,7 @@ impl Tunables {
|
||||||
sort: merge_sorts(self.sort, other.sort),
|
sort: merge_sorts(self.sort, other.sort),
|
||||||
typing_notice_send: self.typing_notice_send.or(other.typing_notice_send),
|
typing_notice_send: self.typing_notice_send.or(other.typing_notice_send),
|
||||||
typing_notice_display: self.typing_notice_display.or(other.typing_notice_display),
|
typing_notice_display: self.typing_notice_display.or(other.typing_notice_display),
|
||||||
users: merge_users(self.users, other.users),
|
users: merge_maps(self.users, other.users),
|
||||||
username_display: self.username_display.or(other.username_display),
|
username_display: self.username_display.or(other.username_display),
|
||||||
message_user_color: self.message_user_color.or(other.message_user_color),
|
message_user_color: self.message_user_color.or(other.message_user_color),
|
||||||
default_room: self.default_room.or(other.default_room),
|
default_room: self.default_room.or(other.default_room),
|
||||||
|
@ -583,6 +665,7 @@ pub struct ProfileConfig {
|
||||||
pub settings: Option<Tunables>,
|
pub settings: Option<Tunables>,
|
||||||
pub dirs: Option<Directories>,
|
pub dirs: Option<Directories>,
|
||||||
pub layout: Option<Layout>,
|
pub layout: Option<Layout>,
|
||||||
|
pub macros: Option<Macros>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Deserialize)]
|
#[derive(Clone, Deserialize)]
|
||||||
|
@ -592,6 +675,7 @@ pub struct IambConfig {
|
||||||
pub settings: Option<Tunables>,
|
pub settings: Option<Tunables>,
|
||||||
pub dirs: Option<Directories>,
|
pub dirs: Option<Directories>,
|
||||||
pub layout: Option<Layout>,
|
pub layout: Option<Layout>,
|
||||||
|
pub macros: Option<Macros>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IambConfig {
|
impl IambConfig {
|
||||||
|
@ -624,6 +708,7 @@ pub struct ApplicationSettings {
|
||||||
pub tunables: TunableValues,
|
pub tunables: TunableValues,
|
||||||
pub dirs: DirectoryValues,
|
pub dirs: DirectoryValues,
|
||||||
pub layout: Layout,
|
pub layout: Layout,
|
||||||
|
pub macros: Macros,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ApplicationSettings {
|
impl ApplicationSettings {
|
||||||
|
@ -645,6 +730,7 @@ impl ApplicationSettings {
|
||||||
dirs,
|
dirs,
|
||||||
settings: global,
|
settings: global,
|
||||||
layout,
|
layout,
|
||||||
|
macros,
|
||||||
} = IambConfig::load(config_json.as_path())?;
|
} = IambConfig::load(config_json.as_path())?;
|
||||||
|
|
||||||
validate_profile_names(&profiles);
|
validate_profile_names(&profiles);
|
||||||
|
@ -668,6 +754,7 @@ impl ApplicationSettings {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let macros = merge_maps(macros, profile.macros.take()).unwrap_or_default();
|
||||||
let layout = profile.layout.take().or(layout).unwrap_or_default();
|
let layout = profile.layout.take().or(layout).unwrap_or_default();
|
||||||
|
|
||||||
let tunables = global.unwrap_or_default();
|
let tunables = global.unwrap_or_default();
|
||||||
|
@ -721,6 +808,7 @@ impl ApplicationSettings {
|
||||||
tunables,
|
tunables,
|
||||||
dirs,
|
dirs,
|
||||||
layout,
|
layout,
|
||||||
|
macros,
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(settings)
|
Ok(settings)
|
||||||
|
@ -851,22 +939,22 @@ mod tests {
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.collect::<HashMap<_, _>>();
|
.collect::<HashMap<_, _>>();
|
||||||
|
|
||||||
let res = merge_users(a.clone(), a.clone());
|
let res = merge_maps(a.clone(), a.clone());
|
||||||
assert_eq!(res, None);
|
assert_eq!(res, None);
|
||||||
|
|
||||||
let res = merge_users(a.clone(), Some(b.clone()));
|
let res = merge_maps(a.clone(), Some(b.clone()));
|
||||||
assert_eq!(res, Some(b.clone()));
|
assert_eq!(res, Some(b.clone()));
|
||||||
|
|
||||||
let res = merge_users(Some(b.clone()), a.clone());
|
let res = merge_maps(Some(b.clone()), a.clone());
|
||||||
assert_eq!(res, Some(b.clone()));
|
assert_eq!(res, Some(b.clone()));
|
||||||
|
|
||||||
let res = merge_users(Some(b.clone()), Some(b.clone()));
|
let res = merge_maps(Some(b.clone()), Some(b.clone()));
|
||||||
assert_eq!(res, Some(b.clone()));
|
assert_eq!(res, Some(b.clone()));
|
||||||
|
|
||||||
let res = merge_users(Some(b.clone()), Some(c.clone()));
|
let res = merge_maps(Some(b.clone()), Some(c.clone()));
|
||||||
assert_eq!(res, Some(c.clone()));
|
assert_eq!(res, Some(c.clone()));
|
||||||
|
|
||||||
let res = merge_users(Some(c.clone()), Some(b.clone()));
|
let res = merge_maps(Some(c.clone()), Some(b.clone()));
|
||||||
assert_eq!(res, Some(b.clone()));
|
assert_eq!(res, Some(b.clone()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1012,4 +1100,45 @@ mod tests {
|
||||||
let tabs = vec![split1, split3];
|
let tabs = vec![split1, split3];
|
||||||
assert_eq!(res, Layout::Config { tabs });
|
assert_eq!(res, Layout::Config { tabs });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_macros() {
|
||||||
|
let res: Macros = serde_json::from_str("{\"i|c\":{\"jj\":\"<Esc>\"}}").unwrap();
|
||||||
|
assert_eq!(res.len(), 1);
|
||||||
|
|
||||||
|
let modes = VimModes(vec![VimMode::Insert, VimMode::Command]);
|
||||||
|
let mapped = res.get(&modes).unwrap();
|
||||||
|
assert_eq!(mapped.len(), 1);
|
||||||
|
|
||||||
|
let j = "j".parse::<TerminalKey>().unwrap();
|
||||||
|
let esc = "<Esc>".parse::<TerminalKey>().unwrap();
|
||||||
|
|
||||||
|
let jj = Keys(vec![j.clone(), j], "jj".into());
|
||||||
|
let run = mapped.get(&jj).unwrap();
|
||||||
|
let exp = Keys(vec![esc], "<Esc>".into());
|
||||||
|
assert_eq!(run, &exp);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_load_example_config_json() {
|
||||||
|
let path = PathBuf::from("docs/example_config.json");
|
||||||
|
let config = IambConfig::load(&path).expect("can load example_config.json");
|
||||||
|
|
||||||
|
let IambConfig {
|
||||||
|
profiles,
|
||||||
|
default_profile,
|
||||||
|
settings,
|
||||||
|
dirs,
|
||||||
|
layout,
|
||||||
|
macros,
|
||||||
|
} = config;
|
||||||
|
|
||||||
|
// There should be an example object for each top-level field.
|
||||||
|
assert!(!profiles.is_empty());
|
||||||
|
assert!(default_profile.is_some());
|
||||||
|
assert!(settings.is_some());
|
||||||
|
assert!(dirs.is_some());
|
||||||
|
assert!(layout.is_some());
|
||||||
|
assert!(macros.is_some());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,16 +3,23 @@
|
||||||
//! The keybindings are set up here. We define some iamb-specific keybindings, but the default Vim
|
//! The keybindings are set up here. We define some iamb-specific keybindings, but the default Vim
|
||||||
//! keys come from [modalkit::env::vim::keybindings].
|
//! keys come from [modalkit::env::vim::keybindings].
|
||||||
use modalkit::{
|
use modalkit::{
|
||||||
actions::WindowAction,
|
actions::{MacroAction, WindowAction},
|
||||||
env::vim::keybindings::{InputStep, VimBindings},
|
env::vim::keybindings::{InputStep, VimBindings},
|
||||||
env::vim::VimMode,
|
env::vim::VimMode,
|
||||||
|
env::CommonKeyClass,
|
||||||
key::TerminalKey,
|
key::TerminalKey,
|
||||||
keybindings::{EdgeEvent, EdgeRepeat, InputBindings},
|
keybindings::{EdgeEvent, EdgeRepeat, InputBindings},
|
||||||
|
prelude::Count,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::base::{IambAction, IambInfo, Keybindings, MATRIX_ID_WORD};
|
use crate::base::{IambAction, IambInfo, Keybindings, MATRIX_ID_WORD};
|
||||||
|
use crate::config::{ApplicationSettings, Keys};
|
||||||
|
|
||||||
type IambStep = InputStep<IambInfo>;
|
pub type IambStep = InputStep<IambInfo>;
|
||||||
|
|
||||||
|
fn once(key: &TerminalKey) -> (EdgeRepeat, EdgeEvent<TerminalKey, CommonKeyClass>) {
|
||||||
|
(EdgeRepeat::Once, EdgeEvent::Key(*key))
|
||||||
|
}
|
||||||
|
|
||||||
/// Initialize the default keybinding state.
|
/// Initialize the default keybinding state.
|
||||||
pub fn setup_keybindings() -> Keybindings {
|
pub fn setup_keybindings() -> Keybindings {
|
||||||
|
@ -24,20 +31,14 @@ pub fn setup_keybindings() -> Keybindings {
|
||||||
|
|
||||||
vim.setup(&mut ism);
|
vim.setup(&mut ism);
|
||||||
|
|
||||||
let ctrl_w = EdgeEvent::Key("<C-W>".parse::<TerminalKey>().unwrap());
|
let ctrl_w = "<C-W>".parse::<TerminalKey>().unwrap();
|
||||||
let ctrl_m = EdgeEvent::Key("<C-M>".parse::<TerminalKey>().unwrap());
|
let ctrl_m = "<C-M>".parse::<TerminalKey>().unwrap();
|
||||||
let ctrl_z = EdgeEvent::Key("<C-Z>".parse::<TerminalKey>().unwrap());
|
let ctrl_z = "<C-Z>".parse::<TerminalKey>().unwrap();
|
||||||
let key_m_lc = EdgeEvent::Key("m".parse::<TerminalKey>().unwrap());
|
let key_m_lc = "m".parse::<TerminalKey>().unwrap();
|
||||||
let key_z_lc = EdgeEvent::Key("z".parse::<TerminalKey>().unwrap());
|
let key_z_lc = "z".parse::<TerminalKey>().unwrap();
|
||||||
|
|
||||||
let cwz = vec![
|
let cwz = vec![once(&ctrl_w), once(&key_z_lc)];
|
||||||
(EdgeRepeat::Once, ctrl_w.clone()),
|
let cwcz = vec![once(&ctrl_w), once(&ctrl_z)];
|
||||||
(EdgeRepeat::Once, key_z_lc),
|
|
||||||
];
|
|
||||||
let cwcz = vec![
|
|
||||||
(EdgeRepeat::Once, ctrl_w.clone()),
|
|
||||||
(EdgeRepeat::Once, ctrl_z),
|
|
||||||
];
|
|
||||||
let zoom = IambStep::new()
|
let zoom = IambStep::new()
|
||||||
.actions(vec![WindowAction::ZoomToggle.into()])
|
.actions(vec![WindowAction::ZoomToggle.into()])
|
||||||
.goto(VimMode::Normal);
|
.goto(VimMode::Normal);
|
||||||
|
@ -47,11 +48,8 @@ pub fn setup_keybindings() -> Keybindings {
|
||||||
ism.add_mapping(VimMode::Normal, &cwcz, &zoom);
|
ism.add_mapping(VimMode::Normal, &cwcz, &zoom);
|
||||||
ism.add_mapping(VimMode::Visual, &cwcz, &zoom);
|
ism.add_mapping(VimMode::Visual, &cwcz, &zoom);
|
||||||
|
|
||||||
let cwm = vec![
|
let cwm = vec![once(&ctrl_w), once(&key_m_lc)];
|
||||||
(EdgeRepeat::Once, ctrl_w.clone()),
|
let cwcm = vec![once(&ctrl_w), once(&ctrl_m)];
|
||||||
(EdgeRepeat::Once, key_m_lc),
|
|
||||||
];
|
|
||||||
let cwcm = vec![(EdgeRepeat::Once, ctrl_w), (EdgeRepeat::Once, ctrl_m)];
|
|
||||||
let stoggle = IambStep::new()
|
let stoggle = IambStep::new()
|
||||||
.actions(vec![IambAction::ToggleScrollbackFocus.into()])
|
.actions(vec![IambAction::ToggleScrollbackFocus.into()])
|
||||||
.goto(VimMode::Normal);
|
.goto(VimMode::Normal);
|
||||||
|
@ -59,6 +57,21 @@ pub fn setup_keybindings() -> Keybindings {
|
||||||
ism.add_mapping(VimMode::Visual, &cwm, &stoggle);
|
ism.add_mapping(VimMode::Visual, &cwm, &stoggle);
|
||||||
ism.add_mapping(VimMode::Normal, &cwcm, &stoggle);
|
ism.add_mapping(VimMode::Normal, &cwcm, &stoggle);
|
||||||
ism.add_mapping(VimMode::Visual, &cwcm, &stoggle);
|
ism.add_mapping(VimMode::Visual, &cwcm, &stoggle);
|
||||||
|
ism
|
||||||
return ism;
|
}
|
||||||
|
|
||||||
|
impl InputBindings<TerminalKey, IambStep> for ApplicationSettings {
|
||||||
|
fn setup(&self, bindings: &mut Keybindings) {
|
||||||
|
for (modes, keys) in &self.macros {
|
||||||
|
for (Keys(input, _), Keys(_, run)) in keys {
|
||||||
|
let act = MacroAction::Run(run.clone(), Count::Contextual);
|
||||||
|
let step = IambStep::new().actions(vec![act.into()]);
|
||||||
|
let input = input.iter().map(once).collect::<Vec<_>>();
|
||||||
|
|
||||||
|
for mode in &modes.0 {
|
||||||
|
bindings.add_mapping(*mode, &input, &step);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,7 @@ use std::time::Duration;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use matrix_sdk::crypto::encrypt_room_key_export;
|
use matrix_sdk::crypto::encrypt_room_key_export;
|
||||||
use matrix_sdk::ruma::OwnedUserId;
|
use matrix_sdk::ruma::OwnedUserId;
|
||||||
|
use modalkit::keybindings::InputBindings;
|
||||||
use rand::{distributions::Alphanumeric, Rng};
|
use rand::{distributions::Alphanumeric, Rng};
|
||||||
use temp_dir::TempDir;
|
use temp_dir::TempDir;
|
||||||
use tokio::sync::Mutex as AsyncMutex;
|
use tokio::sync::Mutex as AsyncMutex;
|
||||||
|
@ -257,7 +258,8 @@ impl Application {
|
||||||
let backend = CrosstermBackend::new(stdout);
|
let backend = CrosstermBackend::new(stdout);
|
||||||
let terminal = Terminal::new(backend)?;
|
let terminal = Terminal::new(backend)?;
|
||||||
|
|
||||||
let bindings = crate::keybindings::setup_keybindings();
|
let mut bindings = crate::keybindings::setup_keybindings();
|
||||||
|
settings.setup(&mut bindings);
|
||||||
let bindings = KeyManager::new(bindings);
|
let bindings = KeyManager::new(bindings);
|
||||||
|
|
||||||
let mut locked = store.lock().await;
|
let mut locked = store.lock().await;
|
||||||
|
|
|
@ -217,10 +217,12 @@ pub fn mock_settings() -> ApplicationSettings {
|
||||||
settings: None,
|
settings: None,
|
||||||
dirs: None,
|
dirs: None,
|
||||||
layout: None,
|
layout: None,
|
||||||
|
macros: None,
|
||||||
},
|
},
|
||||||
tunables: mock_tunables(),
|
tunables: mock_tunables(),
|
||||||
dirs: mock_dirs(),
|
dirs: mock_dirs(),
|
||||||
layout: Default::default(),
|
layout: Default::default(),
|
||||||
|
macros: HashMap::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue