ClickHouse

Форк
0
108 строк · 3.7 Кб
1
use cxx::{CxxString, CxxVector};
2
use skim::prelude::*;
3
use std::panic;
4
use term::terminfo::TermInfo;
5

6
#[cxx::bridge]
7
mod ffi {
8
    extern "Rust" {
9
        fn skim(prefix: &CxxString, words: &CxxVector<CxxString>) -> Result<String>;
10
    }
11
}
12

13
struct Item {
14
    text_no_newlines: String,
15
    orig_text: String,
16
}
17
impl Item {
18
    fn new(text: String) -> Self {
19
        Self {
20
            // Text that will be printed by skim, and will be used for matching.
21
            //
22
            // Text that will be shown should not contains new lines since in this case skim may
23
            // live some symbols on the screen, and this looks odd.
24
            text_no_newlines: text.replace("\n", " "),
25
            // This will be used when the match had been selected.
26
            orig_text: text,
27
        }
28
    }
29
}
30
impl SkimItem for Item {
31
    fn text(&self) -> Cow<str> {
32
        Cow::Borrowed(&self.text_no_newlines)
33
    }
34

35
    fn output(&self) -> Cow<str> {
36
        Cow::Borrowed(&self.orig_text)
37
    }
38
}
39

40
fn skim_impl(prefix: &CxxString, words: &CxxVector<CxxString>) -> Result<String, String> {
41
    // Let's check is terminal available. To avoid panic.
42
    if let Err(err) = TermInfo::from_env() {
43
        return Err(format!("{}", err));
44
    }
45

46
    let options = SkimOptionsBuilder::default()
47
        .height(Some("30%"))
48
        .query(Some(prefix.to_str().unwrap()))
49
        .tac(true)
50
        .tiebreak(Some("-score".to_string()))
51
        // Exact mode performs better for SQL.
52
        //
53
        // Default fuzzy search is too smart for SQL, it even takes into account the case, which
54
        // should not be accounted (you don't want to type "SELECT" instead of "select" to find the
55
        // query).
56
        //
57
        // Exact matching seems better algorithm for SQL, it is not 100% exact, it splits by space,
58
        // and apply separate matcher actually for each word.
59
        // Note, that if you think that "space is not enough" as the delimiter, then you should
60
        // first know that this is the delimiter only for the input query, so to match
61
        // "system.query_log" you can use "sy qu log"
62
        // Also it should be more common for users who did not know how to use fuzzy search.
63
        // (also you can disable exact mode by prepending "'" char).
64
        //
65
        // Also it ignores the case correctly, i.e. it does not have penalty for case mismatch,
66
        // like fuzzy algorithms (take a look at SkimScoreConfig::penalty_case_mismatch).
67
        .exact(true)
68
        .case(CaseMatching::Ignore)
69
        .build()
70
        .unwrap();
71

72
    let (tx, rx): (SkimItemSender, SkimItemReceiver) = unbounded();
73
    for word in words {
74
        tx.send(Arc::new(Item::new(word.to_string()))).unwrap();
75
    }
76
    // so that skim could know when to stop waiting for more items.
77
    drop(tx);
78

79
    let output = Skim::run_with(&options, Some(rx));
80
    if output.is_none() {
81
        return Err("skim return nothing".to_string());
82
    }
83
    let output = output.unwrap();
84
    if output.is_abort {
85
        return Ok("".to_string());
86
    }
87

88
    if output.selected_items.is_empty() {
89
        return Err("No items had been selected".to_string());
90
    }
91
    Ok(output.selected_items[0].output().to_string())
92
}
93

94
fn skim(prefix: &CxxString, words: &CxxVector<CxxString>) -> Result<String, String> {
95
    match panic::catch_unwind(|| skim_impl(prefix, words)) {
96
        Err(err) => {
97
            let e = if let Some(s) = err.downcast_ref::<String>() {
98
                format!("{}", s)
99
            } else if let Some(s) = err.downcast_ref::<&str>() {
100
                format!("{}", s)
101
            } else {
102
                format!("Unknown panic type: {:?}", err.type_id())
103
            };
104
            Err(format!("Rust panic: {:?}", e))
105
        }
106
        Ok(res) => res,
107
    }
108
}
109

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.