aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide/src/typing/on_enter.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide/src/typing/on_enter.rs')
-rw-r--r--crates/ra_ide/src/typing/on_enter.rs171
1 files changed, 171 insertions, 0 deletions
diff --git a/crates/ra_ide/src/typing/on_enter.rs b/crates/ra_ide/src/typing/on_enter.rs
new file mode 100644
index 000000000..359794f67
--- /dev/null
+++ b/crates/ra_ide/src/typing/on_enter.rs
@@ -0,0 +1,171 @@
1//! Handles the `Enter` key press. At the momently, this only continues
2//! comments, but should handle indent some time in the future as well.
3
4use ra_db::{FilePosition, SourceDatabase};
5use ra_ide_db::RootDatabase;
6use ra_syntax::{
7 ast::{self, AstToken},
8 AstNode, SmolStr, SourceFile,
9 SyntaxKind::*,
10 SyntaxToken, TextUnit, TokenAtOffset,
11};
12use ra_text_edit::TextEdit;
13
14use crate::{SourceChange, SourceFileEdit};
15
16pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<SourceChange> {
17 let parse = db.parse(position.file_id);
18 let file = parse.tree();
19 let comment = file
20 .syntax()
21 .token_at_offset(position.offset)
22 .left_biased()
23 .and_then(ast::Comment::cast)?;
24
25 if comment.kind().shape.is_block() {
26 return None;
27 }
28
29 let prefix = comment.prefix();
30 let comment_range = comment.syntax().text_range();
31 if position.offset < comment_range.start() + TextUnit::of_str(prefix) {
32 return None;
33 }
34
35 // Continuing non-doc line comments (like this one :) ) is annoying
36 if prefix == "//" && comment_range.end() == position.offset {
37 return None;
38 }
39
40 let indent = node_indent(&file, comment.syntax())?;
41 let inserted = format!("\n{}{} ", indent, prefix);
42 let cursor_position = position.offset + TextUnit::of_str(&inserted);
43 let edit = TextEdit::insert(position.offset, inserted);
44
45 Some(
46 SourceChange::source_file_edit(
47 "on enter",
48 SourceFileEdit { edit, file_id: position.file_id },
49 )
50 .with_cursor(FilePosition { offset: cursor_position, file_id: position.file_id }),
51 )
52}
53
54fn node_indent(file: &SourceFile, token: &SyntaxToken) -> Option<SmolStr> {
55 let ws = match file.syntax().token_at_offset(token.text_range().start()) {
56 TokenAtOffset::Between(l, r) => {
57 assert!(r == *token);
58 l
59 }
60 TokenAtOffset::Single(n) => {
61 assert!(n == *token);
62 return Some("".into());
63 }
64 TokenAtOffset::None => unreachable!(),
65 };
66 if ws.kind() != WHITESPACE {
67 return None;
68 }
69 let text = ws.text();
70 let pos = text.rfind('\n').map(|it| it + 1).unwrap_or(0);
71 Some(text[pos..].into())
72}
73
74#[cfg(test)]
75mod tests {
76 use test_utils::{add_cursor, assert_eq_text, extract_offset};
77
78 use crate::mock_analysis::single_file;
79
80 use super::*;
81
82 fn apply_on_enter(before: &str) -> Option<String> {
83 let (offset, before) = extract_offset(before);
84 let (analysis, file_id) = single_file(&before);
85 let result = analysis.on_enter(FilePosition { offset, file_id }).unwrap()?;
86
87 assert_eq!(result.source_file_edits.len(), 1);
88 let actual = result.source_file_edits[0].edit.apply(&before);
89 let actual = add_cursor(&actual, result.cursor_position.unwrap().offset);
90 Some(actual)
91 }
92
93 fn do_check(ra_fixture_before: &str, ra_fixture_after: &str) {
94 let actual = apply_on_enter(ra_fixture_before).unwrap();
95 assert_eq_text!(ra_fixture_after, &actual);
96 }
97
98 fn do_check_noop(ra_fixture_text: &str) {
99 assert!(apply_on_enter(ra_fixture_text).is_none())
100 }
101
102 #[test]
103 fn test_on_enter() {
104 do_check(
105 r"
106/// Some docs<|>
107fn foo() {
108}
109",
110 r"
111/// Some docs
112/// <|>
113fn foo() {
114}
115",
116 );
117 do_check(
118 r"
119impl S {
120 /// Some<|> docs.
121 fn foo() {}
122}
123",
124 r"
125impl S {
126 /// Some
127 /// <|> docs.
128 fn foo() {}
129}
130",
131 );
132 do_check(
133 r"
134fn main() {
135 // Fix<|> me
136 let x = 1 + 1;
137}
138",
139 r"
140fn main() {
141 // Fix
142 // <|> me
143 let x = 1 + 1;
144}
145",
146 );
147 do_check(
148 r"
149///<|> Some docs
150fn foo() {
151}
152",
153 r"
154///
155/// <|> Some docs
156fn foo() {
157}
158",
159 );
160 do_check_noop(
161 r"
162fn main() {
163 // Fix me<|>
164 let x = 1 + 1;
165}
166",
167 );
168
169 do_check_noop(r"<|>//! docz");
170 }
171}