reovim_plugin_pickers/
keymaps.rs1use std::{future::Future, pin::Pin};
4
5use {
6 reovim_core::bind::{CommandRef, KeyMap, KeymapScope},
7 reovim_plugin_microscope::{
8 MicroscopeAction, MicroscopeData, MicroscopeItem, Picker, PickerContext, PreviewContent,
9 },
10};
11
12#[derive(Debug, Clone)]
14pub struct KeymapEntry {
15 pub mode: String,
17 pub key: String,
19 pub command: String,
21 pub description: Option<String>,
23}
24
25pub struct KeymapsPicker {
27 keymaps: Vec<KeymapEntry>,
29}
30
31impl KeymapsPicker {
32 #[must_use]
34 pub fn new() -> Self {
35 let keymap = KeyMap::with_defaults();
36 let keymaps = Self::extract_keymaps(&keymap);
37 Self { keymaps }
38 }
39
40 fn extract_keymaps(keymap: &KeyMap) -> Vec<KeymapEntry> {
42 let mut entries = Vec::new();
43
44 for (scope, map) in keymap.iter_scopes() {
45 let mode_name = Self::scope_to_mode_name(scope);
46 for (key, inner) in map {
47 if let Some(cmd_ref) = &inner.command {
48 let command = match cmd_ref {
49 CommandRef::Registered(id) => id.as_str().to_string(),
50 CommandRef::Inline(cmd) => cmd.name().to_string(),
51 };
52 entries.push(KeymapEntry {
53 mode: mode_name.to_string(),
54 key: key.to_string(),
55 command,
56 description: inner.description.clone(),
57 });
58 }
59 }
60 }
61
62 entries.sort_by(|a, b| a.mode.cmp(&b.mode).then_with(|| a.key.cmp(&b.key)));
64
65 entries
66 }
67
68 const fn scope_to_mode_name(scope: &KeymapScope) -> &'static str {
73 use reovim_core::bind::{EditModeKind, SubModeKind};
74
75 match scope {
76 KeymapScope::Component { id: _, mode } => {
77 match mode {
80 EditModeKind::Normal => "Normal",
81 EditModeKind::Insert => "Insert",
82 EditModeKind::Visual => "Visual",
83 }
84 }
85 KeymapScope::SubMode(submode) => match submode {
86 SubModeKind::Command => "Command",
87 SubModeKind::OperatorPending => "Operator",
88 SubModeKind::Interactor(_) => "Interactor",
90 },
91 KeymapScope::DefaultNormal => "Default (Normal)",
92 }
93 }
94
95 pub fn set_keymaps(&mut self, keymaps: Vec<KeymapEntry>) {
97 self.keymaps = keymaps;
98 }
99}
100
101impl Default for KeymapsPicker {
102 fn default() -> Self {
103 Self::new()
104 }
105}
106
107impl Picker for KeymapsPicker {
108 fn name(&self) -> &'static str {
109 "keymaps"
110 }
111
112 fn title(&self) -> &'static str {
113 "Keymaps"
114 }
115
116 fn prompt(&self) -> &'static str {
117 "Keymaps> "
118 }
119
120 fn fetch(
121 &self,
122 _ctx: &PickerContext,
123 ) -> Pin<Box<dyn Future<Output = Vec<MicroscopeItem>> + Send + '_>> {
124 Box::pin(async move {
125 self.keymaps
126 .iter()
127 .map(|km| {
128 let display = format!("[{}] {} -> {}", km.mode, km.key, km.command);
129 MicroscopeItem::new(
130 &display,
131 &display,
132 MicroscopeData::Keymap {
133 mode: km.mode.clone(),
134 key: km.key.clone(),
135 command: km.command.clone(),
136 },
137 "keymaps",
138 )
139 .with_detail(km.description.as_deref().unwrap_or(""))
140 })
141 .collect()
142 })
143 }
144
145 fn on_select(&self, _item: &MicroscopeItem) -> MicroscopeAction {
146 MicroscopeAction::Close
148 }
149
150 fn preview(
151 &self,
152 item: &MicroscopeItem,
153 _ctx: &PickerContext,
154 ) -> Pin<Box<dyn Future<Output = Option<PreviewContent>> + Send + '_>> {
155 let data = item.data.clone();
156 let description = item.detail.clone();
157
158 Box::pin(async move {
159 if let MicroscopeData::Keymap { mode, key, command } = data {
160 let mut lines = vec![
161 "Keymap Details".to_string(),
162 "==============".to_string(),
163 String::new(),
164 format!("Mode: {mode}"),
165 format!("Key: {key}"),
166 format!("Command: {command}"),
167 ];
168
169 if let Some(desc) = description {
170 lines.push(String::new());
171 lines.push(format!("Description: {desc}"));
172 }
173
174 Some(PreviewContent::new(lines))
175 } else {
176 None
177 }
178 })
179 }
180}