fresh/input/quick_open/
mod.rs1pub mod providers;
15
16pub use providers::{BufferProvider, CommandProvider, FileProvider, GotoLineProvider};
17
18use crate::input::commands::Suggestion;
19use crate::input::keybindings::Action;
20use std::collections::HashMap;
21
22#[derive(Debug, Clone)]
24pub enum QuickOpenResult {
25 ExecuteAction(Action),
27 OpenFile {
29 path: String,
30 line: Option<usize>,
31 column: Option<usize>,
32 },
33 ShowBuffer(usize),
35 GotoLine(usize),
37 None,
39 Error(String),
41}
42
43#[derive(Debug, Clone)]
45pub struct QuickOpenContext {
46 pub cwd: String,
48 pub open_buffers: Vec<BufferInfo>,
50 pub active_buffer_id: usize,
52 pub active_buffer_path: Option<String>,
54 pub has_selection: bool,
56 pub key_context: crate::input::keybindings::KeyContext,
58 pub custom_contexts: std::collections::HashSet<String>,
60 pub buffer_mode: Option<String>,
62}
63
64#[derive(Debug, Clone)]
66pub struct BufferInfo {
67 pub id: usize,
68 pub path: String,
69 pub name: String,
70 pub modified: bool,
71}
72
73pub trait QuickOpenProvider: Send + Sync {
78 fn prefix(&self) -> &str;
81
82 fn name(&self) -> &str;
84
85 fn hint(&self) -> &str;
87
88 fn suggestions(&self, query: &str, context: &QuickOpenContext) -> Vec<Suggestion>;
92
93 fn on_select(
98 &self,
99 selected_index: Option<usize>,
100 query: &str,
101 context: &QuickOpenContext,
102 ) -> QuickOpenResult;
103
104 fn preview(
107 &self,
108 _selected_index: usize,
109 _context: &QuickOpenContext,
110 ) -> Option<(String, Option<usize>)> {
111 None
112 }
113}
114
115pub struct QuickOpenRegistry {
117 providers: HashMap<String, Box<dyn QuickOpenProvider>>,
119 prefix_order: Vec<String>,
121}
122
123impl QuickOpenRegistry {
124 pub fn new() -> Self {
125 Self {
126 providers: HashMap::new(),
127 prefix_order: Vec::new(),
128 }
129 }
130
131 pub fn register(&mut self, provider: Box<dyn QuickOpenProvider>) {
135 let prefix = provider.prefix().to_string();
136 if !self.prefix_order.contains(&prefix) {
137 self.prefix_order.push(prefix.clone());
138 }
139 self.providers.insert(prefix, provider);
140 }
141
142 pub fn get_provider_for_input<'a>(
146 &'a self,
147 input: &'a str,
148 ) -> Option<(&'a dyn QuickOpenProvider, &'a str)> {
149 let mut prefixes: Vec<_> = self.providers.keys().collect();
151 prefixes.sort_by(|a, b| b.len().cmp(&a.len()));
152
153 for prefix in prefixes {
154 if prefix.is_empty() {
155 continue; }
157 if input.starts_with(prefix.as_str()) {
158 let query = &input[prefix.len()..];
159 return self.providers.get(prefix).map(|p| (p.as_ref(), query));
160 }
161 }
162
163 self.providers.get("").map(|p| (p.as_ref(), input))
165 }
166
167 pub fn get_default_provider(&self) -> Option<&dyn QuickOpenProvider> {
169 self.providers.get("").map(|p| p.as_ref())
170 }
171
172 pub fn get_hints(&self) -> String {
174 self.prefix_order
175 .iter()
176 .filter_map(|prefix| self.providers.get(prefix).map(|p| p.hint()))
177 .collect::<Vec<_>>()
178 .join(" ")
179 }
180
181 pub fn prefixes(&self) -> Vec<&str> {
183 self.providers.keys().map(|s| s.as_str()).collect()
184 }
185}
186
187impl Default for QuickOpenRegistry {
188 fn default() -> Self {
189 Self::new()
190 }
191}
192
193#[cfg(test)]
194mod tests {
195 use super::*;
196
197 struct TestProvider {
198 prefix: String,
199 }
200
201 impl QuickOpenProvider for TestProvider {
202 fn prefix(&self) -> &str {
203 &self.prefix
204 }
205
206 fn name(&self) -> &str {
207 "Test"
208 }
209
210 fn hint(&self) -> &str {
211 "Test hint"
212 }
213
214 fn suggestions(&self, _query: &str, _context: &QuickOpenContext) -> Vec<Suggestion> {
215 vec![]
216 }
217
218 fn on_select(
219 &self,
220 _selected_index: Option<usize>,
221 _query: &str,
222 _context: &QuickOpenContext,
223 ) -> QuickOpenResult {
224 QuickOpenResult::None
225 }
226 }
227
228 #[test]
229 fn test_provider_routing() {
230 let mut registry = QuickOpenRegistry::new();
231
232 registry.register(Box::new(TestProvider {
233 prefix: "".to_string(),
234 }));
235 registry.register(Box::new(TestProvider {
236 prefix: ">".to_string(),
237 }));
238 registry.register(Box::new(TestProvider {
239 prefix: "#".to_string(),
240 }));
241
242 let (provider, query) = registry.get_provider_for_input("hello").unwrap();
244 assert_eq!(provider.prefix(), "");
245 assert_eq!(query, "hello");
246
247 let (provider, query) = registry.get_provider_for_input(">save").unwrap();
249 assert_eq!(provider.prefix(), ">");
250 assert_eq!(query, "save");
251
252 let (provider, query) = registry.get_provider_for_input("#main").unwrap();
254 assert_eq!(provider.prefix(), "#");
255 assert_eq!(query, "main");
256 }
257}