Skip to content

Commit

Permalink
feat: dynamic keymaps in menus
Browse files Browse the repository at this point in the history
  • Loading branch information
justinpombrio committed Apr 11, 2024
1 parent d654e65 commit cd302bd
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 21 deletions.
5 changes: 0 additions & 5 deletions src/editor/keymap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,6 @@ use crate::util::{bug, OrderedMap, SynlessBug};
use std::borrow::Borrow;
use std::collections::HashMap;

// TODO: merging
// - Layer::merge(layers) -> Layer
// - Keymap::merge(keymaps) -> Keymap
// - OrderedMap::merge(ordered_maps) -> OrderedMap

/// Key bindings.
///
/// All methods that add bindings will overwrite existing bindings.
Expand Down
73 changes: 57 additions & 16 deletions src/editor/layer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ use crate::util::IndexedMap;
use std::collections::HashMap;

// TODO:
// - dynamic candidate lists
// - filtering by sort
// - docs
// - proofread keymap & layer
// - add tests

type MenuName = String;
type LayerIndex = usize;
Expand Down Expand Up @@ -66,7 +67,11 @@ pub struct LayerManager {
global_layers: Vec<LayerIndex>,
local_layers: HashMap<DocName, Vec<LayerIndex>>,
layers: IndexedMap<Layer>,
active_menu: Option<MenuName>,
/// - `None` if there's no active menu.
/// - `Some((MenuName, None))` if there's an active menu.
/// - `Some((MenuName, Some(Keymap)))` if there's an active menu
/// using the given dynamically constructed composite keymap.
active_menu: Option<(MenuName, Option<Keymap>)>,
cached_composite_layers: HashMap<Vec<LayerIndex>, Layer>,
}

Expand Down Expand Up @@ -131,8 +136,31 @@ impl LayerManager {
.flat_map(|layers| layers.iter().map(|i| self.layers[*i].name.as_ref()))
}

pub fn enter_menu(&mut self, menu_name: String) {
self.active_menu = Some(menu_name);
/// Open the named menu. If `dynamic_keymap` is `Some`, layer it on top of the existing
/// keymaps for the menu.
pub fn enter_menu(
&mut self,
doc_name: Option<&DocName>,
menu_name: String,
dynamic_keymap: Option<Keymap>,
) {
let keymap = if let Some(dynamic_keymap) = dynamic_keymap {
let composite_layer = self.composite_layer(doc_name);
if let Some(composite_keymap) = composite_layer
.keymaps
.get(&KeymapLabel::Menu(menu_name.clone()))
{
let mut keymap = composite_keymap.clone();
keymap.append(dynamic_keymap);
Some(keymap)
} else {
Some(dynamic_keymap)
}
} else {
None
};

self.active_menu = Some((menu_name, keymap));
}

pub fn exit_menu(&mut self) {
Expand Down Expand Up @@ -181,21 +209,42 @@ impl LayerManager {
/// Get a composite keymap that merges together all active keymaps for the given mode&document.
/// It is cached.
fn composite_keymap(&mut self, mode: Mode, doc_name: Option<&DocName>) -> Option<&Keymap> {
let keymap_label = self.active_keymap_label(mode);
let composite_layer = self.composite_layer(doc_name);
composite_layer.keymaps.get(&keymap_label)
// It would be nicer to just call `composite_layer()`, but the borrow checker
// dislikes that.
let layer_indices = match &self.active_menu {
None | Some((_, None)) => Some(self.cache_composite_layer(doc_name)),
Some((_menu_name, Some(_keymap))) => None,
};
match &self.active_menu {
None => {
let layer = &self.cached_composite_layers[&layer_indices.unwrap()];
layer.keymaps.get(&KeymapLabel::Mode(mode))
}
Some((menu_name, None)) => {
let layer = &self.cached_composite_layers[&layer_indices.unwrap()];
layer.keymaps.get(&KeymapLabel::Menu(menu_name.clone()))
}
Some((_menu_name, Some(keymap))) => Some(keymap),
}
}

/// Get a composite layer that merges together all active layers. It is cached.
fn composite_layer(&mut self, doc_name: Option<&DocName>) -> &Layer {
let layer_indices = self.cache_composite_layer(doc_name);
&self.cached_composite_layers[&layer_indices]
}

/// Cache a composite layer that merges together all active layers. It can subsequently be
/// found by looking up the return value (layer indices) in `cached_composite_layers`.
fn cache_composite_layer(&mut self, doc_name: Option<&DocName>) -> Vec<usize> {
let layer_indices = self.active_layers(doc_name).collect::<Vec<_>>();
if !self.cached_composite_layers.contains_key(&layer_indices) {
let layers_iter = layer_indices.iter().map(|i| &self.layers[*i]).cloned();
let composite_layer = Layer::merge("COMPOSITE_LAYER".to_owned(), layers_iter);
self.cached_composite_layers
.insert(layer_indices.clone(), composite_layer);
}
&self.cached_composite_layers[&layer_indices]
layer_indices
}

/// Iterates over layers, yielding the lowest priority layers first.
Expand All @@ -208,14 +257,6 @@ impl LayerManager {

local_layer_indices.chain(global_layer_indices).copied()
}

fn active_keymap_label(&self, mode: Mode) -> KeymapLabel {
if let Some(menu_name) = &self.active_menu {
KeymapLabel::Menu(menu_name.clone())
} else {
KeymapLabel::Mode(mode)
}
}
}

fn add_layer(
Expand Down

0 comments on commit cd302bd

Please sign in to comment.