|
13 | 13 | //! Language server executes such typing assists synchronously. That is, they |
14 | 14 | //! block user's typing and should be pretty fast for this reason! |
15 | 15 |
|
| 16 | +mod on_enter; |
| 17 | + |
16 | 18 | use ra_db::{FilePosition, SourceDatabase}; |
17 | 19 | use ra_fmt::leading_indent; |
18 | 20 | use ra_ide_db::RootDatabase; |
19 | 21 | use ra_syntax::{ |
20 | 22 | algo::find_node_at_offset, |
21 | 23 | ast::{self, AstToken}, |
22 | | - AstNode, SmolStr, SourceFile, |
23 | | - SyntaxKind::*, |
24 | | - SyntaxToken, TextRange, TextUnit, TokenAtOffset, |
| 24 | + AstNode, SourceFile, TextRange, TextUnit, |
25 | 25 | }; |
26 | 26 | use ra_text_edit::TextEdit; |
27 | 27 |
|
28 | | -use crate::{source_change::SingleFileChange, SourceChange, SourceFileEdit}; |
29 | | - |
30 | | -pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<SourceChange> { |
31 | | - let parse = db.parse(position.file_id); |
32 | | - let file = parse.tree(); |
33 | | - let comment = file |
34 | | - .syntax() |
35 | | - .token_at_offset(position.offset) |
36 | | - .left_biased() |
37 | | - .and_then(ast::Comment::cast)?; |
38 | | - |
39 | | - if comment.kind().shape.is_block() { |
40 | | - return None; |
41 | | - } |
42 | | - |
43 | | - let prefix = comment.prefix(); |
44 | | - let comment_range = comment.syntax().text_range(); |
45 | | - if position.offset < comment_range.start() + TextUnit::of_str(prefix) { |
46 | | - return None; |
47 | | - } |
48 | | - |
49 | | - // Continuing non-doc line comments (like this one :) ) is annoying |
50 | | - if prefix == "//" && comment_range.end() == position.offset { |
51 | | - return None; |
52 | | - } |
53 | | - |
54 | | - let indent = node_indent(&file, comment.syntax())?; |
55 | | - let inserted = format!("\n{}{} ", indent, prefix); |
56 | | - let cursor_position = position.offset + TextUnit::of_str(&inserted); |
57 | | - let edit = TextEdit::insert(position.offset, inserted); |
| 28 | +use crate::{source_change::SingleFileChange, SourceChange}; |
58 | 29 |
|
59 | | - Some( |
60 | | - SourceChange::source_file_edit( |
61 | | - "on enter", |
62 | | - SourceFileEdit { edit, file_id: position.file_id }, |
63 | | - ) |
64 | | - .with_cursor(FilePosition { offset: cursor_position, file_id: position.file_id }), |
65 | | - ) |
66 | | -} |
67 | | - |
68 | | -fn node_indent(file: &SourceFile, token: &SyntaxToken) -> Option<SmolStr> { |
69 | | - let ws = match file.syntax().token_at_offset(token.text_range().start()) { |
70 | | - TokenAtOffset::Between(l, r) => { |
71 | | - assert!(r == *token); |
72 | | - l |
73 | | - } |
74 | | - TokenAtOffset::Single(n) => { |
75 | | - assert!(n == *token); |
76 | | - return Some("".into()); |
77 | | - } |
78 | | - TokenAtOffset::None => unreachable!(), |
79 | | - }; |
80 | | - if ws.kind() != WHITESPACE { |
81 | | - return None; |
82 | | - } |
83 | | - let text = ws.text(); |
84 | | - let pos = text.rfind('\n').map(|it| it + 1).unwrap_or(0); |
85 | | - Some(text[pos..].into()) |
86 | | -} |
| 30 | +pub(crate) use on_enter::on_enter; |
87 | 31 |
|
88 | 32 | pub(crate) const TRIGGER_CHARS: &str = ".=>"; |
89 | 33 |
|
@@ -196,102 +140,10 @@ fn on_arrow_typed(file: &SourceFile, offset: TextUnit) -> Option<SingleFileChang |
196 | 140 |
|
197 | 141 | #[cfg(test)] |
198 | 142 | mod tests { |
199 | | - use test_utils::{add_cursor, assert_eq_text, extract_offset}; |
200 | | - |
201 | | - use crate::mock_analysis::single_file; |
| 143 | + use test_utils::{assert_eq_text, extract_offset}; |
202 | 144 |
|
203 | 145 | use super::*; |
204 | 146 |
|
205 | | - #[test] |
206 | | - fn test_on_enter() { |
207 | | - fn apply_on_enter(before: &str) -> Option<String> { |
208 | | - let (offset, before) = extract_offset(before); |
209 | | - let (analysis, file_id) = single_file(&before); |
210 | | - let result = analysis.on_enter(FilePosition { offset, file_id }).unwrap()?; |
211 | | - |
212 | | - assert_eq!(result.source_file_edits.len(), 1); |
213 | | - let actual = result.source_file_edits[0].edit.apply(&before); |
214 | | - let actual = add_cursor(&actual, result.cursor_position.unwrap().offset); |
215 | | - Some(actual) |
216 | | - } |
217 | | - |
218 | | - fn do_check(before: &str, after: &str) { |
219 | | - let actual = apply_on_enter(before).unwrap(); |
220 | | - assert_eq_text!(after, &actual); |
221 | | - } |
222 | | - |
223 | | - fn do_check_noop(text: &str) { |
224 | | - assert!(apply_on_enter(text).is_none()) |
225 | | - } |
226 | | - |
227 | | - do_check( |
228 | | - r" |
229 | | -/// Some docs<|> |
230 | | -fn foo() { |
231 | | -} |
232 | | -", |
233 | | - r" |
234 | | -/// Some docs |
235 | | -/// <|> |
236 | | -fn foo() { |
237 | | -} |
238 | | -", |
239 | | - ); |
240 | | - do_check( |
241 | | - r" |
242 | | -impl S { |
243 | | - /// Some<|> docs. |
244 | | - fn foo() {} |
245 | | -} |
246 | | -", |
247 | | - r" |
248 | | -impl S { |
249 | | - /// Some |
250 | | - /// <|> docs. |
251 | | - fn foo() {} |
252 | | -} |
253 | | -", |
254 | | - ); |
255 | | - do_check( |
256 | | - r" |
257 | | -fn main() { |
258 | | - // Fix<|> me |
259 | | - let x = 1 + 1; |
260 | | -} |
261 | | -", |
262 | | - r" |
263 | | -fn main() { |
264 | | - // Fix |
265 | | - // <|> me |
266 | | - let x = 1 + 1; |
267 | | -} |
268 | | -", |
269 | | - ); |
270 | | - do_check( |
271 | | - r" |
272 | | -///<|> Some docs |
273 | | -fn foo() { |
274 | | -} |
275 | | -", |
276 | | - r" |
277 | | -/// |
278 | | -/// <|> Some docs |
279 | | -fn foo() { |
280 | | -} |
281 | | -", |
282 | | - ); |
283 | | - do_check_noop( |
284 | | - r" |
285 | | -fn main() { |
286 | | - // Fix me<|> |
287 | | - let x = 1 + 1; |
288 | | -} |
289 | | -", |
290 | | - ); |
291 | | - |
292 | | - do_check_noop(r"<|>//! docz"); |
293 | | - } |
294 | | - |
295 | 147 | fn do_type_char(char_typed: char, before: &str) -> Option<(String, SingleFileChange)> { |
296 | 148 | let (offset, before) = extract_offset(before); |
297 | 149 | let edit = TextEdit::insert(offset, char_typed.to_string()); |
|
0 commit comments