forked from SpaceManiac/SpacemanDMM
-
Notifications
You must be signed in to change notification settings - Fork 0
/
symbol_search.rs
108 lines (97 loc) · 3.17 KB
/
symbol_search.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
//! Query parser and evaluator for workspace symbol search.
/// A parsed symbol query.
#[derive(Debug)]
pub enum Query {
Anything(String),
Define(String),
Type(String),
Var(String),
Proc(String),
}
impl Query {
/// Parse a symbol query.
pub fn parse(query: &str) -> Option<Query> {
if !any_alphanumeric(query) {
return None;
}
Some(if query.starts_with("#") {
Query::Define(query[1..].to_lowercase())
} else if query.starts_with("var/") {
let query = &query["var/".len()..];
if !any_alphanumeric(query) {
return None;
}
Query::Var(query.to_lowercase())
} else if query.starts_with("proc/") {
let query = &query["proc/".len()..];
if !any_alphanumeric(query) {
return None;
}
Query::Proc(query.to_lowercase())
} else if query.contains("/") {
Query::Type(query.to_lowercase())
} else {
Query::Anything(query.to_lowercase())
})
}
/// Check whether this query matches the define with the given name.
pub fn matches_define(&self, name: &str) -> bool {
match *self {
Query::Anything(ref q) => starts_with(name, q),
Query::Define(ref q) => starts_with(name, q),
_ => false,
}
}
pub fn matches_type(&self, name: &str, path: &str) -> bool {
match *self {
Query::Anything(ref q) => starts_with(name, q),
Query::Type(ref q) => path.to_lowercase().contains(q),
_ => false,
}
}
pub fn matches_on_type(&self, _path: &str) -> bool {
match *self {
Query::Anything(_) |
Query::Proc(_) |
Query::Var(_) => true,
_ => false,
}
}
pub fn matches_var(&self, name: &str) -> bool {
match *self {
Query::Anything(ref q) |
Query::Var(ref q) => starts_with(name, q),
_ => false,
}
}
pub fn matches_proc(&self, name: &str, _kind: ::dm::ast::ProcDeclKind) -> bool {
match *self {
Query::Anything(ref q) |
Query::Proc(ref q) => starts_with(name, q),
_ => false,
}
}
}
fn simplify<'a>(s: &'a str) -> impl Iterator<Item=char> + Clone + 'a {
s.chars().flat_map(|c| c.to_lowercase()).filter(|c| c.is_alphanumeric())
}
// ignore case and underscores
pub fn starts_with<'a>(fulltext: &'a str, query: &'a str) -> bool {
let mut query_chars = simplify(query);
simplify(fulltext).zip(&mut query_chars).all(|(a, b)| a == b) && query_chars.next().is_none()
}
pub fn contains<'a>(fulltext: &'a str, query: &'a str) -> bool {
let (mut fulltext, query) = (simplify(fulltext), simplify(query));
loop {
let mut clone = query.clone();
if fulltext.clone().zip(&mut clone).all(|(a, b)| a == b) && clone.next().is_none() {
return true;
}
if fulltext.next().is_none() {
return false;
}
}
}
fn any_alphanumeric(text: &str) -> bool {
text.chars().flat_map(|c| c.to_lowercase()).any(|c| c.is_alphanumeric())
}