use crate::{CommandId, InputContext, KeyChord};
use std::collections::HashSet;
use std::sync::Arc;
use super::{Binding, Keymap, KeymapContinuation, SequenceMatch};
impl Keymap {
pub fn empty() -> Self {
Self::default()
}
pub fn push_binding(&mut self, binding: Binding) {
self.bindings.push(binding);
}
pub fn resolve(&self, ctx: &InputContext, chord: KeyChord) -> Option<CommandId> {
self.resolve_with_key_contexts(ctx, &[], chord)
}
pub fn resolve_with_key_contexts(
&self,
ctx: &InputContext,
key_contexts: &[Arc<str>],
chord: KeyChord,
) -> Option<CommandId> {
for b in self.bindings.iter().rev() {
if b.sequence.as_slice() != [chord] {
continue;
}
if !b.platform.matches(ctx.platform) {
continue;
}
if let Some(expr) = b.when.as_ref()
&& !expr.eval_with_key_contexts(ctx, key_contexts)
{
continue;
}
return b.command.clone();
}
None
}
pub fn match_sequence(&self, ctx: &InputContext, sequence: &[KeyChord]) -> SequenceMatch {
self.match_sequence_with_key_contexts(ctx, &[], sequence)
}
pub fn match_sequence_with_key_contexts(
&self,
ctx: &InputContext,
key_contexts: &[Arc<str>],
sequence: &[KeyChord],
) -> SequenceMatch {
let mut exact: Option<Option<CommandId>> = None;
let mut has_continuation = false;
let mut seen: HashSet<Vec<KeyChord>> = HashSet::new();
for b in self.bindings.iter().rev() {
if !b.platform.matches(ctx.platform) {
continue;
}
if let Some(expr) = b.when.as_ref()
&& !expr.eval_with_key_contexts(ctx, key_contexts)
{
continue;
}
if b.sequence.len() < sequence.len() {
continue;
}
if b.sequence.get(0..sequence.len()) != Some(sequence) {
continue;
}
if !seen.insert(b.sequence.clone()) {
continue;
}
if b.sequence.len() == sequence.len() {
if exact.is_none() {
exact = Some(b.command.clone());
}
} else if b.command.is_some() {
has_continuation = true;
}
if exact.is_some() && has_continuation {
break;
}
}
SequenceMatch {
exact,
has_continuation,
}
}
pub fn continuations(
&self,
ctx: &InputContext,
prefix: &[KeyChord],
) -> Vec<KeymapContinuation> {
self.continuations_with_key_contexts(ctx, &[], prefix)
}
pub fn continuations_with_key_contexts(
&self,
ctx: &InputContext,
key_contexts: &[Arc<str>],
prefix: &[KeyChord],
) -> Vec<KeymapContinuation> {
if prefix.is_empty() {
return Vec::new();
}
let mut candidates: Vec<KeyChord> = Vec::new();
let mut seen: HashSet<KeyChord> = HashSet::new();
for b in self.bindings.iter().rev() {
if !b.platform.matches(ctx.platform) {
continue;
}
if let Some(expr) = b.when.as_ref()
&& !expr.eval_with_key_contexts(ctx, key_contexts)
{
continue;
}
if b.sequence.len() <= prefix.len() {
continue;
}
if b.sequence.get(0..prefix.len()) != Some(prefix) {
continue;
}
let next = b.sequence[prefix.len()];
if seen.insert(next) {
candidates.push(next);
}
}
let mut out: Vec<KeymapContinuation> = Vec::new();
for next in candidates {
let mut seq: Vec<KeyChord> = Vec::with_capacity(prefix.len() + 1);
seq.extend_from_slice(prefix);
seq.push(next);
let matched = self.match_sequence_with_key_contexts(ctx, key_contexts, &seq);
let exact_command = matched.exact.clone().flatten();
if exact_command.is_some() || matched.has_continuation {
out.push(KeymapContinuation { next, matched });
}
}
out
}
pub fn extend(&mut self, other: Keymap) {
self.bindings.extend(other.bindings);
}
}