1
1
mirror of https://github.com/wez/wezterm.git synced 2024-11-25 21:07:39 +03:00

wezterm show-keys --lua to print a lua version of the config

This is handy for eg: copying and pasting out the default config.

I think I'll use this to update some parts of the docs in a more
automated fashion.
This commit is contained in:
Wez Furlong 2022-08-03 18:14:00 -07:00
parent dfe4d8cb79
commit decde7c3be
3 changed files with 148 additions and 3 deletions

View File

@ -336,4 +336,8 @@ pub struct LsFontsCommand {
}
#[derive(Debug, Parser, Clone)]
pub struct ShowKeysCommand {}
pub struct ShowKeysCommand {
/// Show the keys as lua config statements
#[clap(long = "lua")]
pub lua: bool,
}

View File

@ -6,6 +6,7 @@ use config::keyassignment::{
use config::{ConfigHandle, MouseEventAltScreen, MouseEventTriggerMods};
use std::collections::{BTreeMap, HashMap};
use std::time::Duration;
use wezterm_dynamic::{ToDynamic, Value};
use wezterm_term::input::MouseButton;
use window::{KeyCode, Modifiers};
@ -390,6 +391,32 @@ impl InputMap {
self.mouse.get(&(event, mods)).cloned()
}
pub fn dump_config(&self) {
println!("local wezterm = require 'wezterm'");
println!("local act = wezterm.action");
println!();
println!("return {{");
println!(" keys = {{");
show_key_table_as_lua(&self.keys.default, 4);
println!(" }},");
let mut table_names = self.keys.by_name.keys().collect::<Vec<_>>();
table_names.sort();
println!();
println!(" key_tables = {{");
for name in table_names {
if let Some(table) = self.keys.by_name.get(name) {
println!(" {name} = {{");
show_key_table_as_lua(table, 6);
println!(" }},");
println!();
}
}
println!(" }}");
println!("}}");
}
pub fn show_keys(&self) {
if let Some((key, mods, duration)) = &self.leader {
println!("Leader: {key:?} {mods:?} {duration:?}");
@ -483,6 +510,106 @@ fn human_key(key: &KeyCode) -> String {
}
}
fn lua_key_code(key: &KeyCode) -> String {
match key {
KeyCode::Char('\x1b') => "Escape".to_string(),
KeyCode::Char('\x7f') => "Escape".to_string(),
KeyCode::Char('\x08') => "Backspace".to_string(),
KeyCode::Char('\r') => "Enter".to_string(),
KeyCode::Char(' ') => "Space".to_string(),
KeyCode::Char('\t') => "Tab".to_string(),
KeyCode::Char(c) if c.is_ascii_control() => c.escape_debug().to_string(),
KeyCode::Char(c) => c.to_string(),
KeyCode::Function(n) => format!("F{n}"),
KeyCode::Numpad(n) => format!("Numpad{n}"),
KeyCode::Physical(phys) => format!("phys:{}", phys.to_string()),
_ => format!("{key:?}"),
}
}
fn luaify(value: Value, is_top: bool) -> String {
match value {
Value::String(s) if is_top => format!("act.{s}"),
Value::String(s) => quote_lua_string(&s),
Value::Bool(true) => "true".to_string(),
Value::Bool(false) => "false".to_string(),
Value::Null => "nil".to_string(),
Value::U64(u) => u.to_string(),
Value::F64(u) => u.to_string(),
Value::I64(u) => u.to_string(),
Value::Array(a) => {
format!("wat {a:?}")
}
Value::Object(o) if is_top => {
for (k, v) in o {
let k = match k {
Value::String(s) => s,
_ => unreachable!(),
};
let arg = match v {
Value::String(_) => format!(" {}", luaify(v, false)),
Value::Array(a) => {
let b: Vec<String> = a.into_iter().map(|v| luaify(v, false)).collect();
format!("{{ {} }}", b.join(", "))
}
Value::I64(i) => format!("({i})"),
Value::U64(i) => format!("({i})"),
Value::F64(i) => format!("({i})"),
_ => luaify(v, false),
};
return format!("act.{k}{arg}");
}
unreachable!()
}
Value::Object(o) => {
let mut fields = vec![];
for (k, v) in o {
let k = match k {
Value::String(s) => s,
_ => unreachable!(),
};
let arg = match v {
Value::Null => continue,
Value::String(_) => format!(" {}", luaify(v, false)),
Value::Array(a) => {
let b: Vec<String> = a.into_iter().map(|v| luaify(v, false)).collect();
format!("{{ {} }}", b.join(", "))
}
Value::I64(i) => format!("({i})"),
Value::U64(i) => format!("({i})"),
Value::F64(i) => format!("({i})"),
Value::Object(o) if o.is_empty() => continue,
_ => luaify(v, false),
};
fields.push(format!("{k} = {arg}"));
}
format!("{{ {} }}", fields.join(", "))
}
}
}
fn quote_lua_string(s: &str) -> String {
if s.contains('\'') && !s.contains('"') {
format!("\"{s}\"")
} else if s.contains('\'') {
format!("\"{}\"", s.escape_debug())
} else {
format!("'{s}'")
}
}
fn lua_key(key: &KeyCode, mods: Modifiers, action: &KeyAssignment) -> String {
let dyn_action = action.to_dynamic();
// println!(" -- {dyn_action:?}");
let action = luaify(dyn_action, true);
let key = lua_key_code(key);
let key = quote_lua_string(&key);
let mods = format!("{mods:?}").replace(" ", "");
format!("{{ key = {key}, mods = '{mods}', action = {action} }}")
}
fn show_key_table(table: &config::keyassignment::KeyTable) {
let ordered = table.iter().collect::<BTreeMap<_, _>>();
@ -504,3 +631,13 @@ fn show_key_table(table: &config::keyassignment::KeyTable) {
println!("\t{mods:mod_width$} {key:key_width$} -> {action:?}");
}
}
fn show_key_table_as_lua(table: &config::keyassignment::KeyTable, indent: usize) {
let ordered = table.iter().collect::<BTreeMap<_, _>>();
let pad = " ".repeat(indent);
for ((key, mods), entry) in ordered {
let action = &entry.action;
println!("{pad}{},", lua_key(key, *mods, action));
}
}

View File

@ -739,9 +739,13 @@ fn maybe_show_configuration_error_window() {
}
}
fn run_show_keys(config: config::ConfigHandle, _cmd: &ShowKeysCommand) -> anyhow::Result<()> {
fn run_show_keys(config: config::ConfigHandle, cmd: &ShowKeysCommand) -> anyhow::Result<()> {
let map = crate::inputmap::InputMap::new(&config);
map.show_keys();
if cmd.lua {
map.dump_config();
} else {
map.show_keys();
}
Ok(())
}