aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_analysis/src/completion/completion_item.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_analysis/src/completion/completion_item.rs')
-rw-r--r--crates/ra_analysis/src/completion/completion_item.rs80
1 files changed, 76 insertions, 4 deletions
diff --git a/crates/ra_analysis/src/completion/completion_item.rs b/crates/ra_analysis/src/completion/completion_item.rs
index 8aa9da005..d5d751759 100644
--- a/crates/ra_analysis/src/completion/completion_item.rs
+++ b/crates/ra_analysis/src/completion/completion_item.rs
@@ -6,6 +6,8 @@ pub struct CompletionItem {
6 label: String, 6 label: String,
7 lookup: Option<String>, 7 lookup: Option<String>,
8 snippet: Option<String>, 8 snippet: Option<String>,
9 /// Used only internally in test, to check only specific kind of completion.
10 kind: CompletionKind,
9} 11}
10 12
11pub enum InsertText { 13pub enum InsertText {
@@ -13,6 +15,18 @@ pub enum InsertText {
13 Snippet { text: String }, 15 Snippet { text: String },
14} 16}
15 17
18#[derive(Debug, PartialEq, Eq)]
19pub(crate) enum CompletionKind {
20 /// Parser-based keyword completion.
21 Keyword,
22 /// Your usual "complete all valid identifiers".
23 Reference,
24 /// "Secret sauce" completions.
25 Magic,
26 Snippet,
27 Unspecified,
28}
29
16impl CompletionItem { 30impl CompletionItem {
17 pub(crate) fn new(label: impl Into<String>) -> Builder { 31 pub(crate) fn new(label: impl Into<String>) -> Builder {
18 let label = label.into(); 32 let label = label.into();
@@ -20,6 +34,7 @@ impl CompletionItem {
20 label, 34 label,
21 lookup: None, 35 lookup: None,
22 snippet: None, 36 snippet: None,
37 kind: CompletionKind::Unspecified,
23 } 38 }
24 } 39 }
25 /// What user sees in pop-up in the UI. 40 /// What user sees in pop-up in the UI.
@@ -50,28 +65,34 @@ pub(crate) struct Builder {
50 label: String, 65 label: String,
51 lookup: Option<String>, 66 lookup: Option<String>,
52 snippet: Option<String>, 67 snippet: Option<String>,
68 kind: CompletionKind,
53} 69}
54 70
55impl Builder { 71impl Builder {
56 pub fn add_to(self, acc: &mut Completions) { 72 pub(crate) fn add_to(self, acc: &mut Completions) {
57 acc.add(self.build()) 73 acc.add(self.build())
58 } 74 }
59 75
60 pub fn build(self) -> CompletionItem { 76 pub(crate) fn build(self) -> CompletionItem {
61 CompletionItem { 77 CompletionItem {
62 label: self.label, 78 label: self.label,
63 lookup: self.lookup, 79 lookup: self.lookup,
64 snippet: self.snippet, 80 snippet: self.snippet,
81 kind: self.kind,
65 } 82 }
66 } 83 }
67 pub fn lookup_by(mut self, lookup: impl Into<String>) -> Builder { 84 pub(crate) fn lookup_by(mut self, lookup: impl Into<String>) -> Builder {
68 self.lookup = Some(lookup.into()); 85 self.lookup = Some(lookup.into());
69 self 86 self
70 } 87 }
71 pub fn snippet(mut self, snippet: impl Into<String>) -> Builder { 88 pub(crate) fn snippet(mut self, snippet: impl Into<String>) -> Builder {
72 self.snippet = Some(snippet.into()); 89 self.snippet = Some(snippet.into());
73 self 90 self
74 } 91 }
92 pub(crate) fn kind(mut self, kind: CompletionKind) -> Builder {
93 self.kind = kind;
94 self
95 }
75} 96}
76 97
77impl Into<CompletionItem> for Builder { 98impl Into<CompletionItem> for Builder {
@@ -97,6 +118,57 @@ impl Completions {
97 { 118 {
98 items.into_iter().for_each(|item| self.add(item.into())) 119 items.into_iter().for_each(|item| self.add(item.into()))
99 } 120 }
121
122 #[cfg(test)]
123 pub(crate) fn assert_match(&self, expected: &str, kind: CompletionKind) {
124 let expected = normalize(expected);
125 let actual = self.debug_render(kind);
126 test_utils::assert_eq_text!(expected.as_str(), actual.as_str(),);
127
128 /// Normalize the textual representation of `Completions`:
129 /// replace `;` with newlines, normalize whitespace
130 fn normalize(expected: &str) -> String {
131 use ra_syntax::{tokenize, TextUnit, TextRange, SyntaxKind::SEMI};
132 let mut res = String::new();
133 for line in expected.trim().lines() {
134 let line = line.trim();
135 let mut start_offset: TextUnit = 0.into();
136 // Yep, we use rust tokenize in completion tests :-)
137 for token in tokenize(line) {
138 let range = TextRange::offset_len(start_offset, token.len);
139 start_offset += token.len;
140 if token.kind == SEMI {
141 res.push('\n');
142 } else {
143 res.push_str(&line[range]);
144 }
145 }
146
147 res.push('\n');
148 }
149 res
150 }
151 }
152
153 #[cfg(test)]
154 fn debug_render(&self, kind: CompletionKind) -> String {
155 let mut res = String::new();
156 for c in self.buf.iter() {
157 if c.kind == kind {
158 if let Some(lookup) = &c.lookup {
159 res.push_str(lookup);
160 res.push_str(&format!(" {:?}", c.label));
161 } else {
162 res.push_str(&c.label);
163 }
164 if let Some(snippet) = &c.snippet {
165 res.push_str(&format!(" {:?}", snippet));
166 }
167 res.push('\n');
168 }
169 }
170 res
171 }
100} 172}
101 173
102impl Into<Vec<CompletionItem>> for Completions { 174impl Into<Vec<CompletionItem>> for Completions {