skim/
query.rs

1use std::mem;
2use std::sync::Arc;
3
4use tuikit::prelude::*;
5use unicode_width::UnicodeWidthStr;
6
7use crate::event::{Event, EventHandler, UpdateScreen};
8use crate::options::SkimOptions;
9use crate::theme::{ColorTheme, DEFAULT_THEME};
10use crate::util::clear_canvas;
11
12#[derive(Clone, Copy, PartialEq)]
13enum QueryMode {
14    Cmd,
15    Query,
16}
17
18pub struct Query {
19    cmd_before: Vec<char>,
20    cmd_after: Vec<char>,
21    fz_query_before: Vec<char>,
22    fz_query_after: Vec<char>,
23    yank: Vec<char>,
24
25    mode: QueryMode,
26    base_cmd: String,
27    replstr: String,
28    query_prompt: String,
29    cmd_prompt: String,
30
31    cmd_history_before: Vec<String>,
32    cmd_history_after: Vec<String>,
33    fz_query_history_before: Vec<String>,
34    fz_query_history_after: Vec<String>,
35
36    pasted: Option<String>,
37
38    theme: Arc<ColorTheme>,
39}
40
41#[allow(dead_code)]
42impl Query {
43    pub fn builder() -> Self {
44        Query {
45            cmd_before: Vec::new(),
46            cmd_after: Vec::new(),
47            fz_query_before: Vec::new(),
48            fz_query_after: Vec::new(),
49            yank: Vec::new(),
50            mode: QueryMode::Query,
51            base_cmd: String::new(),
52            replstr: "{}".to_string(),
53            query_prompt: "> ".to_string(),
54            cmd_prompt: "c> ".to_string(),
55
56            cmd_history_before: Vec::new(),
57            cmd_history_after: Vec::new(),
58            fz_query_history_before: Vec::new(),
59            fz_query_history_after: Vec::new(),
60
61            pasted: None,
62
63            theme: Arc::new(*DEFAULT_THEME),
64        }
65    }
66
67    pub fn from_options(options: &SkimOptions) -> Self {
68        let mut query = Self::builder();
69        query.parse_options(options);
70        query
71    }
72
73    pub fn replace_base_cmd_if_not_set(mut self, base_cmd: &str) -> Self {
74        if self.base_cmd.is_empty() {
75            self.base_cmd = base_cmd.to_owned();
76        }
77        self
78    }
79
80    pub fn fz_query(mut self, query: &str) -> Self {
81        self.fz_query_before = query.chars().collect();
82        self
83    }
84
85    pub fn theme(mut self, theme: Arc<ColorTheme>) -> Self {
86        self.theme = theme;
87        self
88    }
89
90    pub fn cmd_history(mut self, mut history: Vec<String>) -> Self {
91        self.cmd_history_before.append(&mut history);
92        self
93    }
94
95    pub fn fz_query_history(mut self, mut history: Vec<String>) -> Self {
96        self.fz_query_history_before.append(&mut history);
97        self
98    }
99
100    pub fn build(self) -> Self {
101        self
102    }
103
104    fn parse_options(&mut self, options: &SkimOptions) {
105        // some options accept multiple values, thus take the last one
106
107        if let Some(base_cmd) = options.cmd {
108            self.base_cmd = base_cmd.to_string();
109        }
110
111        if let Some(query) = options.query {
112            self.fz_query_before = query.chars().collect();
113        }
114
115        if let Some(cmd_query) = options.cmd_query {
116            self.cmd_before = cmd_query.chars().collect();
117        }
118
119        if let Some(replstr) = options.replstr {
120            self.replstr = replstr.to_string();
121        }
122
123        if options.interactive {
124            self.mode = QueryMode::Cmd;
125        }
126
127        if let Some(query_prompt) = options.prompt {
128            self.query_prompt = query_prompt.to_string();
129        }
130
131        if let Some(cmd_prompt) = options.cmd_prompt {
132            self.cmd_prompt = cmd_prompt.to_string();
133        }
134
135        self.fz_query_history_before = options.query_history.to_vec();
136        self.cmd_history_before = options.cmd_history.to_vec();
137    }
138
139    pub fn in_query_mode(&self) -> bool {
140        match self.mode {
141            QueryMode::Cmd => false,
142            QueryMode::Query => true,
143        }
144    }
145
146    pub fn get_fz_query(&self) -> String {
147        self.fz_query_before
148            .iter()
149            .cloned()
150            .chain(self.fz_query_after.iter().cloned().rev())
151            .collect()
152    }
153
154    pub fn get_cmd(&self) -> String {
155        let arg: String = self
156            .cmd_before
157            .iter()
158            .cloned()
159            .chain(self.cmd_after.iter().cloned().rev())
160            .collect();
161        self.base_cmd.replace(&self.replstr, &arg)
162    }
163
164    pub fn get_cmd_query(&self) -> String {
165        self.cmd_before
166            .iter()
167            .cloned()
168            .chain(self.cmd_after.iter().cloned().rev())
169            .collect()
170    }
171
172    fn get_query(&mut self) -> String {
173        match self.mode {
174            QueryMode::Query => self.get_fz_query(),
175            QueryMode::Cmd => self.get_cmd_query(),
176        }
177    }
178
179    fn get_before(&self) -> String {
180        match self.mode {
181            QueryMode::Cmd => self.cmd_before.iter().cloned().collect(),
182            QueryMode::Query => self.fz_query_before.iter().cloned().collect(),
183        }
184    }
185
186    fn get_after(&self) -> String {
187        match self.mode {
188            QueryMode::Cmd => self.cmd_after.iter().cloned().rev().collect(),
189            QueryMode::Query => self.fz_query_after.iter().cloned().rev().collect(),
190        }
191    }
192
193    fn get_prompt(&self) -> &str {
194        match self.mode {
195            QueryMode::Cmd => &self.cmd_prompt,
196            QueryMode::Query => &self.query_prompt,
197        }
198    }
199
200    fn get_query_ref(&mut self) -> (&mut Vec<char>, &mut Vec<char>) {
201        match self.mode {
202            QueryMode::Query => (&mut self.fz_query_before, &mut self.fz_query_after),
203            QueryMode::Cmd => (&mut self.cmd_before, &mut self.cmd_after),
204        }
205    }
206
207    fn get_history_ref(&mut self) -> (&mut Vec<String>, &mut Vec<String>) {
208        match self.mode {
209            QueryMode::Query => (&mut self.fz_query_history_before, &mut self.fz_query_history_after),
210            QueryMode::Cmd => (&mut self.cmd_history_before, &mut self.cmd_history_after),
211        }
212    }
213
214    fn save_yank(&mut self, mut yank: Vec<char>, reverse: bool) {
215        if yank.is_empty() {
216            return;
217        }
218
219        self.yank.clear();
220
221        if reverse {
222            self.yank.append(&mut yank.into_iter().rev().collect());
223        } else {
224            self.yank.append(&mut yank);
225        }
226    }
227
228    //------------------------------------------------------------------------------
229    // Actions
230    //
231    pub fn act_query_toggle_interactive(&mut self) {
232        self.mode = match self.mode {
233            QueryMode::Query => QueryMode::Cmd,
234            QueryMode::Cmd => QueryMode::Query,
235        }
236    }
237
238    pub fn act_add_char(&mut self, ch: char) {
239        let (before, _) = self.get_query_ref();
240        before.push(ch);
241    }
242
243    pub fn act_backward_delete_char(&mut self) {
244        let (before, _) = self.get_query_ref();
245        let _ = before.pop();
246    }
247
248    // delete char foraward
249    pub fn act_delete_char(&mut self) {
250        let (_, after) = self.get_query_ref();
251        let _ = after.pop();
252    }
253
254    pub fn act_backward_char(&mut self) {
255        let (before, after) = self.get_query_ref();
256        if let Some(ch) = before.pop() {
257            after.push(ch);
258        }
259    }
260
261    pub fn act_forward_char(&mut self) {
262        let (before, after) = self.get_query_ref();
263        if let Some(ch) = after.pop() {
264            before.push(ch);
265        }
266    }
267
268    pub fn act_unix_word_rubout(&mut self) {
269        let mut yank = Vec::new();
270
271        {
272            let (before, _) = self.get_query_ref();
273            // kill things other than whitespace
274            while !before.is_empty() && before[before.len() - 1].is_whitespace() {
275                yank.push(before.pop().unwrap());
276            }
277
278            // kill word until whitespace
279            while !before.is_empty() && !before[before.len() - 1].is_whitespace() {
280                yank.push(before.pop().unwrap());
281            }
282        }
283
284        self.save_yank(yank, true);
285    }
286
287    pub fn act_backward_kill_word(&mut self) {
288        let mut yank = Vec::new();
289
290        {
291            let (before, _) = self.get_query_ref();
292            // kill things other than alphanumeric
293            while !before.is_empty() && !before[before.len() - 1].is_alphanumeric() {
294                yank.push(before.pop().unwrap());
295            }
296
297            // kill word until whitespace (not alphanumeric)
298            while !before.is_empty() && before[before.len() - 1].is_alphanumeric() {
299                yank.push(before.pop().unwrap());
300            }
301        }
302
303        self.save_yank(yank, true);
304    }
305
306    pub fn act_kill_word(&mut self) {
307        let mut yank = Vec::new();
308
309        {
310            let (_, after) = self.get_query_ref();
311
312            // kill non alphanumeric
313            while !after.is_empty() && !after[after.len() - 1].is_alphanumeric() {
314                yank.push(after.pop().unwrap());
315            }
316            // kill alphanumeric
317            while !after.is_empty() && after[after.len() - 1].is_alphanumeric() {
318                yank.push(after.pop().unwrap());
319            }
320        }
321        self.save_yank(yank, false);
322    }
323
324    pub fn act_backward_word(&mut self) {
325        let (before, after) = self.get_query_ref();
326        // skip whitespace
327        while !before.is_empty() && !before[before.len() - 1].is_alphanumeric() {
328            if let Some(ch) = before.pop() {
329                after.push(ch);
330            }
331        }
332
333        // backword char until whitespace
334        while !before.is_empty() && before[before.len() - 1].is_alphanumeric() {
335            if let Some(ch) = before.pop() {
336                after.push(ch);
337            }
338        }
339    }
340
341    pub fn act_forward_word(&mut self) {
342        let (before, after) = self.get_query_ref();
343        // backword char until whitespace
344        // skip whitespace
345        while !after.is_empty() && after[after.len() - 1].is_whitespace() {
346            if let Some(ch) = after.pop() {
347                before.push(ch);
348            }
349        }
350
351        while !after.is_empty() && !after[after.len() - 1].is_whitespace() {
352            if let Some(ch) = after.pop() {
353                before.push(ch);
354            }
355        }
356    }
357
358    pub fn act_beginning_of_line(&mut self) {
359        let (before, after) = self.get_query_ref();
360        while !before.is_empty() {
361            if let Some(ch) = before.pop() {
362                after.push(ch);
363            }
364        }
365    }
366
367    pub fn act_end_of_line(&mut self) {
368        let (before, after) = self.get_query_ref();
369        while !after.is_empty() {
370            if let Some(ch) = after.pop() {
371                before.push(ch);
372            }
373        }
374    }
375
376    pub fn act_kill_line(&mut self) {
377        let (_, after) = self.get_query_ref();
378        let after = std::mem::take(after);
379        self.save_yank(after, false);
380    }
381
382    pub fn act_line_discard(&mut self) {
383        let (before, _) = self.get_query_ref();
384        let before = std::mem::take(before);
385        self.save_yank(before, false);
386    }
387
388    pub fn act_yank(&mut self) {
389        let yank = std::mem::take(&mut self.yank);
390        for &c in &yank {
391            self.act_add_char(c);
392        }
393        let _ = mem::replace(&mut self.yank, yank);
394    }
395
396    pub fn previous_history(&mut self) {
397        let current_query = self.get_query();
398        let (history_before, history_after) = self.get_history_ref();
399        if let Some(history) = history_before.pop() {
400            history_after.push(current_query);
401
402            // store history into current query
403            let (query_before, _) = self.get_query_ref();
404            query_before.clear();
405            let mut new_query_chars = history.chars().collect();
406            query_before.append(&mut new_query_chars);
407        }
408    }
409
410    pub fn next_history(&mut self) {
411        let current_query = self.get_query();
412        let (history_before, history_after) = self.get_history_ref();
413        if let Some(history) = history_after.pop() {
414            history_before.push(current_query);
415
416            // store history into current query
417            let (query_before, _) = self.get_query_ref();
418            query_before.clear();
419            let mut new_query_chars = history.chars().collect();
420            query_before.append(&mut new_query_chars);
421        }
422    }
423
424    fn query_changed(
425        &self,
426        mode: QueryMode,
427        query_before_len: usize,
428        query_after_len: usize,
429        cmd_before_len: usize,
430        cmd_after_len: usize,
431    ) -> bool {
432        self.mode != mode
433            || self.fz_query_before.len() != query_before_len
434            || self.fz_query_after.len() != query_after_len
435            || self.cmd_before.len() != cmd_before_len
436            || self.cmd_after.len() != cmd_after_len
437    }
438}
439
440impl EventHandler for Query {
441    fn handle(&mut self, event: &Event) -> UpdateScreen {
442        use crate::event::Event::*;
443
444        let mode = self.mode;
445        let query_before_len = self.fz_query_before.len();
446        let query_after_len = self.fz_query_after.len();
447        let cmd_before_len = self.cmd_before.len();
448        let cmd_after_len = self.cmd_after.len();
449
450        match event {
451            EvActAddChar(ch) => match self.pasted.as_mut() {
452                Some(pasted) => pasted.push(*ch),
453                None => self.act_add_char(*ch),
454            },
455
456            EvActDeleteChar | EvActDeleteCharEOF => {
457                self.act_delete_char();
458            }
459
460            EvActBackwardChar => {
461                self.act_backward_char();
462            }
463
464            EvActBackwardDeleteChar => {
465                self.act_backward_delete_char();
466            }
467
468            EvActBackwardKillWord => {
469                self.act_backward_kill_word();
470            }
471
472            EvActBackwardWord => {
473                self.act_backward_word();
474            }
475
476            EvActBeginningOfLine => {
477                self.act_beginning_of_line();
478            }
479
480            EvActEndOfLine => {
481                self.act_end_of_line();
482            }
483
484            EvActForwardChar => {
485                self.act_forward_char();
486            }
487
488            EvActForwardWord => {
489                self.act_forward_word();
490            }
491
492            EvActKillLine => {
493                self.act_kill_line();
494            }
495
496            EvActKillWord => {
497                self.act_kill_word();
498            }
499
500            EvActPreviousHistory => self.previous_history(),
501
502            EvActNextHistory => {
503                self.next_history();
504            }
505
506            EvActUnixLineDiscard => {
507                self.act_line_discard();
508            }
509
510            EvActUnixWordRubout => {
511                self.act_unix_word_rubout();
512            }
513
514            EvActYank => {
515                self.act_yank();
516            }
517
518            EvActToggleInteractive => {
519                self.act_query_toggle_interactive();
520            }
521
522            EvInputKey(Key::BracketedPasteStart) => {
523                self.pasted.replace(String::new());
524            }
525
526            EvInputKey(Key::BracketedPasteEnd) => {
527                let pasted = self.pasted.take().unwrap_or_default();
528                for ch in pasted.chars() {
529                    self.act_add_char(ch);
530                }
531            }
532
533            _ => {}
534        }
535
536        if self.query_changed(mode, query_before_len, query_after_len, cmd_before_len, cmd_after_len) {
537            UpdateScreen::REDRAW
538        } else {
539            UpdateScreen::DONT_REDRAW
540        }
541    }
542}
543
544impl Draw for Query {
545    fn draw(&self, canvas: &mut dyn Canvas) -> DrawResult<()> {
546        canvas.clear()?;
547        let before = self.get_before();
548        let after = self.get_after();
549        let prompt = self.get_prompt();
550        clear_canvas(canvas)?;
551
552        let prompt_width = canvas.print_with_attr(0, 0, prompt, self.theme.prompt())?;
553        let before_width = canvas.print_with_attr(0, prompt_width, &before, self.theme.query())?;
554        let col = prompt_width + before_width;
555        canvas.print_with_attr(0, col, &after, self.theme.query())?;
556        canvas.set_cursor(0, col)?;
557        canvas.show_cursor(true)?;
558        Ok(())
559    }
560}
561
562impl Widget<Event> for Query {
563    fn size_hint(&self) -> (Option<usize>, Option<usize>) {
564        let before = self.get_before();
565        let after = self.get_after();
566        let prompt = self.get_prompt();
567        (Some(prompt.width() + before.width() + after.width() + 1), None)
568    }
569}
570
571#[cfg(test)]
572mod test {
573    use super::Query;
574
575    #[test]
576    fn test_new_query() {
577        let query1 = Query::builder().fz_query("").build();
578        assert_eq!(query1.get_fz_query(), "");
579
580        let query2 = Query::builder().fz_query("abc").build();
581        assert_eq!(query2.get_fz_query(), "abc");
582    }
583
584    #[test]
585    fn test_add_char() {
586        let mut query1 = Query::builder().fz_query("").build();
587        query1.act_add_char('a');
588        assert_eq!(query1.get_fz_query(), "a");
589        query1.act_add_char('b');
590        assert_eq!(query1.get_fz_query(), "ab");
591        query1.act_add_char('中');
592        assert_eq!(query1.get_fz_query(), "ab中");
593    }
594
595    #[test]
596    fn test_backward_delete_char() {
597        let mut query = Query::builder().fz_query("AB中c").build();
598        assert_eq!(query.get_fz_query(), "AB中c");
599
600        query.act_backward_delete_char();
601        assert_eq!(query.get_fz_query(), "AB中");
602
603        query.act_backward_delete_char();
604        assert_eq!(query.get_fz_query(), "AB");
605
606        query.act_backward_delete_char();
607        assert_eq!(query.get_fz_query(), "A");
608
609        query.act_backward_delete_char();
610        assert_eq!(query.get_fz_query(), "");
611
612        query.act_backward_delete_char();
613        assert_eq!(query.get_fz_query(), "");
614    }
615}