use std::collections::{BTreeMap, HashMap};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use crate::shortcut::{Chord, Platform, parse_list};
use crate::{Host, Registry};
const MAX_OVERRIDES: usize = 2000;
const MAX_KEYS: usize = 8;
const MAX_ID_LEN: usize = 128;
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
pub struct Overrides {
#[serde(default)]
pub bindings: BTreeMap<String, Vec<String>>,
}
#[derive(Debug, Clone, PartialEq, Serialize)]
pub struct Conflict {
pub chord: Chord,
pub ids: Vec<String>,
}
#[derive(Debug, Clone)]
struct Binding {
id: String,
user: bool,
order: usize,
}
#[derive(Debug, Clone)]
pub struct Keymap {
platform: Platform,
defaults: Vec<(String, Vec<Chord>)>,
positions: HashMap<String, usize>,
user: BTreeMap<String, Vec<Chord>>,
index: HashMap<Chord, Vec<Binding>>,
invalid: Vec<(String, String)>,
}
impl Keymap {
pub fn new<'a>(platform: Platform, defaults: impl IntoIterator<Item = (&'a str, Option<&'a str>)>) -> Self {
let mut map = Keymap { platform, defaults: vec![], positions: HashMap::new(), user: BTreeMap::new(), index: HashMap::new(), invalid: vec![] };
for (id, text) in defaults {
if map.positions.contains_key(id) {
continue;
}
let chords = match text.map(parse_list) {
None => vec![],
Some(Ok(c)) => c,
Some(Err(_)) => {
map.invalid.push((id.to_string(), text.unwrap_or_default().to_string()));
vec![]
}
};
map.positions.insert(id.to_string(), map.defaults.len());
map.defaults.push((id.to_string(), chords));
}
map.reindex();
map
}
pub fn from_registry<S: Host>(registry: &Registry<S>, platform: Platform) -> Self {
Self::new(platform, registry.specs().iter().map(|c| (c.id, c.shortcut)))
}
pub fn platform(&self) -> Platform {
self.platform
}
pub fn invalid_defaults(&self) -> &[(String, String)] {
&self.invalid
}
fn reindex(&mut self) {
self.index.clear();
let user = self.user.iter().map(|(id, c)| (id, c, true));
let defaults = self.defaults.iter().filter(|(id, _)| !self.user.contains_key(id)).map(|(id, c)| (id, c, false));
for (id, chords, is_user) in user.chain(defaults) {
let order = self.positions.get(id).copied().unwrap_or(usize::MAX);
for chord in chords {
let list = self.index.entry(chord.on(self.platform)).or_default();
if !list.iter().any(|b| &b.id == id) {
list.push(Binding { id: id.clone(), user: is_user, order });
}
}
}
for list in self.index.values_mut() {
list.sort_by_key(|b| (!b.user, b.order));
}
}
pub fn lookup(&self, chord: &Chord) -> Option<&str> {
self.index.get(&chord.on(self.platform)).and_then(|l| l.first()).map(|b| b.id.as_str())
}
pub fn commands_for(&self, chord: &Chord) -> Vec<&str> {
self.index.get(&chord.on(self.platform)).map(|l| l.iter().map(|b| b.id.as_str()).collect()).unwrap_or_default()
}
pub fn keys_for(&self, id: &str) -> &[Chord] {
if let Some(keys) = self.user.get(id) {
return keys;
}
self.positions.get(id).and_then(|&i| self.defaults.get(i)).map(|(_, c)| c.as_slice()).unwrap_or_default()
}
pub fn default_keys(&self, id: &str) -> &[Chord] {
self.positions.get(id).and_then(|&i| self.defaults.get(i)).map(|(_, c)| c.as_slice()).unwrap_or_default()
}
pub fn knows(&self, id: &str) -> bool {
self.positions.contains_key(id)
}
pub fn bind(&mut self, id: &str, mut keys: Vec<Chord>) -> bool {
if !self.knows(id) {
return false;
}
keys.truncate(MAX_KEYS);
keys.dedup();
if keys.as_slice() == self.default_keys(id) {
self.user.remove(id);
} else {
self.user.insert(id.to_string(), keys);
}
self.reindex();
true
}
pub fn reset(&mut self, id: &str) {
if self.user.remove(id).is_some() {
self.reindex();
}
}
pub fn reset_all(&mut self) {
self.user.clear();
self.reindex();
}
pub fn would_conflict(&self, id: &str, chord: &Chord) -> Vec<&str> {
self.commands_for(chord).into_iter().filter(|c| *c != id).collect()
}
pub fn conflicts(&self) -> Vec<Conflict> {
let mut out: Vec<Conflict> = self
.index
.iter()
.filter(|(_, l)| l.len() > 1)
.map(|(chord, l)| Conflict { chord: *chord, ids: l.iter().map(|b| b.id.clone()).collect() })
.collect();
out.sort_by_key(|c| c.chord);
out
}
pub fn overrides(&self) -> Overrides {
Overrides { bindings: self.user.iter().map(|(id, keys)| (id.clone(), keys.iter().map(Chord::portable).collect())).collect() }
}
pub fn apply_overrides(&mut self, overrides: &Overrides) -> usize {
let mut skipped = 0;
for (id, keys) in overrides.bindings.iter().take(MAX_OVERRIDES) {
if id.len() > MAX_ID_LEN || !self.knows(id) {
skipped += 1;
continue;
}
let mut chords = Vec::new();
for k in keys.iter().take(MAX_KEYS) {
match Chord::parse(k) {
Ok(c) => chords.push(c),
Err(_) => skipped += 1,
}
}
chords.dedup();
if chords.as_slice() == self.default_keys(id) {
self.user.remove(id);
} else {
self.user.insert(id.clone(), chords);
}
}
skipped += overrides.bindings.len().saturating_sub(MAX_OVERRIDES);
self.reindex();
skipped
}
pub fn apply_overrides_json(&mut self, value: &Value) -> usize {
let mut o = Overrides::default();
let Some(b) = value.get("bindings").and_then(Value::as_object) else { return 0 };
let mut skipped = 0;
for (id, keys) in b.iter().take(MAX_OVERRIDES) {
match keys.as_array() {
Some(a) => {
o.bindings.insert(id.clone(), a.iter().take(MAX_KEYS).filter_map(Value::as_str).map(str::to_string).collect());
}
None => skipped += 1,
}
}
skipped + self.apply_overrides(&o)
}
}