diff options
Diffstat (limited to 'crates/ra_ide_api')
-rw-r--r-- | crates/ra_ide_api/src/lib.rs | 4 | ||||
-rw-r--r-- | crates/ra_ide_api/src/typing.rs | 72 |
2 files changed, 41 insertions, 35 deletions
diff --git a/crates/ra_ide_api/src/lib.rs b/crates/ra_ide_api/src/lib.rs index 6b8aa7a8e..d0188da44 100644 --- a/crates/ra_ide_api/src/lib.rs +++ b/crates/ra_ide_api/src/lib.rs | |||
@@ -323,6 +323,10 @@ impl Analysis { | |||
323 | position: FilePosition, | 323 | position: FilePosition, |
324 | char_typed: char, | 324 | char_typed: char, |
325 | ) -> Cancelable<Option<SourceChange>> { | 325 | ) -> Cancelable<Option<SourceChange>> { |
326 | // Fast path to not even parse the file. | ||
327 | if !typing::TRIGGER_CHARS.contains(char_typed) { | ||
328 | return Ok(None); | ||
329 | } | ||
326 | self.with_db(|db| typing::on_char_typed(&db, position, char_typed)) | 330 | self.with_db(|db| typing::on_char_typed(&db, position, char_typed)) |
327 | } | 331 | } |
328 | 332 | ||
diff --git a/crates/ra_ide_api/src/typing.rs b/crates/ra_ide_api/src/typing.rs index c5ec6c1c1..17d0f08a5 100644 --- a/crates/ra_ide_api/src/typing.rs +++ b/crates/ra_ide_api/src/typing.rs | |||
@@ -81,22 +81,32 @@ fn node_indent(file: &SourceFile, token: &SyntaxToken) -> Option<SmolStr> { | |||
81 | Some(text[pos..].into()) | 81 | Some(text[pos..].into()) |
82 | } | 82 | } |
83 | 83 | ||
84 | pub(crate) const TRIGGER_CHARS: &str = ".="; | ||
85 | |||
84 | pub(crate) fn on_char_typed( | 86 | pub(crate) fn on_char_typed( |
85 | db: &RootDatabase, | 87 | db: &RootDatabase, |
86 | position: FilePosition, | 88 | position: FilePosition, |
87 | char_typed: char, | 89 | char_typed: char, |
88 | ) -> Option<SourceChange> { | 90 | ) -> Option<SourceChange> { |
91 | assert!(TRIGGER_CHARS.contains(char_typed)); | ||
89 | let file = &db.parse(position.file_id).tree(); | 92 | let file = &db.parse(position.file_id).tree(); |
90 | assert_eq!(file.syntax().text().char_at(position.offset), Some(char_typed)); | 93 | assert_eq!(file.syntax().text().char_at(position.offset), Some(char_typed)); |
91 | let single_file_change = match char_typed { | 94 | let single_file_change = on_char_typed_inner(file, position.offset, char_typed)?; |
92 | '=' => on_eq_typed(file, position.offset)?, | ||
93 | '.' => on_dot_typed(file, position.offset)?, | ||
94 | _ => return None, | ||
95 | }; | ||
96 | |||
97 | Some(single_file_change.into_source_change(position.file_id)) | 95 | Some(single_file_change.into_source_change(position.file_id)) |
98 | } | 96 | } |
99 | 97 | ||
98 | fn on_char_typed_inner( | ||
99 | file: &SourceFile, | ||
100 | offset: TextUnit, | ||
101 | char_typed: char, | ||
102 | ) -> Option<SingleFileChange> { | ||
103 | match char_typed { | ||
104 | '.' => on_dot_typed(file, offset), | ||
105 | '=' => on_eq_typed(file, offset), | ||
106 | _ => None, | ||
107 | } | ||
108 | } | ||
109 | |||
100 | /// Returns an edit which should be applied after `=` was typed. Primarily, | 110 | /// Returns an edit which should be applied after `=` was typed. Primarily, |
101 | /// this works when adding `let =`. | 111 | /// this works when adding `let =`. |
102 | // FIXME: use a snippet completion instead of this hack here. | 112 | // FIXME: use a snippet completion instead of this hack here. |
@@ -167,22 +177,29 @@ mod tests { | |||
167 | 177 | ||
168 | use super::*; | 178 | use super::*; |
169 | 179 | ||
180 | fn type_char(char_typed: char, before: &str, after: &str) { | ||
181 | let (offset, before) = extract_offset(before); | ||
182 | let edit = TextEdit::insert(offset, char_typed.to_string()); | ||
183 | let before = edit.apply(&before); | ||
184 | let parse = SourceFile::parse(&before); | ||
185 | if let Some(result) = on_char_typed_inner(&parse.tree(), offset, char_typed) { | ||
186 | let actual = result.edit.apply(&before); | ||
187 | assert_eq_text!(after, &actual); | ||
188 | } else { | ||
189 | assert_eq_text!(&before, after) | ||
190 | }; | ||
191 | } | ||
192 | |||
193 | fn type_eq(before: &str, after: &str) { | ||
194 | type_char('=', before, after); | ||
195 | } | ||
196 | |||
197 | fn type_dot(before: &str, after: &str) { | ||
198 | type_char('.', before, after); | ||
199 | } | ||
200 | |||
170 | #[test] | 201 | #[test] |
171 | fn test_on_eq_typed() { | 202 | fn test_on_eq_typed() { |
172 | fn type_eq(before: &str, after: &str) { | ||
173 | let (offset, before) = extract_offset(before); | ||
174 | let mut edit = TextEditBuilder::default(); | ||
175 | edit.insert(offset, "=".to_string()); | ||
176 | let before = edit.finish().apply(&before); | ||
177 | let parse = SourceFile::parse(&before); | ||
178 | if let Some(result) = on_eq_typed(&parse.tree(), offset) { | ||
179 | let actual = result.edit.apply(&before); | ||
180 | assert_eq_text!(after, &actual); | ||
181 | } else { | ||
182 | assert_eq_text!(&before, after) | ||
183 | }; | ||
184 | } | ||
185 | |||
186 | // do_check(r" | 203 | // do_check(r" |
187 | // fn foo() { | 204 | // fn foo() { |
188 | // let foo =<|> | 205 | // let foo =<|> |
@@ -217,21 +234,6 @@ fn foo() { | |||
217 | // "); | 234 | // "); |
218 | } | 235 | } |
219 | 236 | ||
220 | fn type_dot(before: &str, after: &str) { | ||
221 | let (offset, before) = extract_offset(before); | ||
222 | let mut edit = TextEditBuilder::default(); | ||
223 | edit.insert(offset, ".".to_string()); | ||
224 | let before = edit.finish().apply(&before); | ||
225 | let (analysis, file_id) = single_file(&before); | ||
226 | let file = analysis.parse(file_id).unwrap(); | ||
227 | if let Some(result) = on_dot_typed(&file, offset) { | ||
228 | let actual = result.edit.apply(&before); | ||
229 | assert_eq_text!(after, &actual); | ||
230 | } else { | ||
231 | assert_eq_text!(&before, after) | ||
232 | }; | ||
233 | } | ||
234 | |||
235 | #[test] | 237 | #[test] |
236 | fn indents_new_chain_call() { | 238 | fn indents_new_chain_call() { |
237 | type_dot( | 239 | type_dot( |