|
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