summaryrefslogtreecommitdiffstats
path: root/pkgs/by-name/fi/fish-mk-maps/src/main.rs
diff options
context:
space:
mode:
authorBenedikt Peetz <benedikt.peetz@b-peetz.de>2026-11-02 14:06:45 +0100
committerBenedikt Peetz <benedikt.peetz@b-peetz.de>2026-11-02 14:06:45 +0100
commitef08201a906a69ae25a5de0811d3840ae5cc970e (patch)
tree238d85022b4eccc759618b0ba9e4b949d5dfe309 /pkgs/by-name/fi/fish-mk-maps/src/main.rs
parentmodules/fish: Switch to keybindings defined as nix attribute set (diff)
downloadnixos-config-ef08201a906a69ae25a5de0811d3840ae5cc970e.zip
pkgs/fish-mk-maps: Commit the program used to convert the `binds` to nix attrs
Diffstat (limited to 'pkgs/by-name/fi/fish-mk-maps/src/main.rs')
-rw-r--r--pkgs/by-name/fi/fish-mk-maps/src/main.rs362
1 files changed, 362 insertions, 0 deletions
diff --git a/pkgs/by-name/fi/fish-mk-maps/src/main.rs b/pkgs/by-name/fi/fish-mk-maps/src/main.rs
new file mode 100644
index 00000000..57d221db
--- /dev/null
+++ b/pkgs/by-name/fi/fish-mk-maps/src/main.rs
@@ -0,0 +1,362 @@
+use std::{
+ collections::HashMap,
+ env::args,
+ fs,
+ io::{Read, Write},
+ mem,
+ os::unix::net::{UnixListener, UnixStream},
+ path::PathBuf,
+ process,
+ str::FromStr,
+};
+
+use anyhow::{Context, bail};
+use clap::Parser;
+use keymaps::map_tree::Trie;
+use serde::{Deserialize, Serialize};
+
+macro_rules! is_open {
+ ($is_open:ident, @set_element $value:ident[$idx:expr]) => {{
+ if let Some(value) = $value.get($idx - 1) && $value.len() > $idx {
+ if let Some(io) = &mut $is_open {
+ io.truncate($idx);
+ io.push(value.clone());
+ println!("{}\"{}\" = {{", " ".repeat($idx + 1), value);
+
+ eprintln!("Setting io element {} to '{}' (io: '{}')", $idx, value, io.join(","));
+ } else if $idx == 1 {
+ $is_open = Some(vec![value.clone()]);
+ println!(" \"{}\" = {{", value);
+
+ eprintln!("Initializing io with element '{}'", value);
+ } else {
+ unreachable!()
+ }
+ }
+ }};
+ ($is_open:ident, @query $value:ident [..$till:expr]) => {{
+ if $value.len() <= $till {
+ false
+ } else {
+ if let Some(io) = &$is_open {
+ let mut output = true;
+ for (idx, val) in $value.iter().take($till).enumerate() {
+ output = output && io.get(idx).map(|got| got == val).unwrap_or(false);
+ }
+ if output {
+ eprintln!("Query for '{}' (io: '{}') matched at element {}", $value.join(","), io.join(","), $till);
+ }
+ output
+ } else {
+ false
+ }
+ }
+ }};
+ ($is_open:ident, @close $num:expr) => {{
+ if let Some(io) = &mut $is_open {
+ eprintln!("Performing close for {} elements (io: '{}') ..", $num, io.join(","));
+ for idx in (0..io.len()).rev().take($num) {
+ io.pop();
+ eprintln!(" Emitting close at element {} (io now: '{}')", idx + 1, io.join(","));
+ println!("{}}};", " ".repeat(idx + 2));
+ }
+ } else {
+ unreachable!()
+ }
+ }};
+ ($is_open:ident, @close_till $value:ident $num:expr) => {{
+ if let Some(io) = $is_open.clone() {
+ is_open!($is_open, @close io.len() - $num)
+ } else {
+ }
+ }};
+ ($is_open:ident, @close_all) => {{
+ if let Some(io) = $is_open.clone() {
+ is_open!($is_open, @close io.len());
+ } else {
+ }
+ }};
+}
+
+fn print_key(key: Vec<String>, node: Mapping) {
+ print!(
+ "{}\"{}\" = {{ cmd = \"{}\"; ",
+ " ".repeat(key.len() + 1),
+ key.last().expect("That's the len"),
+ node.command.join(" "),
+ );
+ if let Some(sets_mode_to) = node.sets_mode_to {
+ println!("sets_mode_to = \"{}\";}};", sets_mode_to);
+ } else {
+ println!("}};");
+ }
+}
+
+#[derive(clap::Parser, Debug)]
+#[clap(rename_all = "snake_case")]
+pub(crate) struct Cli {
+ #[arg(long)]
+ preset: bool,
+
+ #[arg(short = 'M', default_value = "default")]
+ mode: MapMode,
+
+ #[arg(short = 'm', default_value = "default")]
+ sets_mode_to: MapMode,
+
+ key: String,
+
+ command: Vec<String>,
+}
+
+#[derive(
+ Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord, Hash, Clone, Copy, Debug, Default,
+)]
+#[expect(non_camel_case_types)]
+enum MapMode {
+ #[default]
+ Default,
+ Insert,
+ Operator,
+ ReplaceOne,
+ Replace,
+ Visual,
+ f,
+ F,
+ t,
+ T,
+}
+
+impl std::fmt::Display for MapMode {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ match self {
+ MapMode::Default => f.write_str("default")?,
+ MapMode::Insert => f.write_str("insert")?,
+ MapMode::Operator => f.write_str("operator")?,
+ MapMode::ReplaceOne => f.write_str("replace_one")?,
+ MapMode::Replace => f.write_str("replace")?,
+ MapMode::Visual => f.write_str("visual")?,
+ MapMode::f => f.write_str("f")?,
+ MapMode::F => f.write_str("F")?,
+ MapMode::t => f.write_str("t")?,
+ MapMode::T => f.write_str("T")?,
+ }
+ Ok(())
+ }
+}
+
+impl FromStr for MapMode {
+ type Err = anyhow::Error;
+
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ let me = match s {
+ "operator" => Self::Operator,
+ "insert" => Self::Insert,
+ "replace_one" => Self::ReplaceOne,
+ "replace" => Self::Replace,
+ "visual" => Self::Visual,
+ "default" => Self::Default,
+ "f" => Self::f,
+ "F" => Self::F,
+ "t" => Self::t,
+ "T" => Self::T,
+ other => bail!("Mapping mode '{other}' not yet supported"),
+ };
+ Ok(me)
+ }
+}
+
+enum Message {
+ NewBinding(usize),
+ End(usize),
+}
+type Key = String;
+#[derive(Debug, Deserialize, Serialize)]
+struct Binding {
+ key: Vec<Key>,
+ command: Vec<String>,
+ sets_mode_to: Option<MapMode>,
+ mode: MapMode,
+}
+
+struct MapStore {
+ map: HashMap<MapMode, Trie<Key, Mapping>>,
+}
+
+#[derive(Clone, PartialEq, PartialOrd, Debug)]
+struct Mapping {
+ command: Vec<String>,
+ sets_mode_to: Option<MapMode>,
+}
+
+fn main() -> anyhow::Result<()> {
+ let communication_socket: PathBuf = PathBuf::from("/tmp/fish_map_store.socket");
+
+ match PathBuf::from(args().next().unwrap())
+ .file_name()
+ .unwrap()
+ .to_str()
+ .unwrap()
+ {
+ "bind-end" => {
+ let mut stream = UnixStream::connect(communication_socket)?;
+ let message_raw: [u8; size_of::<Message>()] =
+ unsafe { mem::transmute(Message::End(0)) };
+
+ stream.write_all(&message_raw)?;
+ stream.flush()?;
+ }
+ "bind" => {
+ let args = Cli::parse();
+ assert!(args.preset);
+
+ let binding = Binding {
+ key: args.key.split(',').map(|s| s.to_string()).collect(),
+ command: args.command,
+ sets_mode_to: if args.mode != args.sets_mode_to {
+ Some(args.sets_mode_to)
+ } else {
+ None
+ },
+ mode: args.mode,
+ };
+ let binding_msg = serde_json::to_string(&binding)?;
+
+ let mut stream = UnixStream::connect(communication_socket)?;
+
+ let length = binding_msg.len();
+
+ let message_raw: [u8; size_of::<Message>()] =
+ unsafe { mem::transmute(Message::NewBinding(length)) };
+
+ stream.write_all(&message_raw)?;
+ stream.write_all(binding_msg.as_bytes())?;
+ stream.flush()?;
+ drop(stream);
+ }
+ "bind-server" => bind_server_main(communication_socket)?,
+ other => {
+ eprintln!("Personality '{other}' not supported.");
+ process::exit(2);
+ }
+ }
+
+ // let mappings = store.map.entry(args.mode).or_insert(Vec::default());
+ // mappings.push(mapping);
+ //
+ // {
+ // let mut file = fs::OpenOptions::new()
+ // .create(true)
+ // .truncate(true)
+ // .append(false)
+ // .write(true)
+ // .open(storage_file)?;
+ //
+ // file.write_all(serde_json::to_string(&store)?.as_bytes())?;
+ // }
+ //
+ // {
+ // eprintln!("Added {}", args.key);
+ // }
+
+ Ok(())
+}
+
+fn bind_server_main(communication_socket: PathBuf) -> anyhow::Result<()> {
+ if communication_socket.exists() {
+ fs::remove_file(&communication_socket)?;
+ }
+
+ let mut store = MapStore {
+ map: HashMap::new(),
+ };
+
+ let listener = UnixListener::bind(communication_socket)?;
+
+ for stream in listener.incoming() {
+ match stream {
+ Ok(mut stream) => {
+ let mut next_message_raw = [0; size_of::<Message>()];
+ stream.read_exact(&mut next_message_raw)?;
+ let next_message: Message = unsafe { mem::transmute(next_message_raw) };
+
+ match next_message {
+ Message::NewBinding(length) => {
+ let mut binding_str = String::new();
+ stream.read_to_string(&mut binding_str)?;
+
+ let binding: Binding = serde_json::from_str(&binding_str[..length])
+ .with_context(|| {
+ format!("Failed to parse '{binding_str:?}' as json")
+ })?;
+
+ let trie = store.map.entry(binding.mode).or_default();
+ trie.insert(
+ &binding.key,
+ Mapping {
+ command: binding.command,
+ sets_mode_to: binding.sets_mode_to,
+ },
+ )?;
+ }
+ Message::End(_pointless_value) => {
+ println!("{{");
+ for (mode, trie) in store.map {
+ println!(" \"{mode}\" = {{");
+ {
+ let mut io: Option<Vec<String>> = None;
+ for (key, node) in trie.iter() {
+ if is_open!(io, @query key[..4]) {
+ is_open!(io, @close_till key 4);
+ print_key(key, node);
+ } else if is_open!(io, @query key[..3]) {
+ is_open!(io, @close_till key 3);
+ is_open!(io, @set_element key[4]);
+ print_key(key, node);
+ } else if is_open!(io, @query key[..2]) {
+ is_open!(io, @close_till key 2);
+ is_open!(io, @set_element key[3]);
+ is_open!(io, @set_element key[4]);
+ print_key(key, node);
+ } else if is_open!(io, @query key[..1]) {
+ is_open!(io, @close_till key 1);
+ is_open!(io, @set_element key[2]);
+ is_open!(io, @set_element key[3]);
+ is_open!(io, @set_element key[4]);
+ print_key(key, node);
+ } else {
+ {
+ eprint!("No query matched: '{}'", key.join(","));
+ if let Some(io) = &io {
+ eprintln!(" (io: '{}')", io.join(","));
+ } else {
+ eprintln!(" (io empty)");
+ }
+ }
+
+ is_open!(io, @close_all);
+ is_open!(io, @set_element key[1]);
+ is_open!(io, @set_element key[2]);
+ is_open!(io, @set_element key[3]);
+ is_open!(io, @set_element key[4]);
+ print_key(key, node);
+ }
+ }
+ is_open!(io, @close_all);
+ }
+ println!(" }};");
+ }
+ println!("}}");
+
+ break;
+ }
+ }
+ }
+ Err(_) => {
+ /* connection failed */
+ break;
+ }
+ }
+ }
+ Ok(())
+}