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 pub has_lsp_config: bool,
64}
65
66#[derive(Debug, Clone)]
68pub struct BufferInfo {
69 pub id: usize,
70 pub path: String,
71 pub name: String,
72 pub modified: bool,
73}
74
75pub trait QuickOpenProvider: Send + Sync {
80 fn prefix(&self) -> &str;
83
84 fn name(&self) -> &str;
86
87 fn hint(&self) -> &str;
89
90 fn suggestions(&self, query: &str, context: &QuickOpenContext) -> Vec<Suggestion>;
94
95 fn on_select(
100 &self,
101 selected_index: Option<usize>,
102 query: &str,
103 context: &QuickOpenContext,
104 ) -> QuickOpenResult;
105
106 fn preview(
109 &self,
110 _selected_index: usize,
111 _context: &QuickOpenContext,
112 ) -> Option<(String, Option<usize>)> {
113 None
114 }
115}
116
117pub struct QuickOpenRegistry {
119 providers: HashMap<String, Box<dyn QuickOpenProvider>>,
121 prefix_order: Vec<String>,
123}
124
125impl QuickOpenRegistry {
126 pub fn new() -> Self {
127 Self {
128 providers: HashMap::new(),
129 prefix_order: Vec::new(),
130 }
131 }
132
133 pub fn register(&mut self, provider: Box<dyn QuickOpenProvider>) {
137 let prefix = provider.prefix().to_string();
138 if !self.prefix_order.contains(&prefix) {
139 self.prefix_order.push(prefix.clone());
140 }
141 self.providers.insert(prefix, provider);
142 }
143
144 pub fn get_provider_for_input<'a>(
148 &'a self,
149 input: &'a str,
150 ) -> Option<(&'a dyn QuickOpenProvider, &'a str)> {
151 let mut prefixes: Vec<_> = self.providers.keys().collect();
153 prefixes.sort_by(|a, b| b.len().cmp(&a.len()));
154
155 for prefix in prefixes {
156 if prefix.is_empty() {
157 continue; }
159 if input.starts_with(prefix.as_str()) {
160 let query = &input[prefix.len()..];
161 return self.providers.get(prefix).map(|p| (p.as_ref(), query));
162 }
163 }
164
165 self.providers.get("").map(|p| (p.as_ref(), input))
167 }
168
169 pub fn get_default_provider(&self) -> Option<&dyn QuickOpenProvider> {
171 self.providers.get("").map(|p| p.as_ref())
172 }
173
174 pub fn get_hints(&self) -> String {
176 self.prefix_order
177 .iter()
178 .filter_map(|prefix| self.providers.get(prefix).map(|p| p.hint()))
179 .collect::<Vec<_>>()
180 .join(" ")
181 }
182
183 pub fn prefixes(&self) -> Vec<&str> {
185 self.providers.keys().map(|s| s.as_str()).collect()
186 }
187}
188
189impl Default for QuickOpenRegistry {
190 fn default() -> Self {
191 Self::new()
192 }
193}
194
195#[cfg(test)]
196mod tests {
197 use super::*;
198
199 struct TestProvider {
200 prefix: String,
201 }
202
203 impl QuickOpenProvider for TestProvider {
204 fn prefix(&self) -> &str {
205 &self.prefix
206 }
207
208 fn name(&self) -> &str {
209 "Test"
210 }
211
212 fn hint(&self) -> &str {
213 "Test hint"
214 }
215
216 fn suggestions(&self, _query: &str, _context: &QuickOpenContext) -> Vec<Suggestion> {
217 vec![]
218 }
219
220 fn on_select(
221 &self,
222 _selected_index: Option<usize>,
223 _query: &str,
224 _context: &QuickOpenContext,
225 ) -> QuickOpenResult {
226 QuickOpenResult::None
227 }
228 }
229
230 #[test]
231 fn test_provider_routing() {
232 let mut registry = QuickOpenRegistry::new();
233
234 registry.register(Box::new(TestProvider {
235 prefix: "".to_string(),
236 }));
237 registry.register(Box::new(TestProvider {
238 prefix: ">".to_string(),
239 }));
240 registry.register(Box::new(TestProvider {
241 prefix: "#".to_string(),
242 }));
243
244 let (provider, query) = registry.get_provider_for_input("hello").unwrap();
246 assert_eq!(provider.prefix(), "");
247 assert_eq!(query, "hello");
248
249 let (provider, query) = registry.get_provider_for_input(">save").unwrap();
251 assert_eq!(provider.prefix(), ">");
252 assert_eq!(query, "save");
253
254 let (provider, query) = registry.get_provider_for_input("#main").unwrap();
256 assert_eq!(provider.prefix(), "#");
257 assert_eq!(query, "main");
258 }
259}