diff options
-rw-r--r-- | crates/ra_ide/src/typing.rs | 1 | ||||
-rw-r--r-- | crates/ra_ide/src/typing/on_enter.rs | 67 |
2 files changed, 59 insertions, 9 deletions
diff --git a/crates/ra_ide/src/typing.rs b/crates/ra_ide/src/typing.rs index 83776d2b6..d3ce744b4 100644 --- a/crates/ra_ide/src/typing.rs +++ b/crates/ra_ide/src/typing.rs | |||
@@ -39,7 +39,6 @@ pub(crate) const TRIGGER_CHARS: &str = ".=>"; | |||
39 | // Some features trigger on typing certain characters: | 39 | // Some features trigger on typing certain characters: |
40 | // | 40 | // |
41 | // - typing `let =` tries to smartly add `;` if `=` is followed by an existing expression | 41 | // - typing `let =` tries to smartly add `;` if `=` is followed by an existing expression |
42 | // - Enter inside comments automatically inserts `///` | ||
43 | // - typing `.` in a chain method call auto-indents | 42 | // - typing `.` in a chain method call auto-indents |
44 | pub(crate) fn on_char_typed( | 43 | pub(crate) fn on_char_typed( |
45 | db: &RootDatabase, | 44 | db: &RootDatabase, |
diff --git a/crates/ra_ide/src/typing/on_enter.rs b/crates/ra_ide/src/typing/on_enter.rs index 2faaa8ff0..143b1ae41 100644 --- a/crates/ra_ide/src/typing/on_enter.rs +++ b/crates/ra_ide/src/typing/on_enter.rs | |||
@@ -7,10 +7,31 @@ use ra_syntax::{ | |||
7 | ast::{self, AstToken}, | 7 | ast::{self, AstToken}, |
8 | AstNode, SmolStr, SourceFile, | 8 | AstNode, SmolStr, SourceFile, |
9 | SyntaxKind::*, | 9 | SyntaxKind::*, |
10 | SyntaxToken, TextSize, TokenAtOffset, | 10 | SyntaxToken, TextRange, TextSize, TokenAtOffset, |
11 | }; | 11 | }; |
12 | use ra_text_edit::TextEdit; | 12 | use ra_text_edit::TextEdit; |
13 | use test_utils::mark; | ||
13 | 14 | ||
15 | // Feature: On Enter | ||
16 | // | ||
17 | // rust-analyzer can override kbd:[Enter] key to make it smarter: | ||
18 | // | ||
19 | // - kbd:[Enter] inside triple-slash comments automatically inserts `///` | ||
20 | // - kbd:[Enter] in the middle or after a trailing space in `//` inserts `//` | ||
21 | // | ||
22 | // This action needs to be assigned to shortcut explicitly. | ||
23 | // | ||
24 | // VS Code:: | ||
25 | // | ||
26 | // Add the following to `keybindings.json`: | ||
27 | // [source,json] | ||
28 | // ---- | ||
29 | // { | ||
30 | // "key": "Enter", | ||
31 | // "command": "rust-analyzer.onEnter", | ||
32 | // "when": "editorTextFocus && !suggestWidgetVisible && editorLangId == rust" | ||
33 | // } | ||
34 | // ---- | ||
14 | pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<TextEdit> { | 35 | pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<TextEdit> { |
15 | let parse = db.parse(position.file_id); | 36 | let parse = db.parse(position.file_id); |
16 | let file = parse.tree(); | 37 | let file = parse.tree(); |
@@ -30,15 +51,25 @@ pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<Text | |||
30 | return None; | 51 | return None; |
31 | } | 52 | } |
32 | 53 | ||
54 | let mut remove_last_space = false; | ||
33 | // Continuing single-line non-doc comments (like this one :) ) is annoying | 55 | // Continuing single-line non-doc comments (like this one :) ) is annoying |
34 | if prefix == "//" && comment_range.end() == position.offset && !followed_by_comment(&comment) { | 56 | if prefix == "//" && comment_range.end() == position.offset { |
35 | return None; | 57 | if comment.text().ends_with(' ') { |
58 | mark::hit!(continues_end_of_line_comment_with_space); | ||
59 | remove_last_space = true; | ||
60 | } else if !followed_by_comment(&comment) { | ||
61 | return None; | ||
62 | } | ||
36 | } | 63 | } |
37 | 64 | ||
38 | let indent = node_indent(&file, comment.syntax())?; | 65 | let indent = node_indent(&file, comment.syntax())?; |
39 | let inserted = format!("\n{}{} $0", indent, prefix); | 66 | let inserted = format!("\n{}{} $0", indent, prefix); |
40 | let edit = TextEdit::insert(position.offset, inserted); | 67 | let delete = if remove_last_space { |
41 | 68 | TextRange::new(position.offset - TextSize::of(' '), position.offset) | |
69 | } else { | ||
70 | TextRange::empty(position.offset) | ||
71 | }; | ||
72 | let edit = TextEdit::replace(delete, inserted); | ||
42 | Some(edit) | 73 | Some(edit) |
43 | } | 74 | } |
44 | 75 | ||
@@ -75,10 +106,10 @@ fn node_indent(file: &SourceFile, token: &SyntaxToken) -> Option<SmolStr> { | |||
75 | 106 | ||
76 | #[cfg(test)] | 107 | #[cfg(test)] |
77 | mod tests { | 108 | mod tests { |
78 | use test_utils::assert_eq_text; | 109 | use stdx::trim_indent; |
110 | use test_utils::{assert_eq_text, mark}; | ||
79 | 111 | ||
80 | use crate::mock_analysis::analysis_and_position; | 112 | use crate::mock_analysis::analysis_and_position; |
81 | use stdx::trim_indent; | ||
82 | 113 | ||
83 | fn apply_on_enter(before: &str) -> Option<String> { | 114 | fn apply_on_enter(before: &str) -> Option<String> { |
84 | let (analysis, position) = analysis_and_position(&before); | 115 | let (analysis, position) = analysis_and_position(&before); |
@@ -192,7 +223,7 @@ fn main() { | |||
192 | } | 223 | } |
193 | 224 | ||
194 | #[test] | 225 | #[test] |
195 | fn does_not_continue_end_of_code_comment() { | 226 | fn does_not_continue_end_of_line_comment() { |
196 | do_check_noop( | 227 | do_check_noop( |
197 | r" | 228 | r" |
198 | fn main() { | 229 | fn main() { |
@@ -202,4 +233,24 @@ fn main() { | |||
202 | ", | 233 | ", |
203 | ); | 234 | ); |
204 | } | 235 | } |
236 | |||
237 | #[test] | ||
238 | fn continues_end_of_line_comment_with_space() { | ||
239 | mark::check!(continues_end_of_line_comment_with_space); | ||
240 | do_check( | ||
241 | r#" | ||
242 | fn main() { | ||
243 | // Fix me <|> | ||
244 | let x = 1 + 1; | ||
245 | } | ||
246 | "#, | ||
247 | r#" | ||
248 | fn main() { | ||
249 | // Fix me | ||
250 | // $0 | ||
251 | let x = 1 + 1; | ||
252 | } | ||
253 | "#, | ||
254 | ); | ||
255 | } | ||
205 | } | 256 | } |